brick 1.0.91 → 1.0.92

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
2
  SHA256:
3
- metadata.gz: 81eaa518a99452286bdaf1e5ed33b83962ede9f9c6509f0b472fd0fe19a8eceb
4
- data.tar.gz: 06f5653ce40eb6ee1568b380cf3063073ac7735add8aab6c5873a343e8018a70
3
+ metadata.gz: 6bb6854dd2295476ae00996f23e801587869463aa7114d9240c0e97b1cce25b1
4
+ data.tar.gz: f174948106c6a06be049f8285fb3358813d09b9970ce484554ad3c86bc3cb8fb
5
5
  SHA512:
6
- metadata.gz: 7e0a678885c927a7a80c8758d265c2ff351f67ea44e8e1b2c128e123df441f4f72a62f775b9785615eeacbdbff99427e9d1590890bdffd06165ae7b2339fe1af
7
- data.tar.gz: 7bd3e424225e435bab4517fb14aeaa3ca128041a746aaf413ecdb17a771444c3413f058ff28cc5e303d074fdd7cc7d21ff66f842667c14226d1849e96d4b3a98
6
+ metadata.gz: 9384ef5db0a03fcc8fda6a3ceafc763a468f88033420d785814d657303027593c7fef64770087975d73d4041f577864c1d934629cb50083fb93d2591f719f136
7
+ data.tar.gz: e2b3e21982b9a6d132a3d9d05041759e06a3ac3ffbfdf2ea3675ee0140150f2d33b6e60cbeb0033a7f7469bba227f198a6cc6a0ef34c229ba7a1132cd1175b0d
@@ -11,6 +11,7 @@ unless ActiveRecord.respond_to?(:version)
11
11
  end
12
12
 
13
13
  # ActiveSupport, ActionPack, and ActionView before 4.0 didn't have #version
14
+ require 'active_support' # Needed for Rails 4.x
14
15
  unless ActiveSupport.respond_to?(:version)
15
16
  module ActiveSupport
16
17
  def self.version
@@ -407,7 +407,13 @@ module ActiveRecord
407
407
  end
408
408
 
409
409
  class Relation
410
- attr_reader :_brick_chains, :_arel_applied_aliases
410
+ attr_reader :_arel_applied_aliases
411
+
412
+ # Links from ActiveRecord association pathing names over to real
413
+ # table correlation names built from AREL aliasing
414
+ def brick_links
415
+ @brick_links ||= {}
416
+ end
411
417
 
412
418
  # CLASS STUFF
413
419
  def _recurse_arel(piece, prefix = '')
@@ -448,7 +454,6 @@ module ActiveRecord
448
454
  @_arel_applied_aliases << (alias_name = table.right)
449
455
  table = table.left
450
456
  end
451
- (_brick_chains[table._arel_table_type] ||= []) << (alias_name || table.table_alias || table.name)
452
457
  end
453
458
  # rubocop:enable Style/IdenticalConditionalBranches
454
459
  when Arel::Table # Table
@@ -457,12 +462,8 @@ module ActiveRecord
457
462
  # Can get the real table name from: self._recurse_arel(piece.left)
458
463
  names << [piece.left._arel_table_type, piece.right.to_s] # This is simply a string; the alias name itself
459
464
  when Arel::Nodes::JoinSource # Leaving this until the end because AR < 3.2 doesn't know at all about JoinSource!
460
- # Spin up an empty set of Brick alias name chains at the start
461
- @_brick_chains = {}
462
465
  # The left side is the "FROM" table
463
466
  names << (this_name = [piece.left._arel_table_type, (piece.left.table_alias || piece.left.name)])
464
- # # Do not currently need the root "FROM" table in our list of chains
465
- # (_brick_chains[this_name.first] ||= []) << this_name.last
466
467
  # The right side is an array of all JOINs
467
468
  piece.right.each { |join| names << _recurse_arel(join) }
468
469
  end
@@ -548,7 +549,6 @@ module ActiveRecord
548
549
  # Without working from a duplicate, touching the AREL ast tree sets the @arel instance variable, which causes the relation to be immutable.
