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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f0435c60c6dca20674f60da2681d58407de97f96
4
- data.tar.gz: 9bad26baa66aaf9a6c5d3aa9f7e3797229826b67
2
+ SHA256:
3
+ metadata.gz: 27565577133e3fc1b27ecf7d66b45e0a7870197599fbcfddb75b9e32ad7182a8
4
+ data.tar.gz: f4b5ff6b6d45c34bba767876d7e37444931490e73d355d5e386cc76ee427532f
5
5
  SHA512:
6
- metadata.gz: 97daeb8e0a39bac96c4c840e86fedd7a576a1877537a855c20b21ea29d3b29001fcf2c3e4b8e8f7ce3fbf544d84a6a54bd7742fd7a7c49f4a18579d1cc639645
7
- data.tar.gz: 10983a6d90cd6e83b63a65e8a9fae0a1fc010b9dae266d1365ee11c8494e89bce1f32f18dce2151b4d2b3657d3cb41202d2556464e3d0ba651f933158c40e225
6
+ metadata.gz: ff0f10f2b82b13e6bf144536673b3844fd89675b0cc218ab7c7bd7c347eae7db28e659a7e67dc59adcf2c0f1e951b891f176d02571950f32289751380998d2c5
7
+ data.tar.gz: c410a7335f49d3140dd3fe7252cee2d6945ae0c6cfbff6b48ff4a63af2e4aaebacdeb6d874e43a1549d4ac07bec19d1cb8d1819edaef7167d9bf62a110bb69b9
@@ -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/join_tree'
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
- join_tree = JoinTree.new(resource_klass: self,
23
- options: options,
24
- filters: filters,
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 = find_records(records: records(options),
30
- filters: filters,
31
- join_tree: join_tree,
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
- join_tree = JoinTree.new(resource_klass: self,
46
- options: options,
47
- filters: filters)
44
+ join_manager = JoinManager.new(resource_klass: self,
45
+ filters: filters)
48
46
 
49
- records = find_records(records: records(options),
47
+ records = apply_request_settings_to_records(records: records(options),
50
48
  filters: filters,
51
- join_tree: join_tree,
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
- join_tree = JoinTree.new(resource_klass: resource_klass,
97
- source_relationship: nil,
98
- relationships: linkage_relationships,
99
- sort_criteria: sort_criteria,
100
- filters: filters,
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 = find_records(records: records(options),
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
- join_tree: join_tree,
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 = joins[''][: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 = joins["#{linkage_relationship.name.to_s}##{resource_type}"][: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 = joins[name.to_s][: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
- join_tree = JoinTree.new(resource_klass: self,
233
- source_relationship: relationship,
234
- filters: filters,
235
- options: options)
227
+ join_manager = JoinManager.new(resource_klass: self,
228
+ source_relationship: relationship,
229
+ filters: filters)
236
230
 
237
- records = find_records(records: records(options),
231
+ records = apply_request_settings_to_records(records: records(options),
238
232
  resource_klass: related_klass,
239
233
  primary_keys: source_rid.id,
240
- join_tree: join_tree,
234
+ join_manager: join_manager,
241
235
  filters: filters,
242
236
  options: options)
243
237
 
244
- joins = join_tree.joins
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.distinct.all
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 = find_records(records: records(options), primary_keys: key, options: options).first
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
- find_records(records: records(options), primary_keys: keys, options: options)
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
- join_tree = JoinTree.new(resource_klass: self,
294
- source_relationship: relationship,
295
- relationships: linkage_relationships,
296
- sort_criteria: sort_criteria,
297
- filters: filters,
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 = find_records(records: records(options),
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
- join_tree: join_tree,
345
+ join_manager: join_manager,
309
346
  options: options)
310
347
 
311
- joins = join_tree.joins
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 = joins["#{linkage_relationship.name.to_s}##{resource_type}"][: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 = joins[name.to_s][: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
- join_tree = JoinTree.new(resource_klass: self,
422
- source_relationship: relationship,
423
- relationships: linkage_relationships,
424
- filters: filters,
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 = find_records(records: records(options),
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
- join_tree: join_tree,
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 = joins["##{type}"][: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 = joins[linkage_relationship_path][: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 = joins[linkage_relationship_path.to_s][: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 find_records(records:,
585
- join_tree: JoinTree.new(resource_klass: self),
617
+ def apply_request_settings_to_records(records:,
618
+ join_manager: JoinManager.new(resource_klass: self),
586
619
  resource_klass: self,
587
- filters: nil,
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, join_tree, opts)
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[:joins] = join_tree.joins
634
+ opts[:join_manager] = join_manager
602
635
 
603
- if filters
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 get_join_alias(records, &block)
622
- init_join_sources = records.arel.join_sources
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
- joins = options[:joins] || {}
681
+ join_manager = options[:join_manager]
693
682
 
694
- records = records.order("#{get_aliased_field(field, joins)} #{direction}")
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, joins)
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
- relationship = path.segments[-2]
765
- field = path.segments[-1]
766
- relationship_path = path.relationship_path_string
753
+ relationship_segment = path.segments[-2]
754
+ field_segment = path.segments[-1]
767
755
 
768
- if relationship
769
- join_name = relationship_path
770
- join = joins.try(:[], join_name)
771
- table_alias = join.try(:[], :alias)
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 ||= joins[''][: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
- joins = options[:joins] || {}
786
- records = records.where(get_aliased_field(filter, joins) => value)
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