Merge pull request #461 from fivetanley/embed-in-root-key

Add embed_namespace and embed_in_root_key options
This commit is contained in:
Steve Klabnik 2014-08-13 13:28:28 -04:00
commit 7e7d8a721c
5 changed files with 193 additions and 3 deletions

View File

@ -522,6 +522,33 @@ Now, any associations will be supplied as an Array of IDs:
} }
``` ```
You may also choose to embed the IDs by the association's name underneath an
`embed_key` for the resource. For example, say we want to change `comment_ids`
to `comments` underneath a `links` key:
```ruby
class PostSerializer < ActiveModel::Serializer
attributes :id, :title, :body
has_many :comments, embed: ids, embed_namespace: :links
end
```
The JSON will look like this:
```json
{
"post": {
"id": 1,
"title": "New post",
"body": "A body!",
"links": {
"comments": [ 1, 2, 3 ]
}
}
}
```
Alternatively, you can choose to embed only the ids or the associated objects per association: Alternatively, you can choose to embed only the ids or the associated objects per association:
```ruby ```ruby
@ -589,6 +616,42 @@ this:
} }
``` ```
If you would like to namespace association JSON underneath a certain key in
the root document (say, `linked`), you can specify an `embed_in_root_key`:
```ruby
class PostSerializer < ActiveModel::Serializer
embed: ids, include: true, embed_in_root_key: :linked
attributes: :id, :title, :body
has_many :comments, :tags
end
```
The above would yield the following JSON document:
```json
{
"post": {
"id": 1,
"title": "New post",
"body": "A body!",
"comment_ids": [ 1, 2 ]
},
"linked": {
"comments": [
{ "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] },
{ "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] },
],
"tags": [
{ "id": 1, "name": "short" },
{ "id": 2, "name": "whiny" },
{ "id": 3, "name": "happy" }
]
}
}
```
When side-loading data, your serializer cannot have the `{ root: false }` option, When side-loading data, your serializer cannot have the `{ root: false }` option,
as this would lead to invalid JSON. If you do not have a root key, the `include` as this would lead to invalid JSON. If you do not have a root key, the `include`
instruction will be ignored instruction will be ignored

View File

@ -24,9 +24,21 @@ module ActiveModel
end end
end end
EMBED_IN_ROOT_OPTIONS = [
:include,
:embed_in_root,
:embed_in_root_key,
:embed_namespace
].freeze
def embed(type, options={}) def embed(type, options={})
CONFIG.embed = type CONFIG.embed = type
CONFIG.embed_in_root = true if options[:embed_in_root] || options[:include] if EMBED_IN_ROOT_OPTIONS.any? { |opt| options[opt].present? }
CONFIG.embed_in_root = true
end
if options[:embed_in_root_key].present?
CONFIG.embed_in_root_key = options[:embed_in_root_key]
end
ActiveSupport::Deprecation.warn <<-WARN ActiveSupport::Deprecation.warn <<-WARN
** Notice: embed is deprecated. ** ** Notice: embed is deprecated. **
The use of .embed method on a Serializer will be soon removed, as this should have a global scope and not a class scope. The use of .embed method on a Serializer will be soon removed, as this should have a global scope and not a class scope.
@ -141,8 +153,17 @@ end
associations.each_with_object({}) do |(name, association), hash| associations.each_with_object({}) do |(name, association), hash|
if included_associations.include? name if included_associations.include? name
if association.embed_ids? if association.embed_ids?
hash[association.key] = serialize_ids association ids = serialize_ids association
if association.embed_namespace?
hash = hash[association.embed_namespace] ||= {}
hash[association.key] = ids
else
hash[association.key] = ids
end
elsif association.embed_objects? elsif association.embed_objects?
if association.embed_namespace?
hash = hash[association.embed_namespace] ||= {}
end
hash[association.embedded_key] = serialize association hash[association.embedded_key] = serialize association
end end
end end
@ -165,6 +186,9 @@ end
associations.each_with_object({}) do |(name, association), hash| associations.each_with_object({}) do |(name, association), hash|
if included_associations.include? name if included_associations.include? name
if association.embed_in_root? if association.embed_in_root?
if association.embed_in_root_key?
hash = hash[association.embed_in_root_key] ||= {}
end
association_serializer = build_serializer(association) association_serializer = build_serializer(association)
hash.merge!(association_serializer.embedded_in_root_associations) {|key, oldval, newval| [newval, oldval].flatten } hash.merge!(association_serializer.embedded_in_root_associations) {|key, oldval, newval| [newval, oldval].flatten }
@ -231,4 +255,5 @@ end
end end
alias_method :serializable_hash, :serializable_object alias_method :serializable_hash, :serializable_object
end end
end end