549
550
  (rel_dupe = dup)._arel_alias_names
550
551
  core_selects = selects.dup
551
- chains = rel_dupe._brick_chains
552
552
  id_for_tables = Hash.new { |h, k| h[k] = [] }
553
553
  field_tbl_names = Hash.new { |h, k| h[k] = {} }
554
554
  used_col_aliases = {} # Used to make sure there is not a name clash
@@ -569,7 +569,7 @@ module ActiveRecord
569
569
  key_alias = nil
570
570
  cc.first.each do |cc_part|
571
571
  dest_klass = cc_part[0..-2].inject(klass) { |kl, cc_part_term| kl.reflect_on_association(cc_part_term).klass }
572
- tbl_name = (field_tbl_names[k][cc_part.last] ||= shift_or_first(chains[dest_klass])).split('.').last
572
+ tbl_name = rel_dupe.brick_links[cc_part[0..-2].map(&:to_s).join('.')]
573
573
  # Deal with the conflict if there are two parts in the custom column named the same,
574
574
  # "category.name" and "product.name" for instance will end up with aliases of "name"
575
575
  # and "product__name".
@@ -606,23 +606,18 @@ module ActiveRecord
606
606
 
607
607
  klass._br_bt_descrip.each do |v|
608
608
  v.last.each do |k1, v1| # k1 is class, v1 is array of columns to snag
609
- next if chains[k1].nil?
609
+ next unless (tbl_name = rel_dupe.brick_links[v.first.to_s]&.split('.')&.last)
610
610
 
611
- tbl_name = (field_tbl_names[v.first][k1] ||= shift_or_first(chains[k1])).split('.').last
612
611
  # If it's Oracle, quote any AREL aliases that had been applied
613
612
  tbl_name = "\"#{tbl_name}\"" if ::Brick.is_oracle && rel_dupe._arel_applied_aliases.include?(tbl_name)
614
613
  field_tbl_name = nil
615
- v1.map { |x| [translations[x[0..-2].map(&:to_s).join('.')], x.last] }.each_with_index do |sel_col, idx|
616
- # unless chains[sel_col.first]
617
- # puts 'You might have some bogus DSL in your brick.rb file'
618
- # next
619
- # end
620
- field_tbl_name = (field_tbl_names[v.first][sel_col.first] ||= shift_or_first(chains[sel_col.first])).split('.').last
614
+ v1.map { |x| [x[0..-2].map(&:to_s).join('.'), x.last] }.each_with_index do |sel_col, idx|
615
+ field_tbl_name = rel_dupe.brick_links[sel_col.first].split('.').last
621
616
  # If it's Oracle, quote any AREL aliases that had been applied
622
617
  field_tbl_name = "\"#{field_tbl_name}\"" if ::Brick.is_oracle && rel_dupe._arel_applied_aliases.include?(field_tbl_name)
623
618
 
624
619
  # Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
625
- is_xml = is_distinct && Brick.relations[sel_col.first.table_name]&.[](:cols)&.[](sel_col.last)&.first&.start_with?('xml')
620
+ is_xml = is_distinct && Brick.relations[field_tbl_name]&.[](:cols)&.[](sel_col.last)&.first&.start_with?('xml')
626
621
  # If it's not unique then also include the belongs_to association name before the column name
627
622
  if used_col_aliases.key?(col_alias = "br_fk_#{v.first}__#{sel_col.last}")
628
623
  col_alias = "br_fk_#{v.first}__#{v1[idx][-2..-1].map(&:to_s).join('__')}"
@@ -659,14 +654,9 @@ module ActiveRecord
659
654
  end
660
655
  end
661
656
  join_array.each do |assoc_name|
662
- # %%% Need to support {user: :profile}
663
657
  next unless assoc_name.is_a?(Symbol)
664
658
 
