Skip to content

Commit

Permalink
Add serialization_scope example [ci skip]
Browse files Browse the repository at this point in the history
  • Loading branch information
bf4 committed Jan 14, 2016
1 parent d448481 commit c36271a
Showing 1 changed file with 91 additions and 1 deletion.
92 changes: 91 additions & 1 deletion docs/general/serializers.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,97 @@ PR please :)

#### #scope

PR please :)
Allows you to include in the serializer access to an external method.

It's intended to provide an authorization context to the serializer.

`scope` refers to the name of a method in the serialization context, that
may be made available to the serializer instance. It is the value of the `scope:` option
to the new serializer.

When the `scope_name:` option is passed to the new serializer, and a method by that
name does not already exist, the serializer defines a method with that name that calls
the `scope` method.

That's a lot of words, so here's some examples:

First, let's assume the serializer is instantiated in the controller, since that's the usual scenario.
We'll refer to the serialization context as `controller`.

| options | `Serializer#scope` | method definition |
|-------- | ------------------|
| `scope: :current_user, scope_name: :current_user` | `:current_user` | `Serializer#current_user` calls `controller.current_user`
| `scope: :current_admin, scope_name: :current_admin` | `:current_admin` | `Serializer#current_admin` calls `controller.current_admin`

Since the method is dynamically defined, there is no concern with it being called when not used.
This was a problem in
[`0.9`](https://github.com/rails-api/active_model_serializers/tree/0-9-stable#customizing-scope)
when the [`current_user` method was being called on every request](https://github.com/rails-api/active_model_serializers/pull/1252#issuecomment-159810477).

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:

```ruby
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
```

##### Controller Authorization Context

In the controller, the scope/scope_name options are equal to
the [`serialization_scope`method](https://github.com/rails-api/active_model_serializers/blob/d02cd30fe55a3ea85e1d351b6e039620903c1871/lib/action_controller/serialization.rb#L13-L20),
which is `:current_user`, by default.

Thus, 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`:

```diff
class SomeController < ActionController::Base
+ serialization_scope :current_admin

def current_admin
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
```

The above example will also change the scope from `current_user` to
`current_admin`.

We could then use the controller method `current_admin` in our serializer, like so:

```diff
class AdminUserSerializer < ActiveModel::Serializer
attributes :id, :name, :can_edit

def can_edit?
+ current_admin.admin?
end
end
```

So that when we render the `#edit` action, we'll get

```json
{"data":{"id":"1","type":"users","attributes":{"name":"Pete","can_edit":true}}}
```

Where `can_edit` is `current_admin.admin?` (true).

#### #read_attribute_for_serialization(key)

Expand Down

0 comments on commit c36271a

Please sign in to comment.