Adding 'embed_key' option to allow embedding attributes other than ID

This commit is contained in:
Jeremy Redburn 2013-01-28 09:41:46 -05:00
parent d638e21c8c
commit 7cd7d295e0
3 changed files with 134 additions and 18 deletions

View File

@ -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 **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 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 easily see all of the data per type, rather than having to recursively scan the

View File

@ -103,6 +103,14 @@ module ActiveModel
end end
end end
def embed_key
if key = option(:embed_key)
key
else
:id
end
end
def serialize def serialize
associated_object.map do |item| associated_object.map do |item|
find_serializable(item).serializable_hash find_serializable(item).serializable_hash
@ -120,11 +128,11 @@ module ActiveModel
# return collection.ids if collection.respond_to?(:ids) # return collection.ids if collection.respond_to?(:ids)
ids_key = "#{key.to_s.singularize}_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) source_serializer.object.send(ids_key)
else else
associated_object.map do |item| associated_object.map do |item|
item.read_attribute_for_serialization(:id) item.read_attribute_for_serialization(embed_key)
end end
end end
end end
@ -163,6 +171,14 @@ module ActiveModel
end end
end end
def embed_key
if key = option(:embed_key)
key
else
:id
end
end
def polymorphic_key def polymorphic_key
associated_object.class.to_s.demodulize.underscore.to_sym associated_object.class.to_s.demodulize.underscore.to_sym
end end
@ -191,15 +207,15 @@ module ActiveModel
if associated_object if associated_object
{ {
:type => polymorphic_key, :type => polymorphic_key,
:id => associated_object.read_attribute_for_serialization(:id) :id => associated_object.read_attribute_for_serialization(embed_key)
} }
else else
nil nil
end 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") source_serializer.object.send("#{name}_id")
elsif associated_object elsif associated_object
associated_object.read_attribute_for_serialization(:id) associated_object.read_attribute_for_serialization(embed_key)
else else
nil nil
end end

View File

@ -34,12 +34,12 @@ class AssociationTest < ActiveModel::TestCase
@root_hash = {} @root_hash = {}
@post = Model.new(:title => "New Post", :body => "Body") @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.comments = [ @comment ]
@post.comment = @comment @post.comment = @comment
@comment_serializer_class = def_serializer do @comment_serializer_class = def_serializer do
attributes :id, :body attributes :id, :external_id, :body
end end
@post_serializer_class = def_serializer do @post_serializer_class = def_serializer do
@ -75,7 +75,7 @@ class AssociationTest < ActiveModel::TestCase
assert_equal({ assert_equal({
:comments => [ :comments => [
{ :id => 1, :body => "ZOMG A COMMENT" } { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
] ]
}, @root_hash) }, @root_hash)
end end
@ -105,7 +105,7 @@ class AssociationTest < ActiveModel::TestCase
}, @hash) }, @hash)
assert_equal({ assert_equal({
:comments => [{ :id => 1, :body => "ZOMG A COMMENT" }] :comments => [{ :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }]
}, @root_hash) }, @root_hash)
end end
end end
@ -124,7 +124,7 @@ class AssociationTest < ActiveModel::TestCase
assert_equal({ assert_equal({
:comments => [ :comments => [
{ :id => 1, :body => "ZOMG A COMMENT" } { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
] ]
}, @root_hash) }, @root_hash)
end end
@ -142,7 +142,7 @@ class AssociationTest < ActiveModel::TestCase
assert_equal({ assert_equal({
:comments => [ :comments => [
{ :id => 1, :body => "ZOMG A COMMENT" } { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
] ]
}, @root_hash) }, @root_hash)
end end
@ -160,7 +160,7 @@ class AssociationTest < ActiveModel::TestCase
assert_equal({ assert_equal({
:comments => [ :comments => [
{ :id => 1, :body => "ZOMG A COMMENT" } { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
] ]
}, @root_hash) }, @root_hash)
end end
@ -178,7 +178,79 @@ class AssociationTest < ActiveModel::TestCase
assert_equal({ assert_equal({
:comments => [ :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) }, @root_hash)
end end
@ -192,7 +264,7 @@ class AssociationTest < ActiveModel::TestCase
assert_equal({ assert_equal({
:comments => [ :comments => [
{ :id => 1, :body => "ZOMG A COMMENT" } { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
] ]
}, @hash) }, @hash)
@ -237,7 +309,7 @@ class AssociationTest < ActiveModel::TestCase
assert_equal({ assert_equal({
:comments => [ :comments => [
{ :id => 1, :body => "ZOMG A COMMENT" } { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
] ]
}, @root_hash) }, @root_hash)
end end
@ -280,7 +352,7 @@ class AssociationTest < ActiveModel::TestCase
assert_equal({ assert_equal({
:comments => [ :comments => [
{ :id => 1, :body => "ZOMG A COMMENT" } { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
] ]
}, @root_hash) }, @root_hash)
end end
@ -336,7 +408,7 @@ class AssociationTest < ActiveModel::TestCase
:comment_ids => [ 1 ] :comment_ids => [ 1 ]
}, },
:comments => [ :comments => [
{ :id => 1, :body => "ZOMG A COMMENT" } { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
] ]
}, json) }, json)
end end
@ -387,7 +459,7 @@ class AssociationTest < ActiveModel::TestCase
:comment_ids => [ 1 ] :comment_ids => [ 1 ]
}, },
:comments => [ :comments => [
{ :id => 1, :body => "ZOMG A COMMENT" } { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
] ]
}, json) }, json)
end end