665
- table_alias = if (chain = chains[klass = reflect_on_association(assoc_name)&.klass])
666
- shift_or_first(chain)
667
- else
668
- klass.table_name # ActiveRecord < 4.2 can't (yet) use the cool chains thing
669
- end
659
+ table_alias = rel_dupe.brick_links[assoc_name.to_s]
670
660
  _assoc_names[assoc_name] = [table_alias, klass]
671
661
  end
672
662
  end
@@ -677,26 +667,47 @@ module ActiveRecord
677
667
  # Build the chain of JOINs going to the final destination HMT table
678
668
  # (Usually just one JOIN, but could be many.)
679
669
  hmt_assoc = hm
680
- x = []
681
- x.unshift(hmt_assoc) while hmt_assoc.options[:through] && (hmt_assoc = klass.reflect_on_association(hmt_assoc.options[:through]))
682
- from_clause = +"#{x.first.table_name} br_t0"
683
- fk_col = x.shift.foreign_key
684
- link_back = [klass.primary_key] # %%% Inverse path back to the original object -- used to build out a link with a filter
670
+ through_sources = []
671
+ # %%% Inverse path back to the original object -- not yet used, but soon
672
+ # will be leveraged in order to build links with multi-table-hop filters.
673
+ link_back = []
674
+ # Track polymorphic type field if necessary
675
+ if hm.source_reflection.options[:as]
676
+ poly_ft = [hm.source_reflection.inverse_of.foreign_type, hmt_assoc.source_reflection.class_name]
677
+ end
678
+ # link_back << hm.source_reflection.inverse_of.name
679
+ while hmt_assoc.options[:through] && (hmt_assoc = klass.reflect_on_association(hmt_assoc.options[:through]))
680
+ through_sources.unshift(hmt_assoc)
681
+ end
682
+ # Turn the last member of link_back into a foreign key
683
+ link_back << hmt_assoc.source_reflection.foreign_key
684
+ # If it's a HMT based on a HM -> HM, must JOIN the last table into the mix at the end
685
+ through_sources.push(hm.source_reflection) unless hm.source_reflection.belongs_to?
686
+ from_clause = +"#{through_sources.first.table_name} br_t0"
687
+ fk_col = through_sources.shift.foreign_key
688
+
685
689
  idx = 0
686
690
  bail_out = nil
687
- x.map do |a|
691
+ through_sources.map do |a|
688
692
  from_clause << "\n LEFT OUTER JOIN #{a.table_name} br_t#{idx += 1} "
689
693
  from_clause << if (src_ref = a.source_reflection).macro == :belongs_to
694
+ (nm = hmt_assoc.source_reflection.inverse_of&.name)
695
+ # binding.pry unless nm
696
+ link_back << nm
690
697
  "ON br_t#{idx}.id = br_t#{idx - 1}.#{a.foreign_key}"
691
698
  elsif src_ref.options[:as]
692
699
  "ON br_t#{idx}.#{src_ref.type} = '#{src_ref.active_record.name}'" + # "polymorphable_type"
693
700
  " AND br_t#{idx}.#{src_ref.foreign_key} = br_t#{idx - 1}.id"
694
701
  elsif src_ref.options[:source_type]
695
- print "Skipping #{hm.name} --HMT-> #{hm.source_reflection.name} as it uses source_type which is not supported"
702
+ print "Skipping #{hm.name} --HMT-> #{hm.source_reflection.name} as it uses source_type which is not yet supported"
696
703
  nix << k
697
704
  bail_out = true
698
705
  break
699
706
  else # Standard has_many
707
+ # binding.pry unless (
708
+ nm = hmt_assoc.source_reflection.inverse_of&.name
709
+ # )
710
+ link_back << nm # if nm
700
711
  "ON br_t#{idx}.#{a.foreign_key} = br_t#{idx - 1}.id"
701
712
  end
702
713
  link_back.unshift(a.source_reflection.name)
@@ -704,6 +715,7 @@ module ActiveRecord
704
715
  end
705
716
  next if bail_out
706
717
 
718
+ # puts "LINK BACK! #{k} : #{hm.table_name} #{link_back.map(&:to_s).join('.')}"
707
719
  # count_column is determined from the originating HMT member
