active_model_serializers 0.10.3 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/CHANGELOG.md +8 -1
- data/README.md +142 -2
- data/active_model_serializers.gemspec +2 -1
- data/docs/README.md +0 -1
- data/docs/general/rendering.md +2 -17
- data/docs/howto/add_relationship_links.md +2 -2
- data/lib/active_model/serializer/concerns/attributes.rb +1 -1
- data/lib/active_model/serializer/version.rb +1 -1
- data/lib/active_model_serializers/adapter/base.rb +2 -2
- data/lib/active_model_serializers/adapter/json_api/deserialization.rb +1 -1
- data/test/action_controller/json_api/errors_test.rb +2 -2
- data/test/action_controller/serialization_test.rb +1 -1
- data/test/active_model_serializers/json_pointer_test.rb +1 -1
- data/test/active_model_serializers/test/schema_test.rb +3 -2
- data/test/benchmark/bm_transform.rb +5 -5
- metadata +18 -9
- data/docs/ARCHITECTURE.md +0 -125
- data/lib/active_model_serializers/key_transform.rb +0 -74
- data/test/active_model_serializers/key_transform_test.rb +0 -297
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5839aadc9b4688e8c8d2005c19278080683d589
|
4
|
+
data.tar.gz: acd53226d78b77f5d13b735fbfc8a3aef06b2827
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2e27357e980e8886773280a95b411fd6fecc6c9c1cfa9033618e0b2d5cfe9725440d4c143cef062b5087b58679fa45de5769f77c82dc3e16fd28cbe94273526
|
7
|
+
data.tar.gz: c08d8d8ec50bdf46dba315157f9cc3f75d3561e242aa70ea1892da527cb830d8558910b9208c5030098458c812081519419a58b43c72fed228fd3c2af80822a7
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
## 0.10.x
|
2
2
|
|
3
|
-
### [master (unreleased)](https://github.com/rails-api/active_model_serializers/compare/v0.10.
|
3
|
+
### [master (unreleased)](https://github.com/rails-api/active_model_serializers/compare/v0.10.4...master)
|
4
4
|
|
5
5
|
Breaking changes:
|
6
6
|
|
@@ -10,6 +10,13 @@ Fixes:
|
|
10
10
|
|
11
11
|
Misc:
|
12
12
|
|
13
|
+
### [v0.10.4 (2017-01-06)](https://github.com/rails-api/active_model_serializers/compare/v0.10.3...v0.10.4)
|
14
|
+
|
15
|
+
Misc:
|
16
|
+
|
17
|
+
- [#2005](https://github.com/rails-api/active_model_serializers/pull/2005) Update jsonapi runtime dependency to 0.1.1.beta6, support Ruby 2.4. (@kofronpi)
|
18
|
+
- [#1993](https://github.com/rails-api/active_model_serializers/pull/1993) Swap out KeyTransform for CaseTransform gem for the possibility of native extension use. (@NullVoxPopuli)
|
19
|
+
|
13
20
|
### [v0.10.3 (2016-11-21)](https://github.com/rails-api/active_model_serializers/compare/v0.10.2...v0.10.3)
|
14
21
|
|
15
22
|
Fixes:
|
data/README.md
CHANGED
@@ -84,8 +84,14 @@ If you'd like to chat, we have a [community slack](http://amserializers.herokuap
|
|
84
84
|
Thanks!
|
85
85
|
|
86
86
|
## Documentation
|
87
|
+
|
88
|
+
If you're reading this at https://github.com/rails-api/active_model_serializers you are
|
89
|
+
reading documentation for our `master`, which may include features that have not
|
90
|
+
been released yet. Please see below for the documentation relevant to you.
|
91
|
+
|
87
92
|
- [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master)
|
88
|
-
|
93
|
+
- [0.10.3 (latest release) Documentation](https://github.com/rails-api/active_model_serializers/tree/v0.10.3)
|
94
|
+
- [](http://www.rubydoc.info/gems/active_model_serializers/0.10.3)
|
89
95
|
- [Guides](docs)
|
90
96
|
- [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable)
|
91
97
|
- [](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-9-stable)
|
@@ -156,7 +162,141 @@ serializer = SomeSerializer.new(resource, serializer_options)
|
|
156
162
|
serializer.attributes
|
157
163
|
serializer.associations
|
158
164
|
```
|
159
|
-
|
165
|
+
|
166
|
+
## Architecture
|
167
|
+
|
168
|
+
This section focuses on architecture the 0.10.x version of ActiveModelSerializers. If you are interested in the architecture of the 0.8 or 0.9 versions,
|
169
|
+
please refer to the [0.8 README](https://github.com/rails-api/active_model_serializers/blob/0-8-stable/README.md) or
|
170
|
+
[0.9 README](https://github.com/rails-api/active_model_serializers/blob/0-9-stable/README.md).
|
171
|
+
|
172
|
+
The original design is also available [here](https://github.com/rails-api/active_model_serializers/blob/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e/README.textile).
|
173
|
+
|
174
|
+
### ActiveModel::Serializer
|
175
|
+
|
176
|
+
An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/4-2-stable/activemodel/lib/active_model/serialization.rb)
|
177
|
+
and exposes an `attributes` method, among a few others.
|
178
|
+
It allows you to specify which attributes and associations should be represented in the serializatation of the resource.
|
179
|
+
It requires an adapter to transform its attributes into a JSON document; it cannot be serialized itself.
|
180
|
+
It may be useful to think of it as a
|
181
|
+
[presenter](http://blog.steveklabnik.com/posts/2011-09-09-better-ruby-presenters).
|
182
|
+
|
183
|
+
#### ActiveModel::CollectionSerializer
|
184
|
+
|
185
|
+
The **`ActiveModel::CollectionSerializer`** represents a collection of resources as serializers
|
186
|
+
and, if there is no serializer, primitives.
|
187
|
+
|
188
|
+
### ActiveModelSerializers::Adapter::Base
|
189
|
+
|
190
|
+
The **`ActiveModelSerializeres::Adapter::Base`** describes the structure of the JSON document generated from a
|
191
|
+
serializer. For example, the `Attributes` example represents each serializer as its
|
192
|
+
unmodified attributes. The `JsonApi` adapter represents the serializer as a [JSON
|
193
|
+
API](http://jsonapi.org/) document.
|
194
|
+
|
195
|
+
### ActiveModelSerializers::SerializableResource
|
196
|
+
|
197
|
+
The **`ActiveModelSerializers::SerializableResource`** acts to coordinate the serializer(s) and adapter
|
198
|
+
to an object that responds to `to_json`, and `as_json`. It is used in the controller to
|
199
|
+
encapsulate the serialization resource when rendered. However, it can also be used on its own
|
200
|
+
to serialize a resource outside of a controller, as well.
|
201
|
+
|
202
|
+
### Primitive handling
|
203
|
+
|
204
|
+
Definitions: A primitive is usually a String or Array. There is no serializer
|
205
|
+
defined for them; they will be serialized when the resource is converted to JSON (`as_json` or
|
206
|
+
`to_json`). (The below also applies for any object with no serializer.)
|
207
|
+
|
208
|
+
- ActiveModelSerializers doesn't handle primitives passed to `render json:` at all.
|
209
|
+
|
210
|
+
Internally, if no serializer can be found in the controller, the resource is not decorated by
|
211
|
+
ActiveModelSerializers.
|
212
|
+
|
213
|
+
- However, when a primitive value is an attribute or in a collection, it is not modified.
|
214
|
+
|
215
|
+
When serializing a collection and the collection serializer (CollectionSerializer) cannot
|
216
|
+
identify a serializer for a resource in its collection, it throws [`:no_serializer`](https://github.com/rails-api/active_model_serializers/issues/1191#issuecomment-142327128).
|
217
|
+
For example, when caught by `Reflection#build_association`, and the association value is set directly:
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
reflection_options[:virtual_value] = association_value.try(:as_json) || association_value
|
221
|
+
```
|
222
|
+
|
223
|
+
(which is called by the adapter as `serializer.associations(*)`.)
|
224
|
+
|
225
|
+
### How options are parsed
|
226
|
+
|
227
|
+
High-level overview:
|
228
|
+
|
229
|
+
- For a **collection**
|
230
|
+
- `:serializer` specifies the collection serializer and
|
231
|
+
- `:each_serializer` specifies the serializer for each resource in the collection.
|
232
|
+
- For a **single resource**, the `:serializer` option is the resource serializer.
|
233
|
+
- Options are partitioned in serializer options and adapter options. Keys for adapter options are specified by
|
234
|
+
[`ADAPTER_OPTION_KEYS`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/serializable_resource.rb#L5).
|
235
|
+
The remaining options are serializer options.
|
236
|
+
|
237
|
+
Details:
|
238
|
+
|
239
|
+
1. **ActionController::Serialization**
|
240
|
+
1. `serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)`
|
241
|
+
1. `options` are partitioned into `adapter_opts` and everything else (`serializer_opts`).
|
242
|
+
The `adapter_opts` keys are defined in [`ActiveModelSerializers::SerializableResource::ADAPTER_OPTION_KEYS`](lib/active_model_serializers/serializable_resource.rb#L5).
|
243
|
+
1. **ActiveModelSerializers::SerializableResource**
|
244
|
+
1. `if serializable_resource.serializer?` (there is a serializer for the resource, and an adapter is used.)
|
245
|
+
- Where `serializer?` is `use_adapter? && !!(serializer)`
|
246
|
+
- Where `use_adapter?`: 'True when no explicit adapter given, or explicit value is truthy (non-nil);
|
247
|
+
False when explicit adapter is falsy (nil or false)'
|
248
|
+
- Where `serializer`:
|
249
|
+
1. from explicit `:serializer` option, else
|
250
|
+
2. implicitly from resource `ActiveModel::Serializer.serializer_for(resource)`
|
251
|
+
1. A side-effect of checking `serializer` is:
|
252
|
+
- The `:serializer` option is removed from the serializer_opts hash
|
253
|
+
- If the `:each_serializer` option is present, it is removed from the serializer_opts hash and set as the `:serializer` option
|
254
|
+
1. The serializer and adapter are created as
|
255
|
+
1. `serializer_instance = serializer.new(resource, serializer_opts)`
|
256
|
+
2. `adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)`
|
257
|
+
1. **ActiveModel::Serializer::CollectionSerializer#new**
|
258
|
+
1. If the `serializer_instance` was a `CollectionSerializer` and the `:serializer` serializer_opts
|
259
|
+
is present, then [that serializer is passed into each resource](https://github.com/rails-api/active_model_serializers/blob/a54d237e2828fe6bab1ea5dfe6360d4ecc8214cd/lib/active_model/serializer/array_serializer.rb#L14-L16).
|
260
|
+
1. **ActiveModel::Serializer#attributes** is used by the adapter to get the attributes for
|
261
|
+
resource as defined by the serializer.
|
262
|
+
|
263
|
+
(In Rails, the `options` are also passed to the `as_json(options)` or `to_json(options)`
|
264
|
+
methods on the resource serialization by the Rails JSON renderer. They are, therefore, important
|
265
|
+
to know about, but not part of ActiveModelSerializers.)
|
266
|
+
|
267
|
+
### What does a 'serializable resource' look like?
|
268
|
+
|
269
|
+
- An `ActiveRecord::Base` object.
|
270
|
+
- Any Ruby object that passes the
|
271
|
+
[Lint](http://www.rubydoc.info/github/rails-api/active_model_serializers/ActiveModel/Serializer/Lint/Tests)
|
272
|
+
[code](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer/lint.rb).
|
273
|
+
|
274
|
+
ActiveModelSerializers provides a
|
275
|
+
[`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/model.rb),
|
276
|
+
which is a simple serializable PORO (Plain-Old Ruby Object).
|
277
|
+
|
278
|
+
`ActiveModelSerializers::Model` may be used either as a reference implementation, or in production code.
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
class MyModel < ActiveModelSerializers::Model
|
282
|
+
attr_accessor :id, :name, :level
|
283
|
+
end
|
284
|
+
```
|
285
|
+
|
286
|
+
The default serializer for `MyModel` would be `MyModelSerializer` whether MyModel is an
|
287
|
+
ActiveRecord::Base object or not.
|
288
|
+
|
289
|
+
Outside of the controller the rules are **exactly** the same as for records. For example:
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
render json: MyModel.new(level: 'awesome'), adapter: :json
|
293
|
+
```
|
294
|
+
|
295
|
+
would be serialized the same as
|
296
|
+
|
297
|
+
```ruby
|
298
|
+
ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json
|
299
|
+
```
|
160
300
|
|
161
301
|
## Semantic Versioning
|
162
302
|
|
@@ -42,7 +42,8 @@ Gem::Specification.new do |spec|
|
|
42
42
|
# 'minitest'
|
43
43
|
# 'thread_safe'
|
44
44
|
|
45
|
-
spec.add_runtime_dependency 'jsonapi', '0.1.1.
|
45
|
+
spec.add_runtime_dependency 'jsonapi', '0.1.1.beta6'
|
46
|
+
spec.add_runtime_dependency 'case_transform', '>= 0.2'
|
46
47
|
|
47
48
|
spec.add_development_dependency 'activerecord', rails_versions
|
48
49
|
# arel
|
data/docs/README.md
CHANGED
data/docs/general/rendering.md
CHANGED
@@ -48,26 +48,11 @@ render json: @posts, serializer: CollectionSerializer, each_serializer: PostPrev
|
|
48
48
|
|
49
49
|
## Serializing non-ActiveRecord objects
|
50
50
|
|
51
|
-
|
52
|
-
[ActiveModel::Serializer::Lint::Tests](../../lib/active_model/serializer/lint.rb#L17).
|
53
|
-
|
54
|
-
See the ActiveModelSerializers::Model for a base class that implements the full
|
55
|
-
API for a plain-old Ruby object (PORO).
|
51
|
+
See [README](../../README.md#what-does-a-serializable-resource-look-like)
|
56
52
|
|
57
53
|
## SerializableResource options
|
58
54
|
|
59
|
-
|
60
|
-
are partitioned into `serializer_opts` and `adapter_opts`. `adapter_opts` are passed to new Adapters;
|
61
|
-
`serializer_opts` are passed to new Serializers.
|
62
|
-
|
63
|
-
The `adapter_opts` are specified in [ActiveModelSerializers::SerializableResource::ADAPTER_OPTIONS](../../lib/active_model_serializers/serializable_resource.rb#L5).
|
64
|
-
The `serializer_opts` are the remaining options.
|
65
|
-
|
66
|
-
(In Rails, the `options` are also passed to the `as_json(options)` or `to_json(options)`
|
67
|
-
methods on the resource serialization by the Rails JSON renderer. They are, therefore, important
|
68
|
-
to know about, but not part of ActiveModelSerializers.)
|
69
|
-
|
70
|
-
See [ARCHITECTURE](../ARCHITECTURE.md) for more information.
|
55
|
+
See [README](../../README.md#activemodelserializersserializableresource)
|
71
56
|
|
72
57
|
### adapter_opts
|
73
58
|
|
@@ -37,7 +37,7 @@ class Api::V1::UserSerializer < ActiveModel::Serializer
|
|
37
37
|
end
|
38
38
|
```
|
39
39
|
|
40
|
-
This will
|
40
|
+
This will result in (example is in JSONAPI adapter):
|
41
41
|
```json
|
42
42
|
{
|
43
43
|
"data": {
|
@@ -69,7 +69,7 @@ class Api::V1::UserSerializer < ActiveModel::Serializer
|
|
69
69
|
end
|
70
70
|
```
|
71
71
|
|
72
|
-
This will
|
72
|
+
This will result in (example is in JSONAPI adapter):
|
73
73
|
```json
|
74
74
|
{
|
75
75
|
"data": {
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'case_transform'
|
2
2
|
|
3
3
|
module ActiveModelSerializers
|
4
4
|
module Adapter
|
@@ -31,7 +31,7 @@ module ActiveModelSerializers
|
|
31
31
|
# @param options [Object] serializable resource options
|
32
32
|
# @return [Symbol] the default transform for the adapter
|
33
33
|
def self.transform_key_casing!(value, options)
|
34
|
-
|
34
|
+
CaseTransform.send(transform(options), value)
|
35
35
|
end
|
36
36
|
|
37
37
|
def self.cache_key
|
@@ -14,10 +14,10 @@ module ActionController
|
|
14
14
|
{ source: { pointer: '/data/attributes/id' }, detail: 'must be a uuid' }
|
15
15
|
]
|
16
16
|
}.to_json
|
17
|
-
assert_equal
|
17
|
+
assert_equal json_response_body.to_json, expected_errors_object
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
20
|
+
def json_response_body
|
21
21
|
JSON.load(@response.body)
|
22
22
|
end
|
23
23
|
|
@@ -115,7 +115,8 @@ module ActiveModelSerializers
|
|
115
115
|
end
|
116
116
|
|
117
117
|
def test_that_raises_with_a_invalid_json_body
|
118
|
-
message
|
118
|
+
# message changes from JSON gem 2.0.2 to 2.2.0
|
119
|
+
message = /A JSON text must at least contain two octets!|unexpected token at ''/
|
119
120
|
|
120
121
|
get :invalid_json_body
|
121
122
|
|
@@ -123,7 +124,7 @@ module ActiveModelSerializers
|
|
123
124
|
assert_response_schema('custom/show.json')
|
124
125
|
end
|
125
126
|
|
126
|
-
|
127
|
+
assert_match(message, error.message)
|
127
128
|
end
|
128
129
|
end
|
129
130
|
end
|
@@ -25,21 +25,21 @@ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
|
|
25
25
|
serialization = adapter.as_json
|
26
26
|
|
27
27
|
Benchmark.ams('camel', time: time, disable_gc: disable_gc) do
|
28
|
-
|
28
|
+
CaseTransform.camel(serialization)
|
29
29
|
end
|
30
30
|
|
31
31
|
Benchmark.ams('camel_lower', time: time, disable_gc: disable_gc) do
|
32
|
-
|
32
|
+
CaseTransform.camel_lower(serialization)
|
33
33
|
end
|
34
34
|
|
35
35
|
Benchmark.ams('dash', time: time, disable_gc: disable_gc) do
|
36
|
-
|
36
|
+
CaseTransform.dash(serialization)
|
37
37
|
end
|
38
38
|
|
39
39
|
Benchmark.ams('unaltered', time: time, disable_gc: disable_gc) do
|
40
|
-
|
40
|
+
CaseTransform.unaltered(serialization)
|
41
41
|
end
|
42
42
|
|
43
43
|
Benchmark.ams('underscore', time: time, disable_gc: disable_gc) do
|
44
|
-
|
44
|
+
CaseTransform.underscore(serialization)
|
45
45
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_model_serializers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Klabnik
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -76,14 +76,28 @@ dependencies:
|
|
76
76
|
requirements:
|
77
77
|
- - '='
|
78
78
|
- !ruby/object:Gem::Version
|
79
|
-
version: 0.1.1.
|
79
|
+
version: 0.1.1.beta6
|
80
80
|
type: :runtime
|
81
81
|
prerelease: false
|
82
82
|
version_requirements: !ruby/object:Gem::Requirement
|
83
83
|
requirements:
|
84
84
|
- - '='
|
85
85
|
- !ruby/object:Gem::Version
|
86
|
-
version: 0.1.1.
|
86
|
+
version: 0.1.1.beta6
|
87
|
+
- !ruby/object:Gem::Dependency
|
88
|
+
name: case_transform
|
89
|
+
requirement: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0.2'
|
94
|
+
type: :runtime
|
95
|
+
prerelease: false
|
96
|
+
version_requirements: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0.2'
|
87
101
|
- !ruby/object:Gem::Dependency
|
88
102
|
name: activerecord
|
89
103
|
requirement: !ruby/object:Gem::Requirement
|
@@ -260,7 +274,6 @@ files:
|
|
260
274
|
- bin/bench
|
261
275
|
- bin/bench_regression
|
262
276
|
- bin/serve_benchmark
|
263
|
-
- docs/ARCHITECTURE.md
|
264
277
|
- docs/README.md
|
265
278
|
- docs/STYLE.md
|
266
279
|
- docs/general/adapters.md
|
@@ -343,7 +356,6 @@ files:
|
|
343
356
|
- lib/active_model_serializers/deprecate.rb
|
344
357
|
- lib/active_model_serializers/deserialization.rb
|
345
358
|
- lib/active_model_serializers/json_pointer.rb
|
346
|
-
- lib/active_model_serializers/key_transform.rb
|
347
359
|
- lib/active_model_serializers/logging.rb
|
348
360
|
- lib/active_model_serializers/lookup_chain.rb
|
349
361
|
- lib/active_model_serializers/model.rb
|
@@ -376,7 +388,6 @@ files:
|
|
376
388
|
- test/action_controller/serialization_test.rb
|
377
389
|
- test/active_model_serializers/adapter_for_test.rb
|
378
390
|
- test/active_model_serializers/json_pointer_test.rb
|
379
|
-
- test/active_model_serializers/key_transform_test.rb
|
380
391
|
- test/active_model_serializers/logging_test.rb
|
381
392
|
- test/active_model_serializers/model_test.rb
|
382
393
|
- test/active_model_serializers/railtie_test_isolated.rb
|
@@ -503,7 +514,6 @@ test_files:
|
|
503
514
|
- test/action_controller/serialization_test.rb
|
504
515
|
- test/active_model_serializers/adapter_for_test.rb
|
505
516
|
- test/active_model_serializers/json_pointer_test.rb
|
506
|
-
- test/active_model_serializers/key_transform_test.rb
|
507
517
|
- test/active_model_serializers/logging_test.rb
|
508
518
|
- test/active_model_serializers/model_test.rb
|
509
519
|
- test/active_model_serializers/railtie_test_isolated.rb
|
@@ -590,4 +600,3 @@ test_files:
|
|
590
600
|
- test/support/schemas/simple_json_pointers.json
|
591
601
|
- test/support/serialization_testing.rb
|
592
602
|
- test/test_helper.rb
|
593
|
-
has_rdoc:
|
data/docs/ARCHITECTURE.md
DELETED
@@ -1,125 +0,0 @@
|
|
1
|
-
[Back to Guides](README.md)
|
2
|
-
|
3
|
-
This document focuses on architecture the 0.10.x version of ActiveModelSerializers. If you are interested in the architecture of the 0.8 or 0.9 versions,
|
4
|
-
please refer to the [0.8 README](https://github.com/rails-api/active_model_serializers/blob/0-8-stable/README.md) or
|
5
|
-
[0.9 README](https://github.com/rails-api/active_model_serializers/blob/0-9-stable/README.md).
|
6
|
-
|
7
|
-
The original design is also available [here](https://github.com/rails-api/active_model_serializers/blob/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e/README.textile).
|
8
|
-
|
9
|
-
# ARCHITECTURE
|
10
|
-
|
11
|
-
An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/4-2-stable/activemodel/lib/active_model/serialization.rb)
|
12
|
-
and exposes an `attributes` method, among a few others.
|
13
|
-
It allows you to specify which attributes and associations should be represented in the serializatation of the resource.
|
14
|
-
It requires an adapter to transform its attributes into a JSON document; it cannot be serialized itself.
|
15
|
-
It may be useful to think of it as a
|
16
|
-
[presenter](http://blog.steveklabnik.com/posts/2011-09-09-better-ruby-presenters).
|
17
|
-
|
18
|
-
The **`ActiveModel::CollectionSerializer`** represents a collection of resources as serializers
|
19
|
-
and, if there is no serializer, primitives.
|
20
|
-
|
21
|
-
The **`ActiveModel::Adapter`** describes the structure of the JSON document generated from a
|
22
|
-
serializer. For example, the `Attributes` example represents each serializer as its
|
23
|
-
unmodified attributes. The `JsonApi` adapter represents the serializer as a [JSON
|
24
|
-
API](http://jsonapi.org/) document.
|
25
|
-
|
26
|
-
The **`ActiveModelSerializers::SerializableResource`** acts to coordinate the serializer(s) and adapter
|
27
|
-
to an object that responds to `to_json`, and `as_json`. It is used in the controller to
|
28
|
-
encapsulate the serialization resource when rendered. However, it can also be used on its own
|
29
|
-
to serialize a resource outside of a controller, as well.
|
30
|
-
|
31
|
-
## Primitive handling
|
32
|
-
|
33
|
-
Definitions: A primitive is usually a String or Array. There is no serializer
|
34
|
-
defined for them; they will be serialized when the resource is converted to JSON (`as_json` or
|
35
|
-
`to_json`). (The below also applies for any object with no serializer.)
|
36
|
-
|
37
|
-
ActiveModelSerializers doesn't handle primitives passed to `render json:` at all.
|
38
|
-
|
39
|
-
However, when a primitive value is an attribute or in a collection,
|
40
|
-
it is not modified.
|
41
|
-
|
42
|
-
Internally, if no serializer can be found in the controller, the resource is not decorated by
|
43
|
-
ActiveModelSerializers.
|
44
|
-
|
45
|
-
If the collection serializer (CollectionSerializer) cannot
|
46
|
-
identify a serializer for a resource in its collection, it throws [`:no_serializer`](https://github.com/rails-api/active_model_serializers/issues/1191#issuecomment-142327128).
|
47
|
-
For example, when caught by `Reflection#build_association`, the association value is set directly:
|
48
|
-
|
49
|
-
```ruby
|
50
|
-
reflection_options[:virtual_value] = association_value.try(:as_json) || association_value
|
51
|
-
```
|
52
|
-
|
53
|
-
(which is called by the adapter as `serializer.associations(*)`.)
|
54
|
-
|
55
|
-
## How options are parsed
|
56
|
-
|
57
|
-
High-level overview:
|
58
|
-
|
59
|
-
- For a collection
|
60
|
-
- `:serializer` specifies the collection serializer and
|
61
|
-
- `:each_serializer` specifies the serializer for each resource in the collection.
|
62
|
-
- For a single resource, the `:serializer` option is the resource serializer.
|
63
|
-
- Options are partitioned in serializer options and adapter options. Keys for adapter options are specified by
|
64
|
-
[`ADAPTER_OPTION_KEYS`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/serializable_resource.rb#L5).
|
65
|
-
The remaining options are serializer options.
|
66
|
-
|
67
|
-
Details:
|
68
|
-
|
69
|
-
1. **ActionController::Serialization**
|
70
|
-
1. `serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)`
|
71
|
-
1. `options` are partitioned into `adapter_opts` and everything else (`serializer_opts`).
|
72
|
-
The `adapter_opts` keys are defined in `ActiveModelSerializers::SerializableResource::ADAPTER_OPTION_KEYS`.
|
73
|
-
1. **ActiveModelSerializers::SerializableResource**
|
74
|
-
1. `if serializable_resource.serializer?` (there is a serializer for the resource, and an adapter is used.)
|
75
|
-
- Where `serializer?` is `use_adapter? && !!(serializer)`
|
76
|
-
- Where `use_adapter?`: 'True when no explicit adapter given, or explicit value is truthy (non-nil);
|
77
|
-
False when explicit adapter is falsy (nil or false)'
|
78
|
-
- Where `serializer`:
|
79
|
-
1. from explicit `:serializer` option, else
|
80
|
-
2. implicitly from resource `ActiveModel::Serializer.serializer_for(resource)`
|
81
|
-
1. A side-effect of checking `serializer` is:
|
82
|
-
- The `:serializer` option is removed from the serializer_opts hash
|
83
|
-
- If the `:each_serializer` option is present, it is removed from the serializer_opts hash and set as the `:serializer` option
|
84
|
-
1. The serializer and adapter are created as
|
85
|
-
1. `serializer_instance = serializer.new(resource, serializer_opts)`
|
86
|
-
2. `adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)`
|
87
|
-
1. **ActiveModel::Serializer::CollectionSerializer#new**
|
88
|
-
1. If the `serializer_instance` was a `CollectionSerializer` and the `:serializer` serializer_opts
|
89
|
-
is present, then [that serializer is passed into each resource](https://github.com/rails-api/active_model_serializers/blob/a54d237e2828fe6bab1ea5dfe6360d4ecc8214cd/lib/active_model/serializer/array_serializer.rb#L14-L16).
|
90
|
-
1. **ActiveModel::Serializer#attributes** is used by the adapter to get the attributes for
|
91
|
-
resource as defined by the serializer.
|
92
|
-
|
93
|
-
## What does a 'serializable resource' look like?
|
94
|
-
|
95
|
-
- An `ActiveRecord::Base` object.
|
96
|
-
- Any Ruby object that passes the
|
97
|
-
[Lint](http://www.rubydoc.info/github/rails-api/active_model_serializers/ActiveModel/Serializer/Lint/Tests)
|
98
|
-
[code](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer/lint.rb).
|
99
|
-
|
100
|
-
ActiveModelSerializers provides a
|
101
|
-
[`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/model.rb),
|
102
|
-
which is a simple serializable PORO (Plain-Old Ruby Object).
|
103
|
-
|
104
|
-
ActiveModelSerializers::Model may be used either as a template, or in production code.
|
105
|
-
|
106
|
-
```ruby
|
107
|
-
class MyModel < ActiveModelSerializers::Model
|
108
|
-
attr_accessor :id, :name, :level
|
109
|
-
end
|
110
|
-
```
|
111
|
-
|
112
|
-
The default serializer for `MyModel` would be `MyModelSerializer` whether MyModel is an
|
113
|
-
ActiveRecord::Base object or not.
|
114
|
-
|
115
|
-
Outside of the controller the rules are **exactly** the same as for records. For example:
|
116
|
-
|
117
|
-
```ruby
|
118
|
-
render json: MyModel.new(level: 'awesome'), adapter: :json
|
119
|
-
```
|
120
|
-
|
121
|
-
would be serialized the same as
|
122
|
-
|
123
|
-
```ruby
|
124
|
-
ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json
|
125
|
-
```
|
@@ -1,74 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/hash/keys'
|
2
|
-
|
3
|
-
module ActiveModelSerializers
|
4
|
-
module KeyTransform
|
5
|
-
module_function
|
6
|
-
|
7
|
-
# Transforms values to UpperCamelCase or PascalCase.
|
8
|
-
#
|
9
|
-
# @example:
|
10
|
-
# "some_key" => "SomeKey",
|
11
|
-
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize}
|
12
|
-
def camel(value)
|
13
|
-
case value
|
14
|
-
when Array then value.map { |item| camel(item) }
|
15
|
-
when Hash then value.deep_transform_keys! { |key| camel(key) }
|
16
|
-
when Symbol then camel(value.to_s).to_sym
|
17
|
-
when String then value.underscore.camelize
|
18
|
-
else value
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Transforms values to camelCase.
|
23
|
-
#
|
24
|
-
# @example:
|
25
|
-
# "some_key" => "someKey",
|
26
|
-
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize}
|
27
|
-
def camel_lower(value)
|
28
|
-
case value
|
29
|
-
when Array then value.map { |item| camel_lower(item) }
|
30
|
-
when Hash then value.deep_transform_keys! { |key| camel_lower(key) }
|
31
|
-
when Symbol then camel_lower(value.to_s).to_sym
|
32
|
-
when String then value.underscore.camelize(:lower)
|
33
|
-
else value
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Transforms values to dashed-case.
|
38
|
-
# This is the default case for the JsonApi adapter.
|
39
|
-
#
|
40
|
-
# @example:
|
41
|
-
# "some_key" => "some-key",
|
42
|
-
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L185-L187 ActiveSupport::Inflector.dasherize}
|
43
|
-
def dash(value)
|
44
|
-
case value
|
45
|
-
when Array then value.map { |item| dash(item) }
|
46
|
-
when Hash then value.deep_transform_keys! { |key| dash(key) }
|
47
|
-
when Symbol then dash(value.to_s).to_sym
|
48
|
-
when String then value.underscore.dasherize
|
49
|
-
else value
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Transforms values to underscore_case.
|
54
|
-
# This is the default case for deserialization in the JsonApi adapter.
|
55
|
-
#
|
56
|
-
# @example:
|
57
|
-
# "some-key" => "some_key",
|
58
|
-
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L89-L98 ActiveSupport::Inflector.underscore}
|
59
|
-
def underscore(value)
|
60
|
-
case value
|
61
|
-
when Array then value.map { |item| underscore(item) }
|
62
|
-
when Hash then value.deep_transform_keys! { |key| underscore(key) }
|
63
|
-
when Symbol then underscore(value.to_s).to_sym
|
64
|
-
when String then value.underscore
|
65
|
-
else value
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Returns the value unaltered
|
70
|
-
def unaltered(value)
|
71
|
-
value
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
@@ -1,297 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
module ActiveModelSerializers
|
4
|
-
class KeyTransformTest < ActiveSupport::TestCase
|
5
|
-
def test_camel
|
6
|
-
obj = Object.new
|
7
|
-
scenarios = [
|
8
|
-
{
|
9
|
-
value: { :"some-key" => 'value' },
|
10
|
-
expected: { SomeKey: 'value' }
|
11
|
-
},
|
12
|
-
{
|
13
|
-
value: { someKey: 'value' },
|
14
|
-
expected: { SomeKey: 'value' }
|
15
|
-
},
|
16
|
-
{
|
17
|
-
value: { some_key: 'value' },
|
18
|
-
expected: { SomeKey: 'value' }
|
19
|
-
},
|
20
|
-
{
|
21
|
-
value: { 'some-key' => 'value' },
|
22
|
-
expected: { 'SomeKey' => 'value' }
|
23
|
-
},
|
24
|
-
{
|
25
|
-
value: { 'someKey' => 'value' },
|
26
|
-
expected: { 'SomeKey' => 'value' }
|
27
|
-
},
|
28
|
-
{
|
29
|
-
value: { 'some_key' => 'value' },
|
30
|
-
expected: { 'SomeKey' => 'value' }
|
31
|
-
},
|
32
|
-
{
|
33
|
-
value: :"some-value",
|
34
|
-
expected: :SomeValue
|
35
|
-
},
|
36
|
-
{
|
37
|
-
value: :some_value,
|
38
|
-
expected: :SomeValue
|
39
|
-
},
|
40
|
-
{
|
41
|
-
value: :someValue,
|
42
|
-
expected: :SomeValue
|
43
|
-
},
|
44
|
-
{
|
45
|
-
value: 'some-value',
|
46
|
-
expected: 'SomeValue'
|
47
|
-
},
|
48
|
-
{
|
49
|
-
value: 'someValue',
|
50
|
-
expected: 'SomeValue'
|
51
|
-
},
|
52
|
-
{
|
53
|
-
value: 'some_value',
|
54
|
-
expected: 'SomeValue'
|
55
|
-
},
|
56
|
-
{
|
57
|
-
value: obj,
|
58
|
-
expected: obj
|
59
|
-
},
|
60
|
-
{
|
61
|
-
value: nil,
|
62
|
-
expected: nil
|
63
|
-
},
|
64
|
-
{
|
65
|
-
value: [
|
66
|
-
{ some_value: 'value' }
|
67
|
-
],
|
68
|
-
expected: [
|
69
|
-
{ SomeValue: 'value' }
|
70
|
-
]
|
71
|
-
}
|
72
|
-
]
|
73
|
-
scenarios.each do |s|
|
74
|
-
result = ActiveModelSerializers::KeyTransform.camel(s[:value])
|
75
|
-
assert_equal s[:expected], result
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def test_camel_lower
|
80
|
-
obj = Object.new
|
81
|
-
scenarios = [
|
82
|
-
{
|
83
|
-
value: { :"some-key" => 'value' },
|
84
|
-
expected: { someKey: 'value' }
|
85
|
-
},
|
86
|
-
{
|
87
|
-
value: { SomeKey: 'value' },
|
88
|
-
expected: { someKey: 'value' }
|
89
|
-
},
|
90
|
-
{
|
91
|
-
value: { some_key: 'value' },
|
92
|
-
expected: { someKey: 'value' }
|
93
|
-
},
|
94
|
-
{
|
95
|
-
value: { 'some-key' => 'value' },
|
96
|
-
expected: { 'someKey' => 'value' }
|
97
|
-
},
|
98
|
-
{
|
99
|
-
value: { 'SomeKey' => 'value' },
|
100
|
-
expected: { 'someKey' => 'value' }
|
101
|
-
},
|
102
|
-
{
|
103
|
-
value: { 'some_key' => 'value' },
|
104
|
-
expected: { 'someKey' => 'value' }
|
105
|
-
},
|
106
|
-
{
|
107
|
-
value: :"some-value",
|
108
|
-
expected: :someValue
|
109
|
-
},
|
110
|
-
{
|
111
|
-
value: :SomeValue,
|
112
|
-
expected: :someValue
|
113
|
-
},
|
114
|
-
{
|
115
|
-
value: :some_value,
|
116
|
-
expected: :someValue
|
117
|
-
},
|
118
|
-
{
|
119
|
-
value: 'some-value',
|
120
|
-
expected: 'someValue'
|
121
|
-
},
|
122
|
-
{
|
123
|
-
value: 'SomeValue',
|
124
|
-
expected: 'someValue'
|
125
|
-
},
|
126
|
-
{
|
127
|
-
value: 'some_value',
|
128
|
-
expected: 'someValue'
|
129
|
-
},
|
130
|
-
{
|
131
|
-
value: obj,
|
132
|
-
expected: obj
|
133
|
-
},
|
134
|
-
{
|
135
|
-
value: nil,
|
136
|
-
expected: nil
|
137
|
-
},
|
138
|
-
{
|
139
|
-
value: [
|
140
|
-
{ some_value: 'value' }
|
141
|
-
],
|
142
|
-
expected: [
|
143
|
-
{ someValue: 'value' }
|
144
|
-
]
|
145
|
-
}
|
146
|
-
]
|
147
|
-
scenarios.each do |s|
|
148
|
-
result = ActiveModelSerializers::KeyTransform.camel_lower(s[:value])
|
149
|
-
assert_equal s[:expected], result
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def test_dash
|
154
|
-
obj = Object.new
|
155
|
-
scenarios = [
|
156
|
-
{
|
157
|
-
value: { some_key: 'value' },
|
158
|
-
expected: { :"some-key" => 'value' }
|
159
|
-
},
|
160
|
-
{
|
161
|
-
value: { 'some_key' => 'value' },
|
162
|
-
expected: { 'some-key' => 'value' }
|
163
|
-
},
|
164
|
-
{
|
165
|
-
value: { SomeKey: 'value' },
|
166
|
-
expected: { :"some-key" => 'value' }
|
167
|
-
},
|
168
|
-
{
|
169
|
-
value: { 'SomeKey' => 'value' },
|
170
|
-
expected: { 'some-key' => 'value' }
|
171
|
-
},
|
172
|
-
{
|
173
|
-
value: { someKey: 'value' },
|
174
|
-
expected: { :"some-key" => 'value' }
|
175
|
-
},
|
176
|
-
{
|
177
|
-
value: { 'someKey' => 'value' },
|
178
|
-
expected: { 'some-key' => 'value' }
|
179
|
-
},
|
180
|
-
{
|
181
|
-
value: :some_value,
|
182
|
-
expected: :"some-value"
|
183
|
-
},
|
184
|
-
{
|
185
|
-
value: :SomeValue,
|
186
|
-
expected: :"some-value"
|
187
|
-
},
|
188
|
-
{
|
189
|
-
value: 'SomeValue',
|
190
|
-
expected: 'some-value'
|
191
|
-
},
|
192
|
-
{
|
193
|
-
value: :someValue,
|
194
|
-
expected: :"some-value"
|
195
|
-
},
|
196
|
-
{
|
197
|
-
value: 'someValue',
|
198
|
-
expected: 'some-value'
|
199
|
-
},
|
200
|
-
{
|
201
|
-
value: obj,
|
202
|
-
expected: obj
|
203
|
-
},
|
204
|
-
{
|
205
|
-
value: nil,
|
206
|
-
expected: nil
|
207
|
-
},
|
208
|
-
{
|
209
|
-
value: [
|
210
|
-
{ 'some_value' => 'value' }
|
211
|
-
],
|
212
|
-
expected: [
|
213
|
-
{ 'some-value' => 'value' }
|
214
|
-
]
|
215
|
-
}
|
216
|
-
]
|
217
|
-
scenarios.each do |s|
|
218
|
-
result = ActiveModelSerializers::KeyTransform.dash(s[:value])
|
219
|
-
assert_equal s[:expected], result
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
def test_underscore
|
224
|
-
obj = Object.new
|
225
|
-
scenarios = [
|
226
|
-
{
|
227
|
-
value: { :"some-key" => 'value' },
|
228
|
-
expected: { some_key: 'value' }
|
229
|
-
},
|
230
|
-
{
|
231
|
-
value: { 'some-key' => 'value' },
|
232
|
-
expected: { 'some_key' => 'value' }
|
233
|
-
},
|
234
|
-
{
|
235
|
-
value: { SomeKey: 'value' },
|
236
|
-
expected: { some_key: 'value' }
|
237
|
-
},
|
238
|
-
{
|
239
|
-
value: { 'SomeKey' => 'value' },
|
240
|
-
expected: { 'some_key' => 'value' }
|
241
|
-
},
|
242
|
-
{
|
243
|
-
value: { someKey: 'value' },
|
244
|
-
expected: { some_key: 'value' }
|
245
|
-
},
|
246
|
-
{
|
247
|
-
value: { 'someKey' => 'value' },
|
248
|
-
expected: { 'some_key' => 'value' }
|
249
|
-
},
|
250
|
-
{
|
251
|
-
value: :"some-value",
|
252
|
-
expected: :some_value
|
253
|
-
},
|
254
|
-
{
|
255
|
-
value: :SomeValue,
|
256
|
-
expected: :some_value
|
257
|
-
},
|
258
|
-
{
|
259
|
-
value: :someValue,
|
260
|
-
expected: :some_value
|
261
|
-
},
|
262
|
-
{
|
263
|
-
value: 'some-value',
|
264
|
-
expected: 'some_value'
|
265
|
-
},
|
266
|
-
{
|
267
|
-
value: 'SomeValue',
|
268
|
-
expected: 'some_value'
|
269
|
-
},
|
270
|
-
{
|
271
|
-
value: 'someValue',
|
272
|
-
expected: 'some_value'
|
273
|
-
},
|
274
|
-
{
|
275
|
-
value: obj,
|
276
|
-
expected: obj
|
277
|
-
},
|
278
|
-
{
|
279
|
-
value: nil,
|
280
|
-
expected: nil
|
281
|
-
},
|
282
|
-
{
|
283
|
-
value: [
|
284
|
-
{ 'some-value' => 'value' }
|
285
|
-
],
|
286
|
-
expected: [
|
287
|
-
{ 'some_value' => 'value' }
|
288
|
-
]
|
289
|
-
}
|
290
|
-
]
|
291
|
-
scenarios.each do |s|
|
292
|
-
result = ActiveModelSerializers::KeyTransform.underscore(s[:value])
|
293
|
-
assert_equal s[:expected], result
|
294
|
-
end
|
295
|
-
end
|
296
|
-
end
|
297
|
-
end
|