fast_jsonapi 1.2 → 1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +97 -1
- data/lib/extensions/has_one.rb +14 -16
- data/lib/fast_jsonapi.rb +5 -1
- data/lib/fast_jsonapi/attribute.rb +29 -0
- data/lib/fast_jsonapi/link.rb +18 -0
- data/lib/fast_jsonapi/object_serializer.rb +65 -32
- data/lib/fast_jsonapi/railtie.rb +11 -0
- data/lib/fast_jsonapi/relationship.rb +99 -0
- data/lib/fast_jsonapi/serialization_core.rb +27 -77
- data/lib/fast_jsonapi/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad201aff3dee102a9760daee3777a225d73bcfcb
|
4
|
+
data.tar.gz: a6e4f0f7840f27c95f3530b68542eae6422d8975
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9503ec4e6d642f72b2a5ccb34f4b33bc68e6fda067e1a16e43d6f30f0f5b95f7e86626d608ab38fbe277ff778dae908e71252e354a1834121b013219cd77e0c
|
7
|
+
data.tar.gz: 069b1ac282d7b2a3f559f398c4090f188c72f8120f4bfeff01f7126a57fbc789725ef3f2e6cf1c88c541a99681914c4f32bb5855655b9f65a7c55262c02f449e
|
data/README.md
CHANGED
@@ -30,6 +30,9 @@ Fast JSON API serialized 250 records in 3.01 ms
|
|
30
30
|
* [Collection Serialization](#collection-serialization)
|
31
31
|
* [Caching](#caching)
|
32
32
|
* [Params](#params)
|
33
|
+
* [Conditional Attributes](#conditional-attributes)
|
34
|
+
* [Conditional Relationships](#conditional-relationships)
|
35
|
+
* [Sparse Fieldsets](#sparse-fieldsets)
|
33
36
|
* [Contributing](#contributing)
|
34
37
|
|
35
38
|
|
@@ -205,6 +208,18 @@ class MovieSerializer
|
|
205
208
|
end
|
206
209
|
```
|
207
210
|
|
211
|
+
Attributes can also use a different name by passing the original method or accessor with a proc shortcut:
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
class MovieSerializer
|
215
|
+
include FastJsonapi::ObjectSerializer
|
216
|
+
|
217
|
+
attributes :name
|
218
|
+
|
219
|
+
attribute :released_in_year, &:year
|
220
|
+
end
|
221
|
+
```
|
222
|
+
|
208
223
|
### Links Per Object
|
209
224
|
Links are defined in FastJsonapi using the `link` method. By default, link are read directly from the model property of the same name.In this example, `public_url` is expected to be a property of the object being serialized.
|
210
225
|
|
@@ -259,6 +274,26 @@ hash = MovieSerializer.new([movie, movie], options).serializable_hash
|
|
259
274
|
json_string = MovieSerializer.new([movie, movie], options).serialized_json
|
260
275
|
```
|
261
276
|
|
277
|
+
#### Control Over Collection Serialization
|
278
|
+
|
279
|
+
You can use `is_collection` option to have better control over collection serialization.
|
280
|
+
|
281
|
+
If this option is not provided or `nil` autedetect logic is used to try understand
|
282
|
+
if provided resource is a single object or collection.
|
283
|
+
|
284
|
+
Autodetect logic is compatible with most DB toolkits (ActiveRecord, Sequel, etc.) but
|
285
|
+
**cannot** guarantee that single vs collection will be always detected properly.
|
286
|
+
|
287
|
+
```ruby
|
288
|
+
options[:is_collection]
|
289
|
+
```
|
290
|
+
|
291
|
+
was introduced to be able to have precise control this behavior
|
292
|
+
|
293
|
+
- `nil` or not provided: will try to autodetect single vs collection (please, see notes above)
|
294
|
+
- `true` will always treat input resource as *collection*
|
295
|
+
- `false` will always treat input resource as *single object*
|
296
|
+
|
262
297
|
### Caching
|
263
298
|
Requires a `cache_key` method be defined on model:
|
264
299
|
|
@@ -284,7 +319,6 @@ block you opt-in to using params by adding it as a block parameter.
|
|
284
319
|
|
285
320
|
```ruby
|
286
321
|
class MovieSerializer
|
287
|
-
class MovieSerializer
|
288
322
|
include FastJsonapi::ObjectSerializer
|
289
323
|
|
290
324
|
attributes :name, :year
|
@@ -308,6 +342,68 @@ serializer.serializable_hash
|
|
308
342
|
Custom attributes and relationships that only receive the resource are still possible by defining
|
309
343
|
the block to only receive one argument.
|
310
344
|
|
345
|
+
### Conditional Attributes
|
346
|
+
|
347
|
+
Conditional attributes can be defined by passing a Proc to the `if` key on the `attribute` method. Return `true` if the attribute should be serialized, and `false` if not. The record and any params passed to the serializer are available inside the Proc as the first and second parameters, respectively.
|
348
|
+
|
349
|
+
```ruby
|
350
|
+
class MovieSerializer
|
351
|
+
include FastJsonapi::ObjectSerializer
|
352
|
+
|
353
|
+
attributes :name, :year
|
354
|
+
attribute :release_year, if: Proc.new do |record|
|
355
|
+
# Release year will only be serialized if it's greater than 1990
|
356
|
+
record.release_year > 1990
|
357
|
+
end
|
358
|
+
|
359
|
+
attribute :director, if: Proc.new do |record, params|
|
360
|
+
# The director will be serialized only if the :admin key of params is true
|
361
|
+
params && params[:admin] == true
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
# ...
|
366
|
+
current_user = User.find(cookies[:current_user_id])
|
367
|
+
serializer = MovieSerializer.new(movie, { params: { admin: current_user.admin? }})
|
368
|
+
serializer.serializable_hash
|
369
|
+
```
|
370
|
+
|
371
|
+
### Conditional Relationships
|
372
|
+
|
373
|
+
Conditional relationships can be defined by passing a Proc to the `if` key. Return `true` if the relationship should be serialized, and `false` if not. The record and any params passed to the serializer are available inside the Proc as the first and second parameters, respectively.
|
374
|
+
|
375
|
+
```ruby
|
376
|
+
class MovieSerializer
|
377
|
+
include FastJsonapi::ObjectSerializer
|
378
|
+
|
379
|
+
# Actors will only be serialized if the record has any associated actors
|
380
|
+
has_many :actors, if: Proc.new { |record| record.actors.any? }
|
381
|
+
|
382
|
+
# Owner will only be serialized if the :admin key of params is true
|
383
|
+
belongs_to :owner, if: Proc.new { |record, params| params && params[:admin] == true }
|
384
|
+
end
|
385
|
+
|
386
|
+
# ...
|
387
|
+
current_user = User.find(cookies[:current_user_id])
|
388
|
+
serializer = MovieSerializer.new(movie, { params: { admin: current_user.admin? }})
|
389
|
+
serializer.serializable_hash
|
390
|
+
```
|
391
|
+
|
392
|
+
### Sparse Fieldsets
|
393
|
+
|
394
|
+
Attributes and relationships can be selectively returned per record type by using the `fields` option.
|
395
|
+
|
396
|
+
```ruby
|
397
|
+
class MovieSerializer
|
398
|
+
include FastJsonapi::ObjectSerializer
|
399
|
+
|
400
|
+
attributes :name, :year
|
401
|
+
end
|
402
|
+
|
403
|
+
serializer = MovieSerializer.new(movie, { fields: { movie: [:name] } })
|
404
|
+
serializer.serializable_hash
|
405
|
+
```
|
406
|
+
|
311
407
|
### Customizable Options
|
312
408
|
|
313
409
|
Option | Purpose | Example
|
data/lib/extensions/has_one.rb
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
CODE
|
18
|
-
end
|
3
|
+
::ActiveRecord::Associations::Builder::HasOne.class_eval do
|
4
|
+
# Based on
|
5
|
+
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/collection_association.rb#L50
|
6
|
+
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/singular_association.rb#L11
|
7
|
+
def self.define_accessors(mixin, reflection)
|
8
|
+
super
|
9
|
+
name = reflection.name
|
10
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
11
|
+
def #{name}_id
|
12
|
+
# if an attribute is already defined with this methods name we should just use it
|
13
|
+
return read_attribute(__method__) if has_attribute?(__method__)
|
14
|
+
association(:#{name}).reader.try(:id)
|
15
|
+
end
|
16
|
+
CODE
|
19
17
|
end
|
20
18
|
end
|
data/lib/fast_jsonapi.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
module FastJsonapi
|
2
|
+
class Attribute
|
3
|
+
attr_reader :key, :method, :conditional_proc
|
4
|
+
|
5
|
+
def initialize(key:, method:, options: {})
|
6
|
+
@key = key
|
7
|
+
@method = method
|
8
|
+
@conditional_proc = options[:if]
|
9
|
+
end
|
10
|
+
|
11
|
+
def serialize(record, serialization_params, output_hash)
|
12
|
+
if include_attribute?(record, serialization_params)
|
13
|
+
output_hash[key] = if method.is_a?(Proc)
|
14
|
+
method.arity.abs == 1 ? method.call(record) : method.call(record, serialization_params)
|
15
|
+
else
|
16
|
+
record.public_send(method)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def include_attribute?(record, serialization_params)
|
22
|
+
if conditional_proc.present?
|
23
|
+
conditional_proc.call(record, serialization_params)
|
24
|
+
else
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FastJsonapi
|
2
|
+
class Link
|
3
|
+
attr_reader :key, :method
|
4
|
+
|
5
|
+
def initialize(key:, method:)
|
6
|
+
@key = key
|
7
|
+
@method = method
|
8
|
+
end
|
9
|
+
|
10
|
+
def serialize(record, serialization_params, output_hash)
|
11
|
+
output_hash[key] = if method.is_a?(Proc)
|
12
|
+
method.arity == 1 ? method.call(record) : method.call(record, serialization_params)
|
13
|
+
else
|
14
|
+
record.public_send(method)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -3,6 +3,9 @@
|
|
3
3
|
require 'active_support/core_ext/object'
|
4
4
|
require 'active_support/concern'
|
5
5
|
require 'active_support/inflector'
|
6
|
+
require 'fast_jsonapi/attribute'
|
7
|
+
require 'fast_jsonapi/relationship'
|
8
|
+
require 'fast_jsonapi/link'
|
6
9
|
require 'fast_jsonapi/serialization_core'
|
7
10
|
|
8
11
|
module FastJsonapi
|
@@ -25,7 +28,7 @@ module FastJsonapi
|
|
25
28
|
end
|
26
29
|
|
27
30
|
def serializable_hash
|
28
|
-
return hash_for_collection if is_collection?(@resource)
|
31
|
+
return hash_for_collection if is_collection?(@resource, @is_collection)
|
29
32
|
|
30
33
|
hash_for_one_record
|
31
34
|
end
|
@@ -38,8 +41,8 @@ module FastJsonapi
|
|
38
41
|
|
39
42
|
return serializable_hash unless @resource
|
40
43
|
|
41
|
-
serializable_hash[:data] = self.class.record_hash(@resource, @params)
|
42
|
-
serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @params) if @includes.present?
|
44
|
+
serializable_hash[:data] = self.class.record_hash(@resource, @fieldsets[self.class.record_type.to_sym], @params)
|
45
|
+
serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
|
43
46
|
serializable_hash
|
44
47
|
end
|
45
48
|
|
@@ -48,9 +51,10 @@ module FastJsonapi
|
|
48
51
|
|
49
52
|
data = []
|
50
53
|
included = []
|
54
|
+
fieldset = @fieldsets[self.class.record_type.to_sym]
|
51
55
|
@resource.each do |record|
|
52
|
-
data << self.class.record_hash(record, @params)
|
53
|
-
included.concat self.class.get_included_records(record, @includes, @known_included_objects, @params) if @includes.present?
|
56
|
+
data << self.class.record_hash(record, fieldset, @params)
|
57
|
+
included.concat self.class.get_included_records(record, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
|
54
58
|
end
|
55
59
|
|
56
60
|
serializable_hash[:data] = data
|
@@ -67,11 +71,14 @@ module FastJsonapi
|
|
67
71
|
private
|
68
72
|
|
69
73
|
def process_options(options)
|
74
|
+
@fieldsets = deep_symbolize(options[:fields].presence || {})
|
75
|
+
|
70
76
|
return if options.blank?
|
71
77
|
|
72
78
|
@known_included_objects = {}
|
73
79
|
@meta = options[:meta]
|
74
80
|
@links = options[:links]
|
81
|
+
@is_collection = options[:is_collection]
|
75
82
|
@params = options[:params] || {}
|
76
83
|
raise ArgumentError.new("`params` option passed to serializer must be a hash") unless @params.is_a?(Hash)
|
77
84
|
|
@@ -81,8 +88,22 @@ module FastJsonapi
|
|
81
88
|
end
|
82
89
|
end
|
83
90
|
|
84
|
-
def
|
85
|
-
|
91
|
+
def deep_symbolize(collection)
|
92
|
+
if collection.is_a? Hash
|
93
|
+
Hash[collection.map do |k, v|
|
94
|
+
[k.to_sym, deep_symbolize(v)]
|
95
|
+
end]
|
96
|
+
elsif collection.is_a? Array
|
97
|
+
collection.map { |i| deep_symbolize(i) }
|
98
|
+
else
|
99
|
+
collection.to_sym
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def is_collection?(resource, force_is_collection = nil)
|
104
|
+
return force_is_collection unless force_is_collection.nil?
|
105
|
+
|
106
|
+
resource.respond_to?(:size) && !resource.respond_to?(:each_pair)
|
86
107
|
end
|
87
108
|
|
88
109
|
class_methods do
|
@@ -98,6 +119,7 @@ module FastJsonapi
|
|
98
119
|
subclass.race_condition_ttl = race_condition_ttl
|
99
120
|
subclass.data_links = data_links
|
100
121
|
subclass.cached = cached
|
122
|
+
subclass.set_type(subclass.reflected_record_type) if subclass.reflected_record_type
|
101
123
|
end
|
102
124
|
|
103
125
|
def reflected_record_type
|
@@ -118,6 +140,9 @@ module FastJsonapi
|
|
118
140
|
underscore: :underscore
|
119
141
|
}
|
120
142
|
self.transform_method = mapping[transform_name.to_sym]
|
143
|
+
|
144
|
+
# ensure that the record type is correctly transformed
|
145
|
+
set_type(reflected_record_type) if reflected_record_type
|
121
146
|
end
|
122
147
|
|
123
148
|
def run_key_transform(input)
|
@@ -149,48 +174,51 @@ module FastJsonapi
|
|
149
174
|
|
150
175
|
def attributes(*attributes_list, &block)
|
151
176
|
attributes_list = attributes_list.first if attributes_list.first.class.is_a?(Array)
|
177
|
+
options = attributes_list.last.is_a?(Hash) ? attributes_list.pop : {}
|
152
178
|
self.attributes_to_serialize = {} if self.attributes_to_serialize.nil?
|
179
|
+
|
153
180
|
attributes_list.each do |attr_name|
|
154
181
|
method_name = attr_name
|
155
182
|
key = run_key_transform(method_name)
|
156
|
-
attributes_to_serialize[key] =
|
183
|
+
attributes_to_serialize[key] = Attribute.new(
|
184
|
+
key: key,
|
185
|
+
method: block || method_name,
|
186
|
+
options: options
|
187
|
+
)
|
157
188
|
end
|
158
189
|
end
|
159
190
|
|
160
191
|
alias_method :attribute, :attributes
|
161
192
|
|
162
|
-
def add_relationship(
|
193
|
+
def add_relationship(relationship)
|
163
194
|
self.relationships_to_serialize = {} if relationships_to_serialize.nil?
|
164
195
|
self.cachable_relationships_to_serialize = {} if cachable_relationships_to_serialize.nil?
|
165
196
|
self.uncachable_relationships_to_serialize = {} if uncachable_relationships_to_serialize.nil?
|
166
|
-
|
167
|
-
if !relationship
|
168
|
-
self.uncachable_relationships_to_serialize[name] = relationship
|
197
|
+
|
198
|
+
if !relationship.cached
|
199
|
+
self.uncachable_relationships_to_serialize[relationship.name] = relationship
|
169
200
|
else
|
170
|
-
self.cachable_relationships_to_serialize[name] = relationship
|
201
|
+
self.cachable_relationships_to_serialize[relationship.name] = relationship
|
171
202
|
end
|
172
|
-
self.relationships_to_serialize[name] = relationship
|
173
|
-
|
203
|
+
self.relationships_to_serialize[relationship.name] = relationship
|
204
|
+
end
|
174
205
|
|
175
206
|
def has_many(relationship_name, options = {}, &block)
|
176
|
-
|
177
|
-
|
178
|
-
add_relationship(name, hash)
|
207
|
+
relationship = create_relationship(relationship_name, :has_many, options, block)
|
208
|
+
add_relationship(relationship)
|
179
209
|
end
|
180
210
|
|
181
211
|
def has_one(relationship_name, options = {}, &block)
|
182
|
-
|
183
|
-
|
184
|
-
add_relationship(name, hash)
|
212
|
+
relationship = create_relationship(relationship_name, :has_one, options, block)
|
213
|
+
add_relationship(relationship)
|
185
214
|
end
|
186
215
|
|
187
216
|
def belongs_to(relationship_name, options = {}, &block)
|
188
|
-
|
189
|
-
|
190
|
-
add_relationship(name, hash)
|
217
|
+
relationship = create_relationship(relationship_name, :belongs_to, options, block)
|
218
|
+
add_relationship(relationship)
|
191
219
|
end
|
192
220
|
|
193
|
-
def
|
221
|
+
def create_relationship(base_key, relationship_type, options, block)
|
194
222
|
name = base_key.to_sym
|
195
223
|
if relationship_type == :has_many
|
196
224
|
base_serialization_key = base_key.to_s.singularize
|
@@ -201,7 +229,7 @@ module FastJsonapi
|
|
201
229
|
base_key_sym = name
|
202
230
|
id_postfix = '_id'
|
203
231
|
end
|
204
|
-
|
232
|
+
Relationship.new(
|
205
233
|
key: options[:key] || run_key_transform(base_key),
|
206
234
|
name: name,
|
207
235
|
id_method_name: options[:id_method_name] || "#{base_serialization_key}#{id_postfix}".to_sym,
|
@@ -210,9 +238,10 @@ module FastJsonapi
|
|
210
238
|
object_block: block,
|
211
239
|
serializer: compute_serializer_name(options[:serializer] || base_key_sym),
|
212
240
|
relationship_type: relationship_type,
|
213
|
-
cached: options[:cached]
|
214
|
-
polymorphic: fetch_polymorphic_option(options)
|
215
|
-
|
241
|
+
cached: options[:cached],
|
242
|
+
polymorphic: fetch_polymorphic_option(options),
|
243
|
+
conditional_proc: options[:if]
|
244
|
+
)
|
216
245
|
end
|
217
246
|
|
218
247
|
def compute_serializer_name(serializer_key)
|
@@ -233,7 +262,11 @@ module FastJsonapi
|
|
233
262
|
self.data_links = {} if self.data_links.nil?
|
234
263
|
link_method_name = link_name if link_method_name.nil?
|
235
264
|
key = run_key_transform(link_name)
|
236
|
-
|
265
|
+
|
266
|
+
self.data_links[key] = Link.new(
|
267
|
+
key: key,
|
268
|
+
method: block || link_method_name
|
269
|
+
)
|
237
270
|
end
|
238
271
|
|
239
272
|
def validate_includes!(includes)
|
@@ -244,8 +277,8 @@ module FastJsonapi
|
|
244
277
|
parse_include_item(include_item).each do |parsed_include|
|
245
278
|
relationship_to_include = klass.relationships_to_serialize[parsed_include]
|
246
279
|
raise ArgumentError, "#{parsed_include} is not specified as a relationship on #{klass.name}" unless relationship_to_include
|
247
|
-
raise NotImplementedError if relationship_to_include
|
248
|
-
klass = relationship_to_include
|
280
|
+
raise NotImplementedError if relationship_to_include.polymorphic.is_a?(Hash)
|
281
|
+
klass = relationship_to_include.serializer.to_s.constantize
|
249
282
|
end
|
250
283
|
end
|
251
284
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module FastJsonapi
|
2
|
+
class Relationship
|
3
|
+
attr_reader :key, :name, :id_method_name, :record_type, :object_method_name, :object_block, :serializer, :relationship_type, :cached, :polymorphic, :conditional_proc
|
4
|
+
|
5
|
+
def initialize(
|
6
|
+
key:,
|
7
|
+
name:,
|
8
|
+
id_method_name:,
|
9
|
+
record_type:,
|
10
|
+
object_method_name:,
|
11
|
+
object_block:,
|
12
|
+
serializer:,
|
13
|
+
relationship_type:,
|
14
|
+
cached: false,
|
15
|
+
polymorphic:,
|
16
|
+
conditional_proc:
|
17
|
+
)
|
18
|
+
@key = key
|
19
|
+
@name = name
|
20
|
+
@id_method_name = id_method_name
|
21
|
+
@record_type = record_type
|
22
|
+
@object_method_name = object_method_name
|
23
|
+
@object_block = object_block
|
24
|
+
@serializer = serializer
|
25
|
+
@relationship_type = relationship_type
|
26
|
+
@cached = cached
|
27
|
+
@polymorphic = polymorphic
|
28
|
+
@conditional_proc = conditional_proc
|
29
|
+
end
|
30
|
+
|
31
|
+
def serialize(record, serialization_params, output_hash)
|
32
|
+
if include_relationship?(record, serialization_params)
|
33
|
+
empty_case = relationship_type == :has_many ? [] : nil
|
34
|
+
output_hash[key] = {
|
35
|
+
data: ids_hash_from_record_and_relationship(record, serialization_params) || empty_case
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def fetch_associated_object(record, params)
|
41
|
+
return object_block.call(record, params) unless object_block.nil?
|
42
|
+
record.send(object_method_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def include_relationship?(record, serialization_params)
|
46
|
+
if conditional_proc.present?
|
47
|
+
conditional_proc.call(record, serialization_params)
|
48
|
+
else
|
49
|
+
true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def ids_hash_from_record_and_relationship(record, params = {})
|
56
|
+
return ids_hash(
|
57
|
+
fetch_id(record, params)
|
58
|
+
) unless polymorphic
|
59
|
+
|
60
|
+
return unless associated_object = fetch_associated_object(record, params)
|
61
|
+
|
62
|
+
return associated_object.map do |object|
|
63
|
+
id_hash_from_record object, polymorphic
|
64
|
+
end if associated_object.respond_to? :map
|
65
|
+
|
66
|
+
id_hash_from_record associated_object, polymorphic
|
67
|
+
end
|
68
|
+
|
69
|
+
def id_hash_from_record(record, record_types)
|
70
|
+
# memoize the record type within the record_types dictionary, then assigning to record_type:
|
71
|
+
associated_record_type = record_types[record.class] ||= record.class.name.underscore.to_sym
|
72
|
+
id_hash(record.id, associated_record_type)
|
73
|
+
end
|
74
|
+
|
75
|
+
def ids_hash(ids)
|
76
|
+
return ids.map { |id| id_hash(id, record_type) } if ids.respond_to? :map
|
77
|
+
id_hash(ids, record_type) # ids variable is just a single id here
|
78
|
+
end
|
79
|
+
|
80
|
+
def id_hash(id, record_type, default_return=false)
|
81
|
+
if id.present?
|
82
|
+
{ id: id.to_s, type: record_type }
|
83
|
+
else
|
84
|
+
default_return ? { id: nil, type: record_type } : nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def fetch_id(record, params)
|
89
|
+
unless object_block.nil?
|
90
|
+
object = object_block.call(record, params)
|
91
|
+
|
92
|
+
return object.map(&:id) if object.respond_to? :map
|
93
|
+
return object.try(:id)
|
94
|
+
end
|
95
|
+
|
96
|
+
record.public_send(id_method_name)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -34,73 +34,36 @@ module FastJsonapi
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
def ids_hash(ids, record_type)
|
38
|
-
return ids.map { |id| id_hash(id, record_type) } if ids.respond_to? :map
|
39
|
-
id_hash(ids, record_type) # ids variable is just a single id here
|
40
|
-
end
|
41
|
-
|
42
|
-
def id_hash_from_record(record, record_types)
|
43
|
-
# memoize the record type within the record_types dictionary, then assigning to record_type:
|
44
|
-
record_type = record_types[record.class] ||= record.class.name.underscore.to_sym
|
45
|
-
id_hash(record.id, record_type)
|
46
|
-
end
|
47
|
-
|
48
|
-
def ids_hash_from_record_and_relationship(record, relationship, params = {})
|
49
|
-
polymorphic = relationship[:polymorphic]
|
50
|
-
|
51
|
-
return ids_hash(
|
52
|
-
fetch_id(record, relationship, params),
|
53
|
-
relationship[:record_type]
|
54
|
-
) unless polymorphic
|
55
|
-
|
56
|
-
return unless associated_object = fetch_associated_object(record, relationship, params)
|
57
|
-
|
58
|
-
return associated_object.map do |object|
|
59
|
-
id_hash_from_record object, polymorphic
|
60
|
-
end if associated_object.respond_to? :map
|
61
|
-
|
62
|
-
id_hash_from_record associated_object, polymorphic
|
63
|
-
end
|
64
|
-
|
65
37
|
def links_hash(record, params = {})
|
66
|
-
data_links.each_with_object({}) do |(
|
67
|
-
|
68
|
-
method.arity == 1 ? method.call(record) : method.call(record, params)
|
69
|
-
else
|
70
|
-
record.public_send(method)
|
71
|
-
end
|
38
|
+
data_links.each_with_object({}) do |(_k, link), hash|
|
39
|
+
link.serialize(record, params, hash)
|
72
40
|
end
|
73
41
|
end
|
74
42
|
|
75
|
-
def attributes_hash(record, params = {})
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
record.public_send(method)
|
81
|
-
end
|
43
|
+
def attributes_hash(record, fieldset = nil, params = {})
|
44
|
+
attributes = attributes_to_serialize
|
45
|
+
attributes = attributes.slice(*fieldset) if fieldset.present?
|
46
|
+
attributes.each_with_object({}) do |(_k, attribute), hash|
|
47
|
+
attribute.serialize(record, params, hash)
|
82
48
|
end
|
83
49
|
end
|
84
50
|
|
85
|
-
def relationships_hash(record, relationships = nil, params = {})
|
51
|
+
def relationships_hash(record, relationships = nil, fieldset = nil, params = {})
|
86
52
|
relationships = relationships_to_serialize if relationships.nil?
|
53
|
+
relationships = relationships.slice(*fieldset) if fieldset.present?
|
87
54
|
|
88
55
|
relationships.each_with_object({}) do |(_k, relationship), hash|
|
89
|
-
|
90
|
-
empty_case = relationship[:relationship_type] == :has_many ? [] : nil
|
91
|
-
hash[name] = {
|
92
|
-
data: ids_hash_from_record_and_relationship(record, relationship, params) || empty_case
|
93
|
-
}
|
56
|
+
relationship.serialize(record, params, hash)
|
94
57
|
end
|
95
58
|
end
|
96
59
|
|
97
|
-
def record_hash(record, params = {})
|
60
|
+
def record_hash(record, fieldset, params = {})
|
98
61
|
if cached
|
99
62
|
record_hash = Rails.cache.fetch(record.cache_key, expires_in: cache_length, race_condition_ttl: race_condition_ttl) do
|
100
63
|
temp_hash = id_hash(id_from_record(record), record_type, true)
|
101
|
-
temp_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
|
64
|
+
temp_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?
|
102
65
|
temp_hash[:relationships] = {}
|
103
|
-
temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, params) if cachable_relationships_to_serialize.present?
|
66
|
+
temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, fieldset, params) if cachable_relationships_to_serialize.present?
|
104
67
|
temp_hash[:links] = links_hash(record, params) if data_links.present?
|
105
68
|
temp_hash
|
106
69
|
end
|
@@ -108,8 +71,8 @@ module FastJsonapi
|
|
108
71
|
record_hash
|
109
72
|
else
|
110
73
|
record_hash = id_hash(id_from_record(record), record_type, true)
|
111
|
-
record_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
|
112
|
-
record_hash[:relationships] = relationships_hash(record, nil, params) if relationships_to_serialize.present?
|
74
|
+
record_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?
|
75
|
+
record_hash[:relationships] = relationships_hash(record, nil, fieldset, params) if relationships_to_serialize.present?
|
113
76
|
record_hash[:links] = links_hash(record, params) if data_links.present?
|
114
77
|
record_hash
|
115
78
|
end
|
@@ -140,25 +103,27 @@ module FastJsonapi
|
|
140
103
|
end
|
141
104
|
|
142
105
|
# includes handler
|
143
|
-
def get_included_records(record, includes_list, known_included_objects, params = {})
|
106
|
+
def get_included_records(record, includes_list, known_included_objects, fieldsets, params = {})
|
144
107
|
return unless includes_list.present?
|
145
108
|
|
146
109
|
includes_list.sort.each_with_object([]) do |include_item, included_records|
|
147
110
|
items = parse_include_item(include_item)
|
148
111
|
items.each do |item|
|
149
112
|
next unless relationships_to_serialize && relationships_to_serialize[item]
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
113
|
+
relationship_item = relationships_to_serialize[item]
|
114
|
+
next unless relationship_item.include_relationship?(record, params)
|
115
|
+
raise NotImplementedError if relationship_item.polymorphic.is_a?(Hash)
|
116
|
+
record_type = relationship_item.record_type
|
117
|
+
serializer = relationship_item.serializer.to_s.constantize
|
118
|
+
relationship_type = relationship_item.relationship_type
|
119
|
+
|
120
|
+
included_objects = relationship_item.fetch_associated_object(record, params)
|
156
121
|
next if included_objects.blank?
|
157
122
|
included_objects = [included_objects] unless relationship_type == :has_many
|
158
123
|
|
159
124
|
included_objects.each do |inc_obj|
|
160
125
|
if remaining_items(items)
|
161
|
-
serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects)
|
126
|
+
serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects, fieldsets)
|
162
127
|
included_records.concat(serializer_records) unless serializer_records.empty?
|
163
128
|
end
|
164
129
|
|
@@ -166,27 +131,12 @@ module FastJsonapi
|
|
166
131
|
next if known_included_objects.key?(code)
|
167
132
|
|
168
133
|
known_included_objects[code] = inc_obj
|
169
|
-
|
134
|
+
|
135
|
+
included_records << serializer.record_hash(inc_obj, fieldsets[serializer.record_type], params)
|
170
136
|
end
|
171
137
|
end
|
172
138
|
end
|
173
139
|
end
|
174
|
-
|
175
|
-
def fetch_associated_object(record, relationship, params)
|
176
|
-
return relationship[:object_block].call(record, params) unless relationship[:object_block].nil?
|
177
|
-
record.send(relationship[:object_method_name])
|
178
|
-
end
|
179
|
-
|
180
|
-
def fetch_id(record, relationship, params)
|
181
|
-
unless relationship[:object_block].nil?
|
182
|
-
object = relationship[:object_block].call(record, params)
|
183
|
-
|
184
|
-
return object.map(&:id) if object.respond_to? :map
|
185
|
-
return object.id
|
186
|
-
end
|
187
|
-
|
188
|
-
record.public_send(relationship[:id_method_name])
|
189
|
-
end
|
190
140
|
end
|
191
141
|
end
|
192
142
|
end
|
data/lib/fast_jsonapi/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fast_jsonapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.3'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shishir Kakaraddi
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2018-
|
13
|
+
date: 2018-07-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -193,6 +193,7 @@ files:
|
|
193
193
|
- README.md
|
194
194
|
- lib/extensions/has_one.rb
|
195
195
|
- lib/fast_jsonapi.rb
|
196
|
+
- lib/fast_jsonapi/attribute.rb
|
196
197
|
- lib/fast_jsonapi/instrumentation.rb
|
197
198
|
- lib/fast_jsonapi/instrumentation/serializable_hash.rb
|
198
199
|
- lib/fast_jsonapi/instrumentation/serialized_json.rb
|
@@ -200,8 +201,11 @@ files:
|
|
200
201
|
- lib/fast_jsonapi/instrumentation/skylight/normalizers/base.rb
|
201
202
|
- lib/fast_jsonapi/instrumentation/skylight/normalizers/serializable_hash.rb
|
202
203
|
- lib/fast_jsonapi/instrumentation/skylight/normalizers/serialized_json.rb
|
204
|
+
- lib/fast_jsonapi/link.rb
|
203
205
|
- lib/fast_jsonapi/multi_to_json.rb
|
204
206
|
- lib/fast_jsonapi/object_serializer.rb
|
207
|
+
- lib/fast_jsonapi/railtie.rb
|
208
|
+
- lib/fast_jsonapi/relationship.rb
|
205
209
|
- lib/fast_jsonapi/serialization_core.rb
|
206
210
|
- lib/fast_jsonapi/version.rb
|
207
211
|
- lib/generators/serializer/USAGE
|