fast_jsonapi 1.2 → 1.3
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/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
|