aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@gmail.com>2011-11-25 09:58:18 +0000
committerJosé Valim <jose.valim@gmail.com>2011-11-25 09:59:35 +0000
commitfcacc6986ab60f1fb2e423a73bf47c7abd7b191d (patch)
treeb4176f645915ac80d2ba43b54aa836d3bc4f7d21 /actionpack
parent4565c871a63dddcb9543eb5be315f152ac551c17 (diff)
parent696d01f7f4a8ed787924a41cce6df836cd73c46f (diff)
downloadrails-fcacc6986ab60f1fb2e423a73bf47c7abd7b191d.tar.gz
rails-fcacc6986ab60f1fb2e423a73bf47c7abd7b191d.tar.bz2
rails-fcacc6986ab60f1fb2e423a73bf47c7abd7b191d.zip
Merge branch 'serializers'
This implements the ActiveModel::Serializer object. Includes code, tests, generators and guides. From José and Yehuda with love. Conflicts: railties/CHANGELOG.md
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb1
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb20
-rw-r--r--actionpack/lib/action_controller/metal/serialization.rb51
-rw-r--r--actionpack/test/controller/render_json_test.rb48
5 files changed, 110 insertions, 11 deletions
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index f4eaa2fd1b..3829e60bb0 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -31,6 +31,7 @@ module ActionController
autoload :RequestForgeryProtection
autoload :Rescue
autoload :Responder
+ autoload :Serialization
autoload :SessionManagement
autoload :Streaming
autoload :Testing
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 98bfe72fef..cfb9cf5e6e 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -190,6 +190,7 @@ module ActionController
Redirecting,
Rendering,
Renderers::All,
+ Serialization,
ConditionalGet,
RackDelegation,
SessionManagement,
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index 0ad9dbeda9..6e9ce450ac 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/object/blank'
+require 'set'
module ActionController
# See <tt>Renderers.add</tt>
@@ -12,16 +13,13 @@ module ActionController
included do
class_attribute :_renderers
- self._renderers = {}.freeze
+ self._renderers = Set.new.freeze
end
module ClassMethods
def use_renderers(*args)
- new = _renderers.dup
- args.each do |key|
- new[key] = RENDERERS[key]
- end
- self._renderers = new.freeze
+ renderers = _renderers + args
+ self._renderers = renderers.freeze
end
alias use_renderer use_renderers
end
@@ -31,10 +29,10 @@ module ActionController
end
def _handle_render_options(options)
- _renderers.each do |name, value|
- if options.key?(name.to_sym)
+ _renderers.each do |name|
+ if options.key?(name)
_process_options(options)
- return send("_render_option_#{name}", options.delete(name.to_sym), options)
+ return send("_render_option_#{name}", options.delete(name), options)
end
end
nil
@@ -42,7 +40,7 @@ module ActionController
# Hash of available renderers, mapping a renderer name to its proc.
# Default keys are :json, :js, :xml.
- RENDERERS = {}
+ RENDERERS = Set.new
# Adds a new renderer to call within controller actions.
# A renderer is invoked by passing its name as an option to
@@ -79,7 +77,7 @@ module ActionController
# <tt>ActionController::MimeResponds#respond_with</tt>
def self.add(key, &block)
define_method("_render_option_#{key}", &block)
- RENDERERS[key] = block
+ RENDERERS << key.to_sym
end
module All
diff --git a/actionpack/lib/action_controller/metal/serialization.rb b/actionpack/lib/action_controller/metal/serialization.rb
new file mode 100644
index 0000000000..628d5996d7
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/serialization.rb
@@ -0,0 +1,51 @@
+module ActionController
+ # Action Controller Serialization
+ #
+ # Overrides render :json to check if the given object implements +active_model_serializer+
+ # as a method. If so, use the returned serializer instead of calling +to_json+ in the object.
+ #
+ # This module also provides a serialization_scope method that allows you to configure the
+ # +serialization_scope+ of the serializer. Most apps will likely set the +serialization_scope+
+ # to the current user:
+ #
+ # class ApplicationController < ActionController::Base
+ # serialization_scope :current_user
+ # end
+ #
+ # If you need more complex scope rules, you can simply override the serialization_scope:
+ #
+ # class ApplicationController < ActionController::Base
+ # private
+ #
+ # def serialization_scope
+ # current_user
+ # end
+ # end
+ #
+ module Serialization
+ extend ActiveSupport::Concern
+
+ include ActionController::Renderers
+
+ included do
+ class_attribute :_serialization_scope
+ end
+
+ def serialization_scope
+ send(_serialization_scope)
+ end
+
+ def _render_option_json(json, options)
+ if json.respond_to?(:active_model_serializer) && (serializer = json.active_model_serializer)
+ json = serializer.new(json, serialization_scope)
+ end
+ super
+ end
+
+ module ClassMethods
+ def serialization_scope(scope)
+ self._serialization_scope = scope
+ end
+ end
+ end
+end
diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb
index fc604a2db3..dc09812ba3 100644
--- a/actionpack/test/controller/render_json_test.rb
+++ b/actionpack/test/controller/render_json_test.rb
@@ -15,9 +15,36 @@ class RenderJsonTest < ActionController::TestCase
end
end
+ class JsonSerializer
+ def initialize(object, scope)
+ @object, @scope = object, scope
+ end
+
+ def as_json(*)
+ { :object => @object.as_json, :scope => @scope.as_json }
+ end
+ end
+
+ class JsonSerializable
+ def initialize(skip=false)
+ @skip = skip
+ end
+
+ def active_model_serializer
+ JsonSerializer unless @skip
+ end
+
+ def as_json(*)
+ { :serializable_object => true }
+ end
+ end
+
class TestController < ActionController::Base
protect_from_forgery
+ serialization_scope :current_user
+ attr_reader :current_user
+
def self.controller_path
'test'
end
@@ -61,6 +88,16 @@ class RenderJsonTest < ActionController::TestCase
def render_json_without_options
render :json => JsonRenderable.new
end
+
+ def render_json_with_serializer
+ @current_user = Struct.new(:as_json).new(:current_user => true)
+ render :json => JsonSerializable.new
+ end
+
+ def render_json_with_serializer_api_but_without_serializer
+ @current_user = Struct.new(:as_json).new(:current_user => true)
+ render :json => JsonSerializable.new(true)
+ end
end
tests TestController
@@ -132,4 +169,15 @@ class RenderJsonTest < ActionController::TestCase
get :render_json_without_options
assert_equal '{"a":"b"}', @response.body
end
+
+ def test_render_json_with_serializer
+ get :render_json_with_serializer
+ assert_match '"scope":{"current_user":true}', @response.body
+ assert_match '"object":{"serializable_object":true}', @response.body
+ end
+
+ def test_render_json_with_serializer_api_but_without_serializer
+ get :render_json_with_serializer_api_but_without_serializer
+ assert_match '{"serializable_object":true}', @response.body
+ end
end