Reference scope by same name as serialization scope

By default, the serialization scope uses current_user, and you can
now reference the scope as "current_user" in the serializer. If you
override the scope using "serialization_scope" in your controller,
it will use that method name instead.
This commit is contained in:
beerlington 2013-03-16 17:58:17 -04:00
parent 41a389a900
commit 4a13f86961
6 changed files with 106 additions and 11 deletions

View File

@ -1,5 +1,9 @@
# unreleased
* Add an alias for `scope` method to be the name of the context. By default
this is `current_user`. The name is automatically set when using
`serialization_scope` in the controller.
# VERSION 0.7

View File

@ -232,22 +232,22 @@ end
Within a serializer's methods, you can access the object being
serialized as `object`.
You can also access the `scope` method, which provides an
authorization context to your serializer. By default, scope
You can also access the `current_user` method, which provides an
authorization context to your serializer. By default, the context
is the current user of your application, but this
[can be customized](#customizing-scope).
Serializers will check for the presence of a method named
`include_[ATTRIBUTE]?` to determine whether a particular attribute should be
included in the output. This is typically used to customize output
based on `scope`. For example:
based on `current_user`. For example:
```ruby
class PostSerializer < ActiveModel::Serializer
attributes :id, :title, :body, :author
def include_author?
scope.admin?
current_user.admin?
end
end
```
@ -325,7 +325,7 @@ class PersonSerializer < ActiveModel::Serializer
def attributes
hash = super
if scope.admin?
if current_user.admin?
hash["ssn"] = object.ssn
hash["secret"] = object.mothers_maiden_name
end
@ -353,7 +353,7 @@ class PostSerializer < ActiveModel::Serializer
# only let the user see comments he created.
def comments
object.comments.where(:created_by => scope)
object.comments.where(:created_by => current_user)
end
end
```
@ -395,7 +395,7 @@ class PostSerializer < ActiveModel::Serializer
has_many :comments
def include_associations!
include! :author if scope.admin?
include! :author if current_user.admin?
include! :comments unless object.comments_disabled?
end
end
@ -587,7 +587,7 @@ Ajax requests, you probably just want to use the default embedded behavior.
## Customizing Scope
In a serializer, `scope` is the current authorization scope which the controller
In a serializer, `current_user` is the current authorization scope which the controller
provides to the serializer when you call `render :json`. By default, this is
`current_user`, but can be customized in your controller by calling
`serialization_scope`:
@ -598,6 +598,9 @@ class ApplicationController < ActionController::Base
end
```
The above example will also change the scope name from `current_user` to
`current_admin`.
Please note that, until now, `serialization_scope` doesn't accept a second
object with options for specifying which actions should or should not take a
given scope in consideration.
@ -626,12 +629,12 @@ class CitiesController < ApplicationController
def show
@city = City.find(params[:id])
render :json => @city, :scope => current_admin?
render :json => @city, :scope => current_admin, :scope_name => :current_admin
end
end
```
Assuming that the `current_admin?` method needs to make a query in the database
Assuming that the `current_admin` method needs to make a query in the database
for the current user, the advantage of this approach is that, by setting
`serialization_scope` to `nil`, the `index` action no longer will need to make
that query, only the `show` action will.

View File

@ -261,6 +261,13 @@ module ActiveModel
def initialize(object, options={})
@object, @options = object, options
scope_name = @options[:scope_name]
if scope_name && !respond_to?(scope_name)
self.class.class_eval do
define_method scope_name, lambda { scope }
end
end
end
def root_name

View File

@ -31,6 +31,7 @@ module ActiveModel
if serializer
serialization_scope = controller.send(:serialization_scope)
options[:scope] = serialization_scope unless options.has_key?(:scope)
options[:scope_name] = controller.send(:_serialization_scope)
options[:url_options] = controller.send(:url_options)
render(given_options.merge(self.options).merge(:json => serializer.new(resource, options)))
else

View File

@ -0,0 +1,67 @@
require 'test_helper'
require 'pathname'
class DefaultScopeNameTest < ActionController::TestCase
TestUser = Struct.new(:name, :admin)
class UserSerializer < ActiveModel::Serializer
attributes :admin?
def admin?
current_user.admin
end
end
class UserTestController < ActionController::Base
protect_from_forgery
before_filter { request.format = :json }
def current_user
TestUser.new('Pete', false)
end
def render_new_user
respond_with TestUser.new('pete', false), :serializer => UserSerializer
end
end
tests UserTestController
def test_default_scope_name
get :render_new_user
assert_equal '{"user":{"admin":false}}', @response.body
end
end
class SerializationScopeNameTest < ActionController::TestCase
TestUser = Struct.new(:name, :admin)
class AdminUserSerializer < ActiveModel::Serializer
attributes :admin?
def admin?
current_admin.admin
end
end
class AdminUserTestController < ActionController::Base
protect_from_forgery
serialization_scope :current_admin
before_filter { request.format = :json }
def current_admin
TestUser.new('Bob', true)
end
def render_new_user
respond_with TestUser.new('pete', false), :serializer => AdminUserSerializer
end
end
tests AdminUserTestController
def test_override_scope_name_with_controller
get :render_new_user
assert_equal '{"admin_user":{"admin":true}}', @response.body
end
end

View File

@ -1338,5 +1338,18 @@ class SerializerTest < ActiveModel::TestCase
end
def test_scope_name_method
serializer = Class.new(ActiveModel::Serializer) do
def has_permission?
current_user.super_user?
end
end
user = User.new
user.superuser = true
post = Post.new(:title => 'Foo')
a_serializer = serializer.new(post, :scope => user, :scope_name => :current_user)
assert a_serializer.has_permission?
end
end