active_model_serializers 0.10.0.rc1 → 0.10.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|