lib/jsonapi/resource_serializer.rb in jsonapi-resources-0.4.2 vs lib/jsonapi/resource_serializer.rb in jsonapi-resources-0.4.3
- old
+ new
@@ -20,10 +20,11 @@
@include = options.fetch(:include, [])
@include_directives = options[:include_directives]
@key_formatter = options.fetch(:key_formatter, JSONAPI.configuration.key_formatter)
@route_formatter = options.fetch(:route_formatter, JSONAPI.configuration.route_formatter)
@base_url = options.fetch(:base_url, '')
+ @scope_id = options[:scope_id]
end
# Converts a single resource, or an array of resources to a hash, conforming to the JSONAPI structure
def serialize_to_hash(source)
is_resource_collection = source.respond_to?(:to_ary)
@@ -43,11 +44,11 @@
included_objects.push(object[:object_hash])
end
end
end
- primary_hash = {data: is_resource_collection ? primary_objects : primary_objects[0]}
+ primary_hash = { data: is_resource_collection ? primary_objects : primary_objects[0] }
primary_hash[:included] = included_objects if included_objects.size > 0
primary_hash
end
@@ -70,10 +71,11 @@
def find_link(query_params)
"#{@base_url}/#{formatted_module_path_from_klass(@primary_resource_klass)}#{@route_formatter.format(@primary_resource_klass._type.to_s)}?#{query_params.to_query}"
end
private
+
# Process the primary source object(s). This will then serialize associated object recursively based on the
# requested includes. Fields are controlled fields option for each resource type, such
# as fields: { people: [:id, :email, :comments], posts: [:id, :title, :author], comments: [:id, :body, :post]}
# The fields options controls both fields and included links references.
def process_primary(source, include_directives)
@@ -98,11 +100,11 @@
# Returns a serialized hash for the source model
def object_hash(source, include_directives)
obj_hash = {}
id_format = source.class._attribute_options(:id)[:format]
- #protect against ids that were declared as an attribute, but did not have a format set.
+ # protect against ids that were declared as an attribute, but did not have a format set.
id_format = 'id' if id_format == :default
obj_hash['id'] = format_value(source.id, id_format)
obj_hash['type'] = format_key(source.class._type.to_s)
@@ -113,23 +115,21 @@
obj_hash['attributes'] = attributes unless attributes.empty?
relationships = relationship_data(source, include_directives)
obj_hash['relationships'] = relationships unless relationships.nil? || relationships.empty?
- return obj_hash
+ obj_hash
end
def requested_fields(model)
@fields[model] if @fields
end
def attribute_hash(source)
requested = requested_fields(source.class._type)
fields = source.fetchable_fields & source.class._attributes.keys.to_a
- unless requested.nil?
- fields = requested & fields
- end
+ fields = requested & fields unless requested.nil?
fields.each_with_object({}) do |name, hash|
format = source.class._attribute_options(name)[:format]
unless name == :id
hash[format_key(name)] = format_value(source.send(name), format)
@@ -139,13 +139,11 @@
def relationship_data(source, include_directives)
associations = source.class._associations
requested = requested_fields(source.class._type)
fields = associations.keys
- unless requested.nil?
- fields = requested & fields
- end
+ fields = requested & fields unless requested.nil?
field_set = Set.new(fields)
included_associations = source.fetchable_fields & associations.keys
@@ -170,10 +168,11 @@
if include_linkage || include_linked_children
if association.is_a?(JSONAPI::Association::HasOne)
resource = source.send(name)
if resource
id = resource.id
+ type = association.type_for_source(source)
associations_only = already_serialized?(type, id)
if include_linkage && !associations_only
add_included_object(type, id, object_hash(resource, ia))
elsif include_linked_children || associations_only
relationship_data(resource, ia)
@@ -206,20 +205,25 @@
def formatted_module_path(source)
formatted_module_path_from_klass(source.class)
end
def formatted_module_path_from_klass(klass)
- klass.name =~ /::[^:]+\Z/ ? (@route_formatter.format($`).freeze.gsub('::', '/') + '/').downcase : ''
+ if klass.name =~ /::[^:]+\Z/
+ path = (@route_formatter.format($`).freeze.gsub('::', '/') + '/').downcase
+ @scope_id ? "#{path}#{@scope_id}/" : path
+ else
+ ''
+ end
end
def self_href(source)
"#{@base_url}/#{formatted_module_path(source)}#{@route_formatter.format(source.class._type.to_s)}/#{source.id}"
end
def already_serialized?(type, id)
type = format_key(type)
- return @included_objects.key?(type) && @included_objects[type].key?(id)
+ @included_objects.key?(type) && @included_objects[type].key?(id)
end
def format_route(route)
@route_formatter.format(route.to_s)
end
@@ -233,24 +237,26 @@
end
def has_one_linkage(source, association)
linkage = {}
linkage_id = foreign_key_value(source, association)
+
if linkage_id
- linkage[:type] = format_key(association.type)
+ linkage[:type] = format_key(association.type_for_source(source))
linkage[:id] = linkage_id
else
linkage = nil
end
linkage
end
def has_many_linkage(source, association)
linkage = []
- linkage_ids = foreign_key_value(source, association)
- linkage_ids.each do |linkage_id|
- linkage.append({type: format_key(association.type), id: linkage_id})
+ linkage_types_and_values = foreign_key_types_and_values(source, association)
+
+ linkage_types_and_values.each do |type, value|
+ linkage.append({type: format_key(type), id: value})
end
linkage
end
def link_object_has_one(source, association)
@@ -277,19 +283,28 @@
elsif association.is_a?(JSONAPI::Association::HasMany)
link_object_has_many(source, association, include_linkage)
end
end
- # Extracts the foreign key value for an association.
+ # Extracts the foreign key value for a has_one association.
def foreign_key_value(source, association)
foreign_key = association.foreign_key
value = source.send(foreign_key)
+ IdValueFormatter.format(value)
+ end
+ def foreign_key_types_and_values(source, association)
if association.is_a?(JSONAPI::Association::HasMany)
- value.map { |value| IdValueFormatter.format(value) }
- elsif association.is_a?(JSONAPI::Association::HasOne)
- IdValueFormatter.format(value)
+ if association.polymorphic?
+ source.model.send(association.name).pluck(:type, :id).map do |type, id|
+ [type.pluralize, IdValueFormatter.format(id)]
+ end
+ else
+ source.send(association.foreign_key).map do |value|
+ [association.type, IdValueFormatter.format(value)]
+ end
+ end
end
end
# Sets that an object should be included in the primary document of the response.
def set_primary(type, id)
@@ -299,19 +314,15 @@
# Collects the hashes for all objects processed by the serializer
def add_included_object(type, id, object_hash, primary = false)
type = format_key(type)
- unless @included_objects.key?(type)
- @included_objects[type] = {}
- end
+ @included_objects[type] = {} unless @included_objects.key?(type)
if already_serialized?(type, id)
- if primary
- set_primary(type, id)
- end
+ set_primary(type, id) if primary
else
- @included_objects[type].store(id, {primary: primary, object_hash: object_hash})
+ @included_objects[type].store(id, primary: primary, object_hash: object_hash)
end
end
def format_key(key)
@key_formatter.format(key)