Scope - Authorization
In Serializers
scope
method within a serializer allows access to an external method. It's
intended to provide an authorization context to the serializer, e.g. so that
you may show an admin all comments on a post, else only show published comments.
scope
A method on the serializer instance that comes from options[:scope]
. It may be nil.
scope_name
An option passed in the rendering argument to the new serializer (options[:scope_name]
). The serializer must have a method with that name that calls the scope
, e.g.
def current_user
scope
end
Note: it does not define the method if the serializer instance responds to it.
For examples, assuming the serializer is instantiated in a controller
:
Options Used | Serializer#scope |
method definition |
---|---|---|
scope: current_user, scope_name: :current_user |
current_user |
Serializer#current_user calls controller.current_user |
scope: view_context, scope_name: :view_context |
view_context |
Serializer#view_context calls controller.view_context |
We can take advantage of the scope to customize the objects returned based on the current user (scope).
For example, we can limit the posts the current user sees to those they created:
class PostSerializer < ActiveModel::Serializer
attributes :id, :title, :body
# scope comments to those created_by the current user
has_many :comments do
object.comments.where(created_by: current_user)
end
end
Whether you write the method as above or as object.comments.where(created_by: scope)
is a matter of preference (assuming scope_name
has been set).
In Controllers
In the controller, the scope
and scope_name
options are equal to
serialization_scope
method:
scope
Set to send(scope_name)
when scope_name
is present and the controller responds to scope_name
.
scope_name
- Defaults to
:current_user
when you simply callrender :json
in a controller - Can be changed by setting
serialization_scope
in the controller - In a serializer, you can either either
scope
,current_user
(if defaults weren't overridden) or what you have setscope_name
to as the current authorization scope
IMPORTANT: Since the scope is set at
render
, you may want to customize it so thatcurrent_user
isn't called on every request. This was also a problem in0.9
.
For example, to change the scope from current_user
to view_context
:
class SomeController < ActionController::Base
+ serialization_scope :view_context
def current_user
User.new(id: 2, name: 'Bob', admin: true)
end
def edit
user = User.new(id: 1, name: 'Pete')
render json: user, serializer: AdminUserSerializer, adapter: :json_api
end
end
We could then use the method view_context
in our serializer:
class AdminUserSerializer < ActiveModel::Serializer
attributes :id, :name, :can_edit
def can_edit?
+ view_context.current_user.admin?
end
end
Assuming can_edit
is view_context.current_user.admin?
is true
, rendering #edit
action will give us:
{
"data": {
"id": "1",
"type": "users",
"attributes": {
"name": "Pete",
"can_edit": true
}
}
}