diff --git a/README.textile b/README.textile
index 8cd1cd3c..f31a0ddb 100644
--- a/README.textile
+++ b/README.textile
@@ -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 :as option to specify a different name for an association.
+Here is an exmaple:
+
+
+class UserSerializer < ActiveModel::Serializer
+ has_many :followed_posts, :as => :posts
+ has_one :owne_account, :as => :account
+end
+
+
+Using the :as without a :serializer option will use implicit detection
+to determine a serializer. In this example, you'd have to define two classes: PostSerializer
+and AccountSerializer. You can also add the :serializer option
+to set it explicitly:
+
+
+class UserSerializer < ActiveModel::Serializer
+ has_many :followed_posts, :as => :posts, :serializer => CustomPostSerializer
+ has_one :owne_account, :as => :account, :serializer => PrivateAccountSerializer
+end
+
+
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
diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb
index 0e23df2f..72c9065f 100644
--- a/lib/active_model/serializer.rb
+++ b/lib/active_model/serializer.rb
@@ -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
\ No newline at end of file
+end
diff --git a/test/serializer_test.rb b/test/serializer_test.rb
index 2c390fe3..b09e0e4c 100644
--- a/test/serializer_test.rb
+++ b/test/serializer_test.rb
@@ -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
\ No newline at end of file
+
+ 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