diff --git a/README.md b/README.md index ea69d29d..10332666 100644 --- a/README.md +++ b/README.md @@ -535,6 +535,34 @@ This would generate JSON that would look like this: } ``` +You can also specify a different attribute to use rather than the ID of the +objects: + +```ruby +class PostSerializer < ActiveModel::Serializer + embed :ids, :include => true + + attributes :id, :title, :body + has_many :comments, :embed_key => :external_id +end +``` + +This would generate JSON that would look like this: + +```json +{ + "post": { + "id": 1, + "title": "New post", + "body": "A body!", + "comment_ids": [ "COMM001" ] + }, + "comments": [ + { "id": 1, "external_id": "COMM001", "body": "what a dumb post" } + ] +} +``` + **NOTE**: The `embed :ids` mechanism is primary useful for clients that process data in bulk and load it into a local store. For these clients, the ability to easily see all of the data per type, rather than having to recursively scan the diff --git a/lib/active_model/serializer/associations.rb b/lib/active_model/serializer/associations.rb index e05f2a33..19e6a3fb 100644 --- a/lib/active_model/serializer/associations.rb +++ b/lib/active_model/serializer/associations.rb @@ -103,6 +103,14 @@ module ActiveModel end end + def embed_key + if key = option(:embed_key) + key + else + :id + end + end + def serialize associated_object.map do |item| find_serializable(item).serializable_hash @@ -120,11 +128,11 @@ module ActiveModel # return collection.ids if collection.respond_to?(:ids) ids_key = "#{key.to_s.singularize}_ids" - if !option(:include) && source_serializer.object.respond_to?(ids_key) + if !option(:include) && !option(:embed_key) && source_serializer.object.respond_to?(ids_key) source_serializer.object.send(ids_key) else associated_object.map do |item| - item.read_attribute_for_serialization(:id) + item.read_attribute_for_serialization(embed_key) end end end @@ -163,6 +171,14 @@ module ActiveModel end end + def embed_key + if key = option(:embed_key) + key + else + :id + end + end + def polymorphic_key associated_object.class.to_s.demodulize.underscore.to_sym end @@ -191,15 +207,15 @@ module ActiveModel if associated_object { :type => polymorphic_key, - :id => associated_object.read_attribute_for_serialization(:id) + :id => associated_object.read_attribute_for_serialization(embed_key) } else nil end - elsif source_serializer.object.respond_to?("#{name}_id") + elsif !option(:embed_key) && source_serializer.object.respond_to?("#{name}_id") source_serializer.object.send("#{name}_id") elsif associated_object - associated_object.read_attribute_for_serialization(:id) + associated_object.read_attribute_for_serialization(embed_key) else nil end diff --git a/test/association_test.rb b/test/association_test.rb index b56b124c..7b1f1181 100644 --- a/test/association_test.rb +++ b/test/association_test.rb @@ -34,12 +34,12 @@ class AssociationTest < ActiveModel::TestCase @root_hash = {} @post = Model.new(:title => "New Post", :body => "Body") - @comment = Model.new(:id => 1, :body => "ZOMG A COMMENT") + @comment = Model.new(:id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT") @post.comments = [ @comment ] @post.comment = @comment @comment_serializer_class = def_serializer do - attributes :id, :body + attributes :id, :external_id, :body end @post_serializer_class = def_serializer do @@ -75,7 +75,7 @@ class AssociationTest < ActiveModel::TestCase assert_equal({ :comments => [ - { :id => 1, :body => "ZOMG A COMMENT" } + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } ] }, @root_hash) end @@ -105,7 +105,7 @@ class AssociationTest < ActiveModel::TestCase }, @hash) assert_equal({ - :comments => [{ :id => 1, :body => "ZOMG A COMMENT" }] + :comments => [{ :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }] }, @root_hash) end end @@ -124,7 +124,7 @@ class AssociationTest < ActiveModel::TestCase assert_equal({ :comments => [ - { :id => 1, :body => "ZOMG A COMMENT" } + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } ] }, @root_hash) end @@ -142,7 +142,7 @@ class AssociationTest < ActiveModel::TestCase assert_equal({ :comments => [ - { :id => 1, :body => "ZOMG A COMMENT" } + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } ] }, @root_hash) end @@ -160,7 +160,7 @@ class AssociationTest < ActiveModel::TestCase assert_equal({ :comments => [ - { :id => 1, :body => "ZOMG A COMMENT" } + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } ] }, @root_hash) end @@ -178,7 +178,79 @@ class AssociationTest < ActiveModel::TestCase assert_equal({ :comments => [ - { :id => 1, :body => "ZOMG A COMMENT" } + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } + ] + }, @root_hash) + end + + def test_with_default_has_many_with_custom_embed_key + @post_serializer_class.class_eval do + has_many :comments, :embed_key => :external_id + end + + include! :comments + + assert_equal({ + :comment_ids => [ "COMM001" ] + }, @hash) + + assert_equal({ + :comments => [ + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } + ] + }, @root_hash) + end + + def test_with_default_has_one_with_custom_embed_key + @post_serializer_class.class_eval do + has_one :comment, :embed_key => :external_id + end + + include! :comment + + assert_equal({ + :comment_id => "COMM001" + }, @hash) + + assert_equal({ + :comments => [ + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } + ] + }, @root_hash) + end + + def test_with_default_has_many_with_custom_key_and_custom_embed_key + @post_serializer_class.class_eval do + has_many :comments, :key => :custom_comments, :embed_key => :external_id + end + + include! :comments + + assert_equal({ + :custom_comments => [ "COMM001" ] + }, @hash) + + assert_equal({ + :comments => [ + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } + ] + }, @root_hash) + end + + def test_with_default_has_one_with_custom_key_and_custom_embed_key + @post_serializer_class.class_eval do + has_one :comment, :key => :custom_comment, :embed_key => :external_id + end + + include! :comment + + assert_equal({ + :custom_comment => "COMM001" + }, @hash) + + assert_equal({ + :comments => [ + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } ] }, @root_hash) end @@ -192,7 +264,7 @@ class AssociationTest < ActiveModel::TestCase assert_equal({ :comments => [ - { :id => 1, :body => "ZOMG A COMMENT" } + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } ] }, @hash) @@ -237,7 +309,7 @@ class AssociationTest < ActiveModel::TestCase assert_equal({ :comments => [ - { :id => 1, :body => "ZOMG A COMMENT" } + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } ] }, @root_hash) end @@ -280,7 +352,7 @@ class AssociationTest < ActiveModel::TestCase assert_equal({ :comments => [ - { :id => 1, :body => "ZOMG A COMMENT" } + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } ] }, @root_hash) end @@ -336,7 +408,7 @@ class AssociationTest < ActiveModel::TestCase :comment_ids => [ 1 ] }, :comments => [ - { :id => 1, :body => "ZOMG A COMMENT" } + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } ] }, json) end @@ -387,7 +459,7 @@ class AssociationTest < ActiveModel::TestCase :comment_ids => [ 1 ] }, :comments => [ - { :id => 1, :body => "ZOMG A COMMENT" } + { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" } ] }, json) end