working on new relationship class

This commit is contained in:
Kyle Reeves 2018-06-30 17:22:57 -05:00 committed by Shishir Kakaraddi
parent f864099761
commit 7b23adddc4
4 changed files with 125 additions and 76 deletions

View File

@ -4,6 +4,7 @@ require 'active_support/core_ext/object'
require 'active_support/concern'
require 'active_support/inflector'
require 'fast_jsonapi/attribute'
require 'fast_jsonapi/relationship'
require 'fast_jsonapi/serialization_core'
module FastJsonapi
@ -169,38 +170,32 @@ module FastJsonapi
alias_method :attribute, :attributes
def add_relationship(name, relationship)
def add_relationship(relationship)
self.relationships_to_serialize = {} if relationships_to_serialize.nil?
self.cachable_relationships_to_serialize = {} if cachable_relationships_to_serialize.nil?
self.uncachable_relationships_to_serialize = {} if uncachable_relationships_to_serialize.nil?
if !relationship[:cached]
self.uncachable_relationships_to_serialize[name] = relationship
if !relationship.cached
self.uncachable_relationships_to_serialize[relationship.name] = relationship
else
self.cachable_relationships_to_serialize[name] = relationship
self.cachable_relationships_to_serialize[relationship.name] = relationship
end
self.relationships_to_serialize[name] = relationship
self.relationships_to_serialize[relationship.name] = relationship
end
def has_many(relationship_name, options = {}, &block)
name = relationship_name.to_sym
hash = create_relationship_hash(relationship_name, :has_many, options, block)
add_relationship(name, hash)
create_relationship(relationship_name, :has_many, options, block)
end
def has_one(relationship_name, options = {}, &block)
name = relationship_name.to_sym
hash = create_relationship_hash(relationship_name, :has_one, options, block)
add_relationship(name, hash)
create_relationship(relationship_name, :has_one, options, block)
end
def belongs_to(relationship_name, options = {}, &block)
name = relationship_name.to_sym
hash = create_relationship_hash(relationship_name, :belongs_to, options, block)
add_relationship(name, hash)
create_relationship(relationship_name, :belongs_to, options, block)
end
def create_relationship_hash(base_key, relationship_type, options, block)
def create_relationship(base_key, relationship_type, options, block)
name = base_key.to_sym
if relationship_type == :has_many
base_serialization_key = base_key.to_s.singularize
@ -211,7 +206,7 @@ module FastJsonapi
base_key_sym = name
id_postfix = '_id'
end
{
relationship = Relationship.new(
key: options[:key] || run_key_transform(base_key),
name: name,
id_method_name: options[:id_method_name] || "#{base_serialization_key}#{id_postfix}".to_sym,
@ -220,10 +215,11 @@ module FastJsonapi
object_block: block,
serializer: compute_serializer_name(options[:serializer] || base_key_sym),
relationship_type: relationship_type,
cached: options[:cached] || false,
cached: options[:cached],
polymorphic: fetch_polymorphic_option(options),
conditional_proc: options[:if]
}
)
add_relationship(relationship)
end
def compute_serializer_name(serializer_key)
@ -255,8 +251,8 @@ module FastJsonapi
parse_include_item(include_item).each do |parsed_include|
relationship_to_include = klass.relationships_to_serialize[parsed_include]
raise ArgumentError, "#{parsed_include} is not specified as a relationship on #{klass.name}" unless relationship_to_include
raise NotImplementedError if relationship_to_include[:polymorphic].is_a?(Hash)
klass = relationship_to_include[:serializer].to_s.constantize
raise NotImplementedError if relationship_to_include.polymorphic.is_a?(Hash)
klass = relationship_to_include.serializer.to_s.constantize
end
end
end

View File

@ -0,0 +1,99 @@
module FastJsonapi
class Relationship
attr_reader :key, :name, :id_method_name, :record_type, :object_method_name, :object_block, :serializer, :relationship_type, :cached, :polymorphic, :conditional_proc
def initialize(
key:,
name:,
id_method_name:,
record_type:,
object_method_name:,
object_block:,
serializer:,
relationship_type:,
cached: false,
polymorphic:,
conditional_proc:
)
@key = key
@name = name
@id_method_name = id_method_name
@record_type = record_type
@object_method_name = object_method_name
@object_block = object_block
@serializer = serializer
@relationship_type = relationship_type
@cached = cached
@polymorphic = polymorphic
@conditional_proc = conditional_proc
end
def serialize(record, serialization_params, output_hash)
if include_relationship?(record, serialization_params)
empty_case = relationship_type == :has_many ? [] : nil
output_hash[key] = {
data: ids_hash_from_record_and_relationship(record, serialization_params) || empty_case
}
end
end
private
def include_relationship?(record, serialization_params)
if conditional_proc.present?
conditional_proc.call(record, serialization_params)
else
true
end
end
def ids_hash_from_record_and_relationship(record, params = {})
return ids_hash(
fetch_id(record, params)
) unless polymorphic
return unless associated_object = fetch_associated_object(record, params)
return associated_object.map do |object|
id_hash_from_record object, polymorphic
end if associated_object.respond_to? :map
id_hash_from_record associated_object, polymorphic
end
def id_hash_from_record(record, record_types)
# memoize the record type within the record_types dictionary, then assigning to record_type:
associated_record_type = record_types[record.class] ||= record.class.name.underscore.to_sym
id_hash(record.id, associated_record_type)
end
def ids_hash(ids)
return ids.map { |id| id_hash(id, record_type) } if ids.respond_to? :map
id_hash(ids, record_type) # ids variable is just a single id here
end
def id_hash(id, associated_record_type, default_return=false)
if id.present?
{ id: id.to_s, type: record_type }
else
default_return ? { id: nil, type: associated_record_type } : nil
end
end
def fetch_associated_object(record, params)
return object_block.call(record, params) unless object_block.nil?
record.send(object_method_name)
end
def fetch_id(record, params)
unless object_block.nil?
object = object_block.call(record, params)
return object.map(&:id) if object.respond_to? :map
return object.try(:id)
end
record.public_send(id_method_name)
end
end
end

