brick 1.0.91 → 1.0.92

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