diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index c7889407..cabead2d 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -36,6 +36,16 @@ module ActiveModel end def has_one(*attrs) + associate(Association::HasOne, *attrs) + end + + def has_many(*attrs) + associate(Association::HasMany, *attrs) + end + + private + + def associate(klass, *attrs) options = attrs.extract_options! attrs.each do |attr| @@ -47,7 +57,7 @@ module ActiveModel end end - @_associations << Association::HasOne.new(attr, options) + @_associations << klass.new(attr, options) end end end @@ -76,12 +86,20 @@ module ActiveModel hash[association.key] = serialize_ids association end if association.embed_objects? - associated_data = send(association.name) - hash[association.embedded_key] = association.build_serializer(associated_data).serializable_hash + hash[association.embedded_key] = serialize association end end end + def serialize(association) + associated_data = send(association.name) + if associated_data.respond_to?(:to_ary) + associated_data.map { |elem| association.build_serializer(elem).serializable_hash } + else + association.build_serializer(associated_data).serializable_hash + end + end + def serialize_ids(association) associated_data = send(association.name) if associated_data.respond_to?(:to_ary) diff --git a/lib/active_model/serializer/associations.rb b/lib/active_model/serializer/associations.rb index e04ca09e..aab18b85 100644 --- a/lib/active_model/serializer/associations.rb +++ b/lib/active_model/serializer/associations.rb @@ -47,6 +47,16 @@ module ActiveModel name end end + + class HasMany < Association + def key + "#{name.singularize}_ids" + end + + def embedded_key + name + end + end end end end diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index 7f39f38c..e9d41da3 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -25,6 +25,15 @@ end class Profile < Model end +class Post < Model + def comments + @comments ||= [Comment.new(content: 'C1'), + Comment.new(content: 'C2')] + end +end + +class Comment < Model +end ### ## Serializers @@ -43,3 +52,13 @@ class ProfileSerializer < ActiveModel::Serializer attributes :name, :description end + +class PostSerializer < ActiveModel::Serializer + attributes :title, :body + + has_many :comments +end + +class CommentSerializer < ActiveModel::Serializer + attributes :content +end diff --git a/test/unit/active_model/serializer/has_many_test.rb b/test/unit/active_model/serializer/has_many_test.rb new file mode 100644 index 00000000..7dd05e50 --- /dev/null +++ b/test/unit/active_model/serializer/has_many_test.rb @@ -0,0 +1,63 @@ +require 'test_helper' +require 'active_model/serializer' + +module ActiveModel + class Serializer + class HasManyTest < ActiveModel::TestCase + def setup + @post = Post.new({ title: 'Title 1', body: 'Body 1', date: '1/1/2000' }) + @post_serializer = PostSerializer.new(@post) + @post_serializer.class._associations[0].include = false + @post_serializer.class._associations[0].embed = :ids + end + + def test_associations_definition + associations = @post_serializer.class._associations + + assert_equal 1, associations.length + assert_kind_of Association::HasMany, associations[0] + assert_equal 'comments', associations[0].name + end + + def test_associations_embedding_ids_serialization_using_serializable_hash + assert_equal({ + 'title' => 'Title 1', 'body' => 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } + }, @post_serializer.serializable_hash) + end + + def test_associations_embedding_ids_serialization_using_as_json + assert_equal({ + 'title' => 'Title 1', 'body' => 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } + }, @post_serializer.as_json) + end + + def test_associations_embedding_objects_serialization_using_serializable_hash + @post_serializer.class._associations[0].embed = :objects + assert_equal({ + 'title' => 'Title 1', 'body' => 'Body 1', 'comments' => [{ 'content' => 'C1' }, { 'content' => 'C2' }] + }, @post_serializer.serializable_hash) + end + + def test_associations_embedding_objects_serialization_using_as_json + @post_serializer.class._associations[0].embed = :objects + assert_equal({ + 'title' => 'Title 1', 'body' => 'Body 1', 'comments' => [{ 'content' => 'C1' }, { 'content' => 'C2' }] + }, @post_serializer.as_json) + end + + def test_associations_embedding_ids_including_objects_serialization_using_serializable_hash + @post_serializer.class._associations[0].include = true + assert_equal({ + 'title' => 'Title 1', 'body' => 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id }, 'comments' => [{ 'content' => 'C1' }, { 'content' => 'C2' }] + }, @post_serializer.serializable_hash) + end + + def test_associations_embedding_ids_including_objects_serialization_using_as_json + @post_serializer.class._associations[0].include = true + assert_equal({ + 'title' => 'Title 1', 'body' => 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id }, 'comments' => [{ 'content' => 'C1' }, { 'content' => 'C2' }] + }, @post_serializer.as_json) + end + end + end +end