lib/jsonapi/link_builder.rb in jsonapi-resources-0.10.0.beta3 vs lib/jsonapi/link_builder.rb in jsonapi-resources-0.10.0.beta4
- old
+ new
@@ -1,62 +1,82 @@
module JSONAPI
class LinkBuilder
attr_reader :base_url,
:primary_resource_klass,
- :route_formatter,
- :engine_name
+ :engine,
+ :routes
def initialize(config = {})
@base_url = config[:base_url]
@primary_resource_klass = config[:primary_resource_klass]
- @route_formatter = config[:route_formatter]
- @engine_name = build_engine_name
+ @engine = build_engine
- # Warning: These make LinkBuilder non-thread-safe. That's not a problem with the
- # request-specific way it's currently used, though.
- @resources_path_cache = JSONAPI::NaiveCache.new do |source_klass|
- formatted_module_path_from_class(source_klass) + format_route(source_klass._type.to_s)
+ if engine?
+ @routes = @engine.routes
+ else
+ @routes = Rails.application.routes
end
+
+ # ToDo: Use NaiveCache for values. For this we need to not return nils and create composite keys which work
+ # as efficient cache lookups. This could be an array of the [source.identifier, relationship] since the
+ # ResourceIdentity will compare equality correctly
end
def engine?
- !!@engine_name
+ !!@engine
end
def primary_resources_url
- if engine?
- engine_primary_resources_url
- else
- regular_primary_resources_url
- end
+ @primary_resources_url_cached ||= "#{ base_url }#{ primary_resources_path }"
+ rescue NoMethodError
+ warn "primary_resources_url for #{@primary_resource_klass} could not be generated" if JSONAPI.configuration.warn_on_missing_routes
end
def query_link(query_params)
"#{ primary_resources_url }?#{ query_params.to_query }"
end
def relationships_related_link(source, relationship, query_params = {})
- url = "#{ self_link(source) }/#{ route_for_relationship(relationship) }"
+ if relationship.parent_resource.singleton?
+ url_helper_name = singleton_related_url_helper_name(relationship)
+ url = call_url_helper(url_helper_name)
+ else
+ url_helper_name = related_url_helper_name(relationship)
+ url = call_url_helper(url_helper_name, source.id)
+ end
+
+ url = "#{ base_url }#{ url }"
url = "#{ url }?#{ query_params.to_query }" if query_params.present?
url
+ rescue NoMethodError
+ warn "related_link for #{relationship} could not be generated" if JSONAPI.configuration.warn_on_missing_routes
end
def relationships_self_link(source, relationship)
- "#{ self_link(source) }/relationships/#{ route_for_relationship(relationship) }"
+ if relationship.parent_resource.singleton?
+ url_helper_name = singleton_relationship_self_url_helper_name(relationship)
+ url = call_url_helper(url_helper_name)
+ else
+ url_helper_name = relationship_self_url_helper_name(relationship)
+ url = call_url_helper(url_helper_name, source.id)
+ end
+
+ url = "#{ base_url }#{ url }"
+ url
+ rescue NoMethodError
+ warn "self_link for #{relationship} could not be generated" if JSONAPI.configuration.warn_on_missing_routes
end
def self_link(source)
- if engine?
- engine_resource_url(source)
- else
- regular_resource_url(source)
- end
+ "#{ base_url }#{ resource_path(source) }"
+ rescue NoMethodError
+ warn "self_link for #{source.class} could not be generated" if JSONAPI.configuration.warn_on_missing_routes
end
private
- def build_engine_name
+ def build_engine
scopes = module_scopes_from_class(primary_resource_klass)
begin
unless scopes.empty?
"#{ scopes.first.to_s.camelize }::Engine".safe_constantize
@@ -66,95 +86,98 @@
nil
# :nocov:
end
end
- def engine_path_from_resource_class(klass)
- path_name = engine_resources_path_name_from_class(klass)
- engine_name.routes.url_helpers.public_send(path_name)
+ def call_url_helper(method, *args)
+ routes.url_helpers.public_send(method, args)
+ rescue NoMethodError => e
+ raise e
end
- def engine_primary_resources_path
- engine_path_from_resource_class(primary_resource_klass)
+ def path_from_resource_class(klass)
+ url_helper_name = resources_url_helper_name_from_class(klass)
+ call_url_helper(url_helper_name)
end
- def engine_primary_resources_url
- "#{ base_url }#{ engine_primary_resources_path }"
+ def resource_path(source)
+ url_helper_name = resource_url_helper_name_from_source(source)
+ if source.class.singleton?
+ call_url_helper(url_helper_name)
+ else
+ call_url_helper(url_helper_name, source.id)
+ end
end
- def engine_resource_path(source)
- resource_path_name = engine_resource_path_name_from_source(source)
- engine_name.routes.url_helpers.public_send(resource_path_name, source.id)
+ def primary_resources_path
+ path_from_resource_class(primary_resource_klass)
end
- def engine_resource_path_name_from_source(source)
- scopes = module_scopes_from_class(source.class)[1..-1]
- base_path_name = scopes.map { |scope| scope.underscore }.join("_")
- end_path_name = source.class._type.to_s.singularize
- [base_path_name, end_path_name, "path"].reject(&:blank?).join("_")
+ def url_helper_name_from_parts(parts)
+ (parts << "path").reject(&:blank?).join("_")
end
- def engine_resource_url(source)
- "#{ base_url }#{ engine_resource_path(source) }"
- end
+ def resources_path_parts_from_class(klass)
+ if engine?
+ scopes = module_scopes_from_class(klass)[1..-1]
+ else
+ scopes = module_scopes_from_class(klass)
+ end
- def engine_resources_path_name_from_class(klass)
- scopes = module_scopes_from_class(klass)[1..-1]
base_path_name = scopes.map { |scope| scope.underscore }.join("_")
end_path_name = klass._type.to_s
-
- if base_path_name.blank?
- "#{ end_path_name }_path"
- else
- "#{ base_path_name }_#{ end_path_name }_path"
- end
+ [base_path_name, end_path_name]
end
- def format_route(route)
- route_formatter.format(route)
+ def resources_url_helper_name_from_class(klass)
+ url_helper_name_from_parts(resources_path_parts_from_class(klass))
end
- def formatted_module_path_from_class(klass)
- scopes = module_scopes_from_class(klass)
-
- unless scopes.empty?
- "/#{ scopes.map{ |scope| format_route(scope.to_s.underscore) }.compact.join('/') }/"
+ def resource_path_parts_from_class(klass)
+ if engine?
+ scopes = module_scopes_from_class(klass)[1..-1]
else
- "/"
+ scopes = module_scopes_from_class(klass)
end
- end
- def module_scopes_from_class(klass)
- klass.name.to_s.split("::")[0...-1]
+ base_path_name = scopes.map { |scope| scope.underscore }.join("_")
+ end_path_name = klass._type.to_s.singularize
+ [base_path_name, end_path_name]
end
- def regular_resources_path(source_klass)
- @resources_path_cache.get(source_klass)
+ def resource_url_helper_name_from_source(source)
+ url_helper_name_from_parts(resource_path_parts_from_class(source.class))
end
- def regular_primary_resources_path
- regular_resources_path(primary_resource_klass)
+ def related_url_helper_name(relationship)
+ relationship_parts = resource_path_parts_from_class(relationship.parent_resource)
+ relationship_parts << relationship.name
+ url_helper_name_from_parts(relationship_parts)
end
- def regular_primary_resources_url
- "#{ base_url }#{ regular_primary_resources_path }"
+ def singleton_related_url_helper_name(relationship)
+ relationship_parts = []
+ relationship_parts << relationship.name
+ relationship_parts += resource_path_parts_from_class(relationship.parent_resource)
+ url_helper_name_from_parts(relationship_parts)
end
- def regular_resource_path(source)
- if source.is_a?(JSONAPI::CachedResponseFragment)
- # :nocov:
- "#{regular_resources_path(source.resource_klass)}/#{source.id}"
- # :nocov:
- else
- "#{regular_resources_path(source.class)}/#{source.id}"
- end
+ def relationship_self_url_helper_name(relationship)
+ relationship_parts = resource_path_parts_from_class(relationship.parent_resource)
+ relationship_parts << "relationships"
+ relationship_parts << relationship.name
+ url_helper_name_from_parts(relationship_parts)
end
- def regular_resource_url(source)
- "#{ base_url }#{ regular_resource_path(source) }"
+ def singleton_relationship_self_url_helper_name(relationship)
+ relationship_parts = []
+ relationship_parts << "relationships"
+ relationship_parts << relationship.name
+ relationship_parts += resource_path_parts_from_class(relationship.parent_resource)
+ url_helper_name_from_parts(relationship_parts)
end
- def route_for_relationship(relationship)
- format_route(relationship.name)
+ def module_scopes_from_class(klass)
+ klass.name.to_s.split("::")[0...-1]
end
end
end