Skip to content

Commit 7acbb76

Browse files
committed
Merge pull request #1650 from bf4/fix_serialization_scope
[FIX] serialization scope options
2 parents 53c7e6e + 881edd2 commit 7acbb76

File tree

4 files changed

+70
-43
lines changed

4 files changed

+70
-43
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
Breaking changes:
44

55
Features:
6+
- [#1650](https://github.com/rails-api/active_model_serializers/pull/1650) Fix serialization scope options `scope`, `scope_name`
7+
take precedence over `serialization_scope` in the controller.
8+
Fix tests that required tearing down dynamic methods. (@bf4)
69
- [#1644](https://github.com/rails-api/active_model_serializers/pull/1644) Include adapter name in cache key so
710
that the same serializer can be cached per adapter. (@bf4 via #1346 by @kevintyll)
811
- [#1642](https://github.com/rails-api/active_model_serializers/pull/1642) Prefer object.cache_key over the generated

lib/action_controller/serialization.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ def get_serializer(resource, options = {})
3030
options[:adapter] = false
3131
end
3232
serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)
33-
serializable_resource.serialization_scope ||= serialization_scope
34-
serializable_resource.serialization_scope_name = _serialization_scope
33+
serializable_resource.serialization_scope ||= options.fetch(:scope) { serialization_scope }
34+
serializable_resource.serialization_scope_name = options.fetch(:scope_name) { _serialization_scope }
3535
# For compatibility with the JSON renderer: `json.to_json(options) if json.is_a?(String)`.
3636
# Otherwise, since `serializable_resource` is not a string, the renderer would call
3737
# `to_json` on a String and given odd results, such as `"".to_json #=> '""'`

lib/active_model/serializer.rb

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,18 @@ def self.get_serializer_for(klass)
9696
end
9797
end
9898

99-
def self._serializer_instance_method_defined?(name)
100-
_serializer_instance_methods.include?(name)
99+
# rubocop:disable Style/ClassVars
100+
def self.method_added(method_name)
101+
@@_serializer_instance_methods ||= Hash.new { |h, k| h[k] = Set.new }
102+
@@_serializer_instance_methods[self] << method_name
101103
end
102104

103-
# TODO: Fix load-order failures when different serializer instances define different
104-
# scope methods
105-
def self._serializer_instance_methods
106-
@_serializer_instance_methods ||= (public_instance_methods - Object.public_instance_methods).to_set
105+
def self._serializer_instance_method_defined?(name)
106+
@_serializer_instance_methods ||= (ActiveModel::Serializer.public_instance_methods - Object.public_instance_methods).to_set
107+
@_serializer_instance_methods.include?(name) ||
108+
@@_serializer_instance_methods[self].include?(name)
107109
end
108-
private_class_method :_serializer_instance_methods
110+
# rubocop:enable Style/ClassVars
109111

110112
attr_accessor :object, :root, :scope
111113

test/action_controller/serialization_scope_name_test.rb

Lines changed: 56 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
require 'test_helper'
22

33
module SerializationScopeTesting
4+
def self.undef_serializer_dynamic_scope_methods
5+
[PostSerializer, PostViewContextSerializer]. each do |serializer_class|
6+
instance_method_cache = serializer_class.instance_variable_get(:@_serializer_instance_methods)
7+
class_method_cache = serializer_class.class_variable_get(:@@_serializer_instance_methods)[serializer_class]
8+
[:view_context, :current_user].each do |scope_method|
9+
serializer_class.send(:undef_method, scope_method) if serializer_class.method_defined?(scope_method)
10+
instance_method_cache && instance_method_cache.delete(scope_method)
11+
class_method_cache && class_method_cache.delete(scope_method)
12+
end
13+
end
14+
end
415
class User < ActiveModelSerializers::Model
516
attr_accessor :id, :name, :admin
617
def admin?
@@ -70,6 +81,10 @@ def comments
7081
class DefaultScopeTest < ActionController::TestCase
7182
tests PostTestController
7283

84+
teardown do
85+
SerializationScopeTesting.undef_serializer_dynamic_scope_methods
86+
end
87+
7388
def test_default_serialization_scope
7489
assert_equal :current_user, @controller._serialization_scope
7590
end
@@ -120,6 +135,10 @@ def serializer
120135
end
121136
tests PostViewContextTestController
122137

138+
teardown do
139+
SerializationScopeTesting.undef_serializer_dynamic_scope_methods
140+
end
141+
123142
def test_defined_serialization_scope
124143
assert_equal :view_context, @controller._serialization_scope
125144
end
@@ -158,8 +177,6 @@ def test_serialization_scope_admin
158177
assert_equal expected_json, @response.body
159178
end
160179
end
161-
# FIXME: Has bugs. See comments below and
162-
# https://github.com/rails-api/active_model_serializers/issues/1509
163180
class NilSerializationScopeTest < ActionController::TestCase
164181
class PostViewContextTestController < ActionController::Base
165182
serialization_scope nil
@@ -171,12 +188,15 @@ def render_post_with_no_scope
171188
render json: new_post, serializer: PostSerializer, adapter: :json
172189
end
173190

174-
# TODO: run test when
175-
# global state in Serializer._serializer_instance_methods is fixed
176-
# def render_post_with_passed_in_scope
177-
# self.current_user = User.new(id: 3, name: 'Pete', admin: false)
178-
# render json: new_post, serializer: PostSerializer, adapter: :json, scope: current_user, scope_name: :current_user
179-
# end
191+
def render_post_with_passed_in_scope
192+
self.current_user = User.new(id: 3, name: 'Pete', admin: false)
193+
render json: new_post, serializer: PostSerializer, adapter: :json, scope: current_user, scope_name: :current_user
194+
end
195+
196+
def render_post_with_passed_in_scope_without_scope_name
197+
self.current_user = User.new(id: 3, name: 'Pete', admin: false)
198+
render json: new_post, serializer: PostSerializer, adapter: :json, scope: current_user
199+
end
180200

181201
private
182202

@@ -186,6 +206,10 @@ def new_post
186206
end
187207
tests PostViewContextTestController
188208

209+
teardown do
210+
SerializationScopeTesting.undef_serializer_dynamic_scope_methods
211+
end
212+
189213
def test_nil_serialization_scope
190214
assert_nil @controller._serialization_scope
191215
end
@@ -194,37 +218,35 @@ def test_nil_serialization_scope_object
194218
assert_nil @controller.serialization_scope
195219
end
196220

197-
# TODO: change to NoMethodError and match 'admin?' when the
198-
# global state in Serializer._serializer_instance_methods is fixed
199221
def test_nil_scope
200-
if Rails.version.start_with?('4.0')
201-
exception_class = NoMethodError
202-
exception_matcher = 'admin?'
203-
else
204-
exception_class = NameError
205-
exception_matcher = /admin|current_user/
206-
end
207-
exception = assert_raises(exception_class) do
222+
exception_matcher = /current_user/
223+
exception = assert_raises(NameError) do
208224
get :render_post_with_no_scope
209225
end
210226
assert_match exception_matcher, exception.message
211227
end
212228

213-
# TODO: run test when
214-
# global state in Serializer._serializer_instance_methods is fixed
215-
# def test_nil_scope_passed_in_current_user
216-
# get :render_post_with_passed_in_scope
217-
# expected_json = {
218-
# post: {
219-
# id: 4,
220-
# title: 'Title',
221-
# body: "The 'scope' is the 'current_user': true",
222-
# comments: [
223-
# { id: 2, body: 'Scoped' }
224-
# ]
225-
# }
226-
# }.to_json
227-
# assert_equal expected_json, @response.body
228-
# end
229+
def test_serialization_scope_is_and_nil_scope_passed_in_current_user
230+
get :render_post_with_passed_in_scope
231+
expected_json = {
232+
post: {
233+
id: 4,
234+
title: 'Title',
235+
body: "The 'scope' is the 'current_user': true",
236+
comments: [
237+
{ id: 2, body: 'Scoped' }
238+
]
239+
}
240+
}.to_json
241+
assert_equal expected_json, @response.body
242+
end
243+
244+
def test_serialization_scope_is_nil_and_scope_passed_in_current_user_without_scope_name
245+
exception_matcher = /current_user/
246+
exception = assert_raises(NameError) do
247+
get :render_post_with_passed_in_scope_without_scope_name
248+
end
249+
assert_match exception_matcher, exception.message
250+
end
229251
end
230252
end

0 commit comments

Comments
 (0)