Back to Guides

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_scopemethod:

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 call render :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 set scope_name to as the current authorization scope

IMPORTANT: Since the scope is set at render, you may want to customize it so that current_user isn't called on every request. This was also a problem in 0.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
    }
  }
}

results matching ""

    No results matching ""