jsonapi-resources 0.10.0.beta2 → 0.10.0.beta2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/jsonapi-resources.rb +2 -1
- data/lib/jsonapi/active_relation_resource_finder.rb +117 -130
- data/lib/jsonapi/active_relation_resource_finder/join_manager.rb +297 -0
- data/lib/jsonapi/cached_response_fragment.rb +66 -49
- data/lib/jsonapi/path.rb +8 -0
- data/lib/jsonapi/path_segment.rb +17 -5
- data/lib/jsonapi/relationship.rb +1 -0
- data/lib/jsonapi/resource_set.rb +118 -48
- data/lib/jsonapi/resources/railtie.rb +9 -0
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/tasks/check_upgrade.rake +52 -0
- metadata +6 -5
- data/lib/jsonapi/active_relation_resource_finder/join_tree.rb +0 -227
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 27565577133e3fc1b27ecf7d66b45e0a7870197599fbcfddb75b9e32ad7182a8
|
4
|
+
data.tar.gz: f4b5ff6b6d45c34bba767876d7e37444931490e73d355d5e386cc76ee427532f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff0f10f2b82b13e6bf144536673b3844fd89675b0cc218ab7c7bd7c347eae7db28e659a7e67dc59adcf2c0f1e951b891f176d02571950f32289751380998d2c5
|
7
|
+
data.tar.gz: c410a7335f49d3140dd3fe7252cee2d6945ae0c6cfbff6b48ff4a63af2e4aaebacdeb6d874e43a1549d4ac07bec19d1cb8d1819edaef7167d9bf62a110bb69b9
|
data/lib/jsonapi-resources.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'jsonapi/resources/railtie'
|
1
2
|
require 'jsonapi/naive_cache'
|
2
3
|
require 'jsonapi/compiled_json'
|
3
4
|
require 'jsonapi/resource'
|
@@ -25,7 +26,7 @@ require 'jsonapi/operation_result'
|
|
25
26
|
require 'jsonapi/callbacks'
|
26
27
|
require 'jsonapi/link_builder'
|
27
28
|
require 'jsonapi/active_relation_resource_finder'
|
28
|
-
require 'jsonapi/active_relation_resource_finder/
|
29
|
+
require 'jsonapi/active_relation_resource_finder/join_manager'
|
29
30
|
require 'jsonapi/resource_identity'
|
30
31
|
require 'jsonapi/resource_fragment'
|
31
32
|
require 'jsonapi/resource_id_tree'
|
@@ -19,16 +19,15 @@ module JSONAPI
|
|
19
19
|
def find(filters, options = {})
|
20
20
|
sort_criteria = options.fetch(:sort_criteria) { [] }
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
sort_criteria: sort_criteria)
|
22
|
+
join_manager = JoinManager.new(resource_klass: self,
|
23
|
+
filters: filters,
|
24
|
+
sort_criteria: sort_criteria)
|
26
25
|
|
27
26
|
paginator = options[:paginator]
|
28
27
|
|
29
|
-
records =
|
30
|
-
filters: filters,
|
31
|
-
|
28
|
+
records = apply_request_settings_to_records(records: records(options),
|
29
|
+
sort_criteria: sort_criteria,filters: filters,
|
30
|
+
join_manager: join_manager,
|
32
31
|
paginator: paginator,
|
33
32
|
options: options)
|
34
33
|
|
@@ -42,13 +41,12 @@ module JSONAPI
|
|
42
41
|
#
|
43
42
|
# @return [Integer] the count
|
44
43
|
def count(filters, options = {})
|
45
|
-
|
46
|
-
|
47
|
-
filters: filters)
|
44
|
+
join_manager = JoinManager.new(resource_klass: self,
|
45
|
+
filters: filters)
|
48
46
|
|
49
|
-
records =
|
47
|
+
records = apply_request_settings_to_records(records: records(options),
|
50
48
|
filters: filters,
|
51
|
-
|
49
|
+
join_manager: join_manager,
|
52
50
|
options: options)
|
53
51
|
|
54
52
|
count_records(records)
|
@@ -93,26 +91,23 @@ module JSONAPI
|
|
93
91
|
|
94
92
|
sort_criteria = options.fetch(:sort_criteria) { [] }
|
95
93
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
options: options)
|
94
|
+
join_manager = JoinManager.new(resource_klass: resource_klass,
|
95
|
+
source_relationship: nil,
|
96
|
+
relationships: linkage_relationships,
|
97
|
+
sort_criteria: sort_criteria,
|
98
|
+
filters: filters)
|
102
99
|
|
103
100
|
paginator = options[:paginator]
|
104
101
|
|
105
|
-
records =
|
102
|
+
records = apply_request_settings_to_records(records: records(options),
|
106
103
|
filters: filters,
|
107
104
|
sort_criteria: sort_criteria,
|
108
105
|
paginator: paginator,
|
109
|
-
|
106
|
+
join_manager: join_manager,
|
110
107
|
options: options)
|
111
108
|
|
112
|
-
joins = join_tree.joins
|
113
|
-
|
114
109
|
# This alias is going to be resolve down to the model's table name and will not actually be an alias
|
115
|
-
resource_table_alias =
|
110
|
+
resource_table_alias = resource_klass._table_name
|
116
111
|
|
117
112
|
pluck_fields = [Arel.sql("#{concat_table_field(resource_table_alias, resource_klass._primary_key)} AS #{resource_table_alias}_#{resource_klass._primary_key}")]
|
118
113
|
|
@@ -131,7 +126,7 @@ module JSONAPI
|
|
131
126
|
klass = resource_klass_for(resource_type)
|
132
127
|
linkage_fields << {relationship_name: name, resource_klass: klass}
|
133
128
|
|
134
|
-
linkage_table_alias =
|
129
|
+
linkage_table_alias = join_manager.join_details_by_polymorphic_relationship(linkage_relationship, resource_type)[:alias]
|
135
130
|
primary_key = klass._primary_key
|
136
131
|
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
|
137
132
|
end
|
@@ -139,7 +134,7 @@ module JSONAPI
|
|
139
134
|
klass = linkage_relationship.resource_klass
|
140
135
|
linkage_fields << {relationship_name: name, resource_klass: klass}
|
141
136
|
|
142
|
-
linkage_table_alias =
|
137
|
+
linkage_table_alias = join_manager.join_details_by_relationship(linkage_relationship)[:alias]
|
143
138
|
primary_key = klass._primary_key
|
144
139
|
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
|
145
140
|
end
|
@@ -154,7 +149,7 @@ module JSONAPI
|
|
154
149
|
end
|
155
150
|
|
156
151
|
fragments = {}
|
157
|
-
rows = records.pluck(*pluck_fields)
|
152
|
+
rows = records.distinct.pluck(*pluck_fields)
|
158
153
|
rows.collect do |row|
|
159
154
|
rid = JSONAPI::ResourceIdentity.new(resource_klass, pluck_fields.length == 1 ? row : row[0])
|
160
155
|
|
@@ -229,20 +224,18 @@ module JSONAPI
|
|
229
224
|
filters = options.fetch(:filters, {})
|
230
225
|
|
231
226
|
# Joins in this case are related to the related_klass
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
options: options)
|
227
|
+
join_manager = JoinManager.new(resource_klass: self,
|
228
|
+
source_relationship: relationship,
|
229
|
+
filters: filters)
|
236
230
|
|
237
|
-
records =
|
231
|
+
records = apply_request_settings_to_records(records: records(options),
|
238
232
|
resource_klass: related_klass,
|
239
233
|
primary_keys: source_rid.id,
|
240
|
-
|
234
|
+
join_manager: join_manager,
|
241
235
|
filters: filters,
|
242
236
|
options: options)
|
243
237
|
|
244
|
-
|
245
|
-
related_alias = joins[''][:alias]
|
238
|
+
related_alias = join_manager.join_details_by_relationship(relationship)[:alias]
|
246
239
|
|
247
240
|
records = records.select(Arel.sql("#{concat_table_field(related_alias, related_klass._primary_key)}"))
|
248
241
|
|
@@ -250,7 +243,52 @@ module JSONAPI
|
|
250
243
|
end
|
251
244
|
|
252
245
|
def records(_options = {})
|
253
|
-
_model_class.
|
246
|
+
_model_class.all
|
247
|
+
end
|
248
|
+
|
249
|
+
def apply_join(records:, relationship:, resource_type:, join_type:, options:)
|
250
|
+
if relationship.polymorphic? && relationship.belongs_to?
|
251
|
+
case join_type
|
252
|
+
when :inner
|
253
|
+
records = records.joins(resource_type.to_s.singularize.to_sym)
|
254
|
+
when :left
|
255
|
+
records = records.joins_left(resource_type.to_s.singularize.to_sym)
|
256
|
+
end
|
257
|
+
else
|
258
|
+
relation_name = relationship.relation_name(options)
|
259
|
+
case join_type
|
260
|
+
when :inner
|
261
|
+
records = records.joins(relation_name)
|
262
|
+
when :left
|
263
|
+
records = records.joins_left(relation_name)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
records
|
267
|
+
end
|
268
|
+
|
269
|
+
def relationship_records(relationship:, join_type: :inner, resource_type: nil, options: {})
|
270
|
+
records = relationship.parent_resource.records(options)
|
271
|
+
strategy = relationship.options[:apply_join]
|
272
|
+
|
273
|
+
if strategy
|
274
|
+
records = call_method_or_proc(strategy, records, relationship, resource_type, join_type, options)
|
275
|
+
else
|
276
|
+
records = apply_join(records: records,
|
277
|
+
relationship: relationship,
|
278
|
+
resource_type: resource_type,
|
279
|
+
join_type: join_type,
|
280
|
+
options: options)
|
281
|
+
end
|
282
|
+
|
283
|
+
records
|
284
|
+
end
|
285
|
+
|
286
|
+
def join_relationship(records:, relationship:, resource_type: nil, join_type: :inner, options: {})
|
287
|
+
relationship_records = relationship_records(relationship: relationship,
|
288
|
+
join_type: join_type,
|
289
|
+
resource_type: resource_type,
|
290
|
+
options: options)
|
291
|
+
records.merge(relationship_records)
|
254
292
|
end
|
255
293
|
|
256
294
|
protected
|
@@ -267,13 +305,13 @@ module JSONAPI
|
|
267
305
|
end
|
268
306
|
|
269
307
|
def find_record_by_key(key, options = {})
|
270
|
-
record =
|
308
|
+
record = apply_request_settings_to_records(records: records(options), primary_keys: key, options: options).first
|
271
309
|
fail JSONAPI::Exceptions::RecordNotFound.new(key) if record.nil?
|
272
310
|
record
|
273
311
|
end
|
274
312
|
|
275
313
|
def find_records_by_keys(keys, options = {})
|
276
|
-
|
314
|
+
apply_request_settings_to_records(records: records(options), primary_keys: keys, options: options)
|
277
315
|
end
|
278
316
|
|
279
317
|
def find_related_monomorphic_fragments(source_rids, relationship, options, connect_source_identity)
|
@@ -290,26 +328,24 @@ module JSONAPI
|
|
290
328
|
sort_criteria << { field: field, direction: sort[:direction] }
|
291
329
|
end
|
292
330
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
options: options)
|
331
|
+
join_manager = JoinManager.new(resource_klass: self,
|
332
|
+
source_relationship: relationship,
|
333
|
+
relationships: linkage_relationships,
|
334
|
+
sort_criteria: sort_criteria,
|
335
|
+
filters: filters)
|
299
336
|
|
300
337
|
paginator = options[:paginator] if source_rids.count == 1
|
301
338
|
|
302
|
-
records =
|
339
|
+
records = apply_request_settings_to_records(records: records(options),
|
303
340
|
resource_klass: resource_klass,
|
304
341
|
sort_criteria: sort_criteria,
|
305
342
|
primary_keys: source_ids,
|
306
343
|
paginator: paginator,
|
307
344
|
filters: filters,
|
308
|
-
|
345
|
+
join_manager: join_manager,
|
309
346
|
options: options)
|
310
347
|
|
311
|
-
|
312
|
-
resource_table_alias = joins[''][:alias]
|
348
|
+
resource_table_alias = join_manager.join_details_by_relationship(relationship)[:alias]
|
313
349
|
|
314
350
|
pluck_fields = [
|
315
351
|
Arel.sql("#{_table_name}.#{_primary_key} AS source_id"),
|
@@ -331,7 +367,7 @@ module JSONAPI
|
|
331
367
|
klass = resource_klass_for(resource_type)
|
332
368
|
linkage_fields << {relationship_name: name, resource_klass: klass}
|
333
369
|
|
334
|
-
linkage_table_alias =
|
370
|
+
linkage_table_alias = join_manager.join_details_by_polymorphic_relationship(linkage_relationship, resource_type)[:alias]
|
335
371
|
primary_key = klass._primary_key
|
336
372
|
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
|
337
373
|
end
|
@@ -339,7 +375,7 @@ module JSONAPI
|
|
339
375
|
klass = linkage_relationship.resource_klass
|
340
376
|
linkage_fields << {relationship_name: name, resource_klass: klass}
|
341
377
|
|
342
|
-
linkage_table_alias =
|
378
|
+
linkage_table_alias = join_manager.join_details_by_relationship(linkage_relationship)[:alias]
|
343
379
|
primary_key = klass._primary_key
|
344
380
|
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
|
345
381
|
end
|
@@ -354,7 +390,7 @@ module JSONAPI
|
|
354
390
|
end
|
355
391
|
|
356
392
|
fragments = {}
|
357
|
-
rows = records.pluck(*pluck_fields)
|
393
|
+
rows = records.distinct.pluck(*pluck_fields)
|
358
394
|
rows.each do |row|
|
359
395
|
rid = JSONAPI::ResourceIdentity.new(resource_klass, row[1])
|
360
396
|
|
@@ -418,27 +454,24 @@ module JSONAPI
|
|
418
454
|
end
|
419
455
|
end
|
420
456
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
options: options)
|
457
|
+
join_manager = JoinManager.new(resource_klass: self,
|
458
|
+
source_relationship: relationship,
|
459
|
+
relationships: linkage_relationships,
|
460
|
+
filters: filters)
|
426
461
|
|
427
462
|
paginator = options[:paginator] if source_rids.count == 1
|
428
463
|
|
429
464
|
# Note: We will sort by the source table. Without using unions we can't sort on a polymorphic relationship
|
430
465
|
# in any manner that makes sense
|
431
|
-
records =
|
466
|
+
records = apply_request_settings_to_records(records: records(options),
|
432
467
|
resource_klass: resource_klass,
|
433
468
|
sort_primary: true,
|
434
469
|
primary_keys: source_ids,
|
435
470
|
paginator: paginator,
|
436
471
|
filters: filters,
|
437
|
-
|
472
|
+
join_manager: join_manager,
|
438
473
|
options: options)
|
439
474
|
|
440
|
-
joins = join_tree.joins
|
441
|
-
|
442
475
|
primary_key = concat_table_field(_table_name, _primary_key)
|
443
476
|
related_key = concat_table_field(_table_name, relationship.foreign_key)
|
444
477
|
related_type = concat_table_field(_table_name, relationship.polymorphic_type)
|
@@ -467,7 +500,7 @@ module JSONAPI
|
|
467
500
|
|
468
501
|
cache_field = related_klass.attribute_to_model_field(:_cache_field) if options[:cache]
|
469
502
|
|
470
|
-
table_alias =
|
503
|
+
table_alias = join_manager.source_join_details(type)[:alias]
|
471
504
|
|
472
505
|
cache_offset = relation_index
|
473
506
|
if cache_field
|
@@ -515,7 +548,7 @@ module JSONAPI
|
|
515
548
|
klass = resource_klass_for(resource_type)
|
516
549
|
linkage_fields << {relationship: linkage_relationship, resource_klass: klass}
|
517
550
|
|
518
|
-
linkage_table_alias =
|
551
|
+
linkage_table_alias = join_manager.join_details_by_polymorphic_relationship(linkage_relationship, resource_type)[:alias]
|
519
552
|
primary_key = klass._primary_key
|
520
553
|
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
|
521
554
|
end
|
@@ -523,13 +556,13 @@ module JSONAPI
|
|
523
556
|
klass = linkage_relationship.resource_klass
|
524
557
|
linkage_fields << {relationship: linkage_relationship, resource_klass: klass}
|
525
558
|
|
526
|
-
linkage_table_alias =
|
559
|
+
linkage_table_alias = join_manager.join_details_by_relationship(linkage_relationship)[:alias]
|
527
560
|
primary_key = klass._primary_key
|
528
561
|
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
|
529
562
|
end
|
530
563
|
end
|
531
564
|
|
532
|
-
rows = records.pluck(*pluck_fields)
|
565
|
+
rows = records.distinct.pluck(*pluck_fields)
|
533
566
|
|
534
567
|
related_fragments = {}
|
535
568
|
|
@@ -581,10 +614,10 @@ module JSONAPI
|
|
581
614
|
related_fragments
|
582
615
|
end
|
583
616
|
|
584
|
-
def
|
585
|
-
|
617
|
+
def apply_request_settings_to_records(records:,
|
618
|
+
join_manager: JoinManager.new(resource_klass: self),
|
586
619
|
resource_klass: self,
|
587
|
-
filters:
|
620
|
+
filters: {},
|
588
621
|
primary_keys: nil,
|
589
622
|
sort_criteria: nil,
|
590
623
|
sort_primary: nil,
|
@@ -592,15 +625,15 @@ module JSONAPI
|
|
592
625
|
options: {})
|
593
626
|
|
594
627
|
opts = options.dup
|
595
|
-
records = resource_klass.apply_joins(records,
|
628
|
+
records = resource_klass.apply_joins(records, join_manager, opts)
|
596
629
|
|
597
630
|
if primary_keys
|
598
631
|
records = records.where(_primary_key => primary_keys)
|
599
632
|
end
|
600
633
|
|
601
|
-
opts[:
|
634
|
+
opts[:join_manager] = join_manager
|
602
635
|
|
603
|
-
|
636
|
+
unless filters.empty?
|
604
637
|
records = resource_klass.filter_records(records, filters, opts)
|
605
638
|
end
|
606
639
|
|
@@ -618,52 +651,8 @@ module JSONAPI
|
|
618
651
|
records
|
619
652
|
end
|
620
653
|
|
621
|
-
def
|
622
|
-
|
623
|
-
init_join_sources_length = init_join_sources.length
|
624
|
-
|
625
|
-
records = yield(records)
|
626
|
-
|
627
|
-
join_sources = records.arel.join_sources
|
628
|
-
if join_sources.length > init_join_sources_length
|
629
|
-
last_join = (join_sources - init_join_sources).last
|
630
|
-
join_alias =
|
631
|
-
case last_join.left
|
632
|
-
when Arel::Table
|
633
|
-
last_join.left.name
|
634
|
-
when Arel::Nodes::TableAlias
|
635
|
-
last_join.left.right
|
636
|
-
when Arel::Nodes::StringJoin
|
637
|
-
# :nocov:
|
638
|
-
warn "get_join_alias: Unsupported join type - use custom filtering and sorting"
|
639
|
-
nil
|
640
|
-
# :nocov:
|
641
|
-
end
|
642
|
-
else
|
643
|
-
# :nocov:
|
644
|
-
warn "get_join_alias: No join added"
|
645
|
-
join_alias = nil
|
646
|
-
# :nocov:
|
647
|
-
end
|
648
|
-
|
649
|
-
return records, join_alias
|
650
|
-
end
|
651
|
-
|
652
|
-
def apply_joins(records, join_tree, _options)
|
653
|
-
joins = join_tree.joins
|
654
|
-
|
655
|
-
joins.each_value do |join|
|
656
|
-
case join[:join_type]
|
657
|
-
when :inner
|
658
|
-
records, join_alias = get_join_alias(records) { |records| records.joins(join[:relation_join_hash]) }
|
659
|
-
join[:alias] = join_alias
|
660
|
-
when :left
|
661
|
-
records, join_alias = get_join_alias(records) { |records| records.joins_left(join[:relation_join_hash]) }
|
662
|
-
join[:alias] = join_alias
|
663
|
-
end
|
664
|
-
end
|
665
|
-
|
666
|
-
return records
|
654
|
+
def apply_joins(records, join_manager, options)
|
655
|
+
join_manager.join(records, options)
|
667
656
|
end
|
668
657
|
|
669
658
|
def apply_pagination(records, paginator, order_options)
|
@@ -689,9 +678,9 @@ module JSONAPI
|
|
689
678
|
if strategy
|
690
679
|
records = call_method_or_proc(strategy, records, direction, context)
|
691
680
|
else
|
692
|
-
|
681
|
+
join_manager = options[:join_manager]
|
693
682
|
|
694
|
-
records = records.order("#{get_aliased_field(field,
|
683
|
+
records = records.order(Arel.sql("#{get_aliased_field(field, join_manager)} #{direction}"))
|
695
684
|
end
|
696
685
|
records
|
697
686
|
end
|
@@ -758,22 +747,20 @@ module JSONAPI
|
|
758
747
|
records
|
759
748
|
end
|
760
749
|
|
761
|
-
def get_aliased_field(path_with_field,
|
750
|
+
def get_aliased_field(path_with_field, join_manager)
|
762
751
|
path = JSONAPI::Path.new(resource_klass: self, path_string: path_with_field)
|
763
752
|
|
764
|
-
|
765
|
-
|
766
|
-
relationship_path = path.relationship_path_string
|
753
|
+
relationship_segment = path.segments[-2]
|
754
|
+
field_segment = path.segments[-1]
|
767
755
|
|
768
|
-
if
|
769
|
-
|
770
|
-
|
771
|
-
|
756
|
+
if relationship_segment
|
757
|
+
join_details = join_manager.join_details[path.last_relationship]
|
758
|
+
table_alias = join_details[:alias]
|
759
|
+
else
|
760
|
+
table_alias = self._table_name
|
772
761
|
end
|
773
762
|
|
774
|
-
table_alias
|
775
|
-
|
776
|
-
concat_table_field(table_alias, field.delegated_field_name)
|
763
|
+
concat_table_field(table_alias, field_segment.delegated_field_name)
|
777
764
|
end
|
778
765
|
|
779
766
|
def apply_filter(records, filter, value, options = {})
|
@@ -782,8 +769,8 @@ module JSONAPI
|
|
782
769
|
if strategy
|
783
770
|
records = call_method_or_proc(strategy, records, value, options)
|
784
771
|
else
|
785
|
-
|
786
|
-
records = records.where(get_aliased_field(filter,
|
772
|
+
join_manager = options[:join_manager]
|
773
|
+
records = records.where(Arel.sql(get_aliased_field(filter, join_manager)) => value)
|
787
774
|
end
|
788
775
|
|
789
776
|
records
|