From 7b23adddc4b88203f83e736e2953f8c24a7a12a1 Mon Sep 17 00:00:00 2001 From: Kyle Reeves Date: Sat, 30 Jun 2018 17:22:57 -0500 Subject: [PATCH] working on new relationship class --- lib/fast_jsonapi/object_serializer.rb | 38 ++++--- lib/fast_jsonapi/relationship.rb | 99 +++++++++++++++++++ lib/fast_jsonapi/serialization_core.rb | 62 ++---------- ...bject_serializer_class_methods_examples.rb | 2 +- 4 files changed, 125 insertions(+), 76 deletions(-) create mode 100644 lib/fast_jsonapi/relationship.rb diff --git a/lib/fast_jsonapi/object_serializer.rb b/lib/fast_jsonapi/object_serializer.rb index 7ce418c..b00bdfd 100644 --- a/lib/fast_jsonapi/object_serializer.rb +++ b/lib/fast_jsonapi/object_serializer.rb @@ -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 diff --git a/lib/fast_jsonapi/relationship.rb b/lib/fast_jsonapi/relationship.rb new file mode 100644 index 0000000..a560442 --- /dev/null +++ b/lib/fast_jsonapi/relationship.rb @@ -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 \ No newline at end of file diff --git a/lib/fast_jsonapi/serialization_core.rb b/lib/fast_jsonapi/serialization_core.rb index 284f017..76bcf97 100644 --- a/lib/fast_jsonapi/serialization_core.rb +++ b/lib/fast_jsonapi/serialization_core.rb @@ -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 diff --git a/spec/shared/examples/object_serializer_class_methods_examples.rb b/spec/shared/examples/object_serializer_class_methods_examples.rb index 616ccf5..1481603 100644 --- a/spec/shared/examples/object_serializer_class_methods_examples.rb +++ b/spec/shared/examples/object_serializer_class_methods_examples.rb @@ -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