lib/jsonapi/link_builder.rb in jsonapi-resources-0.9.8 vs lib/jsonapi/link_builder.rb in jsonapi-resources-0.9.9
- old
+ new
@@ -1,77 +1,88 @@
module JSONAPI
class LinkBuilder
attr_reader :base_url,
:primary_resource_klass,
+ :route_formatter,
:engine,
- :routes
+ :engine_mount_point,
+ :url_helpers
+ @@url_helper_methods = {}
+
def initialize(config = {})
- @base_url = config[:base_url]
+ @base_url = config[:base_url]
@primary_resource_klass = config[:primary_resource_klass]
- @engine = build_engine
+ @route_formatter = config[:route_formatter]
+ @engine = build_engine
+ @engine_mount_point = @engine ? @engine.routes.find_script_name({}) : ""
- 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
+ # url_helpers may be either a controller which has the route helper methods, or the application router's
+ # url helpers module, `Rails.application.routes.url_helpers`. Because the method no longer behaves as a
+ # singleton, and it's expensive to generate the module, the controller is preferred.
+ @url_helpers = config[:url_helpers]
end
def engine?
!!@engine
end
def primary_resources_url
- @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
+ if @primary_resource_klass._routed
+ primary_resources_path = resources_path(primary_resource_klass)
+ @primary_resources_url_cached ||= "#{ base_url }#{ engine_mount_point }#{ primary_resources_path }"
+ else
+ if JSONAPI.configuration.warn_on_missing_routes && !@primary_resource_klass._warned_missing_route
+ warn "primary_resources_url for #{@primary_resource_klass} could not be generated"
+ @primary_resource_klass._warned_missing_route = true
+ end
+ nil
+ end
end
def query_link(query_params)
- "#{ primary_resources_url }?#{ query_params.to_query }"
+ url = primary_resources_url
+ return url if url.nil?
+ "#{ url }?#{ query_params.to_query }"
end
def relationships_related_link(source, relationship, query_params = {})
- if relationship.parent_resource.singleton?
- url_helper_name = singleton_related_url_helper_name(relationship)
- url = call_url_helper(url_helper_name)
+ if relationship._routed
+ url = "#{ self_link(source) }/#{ route_for_relationship(relationship) }"
+ url = "#{ url }?#{ query_params.to_query }" if query_params.present?
+ url
else
- url_helper_name = related_url_helper_name(relationship)
- url = call_url_helper(url_helper_name, source.id)
+ if JSONAPI.configuration.warn_on_missing_routes && !relationship._warned_missing_route
+ warn "related_link for #{relationship} could not be generated"
+ relationship._warned_missing_route = true
+ end
+ nil
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)
- if relationship.parent_resource.singleton?
- url_helper_name = singleton_relationship_self_url_helper_name(relationship)
- url = call_url_helper(url_helper_name)
+ if relationship._routed
+ "#{ self_link(source) }/relationships/#{ route_for_relationship(relationship) }"
else
- url_helper_name = relationship_self_url_helper_name(relationship)
- url = call_url_helper(url_helper_name, source.id)
+ if JSONAPI.configuration.warn_on_missing_routes && !relationship._warned_missing_route
+ warn "self_link for #{relationship} could not be generated"
+ relationship._warned_missing_route = true
+ end
+ nil
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)
- "#{ base_url }#{ resource_path(source) }"
- rescue NoMethodError
- warn "self_link for #{source.class} could not be generated" if JSONAPI.configuration.warn_on_missing_routes
+ if source.class._routed
+ resource_url(source)
+ else
+ if JSONAPI.configuration.warn_on_missing_routes && !source.class._warned_missing_route
+ warn "self_link for #{source.class} could not be generated"
+ source.class._warned_missing_route = true
+ end
+ nil
+ end
end
private
def build_engine
@@ -79,107 +90,57 @@
begin
unless scopes.empty?
"#{ scopes.first.to_s.camelize }::Engine".safe_constantize
end
+
# :nocov:
rescue LoadError => _e
nil
# :nocov:
end
end
- def call_url_helper(method, *args)
- routes.url_helpers.public_send(method, args)
- rescue NoMethodError => e
- raise e
+ def format_route(route)
+ route_formatter.format(route)
end
- def path_from_resource_class(klass)
- url_helper_name = resources_url_helper_name_from_class(klass)
- call_url_helper(url_helper_name)
- end
+ def formatted_module_path_from_class(klass)
+ scopes = if @engine
+ module_scopes_from_class(klass)[1..-1]
+ else
+ module_scopes_from_class(klass)
+ end
- def resource_path(source)
- url_helper_name = resource_url_helper_name_from_source(source)
- if source.class.singleton?
- call_url_helper(url_helper_name)
+ unless scopes.empty?
+ "/#{ scopes.map {|scope| format_route(scope.to_s.underscore)}.compact.join('/') }/"
else
- call_url_helper(url_helper_name, source.id)
+ "/"
end
end
- def primary_resources_path
- path_from_resource_class(primary_resource_klass)
+ def module_scopes_from_class(klass)
+ klass.name.to_s.split("::")[0...-1]
end
- def url_helper_name_from_parts(parts)
- (parts << "path").reject(&:blank?).join("_")
+ def resources_path(source_klass)
+ formatted_module_path_from_class(source_klass) + format_route(source_klass._type.to_s)
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 resource_path(source)
+ url = "#{resources_path(source.class)}"
- base_path_name = scopes.map { |scope| scope.underscore }.join("_")
- end_path_name = klass._type.to_s
- [base_path_name, end_path_name]
- end
-
- def resources_url_helper_name_from_class(klass)
- url_helper_name_from_parts(resources_path_parts_from_class(klass))
- end
-
- def resource_path_parts_from_class(klass)
- if engine?
- scopes = module_scopes_from_class(klass)[1..-1]
- else
- scopes = module_scopes_from_class(klass)
+ unless source.class.singleton?
+ url = "#{url}/#{source.id}"
end
-
- base_path_name = scopes.map { |scope| scope.underscore }.join("_")
- end_path_name = klass._type.to_s.singularize
- [base_path_name, end_path_name]
+ url
end
- def resource_url_helper_name_from_source(source)
- url_helper_name_from_parts(resource_path_parts_from_class(source.class))
+ def resource_url(source)
+ "#{ base_url }#{ engine_mount_point }#{ resource_path(source) }"
end
- def related_url_helper_name(relationship)
- relationship_parts = resource_path_parts_from_class(relationship.parent_resource)
- relationship_parts << "related"
- relationship_parts << relationship.name
- url_helper_name_from_parts(relationship_parts)
- end
-
- def singleton_related_url_helper_name(relationship)
- relationship_parts = []
- relationship_parts << "related"
- relationship_parts << relationship.name
- relationship_parts += resource_path_parts_from_class(relationship.parent_resource)
- url_helper_name_from_parts(relationship_parts)
- 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 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 module_scopes_from_class(klass)
- klass.name.to_s.split("::")[0...-1]
+ def route_for_relationship(relationship)
+ format_route(relationship.name)
end
end
end