lib/jsonapi/resource_serializer.rb in jsonapi-resources-0.3.3 vs lib/jsonapi/resource_serializer.rb in jsonapi-resources-0.4.0
- old
+ new
@@ -16,25 +16,25 @@
@primary_resource_klass = primary_resource_klass
@primary_class_name = @primary_resource_klass._type
@fields = options.fetch(:fields, {})
@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, '')
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)
@included_objects = {}
+ @include_directives ||= JSONAPI::IncludeDirectives.new(@include)
- requested_associations = parse_includes(@include)
+ process_primary(source, @include_directives.include_directives)
- process_primary(source, requested_associations)
-
included_objects = []
primary_objects = []
@included_objects.each_value do |objects|
objects.each_value do |object|
if object[:primary]
@@ -45,15 +45,11 @@
end
end
primary_hash = {data: is_resource_collection ? primary_objects : primary_objects[0]}
- if included_objects.size > 0
- primary_hash[:included] = included_objects
- else
- primary_hash
- end
+ primary_hash[:included] = included_objects if included_objects.size > 0
primary_hash
end
def serialize_to_links_hash(source, requested_association)
if requested_association.is_a?(JSONAPI::Association::HasOne)
@@ -70,64 +66,53 @@
data: data
}
end
private
- # Convert an array of associated objects to include along with the primary document in the form of
- # ['comments','author','comments.tags','author.posts'] into a structure that tells what we need to include
- # from each association.
- def parse_includes(includes)
- requested_associations = {}
- includes.each do |include|
- include = include.to_s.underscore
-
- pos = include.index('.')
- if pos
- association_name = include[0, pos].to_sym
- requested_associations[association_name] ||= {}
- requested_associations[association_name].store(:include_children, true)
- requested_associations[association_name].store(:include_related, parse_includes([include[pos+1, include.length]]))
- else
- association_name = include.to_sym
- requested_associations[association_name] ||= {}
- requested_associations[association_name].store(:include, true)
- end
- end if includes.is_a?(Array)
- return requested_associations
- end
-
# 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, requested_associations)
+ def process_primary(source, include_directives)
if source.respond_to?(:to_ary)
source.each do |resource|
id = resource.id
if already_serialized?(@primary_class_name, id)
set_primary(@primary_class_name, id)
end
- add_included_object(@primary_class_name, id, object_hash(resource, requested_associations), true)
+ add_included_object(@primary_class_name, id, object_hash(resource, include_directives), true)
end
else
return {} if source.nil?
resource = source
id = resource.id
- add_included_object(@primary_class_name, id, object_hash(source, requested_associations), true)
+ add_included_object(@primary_class_name, id, object_hash(source, include_directives), true)
end
end
# Returns a serialized hash for the source model
- def object_hash(source, requested_associations)
- obj_hash = attribute_hash(source)
- links = links_hash(source, requested_associations)
+ 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.
+ 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)
- obj_hash['id'] ||= format_value(source.id, :id, source)
- obj_hash.merge!({links: links}) unless links.empty?
+
+ links = relationship_links(source)
+ obj_hash['links'] = links unless links.empty?
+
+ attributes = attribute_hash(source)
+ 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
end
def requested_fields(model)
@fields[model] if @fields
@@ -140,24 +125,17 @@
fields = requested & fields
end
fields.each_with_object({}) do |name, hash|
format = source.class._attribute_options(name)[:format]
- if format == :default && name == :id
- format = 'id'
+ unless name == :id
+ hash[format_key(name)] = format_value(source.send(name), format)
end
- hash[format_key(name)] = format_value(
- source.send(name),
- format,
- source
- )
end
end
- # Returns a hash of links for the requested associations for a resource, filtered by the resource
- # class's fetchable method
- def links_hash(source, requested_associations)
+ 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
@@ -165,19 +143,18 @@
field_set = Set.new(fields)
included_associations = source.fetchable_fields & associations.keys
- links = {}
- links[:self] = self_href(source)
+ data = {}
- associations.each_with_object(links) do |(name, association), hash|
+ associations.each_with_object(data) do |(name, association), hash|
if included_associations.include? name
- ia = requested_associations.is_a?(Hash) ? requested_associations[name] : nil
+ ia = include_directives[:include_related][name]
include_linkage = ia && ia[:include]
- include_linked_children = ia && ia[:include_children]
+ include_linked_children = ia && !ia[:include_related].empty?
if field_set.include?(name)
hash[format_key(name)] = link_object(source, association, include_linkage)
end
@@ -191,32 +168,39 @@
resource = source.send(name)
if resource
id = resource.id
associations_only = already_serialized?(type, id)
if include_linkage && !associations_only
- add_included_object(type, id, object_hash(resource, ia[:include_related]))
+ add_included_object(type, id, object_hash(resource, ia))
elsif include_linked_children || associations_only
- links_hash(resource, ia[:include_related])
+ relationship_data(resource, ia)
end
end
elsif association.is_a?(JSONAPI::Association::HasMany)
resources = source.send(name)
resources.each do |resource|
id = resource.id
associations_only = already_serialized?(type, id)
if include_linkage && !associations_only
- add_included_object(type, id, object_hash(resource, ia[:include_related]))
+ add_included_object(type, id, object_hash(resource, ia))
elsif include_linked_children || associations_only
- links_hash(resource, ia[:include_related])
+ relationship_data(resource, ia)
end
end
end
end
end
end
end
+ def relationship_links(source)
+ links = {}
+ links[:self] = self_href(source)
+
+ links
+ end
+
def formatted_module_path(source)
source.class.name =~ /::[^:]+\Z/ ? (@route_formatter.format($`).freeze.gsub('::', '/') + '/').downcase : ''
end
def self_href(source)
@@ -231,11 +215,11 @@
def format_route(route)
@route_formatter.format(route.to_s)
end
def self_link(source, association)
- "#{self_href(source)}/links/#{format_route(association.name)}"
+ "#{self_href(source)}/relationships/#{format_route(association.name)}"
end
def related_link(source, association)
"#{self_href(source)}/#{format_route(association.name)}"
end
@@ -261,21 +245,23 @@
linkage
end
def link_object_has_one(source, association)
link_object_hash = {}
- link_object_hash[:self] = self_link(source, association)
- link_object_hash[:related] = related_link(source, association)
- link_object_hash[:linkage] = has_one_linkage(source, association)
+ link_object_hash[:links] = {}
+ link_object_hash[:links][:self] = self_link(source, association)
+ link_object_hash[:links][:related] = related_link(source, association)
+ link_object_hash[:data] = has_one_linkage(source, association)
link_object_hash
end
def link_object_has_many(source, association, include_linkage)
link_object_hash = {}
- link_object_hash[:self] = self_link(source, association)
- link_object_hash[:related] = related_link(source, association)
- link_object_hash[:linkage] = has_many_linkage(source, association) if include_linkage
+ link_object_hash[:links] = {}
+ link_object_hash[:links][:self] = self_link(source, association)
+ link_object_hash[:links][:related] = related_link(source, association)
+ link_object_hash[:data] = has_many_linkage(source, association) if include_linkage
link_object_hash
end
def link_object(source, association, include_linkage = false)
if association.is_a?(JSONAPI::Association::HasOne)
@@ -289,13 +275,13 @@
def foreign_key_value(source, association)
foreign_key = association.foreign_key
value = source.send(foreign_key)
if association.is_a?(JSONAPI::Association::HasMany)
- value.map { |value| IdValueFormatter.format(value, {}) }
+ value.map { |value| IdValueFormatter.format(value) }
elsif association.is_a?(JSONAPI::Association::HasOne)
- IdValueFormatter.format(value, {})
+ IdValueFormatter.format(value)
end
end
# Sets that an object should be included in the primary document of the response.
def set_primary(type, id)
@@ -322,11 +308,11 @@
def format_key(key)
@key_formatter.format(key)
end
- def format_value(value, format, source)
+ def format_value(value, format)
value_formatter = JSONAPI::ValueFormatter.value_formatter_for(format)
- value_formatter.format(value, source)
+ value_formatter.format(value)
end
end
end