708
720
  if (src_ref = hm.source_reflection).nil?
709
721
  puts "*** Warning: Could not determine destination model for this HMT association in model #{klass.name}:\n has_many :#{hm.name}, through: :#{hm.options[:through]}"
@@ -711,8 +723,10 @@ module ActiveRecord
711
723
  nix << k
712
724
  next
713
725
  elsif src_ref.macro == :belongs_to # Traditional HMT using an associative table
726
+ # binding.pry if link_back.length > 2
714
727
  "br_t#{idx}.#{hm.foreign_key}"
715
728
  else # A HMT that goes HM -> HM, something like Categories -> Products -> LineItems
729
+ # binding.pry if link_back.length > 2
716
730
  "br_t#{idx}.#{src_ref.active_record.primary_key}"
717
731
  end
718
732
  else
@@ -776,8 +790,7 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
776
790
  if (v_parts = v.first.split('.')).length == 1
777
791
  s[v.first] = v.last
778
792
  else
779
- k1 = klass.reflect_on_association(v_parts.first)&.klass
780
- tbl_name = (field_tbl_names[v_parts.first][k1] ||= shift_or_first(chains[k1])).split('.').last
793
+ tbl_name = rel_dupe.brick_links[v_parts.first].split('.').last
781
794
  s["#{tbl_name}.#{v_parts.last}"] = v.last
782
795
  end
783
796
  end
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 91
8
+ TINY = 92
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
data/lib/brick.rb CHANGED
@@ -1052,6 +1052,40 @@ ActiveSupport.on_load(:active_record) do
1052
1052
  end
1053
1053
  end
1054
1054
 
1055
+ class ActiveRecord::Associations::JoinDependency
1056
+ if JoinBase.instance_method(:initialize).arity == 2 # Older ActiveRecord 4.x?
1057
+ def initialize(base, associations, joins)
1058
+ @alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(base.connection, joins)
1059
+ @alias_tracker.aliased_table_for(base.table_name, base.table_name) # Updates the count for base.table_name to 1
1060
+ tree = self.class.make_tree associations
1061
+
1062
+ # Provide a way to find the original relation that this tree is being used for
1063
+ # (so that we can maintain a list of links for all tables used in JOINs)
1064
+ if (relation = associations.instance_variable_get(:@relation))
1065
+ tree.instance_variable_set(:@relation, relation)
1066
+ end
1067
+
1068
+ @join_root = JoinBase.new base, build(tree, base)
1069
+ @join_root.children.each { |child| construct_tables! @join_root, child }
1070
+ end
1071
+
1072
+ else # For ActiveRecord 5.0 - 7.1
1073
+
1074
+ def initialize(base, table, associations, join_type = nil)
1075
+ tree = self.class.make_tree associations
1076
+
1077
+ # Provide a way to find the original relation that this tree is being used for
1078
+ # (so that we can maintain a list of links for all tables used in JOINs)
1079
+ if (relation = associations.instance_variable_get(:@relation))
1080
+ tree.instance_variable_set(:@relation, relation)
1081
+ end
1082
+
1083
+ @join_root = JoinBase.new(base, table, build(tree, base))
1084
+ @join_type = join_type if join_type
1085
+ end
1086
+ end
1087
+ end
1088
+
1055
1089
  # First part of arel_table_type stuff:
1056
1090
  # ------------------------------------
1057
1091
  # (more found below)
@@ -1178,7 +1212,7 @@ if ActiveRecord.version < ::Gem::Version.new('5.2')
1178
1212
 
1179
1213
  module Associations
1180
1214
  # Specific to AR 4.2 - 5.1:
1181
- if Associations.const_defined?('JoinDependency') && JoinDependency.private_instance_methods.include?(:table_aliases_for)
1215
+ if self.const_defined?('JoinDependency') && JoinDependency.private_instance_methods.include?(:table_aliases_for)
1182
1216
  class JoinDependency
1183
1217
  private
1184
1218
 
