active_model_serializers 0.10.5 → 0.10.6
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/.rubocop.yml +4 -1
- data/CHANGELOG.md +16 -2
- data/README.md +2 -2
- data/Rakefile +1 -30
- data/active_model_serializers.gemspec +1 -1
- data/bin/rubocop +38 -0
- data/docs/general/adapters.md +25 -9
- data/docs/general/getting_started.md +1 -1
- data/docs/general/rendering.md +18 -4
- data/docs/general/serializers.md +21 -2
- data/docs/howto/add_pagination_links.md +1 -1
- data/docs/integrations/ember-and-json-api.md +3 -0
- data/lib/active_model/serializer.rb +252 -74
- data/lib/active_model/serializer/association.rb +51 -14
- data/lib/active_model/serializer/belongs_to_reflection.rb +5 -1
- data/lib/active_model/serializer/concerns/caching.rb +29 -21
- data/lib/active_model/serializer/has_many_reflection.rb +4 -1
- data/lib/active_model/serializer/has_one_reflection.rb +1 -1
- data/lib/active_model/serializer/lazy_association.rb +95 -0
- data/lib/active_model/serializer/reflection.rb +119 -75
- data/lib/active_model/serializer/version.rb +1 -1
- data/lib/active_model_serializers/adapter/json_api.rb +30 -17
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +38 -9
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +9 -0
- data/lib/active_model_serializers/model.rb +5 -4
- data/lib/tasks/rubocop.rake +53 -0
- data/test/action_controller/adapter_selector_test.rb +2 -2
- data/test/serializers/associations_test.rb +64 -31
- data/test/serializers/reflection_test.rb +427 -0
- metadata +9 -12
- data/lib/active_model/serializer/collection_reflection.rb +0 -7
- data/lib/active_model/serializer/concerns/associations.rb +0 -102
- data/lib/active_model/serializer/concerns/attributes.rb +0 -82
- data/lib/active_model/serializer/concerns/configuration.rb +0 -59
- data/lib/active_model/serializer/concerns/links.rb +0 -35
- data/lib/active_model/serializer/concerns/meta.rb +0 -29
- data/lib/active_model/serializer/concerns/type.rb +0 -25
- data/lib/active_model/serializer/singular_reflection.rb +0 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fbc0f811b036c328dccf411b6f8080b6605d8740
|
|
4
|
+
data.tar.gz: d4a0f6f7799b074aff45bc103c9c19b9ea873ab1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 121ed054788e585e48e76af3e98490930fd26f18ebdd28d80d07cba78fe9b7c3aed8ea1e1f15ace59debb73562383657f89e4d52fa2c54391424ea53c3701a38
|
|
7
|
+
data.tar.gz: 88ab9022b49348ec463547196300480bcf02139d67cc11adfcbf8c5e4e38c3e44b27cdde7687baab54f8f8ce4bbe4fa606079dffae51ad7be42427dc15955d6e
|
data/.rubocop.yml
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
AllCops:
|
|
2
2
|
TargetRubyVersion: 2.1
|
|
3
3
|
Exclude:
|
|
4
|
-
- config/initializers/forbidden_yaml.rb
|
|
5
4
|
- !ruby/regexp /(vendor|bundle|bin|db|tmp)\/.*/
|
|
6
5
|
DisplayCopNames: true
|
|
7
6
|
DisplayStyleGuide: true
|
|
7
|
+
# https://github.com/bbatsov/rubocop/blob/master/manual/caching.md
|
|
8
|
+
# https://github.com/bbatsov/rubocop/blob/e8680418b351491e111a18cf5b453fc07a3c5239/config/default.yml#L60-L77
|
|
9
|
+
UseCache: true
|
|
10
|
+
CacheRootDirectory: tmp
|
|
8
11
|
|
|
9
12
|
Rails:
|
|
10
13
|
Enabled: true
|
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.6...master)
|
|
4
4
|
|
|
5
5
|
Breaking changes:
|
|
6
6
|
|
|
@@ -10,6 +10,20 @@ Fixes:
|
|
|
10
10
|
|
|
11
11
|
Misc:
|
|
12
12
|
|
|
13
|
+
### [v0.10.6 (2017-05-01)](https://github.com/rails-api/active_model_serializers/compare/v0.10.5...v0.10.6)
|
|
14
|
+
|
|
15
|
+
Fixes:
|
|
16
|
+
|
|
17
|
+
- [#1857](https://github.com/rails-api/active_model_serializers/pull/1857) JSON:API does not load belongs_to relation to get identifier id. (@bf4)
|
|
18
|
+
- [#2119](https://github.com/rails-api/active_model_serializers/pull/2119) JSON:API returns null resource object identifier when 'id' is null. (@bf4)
|
|
19
|
+
- [#2093](https://github.com/rails-api/active_model_serializers/pull/2093) undef problematic Serializer methods: display, select. (@bf4)
|
|
20
|
+
|
|
21
|
+
Misc:
|
|
22
|
+
|
|
23
|
+
- [#2104](https://github.com/rails-api/active_model_serializers/pull/2104) Documentation for serializers and rendering. (@cassidycodes)
|
|
24
|
+
- [#2081](https://github.com/rails-api/active_model_serializers/pull/2081) Documentation for `include` option in adapters. (@charlie-wasp)
|
|
25
|
+
- [#2120](https://github.com/rails-api/active_model_serializers/pull/2120) Documentation for association options: foreign_key, type, class_name, namespace. (@bf4)
|
|
26
|
+
|
|
13
27
|
### [v0.10.5 (2017-03-07)](https://github.com/rails-api/active_model_serializers/compare/v0.10.4...v0.10.5)
|
|
14
28
|
|
|
15
29
|
Breaking changes:
|
|
@@ -77,7 +91,7 @@ Misc:
|
|
|
77
91
|
|
|
78
92
|
- [#1878](https://github.com/rails-api/active_model_serializers/pull/1878) Cache key generation for serializers now uses `ActiveSupport::Cache.expand_cache_key` instead of `Array#join` by default and is also overridable. This change should be backward-compatible. (@markiz)
|
|
79
93
|
|
|
80
|
-
- [#1799](https://github.com/rails-api/active_model_serializers/pull/1799) Add documentation for setting the adapter. (@
|
|
94
|
+
- [#1799](https://github.com/rails-api/active_model_serializers/pull/1799) Add documentation for setting the adapter. (@cassidycodes)
|
|
81
95
|
- [#1909](https://github.com/rails-api/active_model_serializers/pull/1909) Add documentation for relationship links. (@vasilakisfil, @NullVoxPopuli)
|
|
82
96
|
- [#1959](https://github.com/rails-api/active_model_serializers/pull/1959) Add documentation for root. (@shunsuke227ono)
|
|
83
97
|
- [#1967](https://github.com/rails-api/active_model_serializers/pull/1967) Improve type method documentation. (@yukideluxe)
|
data/README.md
CHANGED
|
@@ -90,8 +90,8 @@ reading documentation for our `master`, which may include features that have not
|
|
|
90
90
|
been released yet. Please see below for the documentation relevant to you.
|
|
91
91
|
|
|
92
92
|
- [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master)
|
|
93
|
-
- [0.10.
|
|
94
|
-
- [](http://www.rubydoc.info/gems/active_model_serializers/0.10.
|
|
93
|
+
- [0.10.6 (latest release) Documentation](https://github.com/rails-api/active_model_serializers/tree/v0.10.6)
|
|
94
|
+
- [](http://www.rubydoc.info/gems/active_model_serializers/0.10.6)
|
|
95
95
|
- [Guides](docs)
|
|
96
96
|
- [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable)
|
|
97
97
|
- [](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-9-stable)
|
data/Rakefile
CHANGED
|
@@ -7,6 +7,7 @@ begin
|
|
|
7
7
|
require 'simplecov'
|
|
8
8
|
rescue LoadError # rubocop:disable Lint/HandleExceptions
|
|
9
9
|
end
|
|
10
|
+
import('lib/tasks/rubocop.rake')
|
|
10
11
|
|
|
11
12
|
Bundler::GemHelper.install_tasks
|
|
12
13
|
|
|
@@ -30,36 +31,6 @@ namespace :yard do
|
|
|
30
31
|
end
|
|
31
32
|
end
|
|
32
33
|
|
|
33
|
-
begin
|
|
34
|
-
require 'rubocop'
|
|
35
|
-
require 'rubocop/rake_task'
|
|
36
|
-
rescue LoadError # rubocop:disable Lint/HandleExceptions
|
|
37
|
-
else
|
|
38
|
-
Rake::Task[:rubocop].clear if Rake::Task.task_defined?(:rubocop)
|
|
39
|
-
require 'rbconfig'
|
|
40
|
-
# https://github.com/bundler/bundler/blob/1b3eb2465a/lib/bundler/constants.rb#L2
|
|
41
|
-
windows_platforms = /(msdos|mswin|djgpp|mingw)/
|
|
42
|
-
if RbConfig::CONFIG['host_os'] =~ windows_platforms
|
|
43
|
-
desc 'No-op rubocop on Windows-- unsupported platform'
|
|
44
|
-
task :rubocop do
|
|
45
|
-
puts 'Skipping rubocop on Windows'
|
|
46
|
-
end
|
|
47
|
-
elsif defined?(::Rubinius)
|
|
48
|
-
desc 'No-op rubocop to avoid rbx segfault'
|
|
49
|
-
task :rubocop do
|
|
50
|
-
puts 'Skipping rubocop on rbx due to segfault'
|
|
51
|
-
puts 'https://github.com/rubinius/rubinius/issues/3499'
|
|
52
|
-
end
|
|
53
|
-
else
|
|
54
|
-
Rake::Task[:rubocop].clear if Rake::Task.task_defined?(:rubocop)
|
|
55
|
-
desc 'Execute rubocop'
|
|
56
|
-
RuboCop::RakeTask.new(:rubocop) do |task|
|
|
57
|
-
task.options = ['--rails', '--display-cop-names', '--display-style-guide']
|
|
58
|
-
task.fail_on_error = true
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
34
|
require 'rake/testtask'
|
|
64
35
|
|
|
65
36
|
Rake::TestTask.new(:test) do |t|
|
|
@@ -57,7 +57,7 @@ Gem::Specification.new do |spec|
|
|
|
57
57
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
|
58
58
|
spec.add_development_dependency 'simplecov', '~> 0.11'
|
|
59
59
|
spec.add_development_dependency 'timecop', '~> 0.7'
|
|
60
|
-
spec.add_development_dependency 'grape', ['>= 0.13', '< 1
|
|
60
|
+
spec.add_development_dependency 'grape', ['>= 0.13', '< 0.19.1']
|
|
61
61
|
spec.add_development_dependency 'json_schema'
|
|
62
62
|
spec.add_development_dependency 'rake', ['>= 10.0', '< 12.0']
|
|
63
63
|
end
|
data/bin/rubocop
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Usage:
|
|
4
|
+
# bin/rubocop [-A|-t|-h]
|
|
5
|
+
# bin/rubocop [file or path] [cli options]
|
|
6
|
+
#
|
|
7
|
+
# Options:
|
|
8
|
+
# Autocorrect -A
|
|
9
|
+
# AutoGenConfig -t
|
|
10
|
+
# Usage -h,--help,help
|
|
11
|
+
|
|
12
|
+
set -e
|
|
13
|
+
|
|
14
|
+
case $1 in
|
|
15
|
+
-A)
|
|
16
|
+
echo "Rubocop autocorrect is ON" >&2
|
|
17
|
+
bundle exec rake -f lib/tasks/rubocop.rake rubocop:auto_correct
|
|
18
|
+
;;
|
|
19
|
+
|
|
20
|
+
-t)
|
|
21
|
+
echo "Rubocop is generating a new TODO" >&2
|
|
22
|
+
bundle exec rake -f lib/tasks/rubocop.rake rubocop:auto_gen_config
|
|
23
|
+
;;
|
|
24
|
+
|
|
25
|
+
-h|--help|help)
|
|
26
|
+
sed -ne '/^#/!q;s/.\{1,2\}//;1d;p' < "$0"
|
|
27
|
+
;;
|
|
28
|
+
|
|
29
|
+
*)
|
|
30
|
+
# with no args, run vanilla rubocop
|
|
31
|
+
# else assume we're passing in arbitrary arguments
|
|
32
|
+
if [ -z "$1" ]; then
|
|
33
|
+
bundle exec rake -f lib/tasks/rubocop.rake rubocop
|
|
34
|
+
else
|
|
35
|
+
bundle exec rubocop "$@"
|
|
36
|
+
fi
|
|
37
|
+
;;
|
|
38
|
+
esac
|
data/docs/general/adapters.md
CHANGED
|
@@ -141,18 +141,25 @@ This adapter follows **version 1.0** of the [format specified](../jsonapi/schema
|
|
|
141
141
|
}
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
### Include option
|
|
145
145
|
|
|
146
|
-
|
|
147
|
-
when the resource names are included in the `include` option.
|
|
148
|
-
Including nested associated resources is also supported.
|
|
146
|
+
Which [serializer associations](https://github.com/rails-api/active_model_serializers/blob/master/docs/general/serializers.md#associations) are rendered can be specified using the `include` option. The option usage is consistent with [the include option in the JSON API spec](http://jsonapi.org/format/#fetching-includes), and is available in all adapters.
|
|
149
147
|
|
|
148
|
+
Example of the usage:
|
|
150
149
|
```ruby
|
|
151
150
|
render json: @posts, include: ['author', 'comments', 'comments.author']
|
|
152
151
|
# or
|
|
153
152
|
render json: @posts, include: 'author,comments,comments.author'
|
|
154
153
|
```
|
|
155
154
|
|
|
155
|
+
The format of the `include` option can be either:
|
|
156
|
+
|
|
157
|
+
- a String composed of a comma-separated list of [relationship paths](http://jsonapi.org/format/#fetching-includes).
|
|
158
|
+
- an Array of Symbols and Hashes.
|
|
159
|
+
- a mix of both.
|
|
160
|
+
|
|
161
|
+
An empty string or an empty array will prevent rendering of any associations.
|
|
162
|
+
|
|
156
163
|
In addition, two types of wildcards may be used:
|
|
157
164
|
|
|
158
165
|
- `*` includes one level of associations.
|
|
@@ -164,11 +171,6 @@ These can be combined with other paths.
|
|
|
164
171
|
render json: @posts, include: '**' # or '*' for a single layer
|
|
165
172
|
```
|
|
166
173
|
|
|
167
|
-
The format of the `include` option can be either:
|
|
168
|
-
|
|
169
|
-
- a String composed of a comma-separated list of [relationship paths](http://jsonapi.org/format/#fetching-includes).
|
|
170
|
-
- an Array of Symbols and Hashes.
|
|
171
|
-
- a mix of both.
|
|
172
174
|
|
|
173
175
|
The following would render posts and include:
|
|
174
176
|
|
|
@@ -182,6 +184,20 @@ It could be combined, like above, with other paths in any combination desired.
|
|
|
182
184
|
render json: @posts, include: 'author.comments.**'
|
|
183
185
|
```
|
|
184
186
|
|
|
187
|
+
**Note:** Wildcards are ActiveModelSerializers-specific, they are not part of the JSON API spec.
|
|
188
|
+
|
|
189
|
+
The default include for the JSON API adapter is no associations. The default for the JSON and Attributes adapters is all associations.
|
|
190
|
+
|
|
191
|
+
For the JSON API adapter associated resources will be gathered in the `"included"` member. For the JSON and Attributes
|
|
192
|
+
adapters associated resources will be rendered among the other attributes.
|
|
193
|
+
|
|
194
|
+
Only for the JSON API adapter you can specify, which attributes of associated resources will be rendered. This feature
|
|
195
|
+
is called [sparse fieldset](http://jsonapi.org/format/#fetching-sparse-fieldsets):
|
|
196
|
+
|
|
197
|
+
```ruby
|
|
198
|
+
render json: @posts, include: 'comments', fields: { comments: ['content', 'created_at'] }
|
|
199
|
+
```
|
|
200
|
+
|
|
185
201
|
##### Security Considerations
|
|
186
202
|
|
|
187
203
|
Since the included options may come from the query params (i.e. user-controller):
|
data/docs/general/rendering.md
CHANGED
|
@@ -203,7 +203,7 @@ link(:link_name) { url_for(controller: 'controller_name', action: 'index', only_
|
|
|
203
203
|
|
|
204
204
|
#### include
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
See [Adapters: Include Option](/docs/general/adapters.md#include-option).
|
|
207
207
|
|
|
208
208
|
#### Overriding the root key
|
|
209
209
|
|
|
@@ -260,15 +260,29 @@ Note that by using a string and symbol, Ruby will assume the namespace is define
|
|
|
260
260
|
|
|
261
261
|
#### serializer
|
|
262
262
|
|
|
263
|
-
|
|
263
|
+
Specify which serializer to use if you want to use a serializer other than the default.
|
|
264
|
+
|
|
265
|
+
For a single resource:
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
@post = Post.first
|
|
269
|
+
render json: @post, serializer: SpecialPostSerializer
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
To specify which serializer to use on individual items in a collection (i.e., an `index` action), use `each_serializer`:
|
|
273
|
+
|
|
274
|
+
```ruby
|
|
275
|
+
@posts = Post.all
|
|
276
|
+
render json: @posts, each_serializer: SpecialPostSerializer
|
|
277
|
+
```
|
|
264
278
|
|
|
265
279
|
#### scope
|
|
266
280
|
|
|
267
|
-
|
|
281
|
+
See [Serializers: Scope](/docs/general/serializers.md#scope).
|
|
268
282
|
|
|
269
283
|
#### scope_name
|
|
270
284
|
|
|
271
|
-
|
|
285
|
+
See [Serializers: Scope](/docs/general/serializers.md#scope).
|
|
272
286
|
|
|
273
287
|
## Using a serializer without `render`
|
|
274
288
|
|
data/docs/general/serializers.md
CHANGED
|
@@ -64,6 +64,10 @@ Where:
|
|
|
64
64
|
- `unless:`
|
|
65
65
|
- `virtual_value:`
|
|
66
66
|
- `polymorphic:` defines if polymorphic relation type should be nested in serialized association.
|
|
67
|
+
- `type:` the resource type as used by JSON:API, especially on a `belongs_to` relationship.
|
|
68
|
+
- `class_name:` used to determine `type` when `type` not given
|
|
69
|
+
- `foreign_key:` used by JSON:API on a `belongs_to` relationship to avoid unnecessarily loading the association object.
|
|
70
|
+
- `namespace:` used when looking up the serializer and `serializer` is not given. Falls back to the parent serializer's `:namespace` instance options, which, when present, comes from the render options. See [Rendering#namespace](rendering.md#namespace] for more details.
|
|
67
71
|
- optional: `&block` is a context that returns the association's attributes.
|
|
68
72
|
- prevents `association_name` method from being called.
|
|
69
73
|
- return value of block is used as the association value.
|
|
@@ -382,11 +386,26 @@ The serialized value for a given key. e.g. `read_attribute_for_serialization(:ti
|
|
|
382
386
|
|
|
383
387
|
#### #links
|
|
384
388
|
|
|
385
|
-
|
|
389
|
+
Allows you to modify the `links` node. By default, this node will be populated with the attributes set using the [::link](#link) method. Using `links: nil` will remove the `links` node.
|
|
390
|
+
|
|
391
|
+
```ruby
|
|
392
|
+
ActiveModelSerializers::SerializableResource.new(
|
|
393
|
+
@post,
|
|
394
|
+
adapter: :json_api,
|
|
395
|
+
links: {
|
|
396
|
+
self: {
|
|
397
|
+
href: 'http://example.com/posts',
|
|
398
|
+
meta: {
|
|
399
|
+
stuff: 'value'
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
)
|
|
404
|
+
```
|
|
386
405
|
|
|
387
406
|
#### #json_key
|
|
388
407
|
|
|
389
|
-
|
|
408
|
+
Returns the key used by the adapter as the resource root. See [root](#root) for more information.
|
|
390
409
|
|
|
391
410
|
## Examples
|
|
392
411
|
|
|
@@ -72,7 +72,7 @@ ActiveModelSerializers pagination relies on a paginated collection with the meth
|
|
|
72
72
|
|
|
73
73
|
### JSON adapter
|
|
74
74
|
|
|
75
|
-
If you are using `JSON` adapter, pagination links will not be included automatically, but it is possible to do so using `meta` key.
|
|
75
|
+
If you are not using `JSON` adapter, pagination links will not be included automatically, but it is possible to do so using `meta` key.
|
|
76
76
|
|
|
77
77
|
Add this method to your base API controller.
|
|
78
78
|
|
|
@@ -74,6 +74,9 @@ Then, in your controller you can tell rails you're accepting and rendering the j
|
|
|
74
74
|
end
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
#### Note:
|
|
78
|
+
In Rails 5, the "unsafe" method ( `jsonapi_parse!` vs the safe `jsonapi_parse`) throws an `InvalidDocument` exception when the payload does not meet basic criteria for JSON API deserialization.
|
|
79
|
+
|
|
77
80
|
|
|
78
81
|
### Adapter Changes
|
|
79
82
|
|
|
@@ -4,13 +4,7 @@ require 'active_model/serializer/collection_serializer'
|
|
|
4
4
|
require 'active_model/serializer/array_serializer'
|
|
5
5
|
require 'active_model/serializer/error_serializer'
|
|
6
6
|
require 'active_model/serializer/errors_serializer'
|
|
7
|
-
require 'active_model/serializer/concerns/associations'
|
|
8
|
-
require 'active_model/serializer/concerns/attributes'
|
|
9
7
|
require 'active_model/serializer/concerns/caching'
|
|
10
|
-
require 'active_model/serializer/concerns/configuration'
|
|
11
|
-
require 'active_model/serializer/concerns/links'
|
|
12
|
-
require 'active_model/serializer/concerns/meta'
|
|
13
|
-
require 'active_model/serializer/concerns/type'
|
|
14
8
|
require 'active_model/serializer/fieldset'
|
|
15
9
|
require 'active_model/serializer/lint'
|
|
16
10
|
|
|
@@ -18,33 +12,40 @@ require 'active_model/serializer/lint'
|
|
|
18
12
|
# reified when subclassed to decorate a resource.
|
|
19
13
|
module ActiveModel
|
|
20
14
|
class Serializer
|
|
15
|
+
undef_method :select, :display # These IO methods, which are mixed into Kernel,
|
|
16
|
+
# sometimes conflict with attribute names. We don't need these IO methods.
|
|
17
|
+
|
|
21
18
|
# @see #serializable_hash for more details on these valid keys.
|
|
22
19
|
SERIALIZABLE_HASH_VALID_KEYS = [:only, :except, :methods, :include, :root].freeze
|
|
23
20
|
extend ActiveSupport::Autoload
|
|
24
21
|
autoload :Adapter
|
|
25
22
|
autoload :Null
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
autoload :Attribute
|
|
24
|
+
autoload :Association
|
|
25
|
+
autoload :Reflection
|
|
26
|
+
autoload :SingularReflection
|
|
27
|
+
autoload :CollectionReflection
|
|
28
|
+
autoload :BelongsToReflection
|
|
29
|
+
autoload :HasOneReflection
|
|
30
|
+
autoload :HasManyReflection
|
|
31
|
+
include ActiveSupport::Configurable
|
|
29
32
|
include Caching
|
|
30
|
-
include Links
|
|
31
|
-
include Meta
|
|
32
|
-
include Type
|
|
33
33
|
|
|
34
34
|
# @param resource [ActiveRecord::Base, ActiveModelSerializers::Model]
|
|
35
35
|
# @return [ActiveModel::Serializer]
|
|
36
36
|
# Preferentially returns
|
|
37
|
-
# 1. resource.
|
|
37
|
+
# 1. resource.serializer_class
|
|
38
38
|
# 2. ArraySerializer when resource is a collection
|
|
39
39
|
# 3. options[:serializer]
|
|
40
40
|
# 4. lookup serializer when resource is a Class
|
|
41
|
-
def self.serializer_for(
|
|
42
|
-
if
|
|
43
|
-
|
|
44
|
-
elsif
|
|
41
|
+
def self.serializer_for(resource_or_class, options = {})
|
|
42
|
+
if resource_or_class.respond_to?(:serializer_class)
|
|
43
|
+
resource_or_class.serializer_class
|
|
44
|
+
elsif resource_or_class.respond_to?(:to_ary)
|
|
45
45
|
config.collection_serializer
|
|
46
46
|
else
|
|
47
|
-
|
|
47
|
+
resource_class = resource_or_class.class == Class ? resource_or_class : resource_or_class.class
|
|
48
|
+
options.fetch(:serializer) { get_serializer_for(resource_class, options[:namespace]) }
|
|
48
49
|
end
|
|
49
50
|
end
|
|
50
51
|
|
|
@@ -91,6 +92,8 @@ module ActiveModel
|
|
|
91
92
|
serializer_class
|
|
92
93
|
elsif klass.superclass
|
|
93
94
|
get_serializer_for(klass.superclass)
|
|
95
|
+
else
|
|
96
|
+
nil # No serializer found
|
|
94
97
|
end
|
|
95
98
|
end
|
|
96
99
|
end
|
|
@@ -111,6 +114,193 @@ module ActiveModel
|
|
|
111
114
|
@serialization_adapter_instance ||= ActiveModelSerializers::Adapter::Attributes
|
|
112
115
|
end
|
|
113
116
|
|
|
117
|
+
# Preferred interface is ActiveModelSerializers.config
|
|
118
|
+
# BEGIN DEFAULT CONFIGURATION
|
|
119
|
+
config.collection_serializer = ActiveModel::Serializer::CollectionSerializer
|
|
120
|
+
config.serializer_lookup_enabled = true
|
|
121
|
+
|
|
122
|
+
# @deprecated Use {#config.collection_serializer=} instead of this. Is
|
|
123
|
+
# compatibility layer for ArraySerializer.
|
|
124
|
+
def config.array_serializer=(collection_serializer)
|
|
125
|
+
self.collection_serializer = collection_serializer
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# @deprecated Use {#config.collection_serializer} instead of this. Is
|
|
129
|
+
# compatibility layer for ArraySerializer.
|
|
130
|
+
def config.array_serializer
|
|
131
|
+
collection_serializer
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
config.default_includes = '*'
|
|
135
|
+
config.adapter = :attributes
|
|
136
|
+
config.key_transform = nil
|
|
137
|
+
config.jsonapi_pagination_links_enabled = true
|
|
138
|
+
config.jsonapi_resource_type = :plural
|
|
139
|
+
config.jsonapi_namespace_separator = '-'.freeze
|
|
140
|
+
config.jsonapi_version = '1.0'
|
|
141
|
+
config.jsonapi_toplevel_meta = {}
|
|
142
|
+
# Make JSON API top-level jsonapi member opt-in
|
|
143
|
+
# ref: http://jsonapi.org/format/#document-top-level
|
|
144
|
+
config.jsonapi_include_toplevel_object = false
|
|
145
|
+
config.include_data_default = true
|
|
146
|
+
|
|
147
|
+
# For configuring how serializers are found.
|
|
148
|
+
# This should be an array of procs.
|
|
149
|
+
#
|
|
150
|
+
# The priority of the output is that the first item
|
|
151
|
+
# in the evaluated result array will take precedence
|
|
152
|
+
# over other possible serializer paths.
|
|
153
|
+
#
|
|
154
|
+
# i.e.: First match wins.
|
|
155
|
+
#
|
|
156
|
+
# @example output
|
|
157
|
+
# => [
|
|
158
|
+
# "CustomNamespace::ResourceSerializer",
|
|
159
|
+
# "ParentSerializer::ResourceSerializer",
|
|
160
|
+
# "ResourceNamespace::ResourceSerializer" ,
|
|
161
|
+
# "ResourceSerializer"]
|
|
162
|
+
#
|
|
163
|
+
# If CustomNamespace::ResourceSerializer exists, it will be used
|
|
164
|
+
# for serialization
|
|
165
|
+
config.serializer_lookup_chain = ActiveModelSerializers::LookupChain::DEFAULT.dup
|
|
166
|
+
|
|
167
|
+
config.schema_path = 'test/support/schemas'
|
|
168
|
+
# END DEFAULT CONFIGURATION
|
|
169
|
+
|
|
170
|
+
with_options instance_writer: false, instance_reader: false do |serializer|
|
|
171
|
+
serializer.class_attribute :_attributes_data # @api private
|
|
172
|
+
self._attributes_data ||= {}
|
|
173
|
+
end
|
|
174
|
+
with_options instance_writer: false, instance_reader: true do |serializer|
|
|
175
|
+
serializer.class_attribute :_reflections
|
|
176
|
+
self._reflections ||= {}
|
|
177
|
+
serializer.class_attribute :_links # @api private
|
|
178
|
+
self._links ||= {}
|
|
179
|
+
serializer.class_attribute :_meta # @api private
|
|
180
|
+
serializer.class_attribute :_type # @api private
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def self.inherited(base)
|
|
184
|
+
super
|
|
185
|
+
base._attributes_data = _attributes_data.dup
|
|
186
|
+
base._reflections = _reflections.dup
|
|
187
|
+
base._links = _links.dup
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# @return [Array<Symbol>] Key names of declared attributes
|
|
191
|
+
# @see Serializer::attribute
|
|
192
|
+
def self._attributes
|
|
193
|
+
_attributes_data.keys
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# BEGIN SERIALIZER MACROS
|
|
197
|
+
|
|
198
|
+
# @example
|
|
199
|
+
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
200
|
+
# attributes :id, :name, :recent_edits
|
|
201
|
+
def self.attributes(*attrs)
|
|
202
|
+
attrs = attrs.first if attrs.first.class == Array
|
|
203
|
+
|
|
204
|
+
attrs.each do |attr|
|
|
205
|
+
attribute(attr)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# @example
|
|
210
|
+
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
211
|
+
# attributes :id, :recent_edits
|
|
212
|
+
# attribute :name, key: :title
|
|
213
|
+
#
|
|
214
|
+
# attribute :full_name do
|
|
215
|
+
# "#{object.first_name} #{object.last_name}"
|
|
216
|
+
# end
|
|
217
|
+
#
|
|
218
|
+
# def recent_edits
|
|
219
|
+
# object.edits.last(5)
|
|
220
|
+
# end
|
|
221
|
+
def self.attribute(attr, options = {}, &block)
|
|
222
|
+
key = options.fetch(:key, attr)
|
|
223
|
+
_attributes_data[key] = Attribute.new(attr, options, block)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# @param [Symbol] name of the association
|
|
227
|
+
# @param [Hash<Symbol => any>] options for the reflection
|
|
228
|
+
# @return [void]
|
|
229
|
+
#
|
|
230
|
+
# @example
|
|
231
|
+
# has_many :comments, serializer: CommentSummarySerializer
|
|
232
|
+
#
|
|
233
|
+
def self.has_many(name, options = {}, &block) # rubocop:disable Style/PredicateName
|
|
234
|
+
associate(HasManyReflection.new(name, options, block))
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# @param [Symbol] name of the association
|
|
238
|
+
# @param [Hash<Symbol => any>] options for the reflection
|
|
239
|
+
# @return [void]
|
|
240
|
+
#
|
|
241
|
+
# @example
|
|
242
|
+
# belongs_to :author, serializer: AuthorSerializer
|
|
243
|
+
#
|
|
244
|
+
def self.belongs_to(name, options = {}, &block)
|
|
245
|
+
associate(BelongsToReflection.new(name, options, block))
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# @param [Symbol] name of the association
|
|
249
|
+
# @param [Hash<Symbol => any>] options for the reflection
|
|
250
|
+
# @return [void]
|
|
251
|
+
#
|
|
252
|
+
# @example
|
|
253
|
+
# has_one :author, serializer: AuthorSerializer
|
|
254
|
+
#
|
|
255
|
+
def self.has_one(name, options = {}, &block) # rubocop:disable Style/PredicateName
|
|
256
|
+
associate(HasOneReflection.new(name, options, block))
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Add reflection and define {name} accessor.
|
|
260
|
+
# @param [ActiveModel::Serializer::Reflection] reflection
|
|
261
|
+
# @return [void]
|
|
262
|
+
#
|
|
263
|
+
# @api private
|
|
264
|
+
def self.associate(reflection)
|
|
265
|
+
key = reflection.options[:key] || reflection.name
|
|
266
|
+
self._reflections[key] = reflection
|
|
267
|
+
end
|
|
268
|
+
private_class_method :associate
|
|
269
|
+
|
|
270
|
+
# Define a link on a serializer.
|
|
271
|
+
# @example
|
|
272
|
+
# link(:self) { resource_url(object) }
|
|
273
|
+
# @example
|
|
274
|
+
# link(:self) { "http://example.com/resource/#{object.id}" }
|
|
275
|
+
# @example
|
|
276
|
+
# link :resource, "http://example.com/resource"
|
|
277
|
+
#
|
|
278
|
+
def self.link(name, value = nil, &block)
|
|
279
|
+
_links[name] = block || value
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Set the JSON API meta attribute of a serializer.
|
|
283
|
+
# @example
|
|
284
|
+
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
285
|
+
# meta { stuff: 'value' }
|
|
286
|
+
# @example
|
|
287
|
+
# meta do
|
|
288
|
+
# { comment_count: object.comments.count }
|
|
289
|
+
# end
|
|
290
|
+
def self.meta(value = nil, &block)
|
|
291
|
+
self._meta = block || value
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Set the JSON API type of a serializer.
|
|
295
|
+
# @example
|
|
296
|
+
# class AdminAuthorSerializer < ActiveModel::Serializer
|
|
297
|
+
# type 'authors'
|
|
298
|
+
def self.type(type)
|
|
299
|
+
self._type = type && type.to_s
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# END SERIALIZER MACROS
|
|
303
|
+
|
|
114
304
|
attr_accessor :object, :root, :scope
|
|
115
305
|
|
|
116
306
|
# `scope_name` is set as :current_user by default in the controller.
|
|
@@ -131,53 +321,49 @@ module ActiveModel
|
|
|
131
321
|
true
|
|
132
322
|
end
|
|
133
323
|
|
|
324
|
+
# Return the +attributes+ of +object+ as presented
|
|
325
|
+
# by the serializer.
|
|
326
|
+
def attributes(requested_attrs = nil, reload = false)
|
|
327
|
+
@attributes = nil if reload
|
|
328
|
+
@attributes ||= self.class._attributes_data.each_with_object({}) do |(key, attr), hash|
|
|
329
|
+
next if attr.excluded?(self)
|
|
330
|
+
next unless requested_attrs.nil? || requested_attrs.include?(key)
|
|
331
|
+
hash[key] = attr.value(self)
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# @param [JSONAPI::IncludeDirective] include_directive (defaults to the
|
|
336
|
+
# +default_include_directive+ config value when not provided)
|
|
337
|
+
# @return [Enumerator<Association>]
|
|
338
|
+
def associations(include_directive = ActiveModelSerializers.default_include_directive, include_slice = nil)
|
|
339
|
+
include_slice ||= include_directive
|
|
340
|
+
return Enumerator.new unless object
|
|
341
|
+
|
|
342
|
+
Enumerator.new do |y|
|
|
343
|
+
self.class._reflections.each do |key, reflection|
|
|
344
|
+
next if reflection.excluded?(self)
|
|
345
|
+
next unless include_directive.key?(key)
|
|
346
|
+
|
|
347
|
+
association = reflection.build_association(self, instance_options, include_slice)
|
|
348
|
+
y.yield association
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
134
353
|
# @return [Hash] containing the attributes and first level
|
|
135
354
|
# associations, similar to how ActiveModel::Serializers::JSON is used
|
|
136
355
|
# in ActiveRecord::Base.
|
|
137
|
-
#
|
|
138
|
-
# TODO: Include <tt>ActiveModel::Serializers::JSON</tt>.
|
|
139
|
-
# So that the below is true:
|
|
140
|
-
# @param options [nil, Hash] The same valid options passed to `serializable_hash`
|
|
141
|
-
# (:only, :except, :methods, and :include).
|
|
142
|
-
#
|
|
143
|
-
# See
|
|
144
|
-
# https://github.com/rails/rails/blob/v5.0.0.beta2/activemodel/lib/active_model/serializers/json.rb#L17-L101
|
|
145
|
-
# https://github.com/rails/rails/blob/v5.0.0.beta2/activemodel/lib/active_model/serialization.rb#L85-L123
|
|
146
|
-
# https://github.com/rails/rails/blob/v5.0.0.beta2/activerecord/lib/active_record/serialization.rb#L11-L17
|
|
147
|
-
# https://github.com/rails/rails/blob/v5.0.0.beta2/activesupport/lib/active_support/core_ext/object/json.rb#L147-L162
|
|
148
|
-
#
|
|
149
|
-
# @example
|
|
150
|
-
# # The :only and :except options can be used to limit the attributes included, and work
|
|
151
|
-
# # similar to the attributes method.
|
|
152
|
-
# serializer.as_json(only: [:id, :name])
|
|
153
|
-
# serializer.as_json(except: [:id, :created_at, :age])
|
|
154
|
-
#
|
|
155
|
-
# # To include the result of some method calls on the model use :methods:
|
|
156
|
-
# serializer.as_json(methods: :permalink)
|
|
157
|
-
#
|
|
158
|
-
# # To include associations use :include:
|
|
159
|
-
# serializer.as_json(include: :posts)
|
|
160
|
-
# # Second level and higher order associations work as well:
|
|
161
|
-
# serializer.as_json(include: { posts: { include: { comments: { only: :body } }, only: :title } })
|
|
162
356
|
def serializable_hash(adapter_options = nil, options = {}, adapter_instance = self.class.serialization_adapter_instance)
|
|
163
357
|
adapter_options ||= {}
|
|
164
358
|
options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
relationships = resource_relationships(adapter_options, options, adapter_instance)
|
|
359
|
+
resource = attributes_hash(adapter_options, options, adapter_instance)
|
|
360
|
+
relationships = associations_hash(adapter_options, options, adapter_instance)
|
|
168
361
|
resource.merge(relationships)
|
|
169
362
|
end
|
|
170
363
|
alias to_hash serializable_hash
|
|
171
364
|
alias to_h serializable_hash
|
|
172
365
|
|
|
173
366
|
# @see #serializable_hash
|
|
174
|
-
# TODO: When moving attributes adapter logic here, @see #serializable_hash
|
|
175
|
-
# So that the below is true:
|
|
176
|
-
# @param options [nil, Hash] The same valid options passed to `as_json`
|
|
177
|
-
# (:root, :only, :except, :methods, and :include).
|
|
178
|
-
# The default for `root` is nil.
|
|
179
|
-
# The default value for include_root is false. You can change it to true if the given
|
|
180
|
-
# JSON string includes a single root node.
|
|
181
367
|
def as_json(adapter_opts = nil)
|
|
182
368
|
serializable_hash(adapter_opts)
|
|
183
369
|
end
|
|
@@ -196,32 +382,24 @@ module ActiveModel
|
|
|
196
382
|
end
|
|
197
383
|
|
|
198
384
|
# @api private
|
|
199
|
-
def
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
385
|
+
def attributes_hash(_adapter_options, options, adapter_instance)
|
|
386
|
+
if self.class.cache_enabled?
|
|
387
|
+
fetch_attributes(options[:fields], options[:cached_attributes] || {}, adapter_instance)
|
|
388
|
+
elsif self.class.fragment_cache_enabled?
|
|
389
|
+
fetch_attributes_fragment(adapter_instance, options[:cached_attributes] || {})
|
|
390
|
+
else
|
|
391
|
+
attributes(options[:fields], true)
|
|
205
392
|
end
|
|
206
|
-
|
|
207
|
-
relationships
|
|
208
393
|
end
|
|
209
394
|
|
|
210
395
|
# @api private
|
|
211
|
-
def
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
relationship_value = association_serializer.serializable_hash(adapter_options, {}, adapter_instance)
|
|
218
|
-
|
|
219
|
-
if association.options[:polymorphic] && relationship_value
|
|
220
|
-
polymorphic_type = association_object.class.name.underscore
|
|
221
|
-
relationship_value = { type: polymorphic_type, polymorphic_type.to_sym => relationship_value }
|
|
396
|
+
def associations_hash(adapter_options, options, adapter_instance)
|
|
397
|
+
include_directive = options.fetch(:include_directive)
|
|
398
|
+
include_slice = options[:include_slice]
|
|
399
|
+
associations(include_directive, include_slice).each_with_object({}) do |association, relationships|
|
|
400
|
+
adapter_opts = adapter_options.merge(include_directive: include_directive[association.key], adapter_instance: adapter_instance)
|
|
401
|
+
relationships[association.key] = association.serializable_hash(adapter_opts, adapter_instance)
|
|
222
402
|
end
|
|
223
|
-
|
|
224
|
-
relationship_value
|
|
225
403
|
end
|
|
226
404
|
|
|
227
405
|
protected
|