View File

@ -18,16 +18,20 @@ module ActiveModel
@embed_key = options[:embed_key] || :id @embed_key = options[:embed_key] || :id
@key = options[:key] @key = options[:key]
@embedded_key = options[:root] || name @embedded_key = options[:root] || name
@embed_in_root_key = options.fetch(:embed_in_root_key) { CONFIG.embed_in_root_key }
@embed_namespace = options.fetch(:embed_namespace) { CONFIG.embed_namespace }
serializer = @options[:serializer] serializer = @options[:serializer]
@serializer_from_options = serializer.is_a?(String) ? serializer.constantize : serializer @serializer_from_options = serializer.is_a?(String) ? serializer.constantize : serializer
end end
attr_reader :name, :embed_ids, :embed_objects attr_reader :name, :embed_ids, :embed_objects
attr_accessor :embed_in_root, :embed_key, :key, :embedded_key, :root_key, :serializer_from_options, :options, :key_format attr_accessor :embed_in_root, :embed_key, :key, :embedded_key, :root_key, :serializer_from_options, :options, :key_format, :embed_in_root_key, :embed_namespace
alias embed_ids? embed_ids alias embed_ids? embed_ids
alias embed_objects? embed_objects alias embed_objects? embed_objects
alias embed_in_root? embed_in_root alias embed_in_root? embed_in_root
alias embed_in_root_key? embed_in_root_key
alias embed_namespace? embed_namespace
def embed=(embed) def embed=(embed)
@embed_ids = embed == :id || embed == :ids @embed_ids = embed == :id || embed == :ids

View File

@ -168,6 +168,63 @@ module ActiveModel
'post' => { title: 'Title 1', body: 'Body 1', comments: { my_content: ['fake'] } } 'post' => { title: 'Title 1', body: 'Body 1', comments: { my_content: ['fake'] } }
}, @post_serializer.as_json) }, @post_serializer.as_json)
end end
def test_associations_embedding_ids_including_objects_serialization_with_embed_in_root_key
@association.embed_in_root = true
@association.embed_in_root_key = :linked
@association.embed = :ids
assert_equal({
linked: {
comments: [
{ content: 'C1' },
{ content: 'C2' }
]
},
'post' => {
title: 'Title 1', body: 'Body 1',
'comment_ids' => @post.comments.map(&:object_id)
}
}, @post_serializer.as_json)
end
def test_associations_embedding_ids_using_embed_namespace_including_object_serialization_with_embed_in_root_key
@association.embed_in_root = true
@association.embed_in_root_key = :linked
@association.embed = :ids
@association.embed_namespace = :links
@association.key = :comments
assert_equal({
linked: {
comments: [
{ content: 'C1' },
{ content: 'C2' }
]
},
'post' => {
title: 'Title 1', body: 'Body 1',
links: {
comments: @post.comments.map(&:object_id)
}
}
}, @post_serializer.as_json)
end
def test_associations_embedding_objects_using_embed_namespace
@association.embed = :objects
@association.embed_namespace = :links
assert_equal({
'post' => {
title: 'Title 1', body: 'Body 1',
links: {
comments: [
{ content: 'C1' },
{ content: 'C2' }
]
}
}
}, @post_serializer.as_json)
end
end end
end end
end end

View File

@ -161,6 +161,47 @@ module ActiveModel
'user' => { name: 'Name 1', email: 'mail@server.com', profile: { name: 'fake' } } 'user' => { name: 'Name 1', email: 'mail@server.com', profile: { name: 'fake' } }
}, @user_serializer.as_json) }, @user_serializer.as_json)
end end
def test_associations_embedding_ids_using_embed_namespace
@association.embed_namespace = :links
@association.embed = :ids
@association.key = :profile
assert_equal({
'user' => {
name: 'Name 1', email: 'mail@server.com',
links: {
profile: @user.profile.object_id
}
}
}, @user_serializer.as_json)
end
def test_asociations_embedding_objects_using_embed_namespace
@association.embed_namespace = :links
@association.embed = :objects
assert_equal({
'user' => {
name: 'Name 1', email: 'mail@server.com',
links: {
profile: { name: 'N1', description: 'D1' }
}
}
}, @user_serializer.as_json)
end
def test_associations_embedding_ids_using_embed_namespace_and_embed_in_root_key
@association.embed_in_root = true
@association.embed_in_root_key = :linked
@association.embed = :ids
assert_equal({
'user' => {
name: 'Name 1', email: 'mail@server.com', 'profile_id' => @user.profile.object_id
},
linked: {
'profiles' => [ { name: 'N1', description: 'D1' } ]
}
}, @user_serializer.as_json)
end
end end
end end
end end