View File

@ -34,34 +34,6 @@ module FastJsonapi
end
end
def ids_hash(ids, record_type)
return ids.map { |id| id_hash(id, record_type) } if ids.respond_to? :map
id_hash(ids, record_type) # ids variable is just a single id here
end
def id_hash_from_record(record, record_types)
# memoize the record type within the record_types dictionary, then assigning to record_type:
record_type = record_types[record.class] ||= record.class.name.underscore.to_sym
id_hash(record.id, record_type)
end
def ids_hash_from_record_and_relationship(record, relationship, params = {})
polymorphic = relationship[:polymorphic]
return ids_hash(
fetch_id(record, relationship, params),
relationship[:record_type]
) unless polymorphic
return unless associated_object = fetch_associated_object(record, relationship, params)
return associated_object.map do |object|
id_hash_from_record object, polymorphic
end if associated_object.respond_to? :map
id_hash_from_record associated_object, polymorphic
end
def links_hash(record, params = {})
data_links.each_with_object({}) do |(key, method), link_hash|
link_hash[key] = if method.is_a?(Proc)
@ -82,14 +54,7 @@ module FastJsonapi
relationships = relationships_to_serialize if relationships.nil?
relationships.each_with_object({}) do |(_k, relationship), hash|
conditional_proc = relationship[:conditional_proc]
if conditional_proc.blank? || conditional_proc.call(record, params)
name = relationship[:key]
empty_case = relationship[:relationship_type] == :has_many ? [] : nil
hash[name] = {
data: ids_hash_from_record_and_relationship(record, relationship, params) || empty_case
}
end
relationship.serialize(record, params, hash)
end
end
@ -146,12 +111,12 @@ module FastJsonapi
items = parse_include_item(include_item)
items.each do |item|
next unless relationships_to_serialize && relationships_to_serialize[item]
conditional_proc = relationships_to_serialize[item][:conditional_proc]
conditional_proc = relationships_to_serialize[item].conditional_proc
next if conditional_proc && !conditional_proc.call(record, params)
raise NotImplementedError if @relationships_to_serialize[item][:polymorphic].is_a?(Hash)
record_type = @relationships_to_serialize[item][:record_type]
serializer = @relationships_to_serialize[item][:serializer].to_s.constantize
relationship_type = @relationships_to_serialize[item][:relationship_type]
raise NotImplementedError if @relationships_to_serialize[item].polymorphic.is_a?(Hash)
record_type = @relationships_to_serialize[item].record_type
serializer = @relationships_to_serialize[item].serializer.to_s.constantize
relationship_type = @relationships_to_serialize[item].relationship_type
included_objects = fetch_associated_object(record, @relationships_to_serialize[item], params)
next if included_objects.blank?
@ -174,19 +139,8 @@ module FastJsonapi
end
def fetch_associated_object(record, relationship, params)
return relationship[:object_block].call(record, params) unless relationship[:object_block].nil?
record.send(relationship[:object_method_name])
end
def fetch_id(record, relationship, params)
unless relationship[:object_block].nil?
object = relationship[:object_block].call(record, params)
return object.map(&:id) if object.respond_to? :map
return object.try(:id)
end
record.public_send(relationship[:id_method_name])
return relationship.object_block.call(record, params) unless relationship.object_block.nil?
record.send(relationship.object_method_name)
end
end
end

View File

@ -1,6 +1,6 @@
RSpec.shared_examples 'returning correct relationship hash' do |serializer, id_method_name, record_type|
it 'returns correct relationship hash' do
expect(relationship).to be_instance_of(Hash)
expect(relationship).to be_instance_of(FastJsonapi::Relationship)
expect(relationship.keys).to all(be_instance_of(Symbol))
expect(relationship[:serializer]).to be serializer
expect(relationship[:id_method_name]).to be id_method_name