active_model_serializers 0.10.0.rc5 → 0.10.0
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/.gitignore +10 -0
- data/.travis.yml +0 -8
- data/CHANGELOG.md +23 -0
- data/CONTRIBUTING.md +14 -4
- data/Gemfile +1 -2
- data/README.md +3 -3
- data/active_model_serializers.gemspec +1 -1
- data/appveyor.yml +6 -10
- data/docs/ARCHITECTURE.md +1 -1
- data/docs/README.md +1 -0
- data/docs/general/deserialization.md +19 -19
- data/docs/general/serializers.md +33 -0
- data/docs/howto/serialize_poro.md +32 -0
- data/lib/active_model/serializer.rb +2 -0
- data/lib/active_model/serializer/caching.rb +185 -3
- data/lib/active_model/serializer/field.rb +36 -2
- data/lib/active_model/serializer/lint.rb +8 -18
- data/lib/active_model/serializer/version.rb +1 -1
- data/lib/active_model_serializers.rb +0 -2
- data/lib/active_model_serializers/adapter/attributes.rb +20 -38
- data/lib/active_model_serializers/adapter/base.rb +8 -15
- data/lib/active_model_serializers/adapter/json.rb +12 -2
- data/lib/active_model_serializers/adapter/json_api.rb +33 -30
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +10 -5
- data/lib/active_model_serializers/adapter/null.rb +1 -2
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +3 -2
- data/lib/active_model_serializers/serializable_resource.rb +1 -1
- data/lib/active_model_serializers/test/schema.rb +44 -9
- data/test/action_controller/json_api/transform_test.rb +2 -1
- data/test/action_controller/serialization_test.rb +3 -1
- data/test/active_model_serializers/test/schema_test.rb +5 -3
- data/test/active_model_serializers/test/serializer_test.rb +1 -2
- data/test/adapter/json/transform_test.rb +14 -14
- data/test/adapter/json_api/has_many_test.rb +3 -2
- data/test/adapter/json_api/has_one_test.rb +3 -2
- data/test/adapter/json_api/pagination_links_test.rb +39 -21
- data/test/adapter/json_api/transform_test.rb +36 -34
- data/test/adapter/polymorphic_test.rb +111 -12
- data/test/adapter_test.rb +27 -0
- data/test/array_serializer_test.rb +10 -25
- data/test/benchmark/bm_caching.rb +17 -15
- data/test/benchmark/controllers.rb +9 -2
- data/test/benchmark/fixtures.rb +56 -4
- data/test/cache_test.rb +103 -6
- data/test/fixtures/active_record.rb +10 -0
- data/test/fixtures/poro.rb +31 -3
- data/test/serializers/associations_test.rb +43 -15
- data/test/serializers/attribute_test.rb +44 -16
- data/test/serializers/meta_test.rb +5 -7
- data/test/support/isolated_unit.rb +0 -1
- data/test/test_helper.rb +19 -21
- metadata +7 -12
- data/lib/active_model_serializers/cached_serializer.rb +0 -87
- data/lib/active_model_serializers/fragment_cache.rb +0 -118
- data/test/active_model_serializers/cached_serializer_test.rb +0 -80
- data/test/active_model_serializers/fragment_cache_test.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb97c8740c2530c085ee9186ea27ddd220367fc3
|
4
|
+
data.tar.gz: 0a768d3ab122abe2c3e7f5b6c00a8dada6872433
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5589022dd6fa160c0a03981cedf46433fb1eb19e4bac5f3489908671e9e0969c48bb7a86cf03d12cf8ce825b2efe3ed6ad1b6594019635470319c899e7964617
|
7
|
+
data.tar.gz: 989cf413780f12d47a553a7aae5cba33de1625bbc022366b7e4533810b87b151302ed6000116c03234bf2b2048a913048624728a54be87cead848076924cf6ce
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -3,7 +3,6 @@ language: ruby
|
|
3
3
|
sudo: false
|
4
4
|
|
5
5
|
rvm:
|
6
|
-
- 2.0.0
|
7
6
|
- 2.1
|
8
7
|
- 2.2.3
|
9
8
|
- 2.3.0
|
@@ -26,25 +25,18 @@ env:
|
|
26
25
|
global:
|
27
26
|
- "JRUBY_OPTS='--dev -J-Xmx1024M --debug'"
|
28
27
|
matrix:
|
29
|
-
- "RAILS_VERSION=4.0"
|
30
28
|
- "RAILS_VERSION=4.1"
|
31
29
|
- "RAILS_VERSION=4.2"
|
32
30
|
- "RAILS_VERSION=master"
|
33
31
|
|
34
32
|
matrix:
|
35
33
|
exclude:
|
36
|
-
- rvm: 2.0.0
|
37
|
-
env: RAILS_VERSION=master
|
38
34
|
- rvm: 2.1
|
39
35
|
env: RAILS_VERSION=master
|
40
36
|
- rvm: jruby-9.0.4.0
|
41
37
|
env: RAILS_VERSION=master
|
42
|
-
- rvm: jruby-9.0.4.0
|
43
|
-
env: RAILS_VERSION=4.0
|
44
38
|
- rvm: jruby-head
|
45
39
|
env: RAILS_VERSION=master
|
46
|
-
- rvm: jruby-head
|
47
|
-
env: RAILS_VERSION=4.0
|
48
40
|
allow_failures:
|
49
41
|
- rvm: ruby-head
|
50
42
|
- rvm: jruby-head
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
## 0.10.x
|
2
2
|
|
3
|
+
### [master (unreleased)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0...master)
|
4
|
+
|
5
|
+
### v0.10.0 (2016-05-17)
|
6
|
+
|
7
|
+
Breaking changes:
|
8
|
+
- [#1662](https://github.com/rails-api/active_model_serializers/pull/1662) Drop support for Rails 4.0 and Ruby 2.0.0. (@remear)
|
9
|
+
|
10
|
+
Features:
|
11
|
+
- [#1677](https://github.com/rails-api/active_model_serializers/pull/1677) Add `assert_schema`, `assert_request_schema`, `assert_request_response_schema`. (@bf4)
|
12
|
+
- [#1697](https://github.com/rails-api/active_model_serializers/pull/1697) Include actual exception message with custom exceptions;
|
13
|
+
`Test::Schema` exceptions are now `Minitest::Assertion`s. (@bf4)
|
14
|
+
- [#1699](https://github.com/rails-api/active_model_serializers/pull/1699) String/Lambda support for conditional attributes/associations (@mtsmfm)
|
15
|
+
- [#1687](https://github.com/rails-api/active_model_serializers/pull/1687) Only calculate `_cache_digest` (in `cache_key`) when `skip_digest` is false. (@bf4)
|
16
|
+
- [#1647](https://github.com/rails-api/active_model_serializers/pull/1647) Restrict usage of `serializable_hash` options
|
17
|
+
to the ActiveModel::Serialization and ActiveModel::Serializers::JSON interface. (@bf4)
|
18
|
+
|
19
|
+
Fixes:
|
20
|
+
- [#1700](https://github.com/rails-api/active_model_serializers/pull/1700) Support pagination link for Kaminari when no data is returned. (@iamnader)
|
21
|
+
- [#1726](https://github.com/rails-api/active_model_serializers/pull/1726) Adds polymorphic option to association definition which includes association type/nesting in serializer (@cgmckeever)
|
22
|
+
|
23
|
+
Misc:
|
24
|
+
- [#1673](https://github.com/rails-api/active_model_serializers/pull/1673) Adds "How to" guide on using AMS with POROs (@DrSayre)
|
25
|
+
|
3
26
|
### [v0.10.0.rc5 (2016-04-04)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc4...v0.10.0.rc5)
|
4
27
|
|
5
28
|
Breaking changes:
|
data/CONTRIBUTING.md
CHANGED
@@ -7,7 +7,7 @@ Before opening an issue, try the following:
|
|
7
7
|
See if your issue can be resolved by information in the documentation.
|
8
8
|
|
9
9
|
- [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master/docs)
|
10
|
-
- [](http://www.rubydoc.info/github/rails-api/active_model_serializers/v0.10.0
|
10
|
+
- [](http://www.rubydoc.info/github/rails-api/active_model_serializers/v0.10.0)
|
11
11
|
- [Guides](docs)
|
12
12
|
- [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable)
|
13
13
|
- [0.8 (0-8-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-8-stable)
|
@@ -74,10 +74,15 @@ Run a single test
|
|
74
74
|
`$ rake test TEST=path/to/test.rb TESTOPTS="--name=test_something"`
|
75
75
|
|
76
76
|
Run tests against different Rails versions by setting the RAILS_VERSION variable
|
77
|
-
and bundling gems.
|
77
|
+
and bundling gems. (save this script somewhere executable and run from top of AMS repository)
|
78
78
|
|
79
79
|
```bash
|
80
|
-
|
80
|
+
#!/usr/bin/env bash
|
81
|
+
|
82
|
+
rcommand='puts YAML.load_file("./.travis.yml")["env"]["matrix"].join(" ").gsub("RAILS_VERSION=", "")'
|
83
|
+
versions=$(ruby -ryaml -e "$rcommand")
|
84
|
+
|
85
|
+
for version in ${versions[@]}; do
|
81
86
|
export RAILS_VERSION="$version"
|
82
87
|
rm -f Gemfile.lock
|
83
88
|
bundle check || bundle --local || bundle
|
@@ -88,7 +93,12 @@ for version in 4.0 4.1 4.2 master; do
|
|
88
93
|
else
|
89
94
|
# red in ANSI
|
90
95
|
echo -e "\033[31m **** Tests failed against Rails ${RAILS_VERSION} **** \033[0m"
|
91
|
-
|
96
|
+
read -p '[Enter] any key to continue, [q] to quit...' prompt
|
97
|
+
if [ "$prompt" = 'q' ]; then
|
98
|
+
unset RAILS_VERSION
|
99
|
+
exit 1
|
100
|
+
fi
|
101
|
+
fi
|
92
102
|
unset RAILS_VERSION
|
93
103
|
done
|
94
104
|
```
|
data/Gemfile
CHANGED
@@ -44,11 +44,10 @@ end
|
|
44
44
|
group :test do
|
45
45
|
gem 'sqlite3', platform: (@windows_platforms + [:ruby])
|
46
46
|
gem 'activerecord-jdbcsqlite3-adapter', platform: :jruby
|
47
|
-
|
48
47
|
gem 'codeclimate-test-reporter', require: false
|
49
48
|
end
|
50
49
|
|
51
50
|
group :development, :test do
|
52
|
-
gem 'rubocop', '~> 0.
|
51
|
+
gem 'rubocop', '~> 0.39.0', require: false
|
53
52
|
gem 'yard', require: false
|
54
53
|
end
|
data/README.md
CHANGED
@@ -28,7 +28,7 @@
|
|
28
28
|
## Documentation
|
29
29
|
|
30
30
|
- [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master)
|
31
|
-
- [](http://www.rubydoc.info/github/rails-api/active_model_serializers/v0.10.0
|
31
|
+
- [](http://www.rubydoc.info/github/rails-api/active_model_serializers/v0.10.0)
|
32
32
|
- [Guides](docs)
|
33
33
|
- [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable)
|
34
34
|
- [](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-9-stable)
|
@@ -137,7 +137,7 @@ The model can be serialized as:
|
|
137
137
|
|
138
138
|
```ruby
|
139
139
|
options = {}
|
140
|
-
serialization = SerializableResource.new(resource, options)
|
140
|
+
serialization = ActiveModelSerializers::SerializableResource.new(resource, options)
|
141
141
|
serialization.to_json
|
142
142
|
serialization.as_json
|
143
143
|
```
|
@@ -146,7 +146,7 @@ SerializableResource delegates to the adapter, which it builds as:
|
|
146
146
|
|
147
147
|
```ruby
|
148
148
|
adapter_options = {}
|
149
|
-
adapter = Adapter.create(serializer, adapter_options)
|
149
|
+
adapter = ActiveModelSerializers::Adapter.create(serializer, adapter_options)
|
150
150
|
adapter.to_json
|
151
151
|
adapter.as_json
|
152
152
|
adapter.serializable_hash
|
@@ -60,7 +60,7 @@ Gem::Specification.new do |spec|
|
|
60
60
|
|
61
61
|
spec.post_install_message = <<-EOF
|
62
62
|
NOTE: The default key case for the JsonApi adapter has changed to dashed.
|
63
|
-
See https://github.com/rails-api/active_model_serializers/blob/master/docs/general/
|
63
|
+
See https://github.com/rails-api/active_model_serializers/blob/master/docs/general/key_transforms.md
|
64
64
|
for more information on configuring this behavior.
|
65
65
|
EOF
|
66
66
|
end
|
data/appveyor.yml
CHANGED
@@ -3,23 +3,19 @@ version: '{build}'
|
|
3
3
|
skip_tags: true
|
4
4
|
|
5
5
|
environment:
|
6
|
+
JRUBY_OPTS: "--dev -J-Xmx1024M --debug"
|
6
7
|
matrix:
|
7
|
-
- ruby_version: "
|
8
|
-
- ruby_version: "
|
9
|
-
- ruby_version: "
|
10
|
-
- ruby_version: "21-x64"
|
11
|
-
- ruby_version: "jruby-9.0.4.0"
|
8
|
+
- ruby_version: "Ruby21"
|
9
|
+
- ruby_version: "Ruby21-x64"
|
10
|
+
- ruby_version: "jruby-9.0.0.0"
|
12
11
|
|
13
12
|
cache:
|
14
13
|
- vendor/bundle
|
15
14
|
|
16
15
|
install:
|
17
|
-
- SET PATH=C
|
18
|
-
- ruby --version
|
19
|
-
- gem --version
|
16
|
+
- SET PATH=C:\%ruby_version%\bin;%PATH%
|
20
17
|
- gem install bundler
|
21
|
-
-
|
22
|
-
- bundle platform
|
18
|
+
- bundle env
|
23
19
|
- bundle install --path=vendor/bundle --retry=3 --jobs=3
|
24
20
|
|
25
21
|
test_script:
|
data/docs/ARCHITECTURE.md
CHANGED
@@ -44,7 +44,7 @@ ActiveModelSerializers.
|
|
44
44
|
|
45
45
|
If the collection serializer (ArraySerializer) cannot
|
46
46
|
identify a serializer for a resource in its collection, it raises [`NoSerializerError`](https://github.com/rails-api/active_model_serializers/issues/1191#issuecomment-142327128)
|
47
|
-
which is rescued in `
|
47
|
+
which is rescued in `ActiveModel::Serializer::Reflection#build_association` which sets
|
48
48
|
the association value directly:
|
49
49
|
|
50
50
|
```ruby
|
data/docs/README.md
CHANGED
@@ -27,6 +27,7 @@ This is the documentation of ActiveModelSerializers, it's focused on the **0.10.
|
|
27
27
|
- [Using ActiveModelSerializers Outside Of Controllers](howto/outside_controller_use.md)
|
28
28
|
- [Testing ActiveModelSerializers](howto/test.md)
|
29
29
|
- [Passing Arbitrary Options](howto/passing_arbitrary_options.md)
|
30
|
+
- [How to serialize a Plain-Old Ruby Object (PORO)](howto/serialize_poro.md)
|
30
31
|
|
31
32
|
## Integrations
|
32
33
|
|
@@ -35,30 +35,30 @@ Given a JSON API document,
|
|
35
35
|
|
36
36
|
```
|
37
37
|
document = {
|
38
|
-
data
|
39
|
-
id
|
40
|
-
type
|
41
|
-
attributes
|
42
|
-
title
|
43
|
-
date
|
38
|
+
'data' => {
|
39
|
+
'id' => 1,
|
40
|
+
'type' => 'post',
|
41
|
+
'attributes' => {
|
42
|
+
'title' => 'Title 1',
|
43
|
+
'date' => '2015-12-20'
|
44
44
|
},
|
45
|
-
associations
|
46
|
-
author
|
47
|
-
data
|
48
|
-
type
|
49
|
-
id
|
45
|
+
'associations' => {
|
46
|
+
'author' => {
|
47
|
+
'data' => {
|
48
|
+
'type' => 'user',
|
49
|
+
'id' => '2'
|
50
50
|
}
|
51
51
|
},
|
52
|
-
second_author
|
53
|
-
data
|
52
|
+
'second_author' => {
|
53
|
+
'data' => nil
|
54
54
|
},
|
55
|
-
comments
|
56
|
-
data
|
57
|
-
type
|
58
|
-
id
|
55
|
+
'comments' => {
|
56
|
+
'data' => [{
|
57
|
+
'type' => 'comment',
|
58
|
+
'id' => '3'
|
59
59
|
},{
|
60
|
-
type
|
61
|
-
id
|
60
|
+
'type' => 'comment',
|
61
|
+
'id' => '4'
|
62
62
|
}]
|
63
63
|
}
|
64
64
|
}
|
data/docs/general/serializers.md
CHANGED
@@ -38,6 +38,27 @@ Serialization of the resource `title`
|
|
38
38
|
|
39
39
|
### Associations
|
40
40
|
|
41
|
+
The interface for associations is, generically:
|
42
|
+
|
43
|
+
> `association_type(association_name, options, &block)`
|
44
|
+
|
45
|
+
Where:
|
46
|
+
|
47
|
+
- `association_type` may be `has_one`, `has_many`, `belongs_to`.
|
48
|
+
- `association_name` is a method name the serializer calls.
|
49
|
+
- optional: `options` may be:
|
50
|
+
- `key:` The name used for the serialized association.
|
51
|
+
- `serializer:`
|
52
|
+
- `if:`
|
53
|
+
- `unless:`
|
54
|
+
- `virtual_value:`
|
55
|
+
- `polymorphic:` defines if polymorphic relation type should be nested in serialized association.
|
56
|
+
- optional: `&block` is a context that returns the association's attributes.
|
57
|
+
- prevents `association_name` method from being called.
|
58
|
+
- return value of block is used as the association value.
|
59
|
+
- yields the `serializer` to the block.
|
60
|
+
- `include_data false` prevents the `data` key from being rendered in the JSON API relationship.
|
61
|
+
|
41
62
|
#### ::has_one
|
42
63
|
|
43
64
|
e.g.
|
@@ -58,6 +79,18 @@ def cached_blog
|
|
58
79
|
end
|
59
80
|
```
|
60
81
|
|
82
|
+
```ruby
|
83
|
+
has_one :blog, if: :show_blog?
|
84
|
+
# you can also use a string or lambda
|
85
|
+
# has_one :blog, if: 'scope.admin?'
|
86
|
+
# has_one :blog, if: -> (serializer) { serializer.scope.admin? }
|
87
|
+
# has_one :blog, if: -> { scope.admin? }
|
88
|
+
|
89
|
+
def show_blog?
|
90
|
+
scope.admin?
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
61
94
|
#### ::has_many
|
62
95
|
|
63
96
|
e.g.
|
@@ -0,0 +1,32 @@
|
|
1
|
+
[Back to Guides](../README.md)
|
2
|
+
|
3
|
+
# How to serialize a Plain-Old Ruby Object (PORO)
|
4
|
+
|
5
|
+
When you are first getting started with ActiveModelSerializers, it may seem only `ActiveRecord::Base` objects can be serializable, but pretty much any object can be serializable with ActiveModelSerializers. Here is an example of a PORO that is serializable:
|
6
|
+
```ruby
|
7
|
+
# my_model.rb
|
8
|
+
class MyModel
|
9
|
+
alias :read_attribute_for_serialization :send
|
10
|
+
attr_accessor :id, :name, :level
|
11
|
+
|
12
|
+
def initialize(attributes)
|
13
|
+
@id = attributes[:id]
|
14
|
+
@name = attributes[:name]
|
15
|
+
@level = attributes[:level]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.model_name
|
19
|
+
@_model_name ||= ActiveModel::Name.new(self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
Fortunately, ActiveModelSerializers provides a [`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/model.rb) which you can use in production code that will make your PORO a lot cleaner. The above code now becomes:
|
25
|
+
```ruby
|
26
|
+
# my_model.rb
|
27
|
+
class MyModel < ActiveModelSerializers::Model
|
28
|
+
attr_accessor :id, :name, :level
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
32
|
+
The default serializer would be `MyModelSerializer`.
|
@@ -18,6 +18,8 @@ require 'active_model/serializer/type'
|
|
18
18
|
# reified when subclassed to decorate a resource.
|
19
19
|
module ActiveModel
|
20
20
|
class Serializer
|
21
|
+
# @see #serializable_hash for more details on these valid keys.
|
22
|
+
SERIALIZABLE_HASH_VALID_KEYS = [:only, :except, :methods, :include, :root].freeze
|
21
23
|
extend ActiveSupport::Autoload
|
22
24
|
autoload :Adapter
|
23
25
|
autoload :Null
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module ActiveModel
|
2
2
|
class Serializer
|
3
|
+
UndefinedCacheKey = Class.new(StandardError)
|
3
4
|
module Caching
|
4
5
|
extend ActiveSupport::Concern
|
5
6
|
|
@@ -17,8 +18,9 @@ module ActiveModel
|
|
17
18
|
# force
|
18
19
|
# race_condition_ttl
|
19
20
|
# Passed to ::_cache as
|
20
|
-
# serializer.
|
21
|
-
|
21
|
+
# serializer.cache_store.fetch(cache_key, @klass._cache_options)
|
22
|
+
# Passed as second argument to serializer.cache_store.fetch(cache_key, self.class._cache_options)
|
23
|
+
serializer.class_attribute :_cache_digest_file_path # @api private : Derived at inheritance
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
@@ -41,7 +43,12 @@ module ActiveModel
|
|
41
43
|
def inherited(base)
|
42
44
|
super
|
43
45
|
caller_line = caller[1]
|
44
|
-
base.
|
46
|
+
base._cache_digest_file_path = caller_line
|
47
|
+
end
|
48
|
+
|
49
|
+
def _cache_digest
|
50
|
+
return @_cache_digest if defined?(@_cache_digest)
|
51
|
+
@_cache_digest = digest_caller_file(_cache_digest_file_path)
|
45
52
|
end
|
46
53
|
|
47
54
|
# Hashes contents of file for +_cache_digest+
|
@@ -62,6 +69,14 @@ module ActiveModel
|
|
62
69
|
_cache_options && _cache_options[:skip_digest]
|
63
70
|
end
|
64
71
|
|
72
|
+
def cached_attributes
|
73
|
+
_cache_only ? _cache_only : _attributes - _cache_except
|
74
|
+
end
|
75
|
+
|
76
|
+
def non_cached_attributes
|
77
|
+
_attributes - cached_attributes
|
78
|
+
end
|
79
|
+
|
65
80
|
# @api private
|
66
81
|
# Used by FragmentCache on the CachedSerializer
|
67
82
|
# to call attribute methods on the fragmented cached serializer.
|
@@ -145,6 +160,173 @@ module ActiveModel
|
|
145
160
|
perform_caching? && cache_store &&
|
146
161
|
(_cache_only && !_cache_except || !_cache_only && _cache_except)
|
147
162
|
end
|
163
|
+
|
164
|
+
# Read cache from cache_store
|
165
|
+
# @return [Hash]
|
166
|
+
def cache_read_multi(collection_serializer, adapter_instance, include_tree)
|
167
|
+
return {} if ActiveModelSerializers.config.cache_store.blank?
|
168
|
+
|
169
|
+
keys = object_cache_keys(collection_serializer, adapter_instance, include_tree)
|
170
|
+
|
171
|
+
return {} if keys.blank?
|
172
|
+
|
173
|
+
ActiveModelSerializers.config.cache_store.read_multi(*keys)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Find all cache_key for the collection_serializer
|
177
|
+
# @param serializers [ActiveModel::Serializer::CollectionSerializer]
|
178
|
+
# @param adapter_instance [ActiveModelSerializers::Adapter::Base]
|
179
|
+
# @param include_tree [ActiveModel::Serializer::IncludeTree]
|
180
|
+
# @return [Array] all cache_key of collection_serializer
|
181
|
+
def object_cache_keys(collection_serializer, adapter_instance, include_tree)
|
182
|
+
cache_keys = []
|
183
|
+
|
184
|
+
collection_serializer.each do |serializer|
|
185
|
+
cache_keys << object_cache_key(serializer, adapter_instance)
|
186
|
+
|
187
|
+
serializer.associations(include_tree).each do |association|
|
188
|
+
if association.serializer.respond_to?(:each)
|
189
|
+
association.serializer.each do |sub_serializer|
|
190
|
+
cache_keys << object_cache_key(sub_serializer, adapter_instance)
|
191
|
+
end
|
192
|
+
else
|
193
|
+
cache_keys << object_cache_key(association.serializer, adapter_instance)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
cache_keys.compact.uniq
|
199
|
+
end
|
200
|
+
|
201
|
+
# @return [String, nil] the cache_key of the serializer or nil
|
202
|
+
def object_cache_key(serializer, adapter_instance)
|
203
|
+
return unless serializer.present? && serializer.object.present?
|
204
|
+
|
205
|
+
serializer.class.cache_enabled? ? serializer.cache_key(adapter_instance) : nil
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Get attributes from @cached_attributes
|
210
|
+
# @return [Hash] cached attributes
|
211
|
+
# def cached_attributes(fields, adapter_instance)
|
212
|
+
def cached_fields(fields, adapter_instance)
|
213
|
+
cache_check(adapter_instance) do
|
214
|
+
attributes(fields)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def cache_check(adapter_instance)
|
219
|
+
if self.class.cache_enabled?
|
220
|
+
self.class.cache_store.fetch(cache_key(adapter_instance), self.class._cache_options) do
|
221
|
+
yield
|
222
|
+
end
|
223
|
+
elsif self.class.fragment_cache_enabled?
|
224
|
+
fetch_fragment_cache(adapter_instance)
|
225
|
+
else
|
226
|
+
yield
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# 1. Create a CachedSerializer and NonCachedSerializer from the serializer class
|
231
|
+
# 2. Serialize the above two with the given adapter
|
232
|
+
# 3. Pass their serializations to the adapter +::fragment_cache+
|
233
|
+
#
|
234
|
+
# It will split the serializer into two, one that will be cached and one that will not
|
235
|
+
#
|
236
|
+
# Given a resource name
|
237
|
+
# 1. Dynamically creates a CachedSerializer and NonCachedSerializer
|
238
|
+
# for a given class 'name'
|
239
|
+
# 2. Call
|
240
|
+
# CachedSerializer.cache(serializer._cache_options)
|
241
|
+
# CachedSerializer.fragmented(serializer)
|
242
|
+
# NonCachedSerializer.cache(serializer._cache_options)
|
243
|
+
# 3. Build a hash keyed to the +cached+ and +non_cached+ serializers
|
244
|
+
# 4. Call +cached_attributes+ on the serializer class and the above hash
|
245
|
+
# 5. Return the hash
|
246
|
+
#
|
247
|
+
# @example
|
248
|
+
# When +name+ is <tt>User::Admin</tt>
|
249
|
+
# creates the Serializer classes (if they don't exist).
|
250
|
+
# CachedUser_AdminSerializer
|
251
|
+
# NonCachedUser_AdminSerializer
|
252
|
+
#
|
253
|
+
# Given a hash of its cached and non-cached serializers
|
254
|
+
# 1. Determine cached attributes from serializer class options
|
255
|
+
# 2. Add cached attributes to cached Serializer
|
256
|
+
# 3. Add non-cached attributes to non-cached Serializer
|
257
|
+
def fetch_fragment_cache(adapter_instance)
|
258
|
+
serializer_class_name = self.class.name.gsub('::'.freeze, '_'.freeze)
|
259
|
+
self.class._cache_options ||= {}
|
260
|
+
self.class._cache_options[:key] = self.class._cache_key if self.class._cache_key
|
261
|
+
|
262
|
+
cached_serializer = _get_or_create_fragment_cached_serializer(serializer_class_name)
|
263
|
+
cached_hash = ActiveModelSerializers::SerializableResource.new(
|
264
|
+
object,
|
265
|
+
serializer: cached_serializer,
|
266
|
+
adapter: adapter_instance.class
|
267
|
+
).serializable_hash
|
268
|
+
|
269
|
+
non_cached_serializer = _get_or_create_fragment_non_cached_serializer(serializer_class_name)
|
270
|
+
non_cached_hash = ActiveModelSerializers::SerializableResource.new(
|
271
|
+
object,
|
272
|
+
serializer: non_cached_serializer,
|
273
|
+
adapter: adapter_instance.class
|
274
|
+
).serializable_hash
|
275
|
+
|
276
|
+
# Merge both results
|
277
|
+
adapter_instance.fragment_cache(cached_hash, non_cached_hash)
|
278
|
+
end
|
279
|
+
|
280
|
+
def _get_or_create_fragment_cached_serializer(serializer_class_name)
|
281
|
+
cached_serializer = _get_or_create_fragment_serializer "Cached#{serializer_class_name}"
|
282
|
+
cached_serializer.cache(self.class._cache_options)
|
283
|
+
cached_serializer.type(self.class._type)
|
284
|
+
cached_serializer.fragmented(self)
|
285
|
+
self.class.cached_attributes.each do |attribute|
|
286
|
+
options = self.class._attributes_keys[attribute] || {}
|
287
|
+
cached_serializer.attribute(attribute, options)
|
288
|
+
end
|
289
|
+
cached_serializer
|
290
|
+
end
|
291
|
+
|
292
|
+
def _get_or_create_fragment_non_cached_serializer(serializer_class_name)
|
293
|
+
non_cached_serializer = _get_or_create_fragment_serializer "NonCached#{serializer_class_name}"
|
294
|
+
non_cached_serializer.type(self.class._type)
|
295
|
+
non_cached_serializer.fragmented(self)
|
296
|
+
self.class.non_cached_attributes.each do |attribute|
|
297
|
+
options = self.class._attributes_keys[attribute] || {}
|
298
|
+
non_cached_serializer.attribute(attribute, options)
|
299
|
+
end
|
300
|
+
non_cached_serializer
|
301
|
+
end
|
302
|
+
|
303
|
+
def _get_or_create_fragment_serializer(name)
|
304
|
+
return Object.const_get(name) if Object.const_defined?(name)
|
305
|
+
Object.const_set(name, Class.new(ActiveModel::Serializer))
|
306
|
+
end
|
307
|
+
|
308
|
+
def cache_key(adapter_instance)
|
309
|
+
return @cache_key if defined?(@cache_key)
|
310
|
+
|
311
|
+
parts = []
|
312
|
+
parts << object_cache_key
|
313
|
+
parts << adapter_instance.cached_name
|
314
|
+
parts << self.class._cache_digest unless self.class._skip_digest?
|
315
|
+
@cache_key = parts.join('/')
|
316
|
+
end
|
317
|
+
|
318
|
+
# Use object's cache_key if available, else derive a key from the object
|
319
|
+
# Pass the `key` option to the `cache` declaration or override this method to customize the cache key
|
320
|
+
def object_cache_key
|
321
|
+
if object.respond_to?(:cache_key)
|
322
|
+
object.cache_key
|
323
|
+
elsif (serializer_cache_key = (self.class._cache_key || self.class._cache_options[:key]))
|
324
|
+
object_time_safe = object.updated_at
|
325
|
+
object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime)
|
326
|
+
"#{serializer_cache_key}/#{object.id}-#{object_time_safe}"
|
327
|
+
else
|
328
|
+
fail UndefinedCacheKey, "#{object.class} must define #cache_key, or the 'key:' option must be passed into '#{self.class}.cache'"
|
329
|
+
end
|
148
330
|
end
|
149
331
|
end
|
150
332
|
end
|