Remove implicit constant lookup in serializers in favor active_model_serializer API.

This commit is contained in:
José Valim 2011-12-21 08:48:29 +01:00
parent 97ff4d28e6
commit cc5f102e2d
3 changed files with 36 additions and 97 deletions

View File

@ -456,36 +456,7 @@ The +association_ids+ helper will use the overridden version of the association,
this case, +association_ids+ will only include the ids of the comments provided by the
+comments+ method.
h3. Special Association Serializers
So far, associations defined in serializers use either the +as_json+ method on the model
or the defined serializer for the association type. Sometimes, you may want to serialize
associated models differently when they are requested as part of another resource than
when they are requested on their own.
For instance, we might want to provide the full comment when it is requested directly,
but only its title when requested as part of the post. To achieve this, you can define
a serializer for associated objects nested inside the main serializer.
<pre lang="ruby">
class PostSerializer < ActiveModel::Serializer
class CommentSerializer < ActiveModel::Serializer
attributes :id, :title
end
# same as before
# ...
end
</pre>
In other words, if a +PostSerializer+ is trying to serialize comments, it will first
look for +PostSerializer::CommentSerializer+ before falling back to +CommentSerializer+
and finally +comment.as_json+.
h3. Overriding the Defaults
h4. Authorization Scope
h3. Authorization Scope
By default, the authorization scope for serializers is +:current_user+. This means
that when you call +render json: @post+, the controller will automatically call

View File

@ -80,32 +80,47 @@ module ActiveModel
def key
options[:key] || name
end
protected
def find_serializable(object, scope, context, options)
if serializer
serializer.new(object, scope, options)
elsif object.respond_to?(:active_model_serializer) && (ams = object.active_model_serializer)
ams.new(object, scope, options)
else
object
end
end
end
class HasMany < Config #:nodoc:
def serialize(collection, scope, options)
collection.map do |item|
serializer.new(item, scope, options).serializable_hash
def serialize(collection, scope, context, options)
array = collection.map do |item|
find_serializable(item, scope, context, options).as_json(:root => false)
end
{ key => array }
end
def serialize_ids(collection, scope)
# use named scopes if they are present
# Use pluck or select_columns if available
# return collection.ids if collection.respond_to?(:ids)
collection.map do |item|
array = collection.map do |item|
item.read_attribute_for_serialization(:id)
end
{ key => array }
end
end
class HasOne < Config #:nodoc:
def serialize(object, scope, options)
object && serializer.new(object, scope, options).serializable_hash
def serialize(object, scope, context, options)
{ key => object && find_serializable(object, scope, context, options).as_json(:root => false) }
end
def serialize_ids(object, scope)
object && object.read_attribute_for_serialization(:id)
{ key => object && object.read_attribute_for_serialization(:id) }
end
end
end
@ -141,12 +156,6 @@ module ActiveModel
unless method_defined?(attr)
class_eval "def #{attr}() object.#{attr} end", __FILE__, __LINE__
end
options[:serializer] ||= begin
serializer_class = (options[:key] || attr).to_s.classify
const_get("#{serializer_class}Serializer")
end
klass.new(attr, options)
end
end
@ -286,6 +295,8 @@ module ActiveModel
end
end
# Merge associations for embed case by always adding
# root associations to the given hash.
def merge_associations(hash, associations)
associations.each do |key, value|
if hash[key]
@ -303,7 +314,7 @@ module ActiveModel
_associations.each do |association|
associated_object = send(association.name)
hash[association.key] = association.serialize(associated_object, scope, :hash => @hash)
hash.merge! association.serialize(associated_object, scope, self, :hash => @hash)
end
hash
@ -316,7 +327,7 @@ module ActiveModel
_associations.each do |association|
associated_object = send(association.name)
hash[association.key] = association.serialize_ids(associated_object, scope)
hash.merge! association.serialize_ids(associated_object, scope)
end
hash

View File

@ -78,8 +78,13 @@ class SerializerTest < ActiveModel::TestCase
{ :title => @comment.read_attribute_for_serialization(:title) }
end
def as_json(*)
{ :comment => serializable_hash }
def as_json(options=nil)
options ||= {}
if options[:root] == false
serializable_hash
else
{ :comment => serializable_hash }
end
end
end
@ -189,65 +194,17 @@ class SerializerTest < ActiveModel::TestCase
}, json)
end
def test_implicit_serializer
author_serializer = Class.new(ActiveModel::Serializer) do
attributes :first_name
end
blog_serializer = Class.new(ActiveModel::Serializer) do
const_set(:AuthorSerializer, author_serializer)
has_one :author
end
user = User.new
blog = Blog.new
blog.author = user
json = blog_serializer.new(blog, user).as_json
assert_equal({
:author => {
:first_name => "Jose"
}
}, json)
end
def test_implicit_serializer_for_has_many
blog_with_posts = Class.new(Blog) do
attr_accessor :posts
end
blog_serializer = Class.new(ActiveModel::Serializer) do
const_set(:PostSerializer, PostSerializer)
has_many :posts
end
user = User.new
blog = blog_with_posts.new
blog.posts = [Post.new(:title => 'test')]
json = blog_serializer.new(blog, user).as_json
assert_equal({
:posts => [{
:title => "test",
:body => nil,
:comments => []
}]
}, json)
end
def test_overridden_associations
author_serializer = Class.new(ActiveModel::Serializer) do
attributes :first_name
end
blog_serializer = Class.new(ActiveModel::Serializer) do
const_set(:PersonSerializer, author_serializer)
def person
object.author
end
has_one :person
has_one :person, :serializer => author_serializer
end
user = User.new