From cc5f102e2db5cc23fda39175abe6143348ce1587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Dec 2011 08:48:29 +0100 Subject: [PATCH] Remove implicit constant lookup in serializers in favor active_model_serializer API. --- README.textile | 31 +----------------- lib/active_model/serializer.rb | 43 ++++++++++++++++--------- test/serializer_test.rb | 59 +++++----------------------------- 3 files changed, 36 insertions(+), 97 deletions(-) diff --git a/README.textile b/README.textile index 5750cae7..02943ede 100644 --- a/README.textile +++ b/README.textile @@ -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. - -
-class PostSerializer < ActiveModel::Serializer
-  class CommentSerializer < ActiveModel::Serializer
-    attributes :id, :title
-  end
-
-  # same as before
-  # ...
-end
-
- -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 diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 32a3abfa..031fc2eb 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -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 diff --git a/test/serializer_test.rb b/test/serializer_test.rb index 6bbc7d33..84c1d4f6 100644 --- a/test/serializer_test.rb +++ b/test/serializer_test.rb @@ -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