From 72b8213bee1dc0699f79354f05166e14d82641e1 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Wed, 11 Jan 2012 13:06:12 -0700 Subject: [PATCH] If an existing association exists, use it to get the value if none was provided. --- lib/active_model/serializer.rb | 68 +++++++++++++++++++------------ test/association_test.rb | 74 +++++++++++++++++++++------------- 2 files changed, 89 insertions(+), 53 deletions(-) diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index dd653f80..ce24d4b4 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -80,6 +80,16 @@ module ActiveModel options[:key] || name end + def associated_object(serializer) + options[:value] || serializer.send(name) + end + + def with_options(options) + config = dup + config.options.merge!(options) + config + end + protected def find_serializable(object, scope, context, options) @@ -96,18 +106,18 @@ module ActiveModel class HasMany < Config #:nodoc: alias plural_key key - def serialize(collection, scope, context, options) - collection.map do |item| - find_serializable(item, scope, context, options).as_json(:root => false) + def serialize(serializer, scope) + associated_object(serializer).map do |item| + find_serializable(item, scope, serializer, options).as_json(:root => false) end end alias serialize_many serialize - def serialize_ids(collection, scope) + def serialize_ids(serializer, scope) # Use pluck or select_columns if available # return collection.ids if collection.respond_to?(:ids) - collection.map do |item| + associated_object(serializer).map do |item| item.read_attribute_for_serialization(:id) end end @@ -118,17 +128,19 @@ module ActiveModel key.to_s.pluralize.to_sym end - def serialize(object, scope, context, options) - object && find_serializable(object, scope, context, options).as_json(:root => false) + def serialize(serializer, scope) + object = associated_object(serializer) + object && find_serializable(object, scope, serializer, options).as_json(:root => false) end - def serialize_many(object, scope, context, options) - value = object && find_serializable(object, scope, context, options).as_json(:root => false) + def serialize_many(serializer, scope) + object = associated_object(serializer) + value = object && find_serializable(object, scope, serializer, options).as_json(:root => false) value ? [value] : [] end - def serialize_ids(object, scope) - if object + def serialize_ids(serializer, scope) + if object = associated_object(serializer) object.read_attribute_for_serialization(:id) else nil @@ -202,8 +214,7 @@ module ActiveModel # { :name => :string, :age => :integer } # # The +associations+ hash looks like this: - # - # { :posts => { :has_many => :posts } } + { :posts => { :has_many => :posts } } # # If :key is used: # @@ -307,7 +318,7 @@ module ActiveModel end end - def include!(key, options={}) + def include!(name, options={}) embed = options[:embed] root_embed = options[:include] hash = options[:hash] @@ -316,20 +327,26 @@ module ActiveModel serializer = options[:serializer] scope = options[:scope] - if value.respond_to?(:to_ary) - association = Associations::HasMany.new(key, :serializer => serializer) + association = _associations.find do |a| + a.name == name + end + + association = association.with_options(options) if association + + association ||= if value.respond_to?(:to_ary) + Associations::HasMany.new(name, options) else - association = Associations::HasOne.new(key, :serializer => serializer) + Associations::HasOne.new(name, options) end if embed == :ids - node[key] = association.serialize_ids(value, scope) + node[association.key] = association.serialize_ids(self, scope) if root_embed - merge_association hash, association.plural_key, association.serialize_many(value, scope, self, {}) + merge_association hash, association.plural_key, association.serialize_many(self, scope) end elsif embed == :objects - node[key] = association.serialize(value, scope) + node[association.key] = association.serialize(self, scope) end end @@ -355,8 +372,8 @@ module ActiveModel hash = {} _associations.each do |association| - associated_object = send(association.name) - hash[association.key] = association.serialize(associated_object, scope, self, :hash => @hash) + association = association.with_options(:hash => @hash) + hash[association.key] = association.serialize(self, scope) end hash @@ -366,8 +383,8 @@ module ActiveModel hash = {} _associations.each do |association| - associated_object = send(association.name) - hash[association.plural_key] = association.serialize_many(associated_object, scope, self, :hash => @hash) + association = association.with_options(:hash => @hash) + hash[association.plural_key] = association.serialize_many(self, scope) end hash @@ -379,8 +396,7 @@ module ActiveModel hash = {} _associations.each do |association| - associated_object = send(association.name) - hash[association.key] = association.serialize_ids(associated_object, scope) + hash[association.key] = association.serialize_ids(self, scope) end hash diff --git a/test/association_test.rb b/test/association_test.rb index fba671f6..d7c89640 100644 --- a/test/association_test.rb +++ b/test/association_test.rb @@ -49,41 +49,61 @@ class AssociationTest < ActiveModel::TestCase @root_hash = {} end - def test_include_bang_has_many_associations - @post_serializer.include! :comments, + def include!(key, options={}) + @post_serializer.include! key, options.merge( :embed => :ids, :include => true, :hash => @root_hash, :node => @hash, - :value => @post.comments, :serializer => @comment_serializer_class - - assert_equal({ - :comments => [ 1 ] - }, @hash) - - assert_equal({ - :comments => [ - { :body => "ZOMG A COMMENT" } - ] - }, @root_hash) + ) end - def test_include_bang_has_one_associations - @post_serializer.include! :comment, - :embed => :ids, - :include => true, - :hash => @root_hash, - :node => @hash, - :value => @post.comment, - :serializer => @comment_serializer_class + class NoDefaults < AssociationTest + def test_include_bang_has_many_associations + include! :comments, :value => @post.comments - assert_equal({ - :comment => 1 - }, @hash) + assert_equal({ + :comments => [ 1 ] + }, @hash) - assert_equal({ - :comments => [{ :body => "ZOMG A COMMENT" }] - }, @root_hash) + assert_equal({ + :comments => [ + { :body => "ZOMG A COMMENT" } + ] + }, @root_hash) + end + + def test_include_bang_has_one_associations + include! :comment, :value => @post.comment + + assert_equal({ + :comment => 1 + }, @hash) + + assert_equal({ + :comments => [{ :body => "ZOMG A COMMENT" }] + }, @root_hash) + end + end + + class DefaultsTest < AssociationTest + def test_with_default_has_many + @post_serializer_class.class_eval do + has_many :comments + end + + include! :comments + + assert_equal({ + :comments => [ 1 ] + }, @hash) + + assert_equal({ + :comments => [ + { :body => "ZOMG A COMMENT" } + ] + }, @root_hash) + end end end