Skip to content

Support conditional setting of serialization_scope in controllers #1711

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Breaking changes:
- [#1662](https://github.com/rails-api/active_model_serializers/pull/1662) Drop support for Rails 4.0 and Ruby 2.0.0. (@remear)

Features:
- [#1711](https://github.com/rails-api/active_model_serializers/pull/1711) Set `serialization_scope` conditionally for specif actions in controller. (@yogeshjain999)
- [#1699](https://github.com/rails-api/active_model_serializers/pull/1699) String/Lambda support for conditional attributes/associations (@mtsmfm)
- [#1687](https://github.com/rails-api/active_model_serializers/pull/1687) Only calculate `_cache_digest` (in `cache_key`) when `skip_digest` is false. (@bf4)
- [#1647](https://github.com/rails-api/active_model_serializers/pull/1647) Restrict usage of `serializable_hash` options
Expand Down
28 changes: 28 additions & 0 deletions docs/general/serializers.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,34 @@ So that when we render the `#edit` action, we'll get

Where `can_edit` is `view_context.current_user.admin?` (true).

You can optionally tell what to set as `serialization_scope` for specific actions,
similar to `before_action` method in controllers.

```diff
class PostsController < ActionController::Base
serialization_scope :admin_user, only: :edit

def show
render json: @post, serializer: PostSerializer
end

def edit
@post.save
render json: @post, serializer: Admin::PostSerializer
end

private

def admin_user
User.new(id: 2, name: 'Bob', admin: true)
end

def current_user
User.new(id: 2, name: 'Bob', admin: false)
end
end
```

#### #read_attribute_for_serialization(key)

The serialized value for a given key. e.g. `read_attribute_for_serialization(:title) #=> 'Hello World'`
Expand Down
6 changes: 4 additions & 2 deletions lib/action_controller/serialization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ module Serialization
include ActionController::Renderers

module ClassMethods
def serialization_scope(scope)
self._serialization_scope = scope
def serialization_scope(scope, opts = {})
before_action(opts.slice(:only, :except, :if, :unless)) do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the method is only called at render, if not passed in as an option, I don't think I like having it eagerly evaluated in code that is pretty easy to handle in userland. Would you be okay with removing the code changes in favor documenting using a before_action as an alternative to passing an option to render?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also would except there to be bugs with current_user being called in a before_action with all the side-effects authentication-related libs can have

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree to your suggestion @bf4 about moving this to userland, created #1824 with added documentation.

self._serialization_scope = scope
end
end
end

Expand Down
54 changes: 46 additions & 8 deletions test/action_controller/serialization_scope_name_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ def json_key
end
class PostTestController < ActionController::Base
attr_accessor :current_user

def render_post
self.current_user = User.new(id: 3, name: 'Pete')
render json: new_post, serializer: serializer, adapter: :json
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this condition is identical to the one below it :)


def render_post_by_non_admin
self.current_user = User.new(id: 3, name: 'Pete', admin: false)
render json: new_post, serializer: serializer, adapter: :json
Expand Down Expand Up @@ -71,10 +77,12 @@ class DefaultScopeTest < ActionController::TestCase
tests PostTestController

def test_default_serialization_scope
get :render_post
Copy link
Member

@bf4 bf4 May 31, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is supposed to be missing in this test :)

assert_equal :current_user, @controller._serialization_scope
end

def test_default_serialization_scope_object
get :render_post
assert_equal @controller.current_user, @controller.serialization_scope
end

Expand Down Expand Up @@ -121,10 +129,12 @@ def serializer
tests PostViewContextTestController

def test_defined_serialization_scope
get :render_post
assert_equal :view_context, @controller._serialization_scope
end

def test_defined_serialization_scope_object
get :render_post
assert_equal @controller.view_context.class, @controller.serialization_scope.class
end

Expand Down Expand Up @@ -158,6 +168,42 @@ def test_serialization_scope_admin
assert_equal expected_json, @response.body
end
end
class ConditionalSerializationScopeTest < ActionController::TestCase
class PostConditionalViewContextTestController < PostTestController
serialization_scope :view_context, only: :render_post_by_admin

private

def serializer
PostViewContextSerializer
end
end

tests PostConditionalViewContextTestController

def test_conditional_serialization_scope_admin
get :render_post_by_admin
expected_json = {
post: {
id: 4,
title: 'Title',
body: "The 'scope' is the 'view_context': true",
comments: [
{ id: 1, body: 'Admin' }
]
}
}.to_json
assert_equal expected_json, @response.body
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change, no?


def test_conditional_serialization_scope_non_admin
exception_matcher = /view_context/
exception = assert_raises(NameError) do
get :render_post_by_non_admin
end
assert_match exception_matcher, exception.message
end
end
class NilSerializationScopeTest < ActionController::TestCase
class PostViewContextTestController < ActionController::Base
serialization_scope nil
Expand Down Expand Up @@ -187,14 +233,6 @@ def new_post
end
tests PostViewContextTestController

def test_nil_serialization_scope
assert_nil @controller._serialization_scope
end

def test_nil_serialization_scope_object
assert_nil @controller.serialization_scope
end

def test_nil_scope
exception_matcher = /current_user/
exception = assert_raises(NameError) do
Expand Down