Merge pull request #8 from Adman65/association_options

Closes #4 (Associations with :as)
This commit is contained in:
José Valim 2011-12-06 06:19:52 -08:00
commit 3b38af1b49
3 changed files with 122 additions and 5 deletions

View File

@ -351,6 +351,32 @@ for the current user.
NOTE: The logic for deciding which comments a user should see still belongs in the model layer. In general, you should encapsulate concerns that require making direct Active Record queries in scopes or public methods on your models.
h4. Modifying Associations
You can also rename associations if required. Say for example you have an association that
makes sense to be named one thing in your code, but another when data is serialized.
You can use the <code>:as</code> option to specify a different name for an association.
Here is an exmaple:
<pre lang="ruby">
class UserSerializer < ActiveModel::Serializer
has_many :followed_posts, :as => :posts
has_one :owne_account, :as => :account
end
</pre>
Using the <code>:as</code> without a <code>:serializer</code> option will use implicit detection
to determine a serializer. In this example, you'd have to define two classes: <code>PostSerializer</code>
and <code>AccountSerializer</code>. You can also add the <code>:serializer</code> option
to set it explicitly:
<pre lang="ruby">
class UserSerializer < ActiveModel::Serializer
has_many :followed_posts, :as => :posts, :serializer => CustomPostSerializer
has_one :owne_account, :as => :account, :serializer => PrivateAccountSerializer
end
</pre>
h3. Customizing Associations
Not all front-ends expect embedded documents in the same form. In these cases, you can override the
@ -431,6 +457,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

View File

@ -68,6 +68,10 @@ module ActiveModel
def serializer
options[:serializer]
end
def key
options[:as] || name
end
end
class HasMany < Config #:nodoc:
@ -122,7 +126,14 @@ module ActiveModel
class_eval "def #{attr}() object.#{attr} end", __FILE__, __LINE__
end
options[:serializer] ||= const_get("#{attr.to_s.camelize}Serializer")
# if :as is specified without :serializer, then use conventions
# to determine the serializer
if options[:as] && !options[:serializer]
options[:serializer] = const_get("#{options[:as].to_s.camelize.singularize}Serializer")
else
options[:serializer] ||= const_get("#{attr.to_s.camelize}Serializer")
end
klass.new(attr, options)
end
end
@ -212,7 +223,7 @@ module ActiveModel
_associations.each do |association|
associated_object = send(association.name)
hash[association.name] = association.serialize(associated_object, scope)
hash[association.key] = association.serialize(associated_object, scope)
end
hash
@ -225,7 +236,7 @@ module ActiveModel
_associations.each do |association|
associated_object = send(association.name)
hash[association.name] = association.serialize_ids(associated_object, scope)
hash[association.key] = association.serialize_ids(associated_object, scope)
end
hash
@ -250,4 +261,4 @@ class Array
def active_model_serializer
ActiveModel::ArraySerializer
end
end
end

View File

@ -34,6 +34,11 @@ class SerializerTest < ActiveModel::TestCase
end
class Post < Model
def initialize(attributes)
super(attributes)
self.comments ||= []
end
attr_accessor :comments
def active_model_serializer; PostSerializer; end
end
@ -429,4 +434,78 @@ class SerializerTest < ActiveModel::TestCase
{ :comment => { :title => "Comment1" } }
], serializer.as_json)
end
end
class CustomBlog < Blog
attr_accessor :public_posts, :public_user
end
class CustomBlogSerializer < ActiveModel::Serializer
has_many :public_posts, :as => :posts, :serializer => PostSerializer
has_one :public_user, :as => :user, :serializer => UserSerializer
end
def test_associations_with_as
posts = [
Post.new(:title => 'First Post', :body => 'text'),
Post.new(:title => 'Second Post', :body => 'text')
]
user = User.new
custom_blog = CustomBlog.new
custom_blog.public_posts = posts
custom_blog.public_user = user
serializer = CustomBlogSerializer.new(custom_blog, :scope => true)
assert_equal({
:custom_blog => {
:posts => [
{:title => 'First Post', :body => 'text', :comments => []},
{:title => 'Second Post', :body => 'text', :comments => []}
],
:user => {
:first_name => "Jose",
:last_name => "Valim", :ok => true,
:scope => true
}
}
}, serializer.as_json)
end
def test_implicity_detection_for_association_serializers
implicit_serializer = Class.new(ActiveModel::Serializer) do
root :custom_blog
const_set(:UserSerializer, UserSerializer)
const_set(:PostSerializer, PostSerializer)
has_many :public_posts, :as => :posts
has_one :public_user, :as => :user
end
posts = [
Post.new(:title => 'First Post', :body => 'text', :comments => []),
Post.new(:title => 'Second Post', :body => 'text', :comments => [])
]
user = User.new
custom_blog = CustomBlog.new
custom_blog.public_posts = posts
custom_blog.public_user = user
serializer = implicit_serializer.new(custom_blog, :scope => true)
assert_equal({
:custom_blog => {
:posts => [
{:title => 'First Post', :body => 'text', :comments => []},
{:title => 'Second Post', :body => 'text', :comments => []}
],
:user => {
:first_name => "Jose",
:last_name => "Valim", :ok => true,
:scope => true
}
}
}, serializer.as_json)
end
end