active_model_serializers 0.10.5 → 0.10.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
- [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](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
|
+
- [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](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
|
- [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](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
|