active_model_serializers 0.10.0.rc1 → 0.10.0.rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/Gemfile +1 -1
- data/README.md +6 -3
- data/lib/action_controller/serialization.rb +9 -1
- data/lib/active_model/serializer.rb +15 -21
- data/lib/active_model/serializer/adapter.rb +13 -4
- data/lib/active_model/serializer/adapter/flatten_json.rb +12 -0
- data/lib/active_model/serializer/adapter/fragment_cache.rb +5 -5
- data/lib/active_model/serializer/adapter/json.rb +8 -10
- data/lib/active_model/serializer/adapter/json_api.rb +34 -30
- data/lib/active_model/serializer/adapter/json_api/fragment_cache.rb +3 -2
- data/lib/active_model/serializer/array_serializer.rb +8 -5
- data/lib/active_model/serializer/configuration.rb +1 -1
- data/lib/active_model/serializer/fieldset.rb +2 -2
- data/lib/active_model/serializer/railtie.rb +8 -0
- data/lib/active_model/serializer/version.rb +1 -1
- data/lib/active_model_serializers.rb +1 -0
- data/lib/generators/serializer/resource_override.rb +12 -0
- data/test/action_controller/adapter_selector_test.rb +7 -5
- data/test/action_controller/explicit_serializer_test.rb +33 -9
- data/test/action_controller/json_api_linked_test.rb +25 -19
- data/test/action_controller/rescue_from_test.rb +32 -0
- data/test/action_controller/serialization_scope_name_test.rb +2 -2
- data/test/action_controller/serialization_test.rb +41 -23
- data/test/adapter/fragment_cache_test.rb +1 -1
- data/test/adapter/json/belongs_to_test.rb +9 -2
- data/test/adapter/json/collection_test.rb +16 -2
- data/test/adapter/json/has_many_test.rb +1 -1
- data/test/adapter/json_api/belongs_to_test.rb +45 -35
- data/test/adapter/json_api/collection_test.rb +30 -23
- data/test/adapter/json_api/has_many_embed_ids_test.rb +2 -2
- data/test/adapter/json_api/has_many_explicit_serializer_test.rb +14 -14
- data/test/adapter/json_api/has_many_test.rb +22 -18
- data/test/adapter/json_api/has_one_test.rb +8 -6
- data/test/adapter/json_api/linked_test.rb +97 -71
- data/test/adapter/json_test.rb +1 -1
- data/test/adapter_test.rb +1 -1
- data/test/array_serializer_test.rb +14 -0
- data/test/fixtures/poro.rb +31 -7
- data/test/generators/scaffold_controller_generator_test.rb +24 -0
- data/test/{serializers/generators_test.rb → generators/serializer_generator_test.rb} +2 -10
- data/test/serializers/adapter_for_test.rb +1 -1
- data/test/serializers/associations_test.rb +21 -0
- data/test/serializers/attribute_test.rb +16 -1
- data/test/serializers/attributes_test.rb +35 -0
- data/test/serializers/cache_test.rb +14 -4
- data/test/serializers/configuration_test.rb +1 -1
- data/test/serializers/meta_test.rb +38 -9
- data/test/serializers/serializer_for_test.rb +9 -0
- data/test/test_helper.rb +10 -7
- metadata +12 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47b346a9a30d6c7e246505a4b48e3c8dd593c2a6
|
4
|
+
data.tar.gz: 74a94269fb1ffd10b1e30b65cd99e43ca3a62b30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61c621fc667fc3e1e168e50c4af8f267f4b7e3e82ce634d2555e814b2dde4a4514a9033cfa6f917b225bcc01b1f86fee36625a74e4a5d3dbeffc4209ff17aa70
|
7
|
+
data.tar.gz: a365c42818a1d413d10d78abce01954160879249b6b4e6119db93178d54aa4b608c026c730b2da2f00af7c77537a1260a506ed8fbf8cecf73341f90d437b5f89
|
data/.travis.yml
CHANGED
@@ -15,13 +15,12 @@ install:
|
|
15
15
|
- bundle install --retry=3
|
16
16
|
|
17
17
|
env:
|
18
|
-
- "RAILS_VERSION=3.2"
|
19
18
|
- "RAILS_VERSION=4.0"
|
20
19
|
- "RAILS_VERSION=4.1"
|
20
|
+
- "RAILS_VERSION=4.2"
|
21
21
|
- "RAILS_VERSION=master"
|
22
22
|
|
23
23
|
matrix:
|
24
24
|
allow_failures:
|
25
25
|
- rvm: ruby-head
|
26
26
|
- env: "RAILS_VERSION=master"
|
27
|
-
- env: "RAILS_VERSION=3.2"
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -8,6 +8,9 @@ AMS does this through two components: **serializers** and **adapters**.
|
|
8
8
|
Serializers describe _which_ attributes and relationships should be serialized.
|
9
9
|
Adapters describe _how_ attributes and relationships should be serialized.
|
10
10
|
|
11
|
+
By default AMS will use the **Json Adapter**. But we strongly advise you to use JsonApi Adapter that follows 1.0 of the format specified in [jsonapi.org/format](http://jsonapi.org/format).
|
12
|
+
Check how to change the adapter in the sections bellow.
|
13
|
+
|
11
14
|
# RELEASE CANDIDATE, PLEASE READ
|
12
15
|
|
13
16
|
This is the master branch of AMS. It will become the `0.10.0` release when it's
|
@@ -175,7 +178,7 @@ end
|
|
175
178
|
|
176
179
|
#### JSONAPI
|
177
180
|
|
178
|
-
This adapter follows
|
181
|
+
This adapter follows RC4 of the format specified in
|
179
182
|
[jsonapi.org/format](http://jsonapi.org/format). It will include the associated
|
180
183
|
resources in the `"included"` member when the resource names are included in the
|
181
184
|
`include` option.
|
@@ -265,7 +268,7 @@ The options are the same options of ```ActiveSupport::Cache::Store```, plus
|
|
265
268
|
a ```key``` option that will be the prefix of the object cache
|
266
269
|
on a pattern ```"#{key}/#{object.id}-#{object.updated_at}"```.
|
267
270
|
|
268
|
-
The cache support is optimized to use the cached object in multiple request. An object cached on
|
271
|
+
The cache support is optimized to use the cached object in multiple request. An object cached on a ```show``` request will be reused at the ```index```. If there is a relationship with another cached serializer it will also be created and reused automatically.
|
269
272
|
|
270
273
|
**[NOTE] Every object is individually cached.**
|
271
274
|
|
@@ -294,7 +297,7 @@ but in this case it will be automatically expired after 3 hours.
|
|
294
297
|
|
295
298
|
### Fragmenting Caching
|
296
299
|
|
297
|
-
If there is some API endpoint that shouldn't be fully cached, you can still
|
300
|
+
If there is some API endpoint that shouldn't be fully cached, you can still optimise it, using Fragment Cache on the attributes and relationships that you want to cache.
|
298
301
|
|
299
302
|
You can define the attribute by using ```only``` or ```except``` option on cache method.
|
300
303
|
|
@@ -6,7 +6,7 @@ module ActionController
|
|
6
6
|
|
7
7
|
include ActionController::Renderers
|
8
8
|
|
9
|
-
ADAPTER_OPTION_KEYS = [:include, :fields, :
|
9
|
+
ADAPTER_OPTION_KEYS = [:include, :fields, :adapter]
|
10
10
|
|
11
11
|
included do
|
12
12
|
class_attribute :_serialization_scope
|
@@ -53,6 +53,14 @@ module ActionController
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
+
def rescue_with_handler(exception)
|
57
|
+
@_serializer = nil
|
58
|
+
@_serializer_opts = nil
|
59
|
+
@_adapter_opts = nil
|
60
|
+
|
61
|
+
super(exception)
|
62
|
+
end
|
63
|
+
|
56
64
|
module ClassMethods
|
57
65
|
def serialization_scope(scope)
|
58
66
|
self._serialization_scope = scope
|
@@ -19,18 +19,22 @@ module ActiveModel
|
|
19
19
|
attr_accessor :_cache_only
|
20
20
|
attr_accessor :_cache_except
|
21
21
|
attr_accessor :_cache_options
|
22
|
+
attr_accessor :_cache_digest
|
22
23
|
end
|
23
24
|
|
24
25
|
def self.inherited(base)
|
25
|
-
base._attributes = []
|
26
|
-
base._attributes_keys = {}
|
27
|
-
base._associations = {}
|
26
|
+
base._attributes = self._attributes.try(:dup) || []
|
27
|
+
base._attributes_keys = self._attributes_keys.try(:dup) || {}
|
28
|
+
base._associations = self._associations.try(:dup) || {}
|
28
29
|
base._urls = []
|
30
|
+
serializer_file = File.open(caller.first[/^[^:]+/])
|
31
|
+
base._cache_digest = Digest::MD5.hexdigest(serializer_file.read)
|
29
32
|
end
|
30
33
|
|
31
34
|
def self.attributes(*attrs)
|
32
35
|
attrs = attrs.first if attrs.first.class == Array
|
33
36
|
@_attributes.concat attrs
|
37
|
+
@_attributes.uniq!
|
34
38
|
|
35
39
|
attrs.each do |attr|
|
36
40
|
define_method attr do
|
@@ -42,7 +46,7 @@ module ActiveModel
|
|
42
46
|
def self.attribute(attr, options = {})
|
43
47
|
key = options.fetch(:key, attr)
|
44
48
|
@_attributes_keys[attr] = {key: key} if key != attr
|
45
|
-
@_attributes
|
49
|
+
@_attributes << key unless @_attributes.include?(key)
|
46
50
|
define_method key do
|
47
51
|
object.read_attribute_for_serialization(attr)
|
48
52
|
end unless method_defined?(key) || _fragmented.respond_to?(attr)
|
@@ -115,7 +119,9 @@ module ActiveModel
|
|
115
119
|
end
|
116
120
|
|
117
121
|
def self.serializer_for(resource, options = {})
|
118
|
-
if resource.respond_to?(:
|
122
|
+
if resource.respond_to?(:serializer_class)
|
123
|
+
resource.serializer_class
|
124
|
+
elsif resource.respond_to?(:to_ary)
|
119
125
|
config.array_serializer
|
120
126
|
else
|
121
127
|
options
|
@@ -139,14 +145,6 @@ module ActiveModel
|
|
139
145
|
adapter_class
|
140
146
|
end
|
141
147
|
|
142
|
-
def self._root
|
143
|
-
@@root ||= false
|
144
|
-
end
|
145
|
-
|
146
|
-
def self._root=(root)
|
147
|
-
@@root = root
|
148
|
-
end
|
149
|
-
|
150
148
|
def self.root_name
|
151
149
|
name.demodulize.underscore.sub(/_serializer$/, '') if name
|
152
150
|
end
|
@@ -156,7 +154,7 @@ module ActiveModel
|
|
156
154
|
def initialize(object, options = {})
|
157
155
|
@object = object
|
158
156
|
@options = options
|
159
|
-
@root = options[:root]
|
157
|
+
@root = options[:root]
|
160
158
|
@meta = options[:meta]
|
161
159
|
@meta_key = options[:meta_key]
|
162
160
|
@scope = options[:scope]
|
@@ -170,11 +168,7 @@ module ActiveModel
|
|
170
168
|
end
|
171
169
|
|
172
170
|
def json_key
|
173
|
-
|
174
|
-
self.class.root_name
|
175
|
-
else
|
176
|
-
root
|
177
|
-
end
|
171
|
+
self.class.root_name
|
178
172
|
end
|
179
173
|
|
180
174
|
def id
|
@@ -182,7 +176,7 @@ module ActiveModel
|
|
182
176
|
end
|
183
177
|
|
184
178
|
def type
|
185
|
-
object.class.
|
179
|
+
object.class.model_name.plural
|
186
180
|
end
|
187
181
|
|
188
182
|
def attributes(options = {})
|
@@ -214,7 +208,7 @@ module ActiveModel
|
|
214
208
|
if serializer_class
|
215
209
|
serializer = serializer_class.new(
|
216
210
|
association_value,
|
217
|
-
options.merge(serializer_from_options(association_options))
|
211
|
+
options.except(:serializer).merge(serializer_from_options(association_options))
|
218
212
|
)
|
219
213
|
elsif !association_value.nil? && !association_value.instance_of?(Object)
|
220
214
|
association_options[:association_options][:virtual_value] = association_value
|
@@ -5,6 +5,7 @@ module ActiveModel
|
|
5
5
|
class Adapter
|
6
6
|
extend ActiveSupport::Autoload
|
7
7
|
autoload :Json
|
8
|
+
autoload :FlattenJson
|
8
9
|
autoload :Null
|
9
10
|
autoload :JsonApi
|
10
11
|
|
@@ -21,7 +22,8 @@ module ActiveModel
|
|
21
22
|
|
22
23
|
def as_json(options = {})
|
23
24
|
hash = serializable_hash(options)
|
24
|
-
include_meta(hash)
|
25
|
+
include_meta(hash) unless self.class == FlattenJson
|
26
|
+
hash
|
25
27
|
end
|
26
28
|
|
27
29
|
def self.create(resource, options = {})
|
@@ -48,7 +50,7 @@ module ActiveModel
|
|
48
50
|
yield
|
49
51
|
end
|
50
52
|
elsif is_fragment_cached?
|
51
|
-
FragmentCache.new(self, @cached_serializer, @options
|
53
|
+
FragmentCache.new(self, @cached_serializer, @options).fetch
|
52
54
|
else
|
53
55
|
yield
|
54
56
|
end
|
@@ -63,6 +65,13 @@ module ActiveModel
|
|
63
65
|
end
|
64
66
|
|
65
67
|
def cache_key
|
68
|
+
parts = []
|
69
|
+
parts << object_cache_key
|
70
|
+
parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest]
|
71
|
+
parts.join("/")
|
72
|
+
end
|
73
|
+
|
74
|
+
def object_cache_key
|
66
75
|
(@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{@cached_serializer.object.updated_at}" : @cached_serializer.object.cache_key
|
67
76
|
end
|
68
77
|
|
@@ -75,11 +84,11 @@ module ActiveModel
|
|
75
84
|
end
|
76
85
|
|
77
86
|
def root
|
78
|
-
serializer.json_key
|
87
|
+
serializer.json_key.to_sym if serializer.json_key
|
79
88
|
end
|
80
89
|
|
81
90
|
def include_meta(json)
|
82
|
-
json[meta_key] = meta if meta
|
91
|
+
json[meta_key] = meta if meta
|
83
92
|
json
|
84
93
|
end
|
85
94
|
end
|
@@ -5,8 +5,7 @@ module ActiveModel
|
|
5
5
|
|
6
6
|
attr_reader :serializer
|
7
7
|
|
8
|
-
def initialize(adapter, serializer, options
|
9
|
-
@root = root
|
8
|
+
def initialize(adapter, serializer, options)
|
10
9
|
@options = options
|
11
10
|
@adapter = adapter
|
12
11
|
@serializer = serializer
|
@@ -35,8 +34,9 @@ module ActiveModel
|
|
35
34
|
private
|
36
35
|
|
37
36
|
def cached_attributes(klass, serializers)
|
38
|
-
|
39
|
-
|
37
|
+
attributes = serializer.class._attributes
|
38
|
+
cached_attributes = (klass._cache_only) ? klass._cache_only : attributes.reject {|attr| klass._cache_except.include?(attr) }
|
39
|
+
non_cached_attributes = attributes - cached_attributes
|
40
40
|
|
41
41
|
cached_attributes.each do |attribute|
|
42
42
|
options = serializer.class._attributes_keys[attribute]
|
@@ -75,4 +75,4 @@ module ActiveModel
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
78
|
-
end
|
78
|
+
end
|
@@ -6,7 +6,7 @@ module ActiveModel
|
|
6
6
|
class Json < Adapter
|
7
7
|
def serializable_hash(options = {})
|
8
8
|
if serializer.respond_to?(:each)
|
9
|
-
@result = serializer.map{|s|
|
9
|
+
@result = serializer.map{|s| FlattenJson.new(s).serializable_hash }
|
10
10
|
else
|
11
11
|
@hash = {}
|
12
12
|
|
@@ -23,7 +23,7 @@ module ActiveModel
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
else
|
26
|
-
if association
|
26
|
+
if association && association.object
|
27
27
|
@hash[name] = cache_check(association) do
|
28
28
|
association.attributes(options)
|
29
29
|
end
|
@@ -37,16 +37,14 @@ module ActiveModel
|
|
37
37
|
@result = @core.merge @hash
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
{ root => @result }
|
41
|
+
end
|
42
|
+
|
43
|
+
def fragment_cache(cached_hash, non_cached_hash)
|
44
|
+
Json::FragmentCache.new().fragment_cache(cached_hash, non_cached_hash)
|
44
45
|
end
|
45
|
-
end
|
46
46
|
|
47
|
-
def fragment_cache(cached_hash, non_cached_hash)
|
48
|
-
Json::FragmentCache.new().fragment_cache(cached_hash, non_cached_hash)
|
49
47
|
end
|
50
48
|
end
|
51
49
|
end
|
52
|
-
end
|
50
|
+
end
|
@@ -6,7 +6,6 @@ module ActiveModel
|
|
6
6
|
class JsonApi < Adapter
|
7
7
|
def initialize(serializer, options = {})
|
8
8
|
super
|
9
|
-
serializer.root = true
|
10
9
|
@hash = { data: [] }
|
11
10
|
|
12
11
|
if fields = options.delete(:fields)
|
@@ -29,7 +28,7 @@ module ActiveModel
|
|
29
28
|
end
|
30
29
|
else
|
31
30
|
@hash[:data] = attributes_for_serializer(serializer, @options)
|
32
|
-
|
31
|
+
add_resource_relationships(@hash[:data], serializer)
|
33
32
|
end
|
34
33
|
@hash
|
35
34
|
end
|
@@ -41,18 +40,18 @@ module ActiveModel
|
|
41
40
|
|
42
41
|
private
|
43
42
|
|
44
|
-
def
|
45
|
-
resource[:
|
46
|
-
resource[:
|
47
|
-
resource[:
|
43
|
+
def add_relationships(resource, name, serializers)
|
44
|
+
resource[:relationships] ||= {}
|
45
|
+
resource[:relationships][name] ||= { data: [] }
|
46
|
+
resource[:relationships][name][:data] += serializers.map { |serializer| { type: serializer.type, id: serializer.id.to_s } }
|
48
47
|
end
|
49
48
|
|
50
|
-
def
|
51
|
-
resource[:
|
52
|
-
resource[:
|
49
|
+
def add_relationship(resource, name, serializer, val=nil)
|
50
|
+
resource[:relationships] ||= {}
|
51
|
+
resource[:relationships][name] = { data: nil }
|
53
52
|
|
54
53
|
if serializer && serializer.object
|
55
|
-
resource[:
|
54
|
+
resource[:relationships][name][:data] = { type: serializer.type, id: serializer.id.to_s }
|
56
55
|
end
|
57
56
|
end
|
58
57
|
|
@@ -68,7 +67,7 @@ module ActiveModel
|
|
68
67
|
serializers.each do |serializer|
|
69
68
|
attrs = attributes_for_serializer(serializer, @options)
|
70
69
|
|
71
|
-
|
70
|
+
add_resource_relationships(attrs, serializer, add_included: false)
|
72
71
|
|
73
72
|
@hash[:included].push(attrs) unless @hash[:included].include?(attrs)
|
74
73
|
end
|
@@ -85,26 +84,31 @@ module ActiveModel
|
|
85
84
|
if serializer.respond_to?(:each)
|
86
85
|
result = []
|
87
86
|
serializer.each do |object|
|
88
|
-
|
89
|
-
result << cache_check(object) do
|
90
|
-
options[:required_fields] = [:id, :type]
|
91
|
-
attributes = object.attributes(options)
|
92
|
-
attributes[:id] = attributes[:id].to_s
|
93
|
-
result << attributes
|
94
|
-
end
|
87
|
+
result << resource_object_for(object, options)
|
95
88
|
end
|
96
89
|
else
|
97
|
-
|
98
|
-
options[:required_fields] = [:id, :type]
|
99
|
-
result = cache_check(serializer) do
|
100
|
-
result = serializer.attributes(options)
|
101
|
-
result[:id] = result[:id].to_s
|
102
|
-
result
|
103
|
-
end
|
90
|
+
result = resource_object_for(serializer, options)
|
104
91
|
end
|
105
92
|
result
|
106
93
|
end
|
107
94
|
|
95
|
+
def resource_object_for(serializer, options)
|
96
|
+
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
|
97
|
+
options[:required_fields] = [:id, :type]
|
98
|
+
|
99
|
+
cache_check(serializer) do
|
100
|
+
attributes = serializer.attributes(options)
|
101
|
+
|
102
|
+
result = {
|
103
|
+
id: attributes.delete(:id).to_s,
|
104
|
+
type: attributes.delete(:type)
|
105
|
+
}
|
106
|
+
|
107
|
+
result[:attributes] = attributes if attributes.any?
|
108
|
+
result
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
108
112
|
def include_assoc?(assoc)
|
109
113
|
return false unless @options[:include]
|
110
114
|
check_assoc("#{assoc}$")
|
@@ -123,19 +127,19 @@ module ActiveModel
|
|
123
127
|
end
|
124
128
|
end
|
125
129
|
|
126
|
-
def
|
130
|
+
def add_resource_relationships(attrs, serializer, options = {})
|
127
131
|
options[:add_included] = options.fetch(:add_included, true)
|
128
132
|
|
129
133
|
serializer.each_association do |name, association, opts|
|
130
|
-
attrs[:
|
134
|
+
attrs[:relationships] ||= {}
|
131
135
|
|
132
136
|
if association.respond_to?(:each)
|
133
|
-
|
137
|
+
add_relationships(attrs, name, association)
|
134
138
|
else
|
135
139
|
if opts[:virtual_value]
|
136
|
-
|
140
|
+
add_relationship(attrs, name, nil, opts[:virtual_value])
|
137
141
|
else
|
138
|
-
|
142
|
+
add_relationship(attrs, name, association)
|
139
143
|
end
|
140
144
|
end
|
141
145
|
|