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 +4 -4
- data/lib/brick/compatibility.rb +1 -0
- data/lib/brick/extensions.rb +45 -32
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +122 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6bb6854dd2295476ae00996f23e801587869463aa7114d9240c0e97b1cce25b1
|
4
|
+
data.tar.gz: f174948106c6a06be049f8285fb3358813d09b9970ce484554ad3c86bc3cb8fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9384ef5db0a03fcc8fda6a3ceafc763a468f88033420d785814d657303027593c7fef64770087975d73d4041f577864c1d934629cb50083fb93d2591f719f136
|
7
|
+
data.tar.gz: e2b3e21982b9a6d132a3d9d05041759e06a3ac3ffbfdf2ea3675ee0140150f2d33b6e60cbeb0033a7f7469bba227f198a6cc6a0ef34c229ba7a1132cd1175b0d
|
data/lib/brick/compatibility.rb
CHANGED
@@ -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
|
data/lib/brick/extensions.rb
CHANGED
@@ -407,7 +407,13 @@ module ActiveRecord
|
|
407
407
|
end
|
408
408
|
|
409
409
|
class Relation
|
410
|
-
attr_reader :
|
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 =
|
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
|
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| [
|
616
|
-
|
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[
|
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 =
|
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
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/brick/version_number.rb
CHANGED
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
|
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.
|
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-
|
11
|
+
date: 2022-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|