jsonapi-resources 0.10.0.beta2 → 0.10.0.beta2.1

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 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