@@ -1230,4 +1264,91 @@ if ActiveRecord.version < ::Gem::Version.new('5.2')
1230
1264
  # rubocop:enable Style/CommentedKeyword
1231
1265
  end
1232
1266
 
1267
+ module ActiveRecord
1268
+ module QueryMethods
1269
+ private
1270
+
1271
+ if private_instance_methods.include?(:build_join_query)
1272
+ alias _brick_build_join_query build_join_query
1273
+ def build_join_query(manager, buckets, *args)
1274
+ # %%% Better way to bring relation into the mix
1275
+ if (aj = buckets.fetch(:association_join, nil))
1276
+ aj.instance_variable_set(:@relation, self)
1277
+ end
1278
+
1279
+ _brick_build_join_query(manager, buckets, *args)
1280
+ end
1281
+
1282
+ else
1283
+
1284
+ alias _brick_select_association_list select_association_list
1285
+ def select_association_list(associations, stashed_joins = nil)
1286
+ result = _brick_select_association_list(associations, stashed_joins)
1287
+ result.instance_variable_set(:@relation, self)
1288
+ result
1289
+ end
1290
+ end
1291
+ end
1292
+
1293
+ # require 'activerecord/associations/join_dependency'
1294
+ module Associations
1295
+ # For AR >= 4.2
1296
+ if self.const_defined?('JoinDependency')
1297
+ class JoinDependency
1298
+ private
1299
+
1300
+ # %%% Pretty much have to flat-out replace this guy (I think anyway)
1301
+ # Good with Rails 5.24 and 7 on this
1302
+ def build(associations, base_klass, root = nil, path = '')
1303
+ root ||= associations
1304
+ associations.map do |name, right|
1305
+ reflection = find_reflection base_klass, name
1306
+ reflection.check_validity!
1307
+ reflection.check_eager_loadable!
1308
+
1309
+ if reflection.polymorphic?
1310
+ raise EagerLoadPolymorphicError.new(reflection)
1311
+ end
1312
+
1313
+ # %%% The path
1314
+ link_path = path.blank? ? name.to_s : path + ".#{name}"
1315
+ ja = JoinAssociation.new(reflection, build(right, reflection.klass, root, link_path))
1316
+ ja.instance_variable_set(:@link_path, link_path) # Make note on the JoinAssociation of its AR path
1317
+ ja.instance_variable_set(:@assocs, root)
1318
+ ja
1319
+ end
1320
+ end
1321
+
1322
+ if JoinDependency.private_instance_methods.include?(:table_aliases_for)
1323
+ # No matter if it's older or newer Rails, now extend so that we can associate AR links to table_alias names
1324
+ alias _brick_table_aliases_for table_aliases_for
1325
+ def table_aliases_for(parent, node)
1326
+ result = _brick_table_aliases_for(parent, node)
1327
+
1328
+ # Capture the table alias name that was chosen
1329
+ link_path = node.instance_variable_get(:@link_path)
1330
+ if (relation = node.instance_variable_get(:@assocs)&.instance_variable_get(:@relation))
1331
+ relation.brick_links[link_path] = result.first.table_alias || result.first.table_name
1332
+ end
1333
+
1334
+ result
1335
+ end
1336
+ else # Same idea but for Rails 7
1337
+ alias _brick_make_constraints make_constraints
1338
+ def make_constraints(parent, child, join_type)
1339
+ result = _brick_make_constraints(parent, child, join_type)
1340
+
1341
+ # Capture the table alias name that was chosen
1342
+ link_path = child.instance_variable_get(:@link_path)
1343
+ relation = child.instance_variable_get(:@assocs)&.instance_variable_get(:@relation)
1344
+ # binding.pry if relation
1345
+ relation.brick_links[link_path] = result.first.left.table_alias || result.first.left.table_name
1346
+ result
1347
+ end
1348
+ end
1349
+ end
1350
+ end
1351
+ end
1352
+ end
1353
+
1233
1354
  require 'brick/extensions'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brick
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.91
4
+ version: 1.0.92
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-09 00:00:00.000000000 Z
11
+ date: 2022-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord