Skip to content

Commit aad7779

Browse files
committed
Restrict serializable_hash to accepted options (#1647)
Restrict tests/impl from passing AMS options into serializable_hash
1 parent aec2848 commit aad7779

File tree

10 files changed

+109
-108
lines changed

10 files changed

+109
-108
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Breaking changes:
88
- [#1574](https://github.com/rails-api/active_model_serializers/pull/1574) Default key case for the JsonApi adapter changed to dashed. (@remear)
99

1010
Features:
11+
- [#1647](https://github.com/rails-api/active_model_serializers/pull/1647) Restrict usage of `serializable_hash` options
12+
to the ActiveModel::Serialization and ActiveModel::Serializers::JSON interface. (@bf4)
1113
- [#1645](https://github.com/rails-api/active_model_serializers/pull/1645) Transform keys referenced in values. (@remear)
1214
- [#1650](https://github.com/rails-api/active_model_serializers/pull/1650) Fix serialization scope options `scope`, `scope_name`
1315
take precedence over `serialization_scope` in the controller.

lib/active_model_serializers/adapter/json.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class Json < Base
44
def serializable_hash(options = nil)
55
options ||= {}
66
serialized_hash = { root => Attributes.new(serializer, instance_options).serializable_hash(options) }
7-
self.class.transform_key_casing!(serialized_hash, options)
7+
self.class.transform_key_casing!(serialized_hash, instance_options)
88
end
99
end
1010
end

lib/active_model_serializers/adapter/json_api.rb

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,13 @@ def self.default_key_transform
4343

4444
# {http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure}
4545
# {http://jsonapi.org/format/#document-top-level data and errors MUST NOT coexist in the same document.}
46-
def serializable_hash(options = nil)
47-
options ||= {}
46+
def serializable_hash(*)
4847
document = if serializer.success?
49-
success_document(options)
48+
success_document
5049
else
51-
failure_document(options)
50+
failure_document
5251
end
53-
self.class.transform_key_casing!(document, options)
52+
self.class.transform_key_casing!(document, instance_options)
5453
end
5554

5655
# {http://jsonapi.org/format/#document-top-level Primary data}
@@ -68,10 +67,10 @@ def serializable_hash(options = nil)
6867
# links: toplevel_links,
6968
# jsonapi: toplevel_jsonapi
7069
# }.reject! {|_,v| v.nil? }
71-
def success_document(options)
70+
def success_document
7271
is_collection = serializer.respond_to?(:each)
7372
serializers = is_collection ? serializer : [serializer]
74-
primary_data, included = resource_objects_for(serializers, options)
73+
primary_data, included = resource_objects_for(serializers)
7574

7675
hash = {}
7776
# toplevel_data
@@ -128,7 +127,7 @@ def success_document(options)
128127

129128
if is_collection && serializer.paginated?
130129
hash[:links] ||= {}
131-
hash[:links].update(pagination_links_for(serializer, options))
130+
hash[:links].update(pagination_links_for(serializer))
132131
end
133132

134133
hash
@@ -148,7 +147,7 @@ def success_document(options)
148147
# }.reject! {|_,v| v.nil? }
149148
# prs:
150149
# https://github.com/rails-api/active_model_serializers/pull/1004
151-
def failure_document(options)
150+
def failure_document
152151
hash = {}
153152
# PR Please :)
154153
# Jsonapi.add!(hash)
@@ -163,10 +162,10 @@ def failure_document(options)
163162
# ]
164163
if serializer.respond_to?(:each)
165164
hash[:errors] = serializer.flat_map do |error_serializer|
166-
Error.resource_errors(error_serializer, options)
165+
Error.resource_errors(error_serializer, instance_options)
167166
end
168167
else
169-
hash[:errors] = Error.resource_errors(serializer, options)
168+
hash[:errors] = Error.resource_errors(serializer, instance_options)
170169
end
171170
hash
172171
end
@@ -224,21 +223,21 @@ def fragment_cache(cached_hash, non_cached_hash)
224223
# [x] url helpers https://github.com/rails-api/active_model_serializers/issues/1269
225224
# meta
226225
# [x] https://github.com/rails-api/active_model_serializers/pull/1340
227-
def resource_objects_for(serializers, options)
226+
def resource_objects_for(serializers)
228227
@primary = []
229228
@included = []
230229
@resource_identifiers = Set.new
231-
serializers.each { |serializer| process_resource(serializer, true, options) }
232-
serializers.each { |serializer| process_relationships(serializer, @include_tree, options) }
230+
serializers.each { |serializer| process_resource(serializer, true) }
231+
serializers.each { |serializer| process_relationships(serializer, @include_tree) }
233232

234233
[@primary, @included]
235234
end
236235

237-
def process_resource(serializer, primary, options)
238-
resource_identifier = ResourceIdentifier.new(serializer, options).as_json
236+
def process_resource(serializer, primary)
237+
resource_identifier = ResourceIdentifier.new(serializer, instance_options).as_json
239238
return false unless @resource_identifiers.add?(resource_identifier)
240239

241-
resource_object = resource_object_for(serializer, options)
240+
resource_object = resource_object_for(serializer)
242241
if primary
243242
@primary << resource_object
244243
else
@@ -248,21 +247,21 @@ def process_resource(serializer, primary, options)
248247
true
249248
end
250249

251-
def process_relationships(serializer, include_tree, options)
250+
def process_relationships(serializer, include_tree)
252251
serializer.associations(include_tree).each do |association|
253-
process_relationship(association.serializer, include_tree[association.key], options)
252+
process_relationship(association.serializer, include_tree[association.key])
254253
end
255254
end
256255

257-
def process_relationship(serializer, include_tree, options)
256+
def process_relationship(serializer, include_tree)
258257
if serializer.respond_to?(:each)
259-
serializer.each { |s| process_relationship(s, include_tree, options) }
258+
serializer.each { |s| process_relationship(s, include_tree) }
260259
return
261260
end
262261
return unless serializer && serializer.object
263-
return unless process_resource(serializer, false, options)
262+
return unless process_resource(serializer, false)
264263

265-
process_relationships(serializer, include_tree, options)
264+
process_relationships(serializer, include_tree)
266265
end
267266

268267
# {http://jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes}
@@ -286,9 +285,9 @@ def attributes_for(serializer, fields)
286285
end
287286

288287
# {http://jsonapi.org/format/#document-resource-objects Document Resource Objects}
289-
def resource_object_for(serializer, options)
288+
def resource_object_for(serializer)
290289
resource_object = cache_check(serializer) do
291-
resource_object = ResourceIdentifier.new(serializer, options).as_json
290+
resource_object = ResourceIdentifier.new(serializer, instance_options).as_json
292291

293292
requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
294293
attributes = attributes_for(serializer, requested_fields)
@@ -297,7 +296,7 @@ def resource_object_for(serializer, options)
297296
end
298297

299298
requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
300-
relationships = relationships_for(serializer, requested_associations, options)
299+
relationships = relationships_for(serializer, requested_associations)
301300
resource_object[:relationships] = relationships if relationships.any?
302301

303302
links = links_for(serializer)
@@ -425,13 +424,13 @@ def resource_object_for(serializer, options)
425424
# id: 'required-id',
426425
# meta: meta
427426
# }.reject! {|_,v| v.nil? }
428-
def relationships_for(serializer, requested_associations, options)
427+
def relationships_for(serializer, requested_associations)
429428
include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(requested_associations)
430429
serializer.associations(include_tree).each_with_object({}) do |association, hash|
431430
hash[association.key] = Relationship.new(
432431
serializer,
433432
association.serializer,
434-
options,
433+
instance_options,
435434
options: association.options,
436435
links: association.links,
437436
meta: association.meta
@@ -499,8 +498,8 @@ def links_for(serializer)
499498
# end
500499
# prs:
501500
# https://github.com/rails-api/active_model_serializers/pull/1041
502-
def pagination_links_for(serializer, options)
503-
PaginationLinks.new(serializer.object, options[:serialization_context]).serializable_hash(options)
501+
def pagination_links_for(serializer)
502+
PaginationLinks.new(serializer.object, instance_options).as_json
504503
end
505504

506505
# {http://jsonapi.org/format/#document-meta Docment Meta}

lib/active_model_serializers/adapter/json_api/pagination_links.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,25 @@ class PaginationLinks
66

77
attr_reader :collection, :context
88

9-
def initialize(collection, context)
9+
def initialize(collection, adapter_options)
1010
@collection = collection
11-
@context = context
11+
@adapter_options = adapter_options
12+
@context = adapter_options.fetch(:serialization_context)
1213
end
1314

14-
def serializable_hash(options = {})
15+
def as_json
1516
per_page = collection.try(:per_page) || collection.try(:limit_value) || collection.size
1617
pages_from.each_with_object({}) do |(key, value), hash|
1718
params = query_parameters.merge(page: { number: value, size: per_page }).to_query
1819

19-
hash[key] = "#{url(options)}?#{params}"
20+
hash[key] = "#{url(adapter_options)}?#{params}"
2021
end
2122
end
2223

24+
protected
25+
26+
attr_reader :adapter_options
27+
2328
private
2429

2530
def pages_from

lib/active_model_serializers/adapter/null.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
module ActiveModelSerializers
22
module Adapter
33
class Null < Base
4-
# Since options param is not being used, underscored naming of the param
5-
def serializable_hash(_options = nil)
4+
def serializable_hash(*)
65
{}
76
end
87
end

lib/active_model_serializers/serializable_resource.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
module ActiveModelSerializers
44
class SerializableResource
5-
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links])
5+
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links, :serialization_context, :key_transform])
66
include ActiveModelSerializers::Logging
77

88
delegate :serializable_hash, :as_json, :to_json, to: :adapter

test/adapter/json/transform_test.rb

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,34 @@ def mock_request(key_transform = nil)
88
context = Minitest::Mock.new
99
context.expect(:request_url, URI)
1010
context.expect(:query_parameters, {})
11-
@options = {}
12-
@options[:key_transform] = key_transform if key_transform
13-
@options[:serialization_context] = context
11+
options = {}
12+
options[:key_transform] = key_transform if key_transform
13+
options[:serialization_context] = context
14+
serializer = CustomBlogSerializer.new(@blog)
15+
@adapter = ActiveModelSerializers::Adapter::Json.new(serializer, options)
1416
end
1517

1618
Post = Class.new(::Model)
1719
class PostSerializer < ActiveModel::Serializer
1820
attributes :id, :title, :body, :publish_at
1921
end
2022

21-
def setup
23+
setup do
2224
ActionController::Base.cache_store.clear
2325
@blog = Blog.new(id: 1, name: 'My Blog!!', special_attribute: 'neat')
24-
serializer = CustomBlogSerializer.new(@blog)
25-
@adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
2626
end
2727

2828
def test_transform_default
2929
mock_request
3030
assert_equal({
3131
blog: { id: 1, special_attribute: 'neat', articles: nil }
32-
}, @adapter.serializable_hash(@options))
32+
}, @adapter.serializable_hash)
3333
end
3434

3535
def test_transform_global_config
3636
mock_request
3737
result = with_config(key_transform: :camel_lower) do
38-
@adapter.serializable_hash(@options)
38+
@adapter.serializable_hash
3939
end
4040
assert_equal({
4141
blog: { id: 1, specialAttribute: 'neat', articles: nil }
@@ -45,7 +45,7 @@ def test_transform_global_config
4545
def test_transform_serialization_ctx_overrides_global_config
4646
mock_request(:camel)
4747
result = with_config(key_transform: :camel_lower) do
48-
@adapter.serializable_hash(@options)
48+
@adapter.serializable_hash
4949
end
5050
assert_equal({
5151
Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil }
@@ -56,36 +56,36 @@ def test_transform_undefined
5656
mock_request(:blam)
5757
result = nil
5858
assert_raises NoMethodError do
59-
result = @adapter.serializable_hash(@options)
59+
result = @adapter.serializable_hash
6060
end
6161
end
6262

6363
def test_transform_dash
6464
mock_request(:dash)
6565
assert_equal({
6666
blog: { id: 1, :"special-attribute" => 'neat', articles: nil }
67-
}, @adapter.serializable_hash(@options))
67+
}, @adapter.serializable_hash)
6868
end
6969

7070
def test_transform_unaltered
7171
mock_request(:unaltered)
7272
assert_equal({
7373
blog: { id: 1, special_attribute: 'neat', articles: nil }
74-
}, @adapter.serializable_hash(@options))
74+
}, @adapter.serializable_hash)
7575
end
7676

7777
def test_transform_camel
7878
mock_request(:camel)
7979
assert_equal({
8080
Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil }
81-
}, @adapter.serializable_hash(@options))
81+
}, @adapter.serializable_hash)
8282
end
8383

8484
def test_transform_camel_lower
8585
mock_request(:camel_lower)
8686
assert_equal({
8787
blog: { id: 1, specialAttribute: 'neat', articles: nil }
88-
}, @adapter.serializable_hash(@options))
88+
}, @adapter.serializable_hash)
8989
end
9090
end
9191
end

0 commit comments

Comments
 (0)