diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 0c5b5ee6..f4e541eb 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -169,16 +169,31 @@ module ActiveModel klass = model_class columns = klass.columns_hash - attrs = _attributes.inject({}) do |hash, (name,key)| - column = columns[name.to_s] - hash.merge key => column.type + attrs = {} + _attributes.each do |name, key| + if column = columns[name.to_s] + attrs[key] = column.type + else + # Computed attribute (method on serializer or model). We cannot + # infer the type, so we put nil. + attrs[key] = nil + end end - associations = _associations.inject({}) do |hash, (attr,association_class)| + associations = {} + _associations.each do |attr, association_class| association = association_class.new(attr, self) - model_association = klass.reflect_on_association(association.name) - hash.merge association.key => { model_association.macro => model_association.name } + if model_association = klass.reflect_on_association(association.name) + # Real association. + associations[association.key] = { model_association.macro => model_association.name } + else + # Computed association. We could infer has_many vs. has_one from + # the association class, but that would make it different from + # real associations, which read has_one vs. belongs_to from the + # model. + associations[association.key] = nil + end end { :attributes => attrs, :associations => associations } diff --git a/test/serializer_test.rb b/test/serializer_test.rb index 45965618..f8ee5fb0 100644 --- a/test/serializer_test.rb +++ b/test/serializer_test.rb @@ -379,7 +379,7 @@ class SerializerTest < ActiveModel::TestCase def test_associations_with_as posts = [ - Post.new(:title => 'First Post', :body => 'text'), + Post.new(:title => 'First Post', :body => 'text'), Post.new(:title => 'Second Post', :body => 'text') ] user = User.new @@ -397,15 +397,15 @@ class SerializerTest < ActiveModel::TestCase {:title => 'Second Post', :body => 'text', :comments => []} ], :user => { - :first_name => "Jose", - :last_name => "Valim", :ok => true, + :first_name => "Jose", + :last_name => "Valim", :ok => true, :scope => true } } }, serializer.as_json) end - def test_implicity_detection_for_association_serializers + def test_implicity_detection_for_association_serializers implicit_serializer = Class.new(ActiveModel::Serializer) do root :custom_blog const_set(:UserSerializer, UserSerializer) @@ -416,7 +416,7 @@ class SerializerTest < ActiveModel::TestCase end posts = [ - Post.new(:title => 'First Post', :body => 'text', :comments => []), + Post.new(:title => 'First Post', :body => 'text', :comments => []), Post.new(:title => 'Second Post', :body => 'text', :comments => []) ] user = User.new @@ -434,8 +434,8 @@ class SerializerTest < ActiveModel::TestCase {:title => 'Second Post', :body => 'text', :comments => []} ], :user => { - :first_name => "Jose", - :last_name => "Valim", :ok => true, + :first_name => "Jose", + :last_name => "Valim", :ok => true, :scope => true } } @@ -489,15 +489,21 @@ class SerializerTest < ActiveModel::TestCase define_method(:model_class) do model end end - attributes :name, :age + # Computed attributes (not real columns or associations). + def can_edit; end + def drafts; end + + attributes :name, :age, :can_edit has_many :posts, :serializer => Class.new + has_many :drafts, :serializer => Class.new has_one :parent, :serializer => Class.new end assert_equal serializer.schema, { - :attributes => { :name => :string, :age => :integer }, + :attributes => { :name => :string, :age => :integer, :can_edit => nil }, :associations => { :posts => { :has_many => :posts }, + :drafts => nil, :parent => { :belongs_to => :parent } } } @@ -673,7 +679,7 @@ class SerializerTest < ActiveModel::TestCase expected = serializer_class.new(post).as_json assert_equal expected, hash_object end - + def test_embed_ids_include_true_with_root serializer_class = post_serializer @@ -722,7 +728,7 @@ class SerializerTest < ActiveModel::TestCase :author => [{ :first_name => "Jose", :last_name => "Valim" }] }, serializer.as_json) end - + # the point of this test is to illustrate that deeply nested serializers # still side-load at the root. def test_embed_with_include_inserts_at_root @@ -919,7 +925,7 @@ class SerializerTest < ActiveModel::TestCase actual = attachment_serializer.new(attachment, {}).as_json assert_equal({ - :name => 'logo.png', + :name => 'logo.png', :url => 'http://example.com/logo.png', :attachable => { :type => :email, @@ -956,7 +962,7 @@ class SerializerTest < ActiveModel::TestCase actual = attachment_serializer.new(attachment, {}).as_json assert_equal({ - :name => 'logo.png', + :name => 'logo.png', :url => 'http://example.com/logo.png', :attachable => { :type => :email, @@ -995,7 +1001,7 @@ class SerializerTest < ActiveModel::TestCase assert_equal({ :attachment => { - :name => 'logo.png', + :name => 'logo.png', :url => 'http://example.com/logo.png', :attachable => { :type => :email,