brick 1.0.100 → 1.0.102

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: ba0de7f332d1d98502a4e66bc4fda9b44d4f97fe6d6a259ac7527a856c726000
4
- data.tar.gz: 45898a638f4c944cf33e98ff73a41da97686819bbdfcc8534257c013a6b28582
3
+ metadata.gz: 167e29431250efc9170ec3f174cce913584c700ce8405a0d275c63636d04cf81
4
+ data.tar.gz: 34826b000c093ab4fb8d63edd4509d8244f3d5e3712066489e00076f67ba9704
5
5
  SHA512:
6
- metadata.gz: 28079c58b020d395845c681710c88db3e7a32ae312fbdf607ab496854aab37d29f3a9dfed78a2495a011b3817aed8259260a1d0d5ecd409803d89b4eda70b87f
7
- data.tar.gz: 834499603e05412ee19c2f5db90f55df1f927eff85a41488e4f57381065580aeb4477700596988e4cc630282268f40045d1831822bde2c80b659a23e257f692a
6
+ metadata.gz: e0d8d82c98b918c2a6b61c06a636ce60e2c4eaaed32b9556b09984f0b778f624cfb887cf9d82d48042cb75057b1e3688be661c05fa9c79f90052da32d029e1b2
7
+ data.tar.gz: 53c296598e9bc85ca2afd5456ff02d3c56fde7f79cd6de9f4684c5fcef0ad1be489e6ccda37c26b81a109393de1a10fa70a08d352825e69437ffdcd8b2bb2bfe
@@ -119,30 +119,32 @@ module ActiveRecord
119
119
  end
120
120
  s << part_sym
121
121
  end
122
- if (parts = prefix + first_parts + [parts[-1]]).length > 1 && klass
123
- unless is_polymorphic
124
- s = build_array
125
- parts[0..-3].each { |v| s = s[v.to_sym] }
126
- s[parts[-2]] = nil # unless parts[-2].empty? # Using []= will "hydrate" any missing part(s) in our whole series
127
- end
128
- translations[parts[0..-2].join('.')] = klass
129
- end
130
- if klass&.column_names.exclude?(parts.last) &&
131
- (klass = (orig_class = klass).reflect_on_association(possible_dsl = parts.pop.to_sym)&.klass)
132
- if prefix.empty? # Custom columns start with an empty prefix
133
- prefix << parts.shift until parts.empty?
122
+ if first_parts
123
+ if (parts = prefix + first_parts + [parts[-1]]).length > 1 && klass
124
+ unless is_polymorphic
125
+ s = build_array
126
+ parts[0..-3].each { |v| s = s[v.to_sym] }
127
+ s[parts[-2]] = nil # unless parts[-2].empty? # Using []= will "hydrate" any missing part(s) in our whole series
128
+ end
129
+ translations[parts[0..-2].join('.')] = klass
134
130
  end
135
- # Expand this entry which refers to an association name
136
- members2, dsl2a = klass.brick_parse_dsl(build_array, prefix + [possible_dsl], translations, is_polymorphic, nil, true)
137
- members += members2
138
- dsl2 << dsl2a
139
- dsl3 << dsl2a
140
- else
141
- dsl2 << "[#{bracket_name}]"
142
- if emit_dsl
143
- dsl3 << "[#{prefix[1..-1].map { |p| "#{p.to_s}." }.join if prefix.length > 1}#{bracket_name}]"
131
+ if klass&.column_names.exclude?(parts.last) &&
132
+ (klass = (orig_class = klass).reflect_on_association(possible_dsl = parts.pop.to_sym)&.klass)
133
+ if prefix.empty? # Custom columns start with an empty prefix
134
+ prefix << parts.shift until parts.empty?
135
+ end
136
+ # Expand this entry which refers to an association name
137
+ members2, dsl2a = klass.brick_parse_dsl(build_array, prefix + [possible_dsl], translations, is_polymorphic, nil, true)
138
+ members += members2
139
+ dsl2 << dsl2a
140
+ dsl3 << dsl2a
141
+ else
142
+ dsl2 << "[#{bracket_name}]"
143
+ if emit_dsl
144
+ dsl3 << "[#{prefix[1..-1].map { |p| "#{p.to_s}." }.join if prefix.length > 1}#{bracket_name}]"
145
+ end
146
+ members << parts
144
147
  end
145
- members << parts
146
148
  end
147
149
  bracket_name = nil
148
150
  else
@@ -409,7 +411,7 @@ module ActiveRecord
409
411
  model._brick_calculate_bts_hms(translations, join_array) if is_add_bts || is_add_hms
410
412
 
411
413
  is_postgres = ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
412
- is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2'
414
+ is_mysql = ['Mysql2', 'Trilogy'].include?(ActiveRecord::Base.connection.adapter_name)
413
415
  is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer'
414
416
  is_distinct = nil
415
417
  wheres = {}
@@ -446,8 +448,13 @@ module ActiveRecord
446
448
  selects << if is_mysql
447
449
  "`#{tbl_no_schema}`.`#{col_name}`#{col_alias}"
448
450
  elsif is_postgres || is_mssql
449
- # Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
450
- cast_as_text = '::text' if is_distinct && Brick.relations[klass.table_name]&.[](:cols)&.[](col_name)&.first&.start_with?('xml')
451
+ if is_distinct # Postgres can not use DISTINCT with any columns that are XML or JSON
452
+ cast_as_text = if Brick.relations[klass.table_name]&.[](:cols)&.[](col_name)&.first == 'json'
453
+ '::jsonb' # Convert JSON to JSONB
454
+ elsif Brick.relations[klass.table_name]&.[](:cols)&.[](col_name)&.first&.start_with?('xml')
455
+ '::text' # Convert XML to text
456
+ end
457
+ end
451
458
  "\"#{tbl_no_schema}\".\"#{col_name}\"#{cast_as_text}#{col_alias}"
452
459
  elsif col.type # Could be Sqlite or Oracle
453
460
  if col_alias || !(/^[a-z0-9_]+$/ =~ col_name)
@@ -552,7 +559,7 @@ module ActiveRecord
552
559
  brick_link = rel_dupe.brick_links[sel_col.first]
553
560
  field_tbl_name = brick_link&.split('.')&.last ||
554
561
  # ... so here's a best-effort guess for what the table name might be.
555
- rel_dupe.klass.reflect_on_association(sel_col.first).klass.table_name
562
+ rel_dupe.klass.reflect_on_association(sel_col.first)&.klass&.table_name
556
563
  # If it's Oracle, quote any AREL aliases that had been applied
557
564
  field_tbl_name = "\"#{field_tbl_name}\"" if ::Brick.is_oracle && rel_dupe.brick_links.values.include?(field_tbl_name)
558
565
 
@@ -631,19 +638,26 @@ module ActiveRecord
631
638
  through_sources.map do |a|
632
639
  from_clause << "\n LEFT OUTER JOIN #{a.table_name} br_t#{idx += 1} "
633
640
  from_clause << if (src_ref = a.source_reflection).macro == :belongs_to
634
- (nm = hmt_assoc.source_reflection.inverse_of&.name)
635
- # binding.pry unless nm
641
+ nm = hmt_assoc.source_reflection.inverse_of&.name
636
642
  link_back << nm
637
643
  "ON br_t#{idx}.id = br_t#{idx - 1}.#{a.foreign_key}"
638
644
  elsif src_ref.options[:as]
639
645
  "ON br_t#{idx}.#{src_ref.type} = '#{src_ref.active_record.name}'" + # "polymorphable_type"
640
646
  " AND br_t#{idx}.#{src_ref.foreign_key} = br_t#{idx - 1}.id"
641
647
  elsif src_ref.options[:source_type]
642
- print "Skipping #{hm.name} --HMT-> #{hm.source_reflection.name} as it uses source_type which is not yet supported"
643
- nix << k
644
- bail_out = true
645
- break
646
- else # Standard has_many
648
+ if a == hm.source_reflection
649
+ print "Skipping #{hm.name} --HMT-> #{hm.source_reflection.name} as it uses source_type in a way which is not yet supported"
650
+ nix << k
651
+ bail_out = true
652
+ break
653
+ # "ON br_t#{idx}.#{a.foreign_type} = '#{src_ref.options[:source_type]}' AND " \
654
+ # "br_t#{idx}.#{a.foreign_key} = br_t#{idx - 1}.id"
655
+ else # Works for HMT through a polymorphic HO
656
+ link_back << hmt_assoc.source_reflection.inverse_of&.name # Some polymorphic "_able" thing
657
+ "ON br_t#{idx - 1}.#{a.foreign_type} = '#{src_ref.options[:source_type]}' AND " \
658
+ "br_t#{idx - 1}.#{a.foreign_key} = br_t#{idx}.id"
659
+ end
660
+ else # Standard has_many or has_one
647
661
  # binding.pry unless (
648
662
  nm = hmt_assoc.source_reflection.inverse_of&.name
649
663
  # )
@@ -706,12 +720,13 @@ module ActiveRecord
706
720
  on_clause << "#{tbl_alias}.#{poly_type} = '#{name}'"
707
721
  end
708
722
  unless from_clause
723
+ tbl_nm = hm.macro == :has_and_belongs_to_many ? hm.join_table : hm.table_name
709
724
  hm_table_name = if is_mysql
710
- "`#{hm.table_name}`"
725
+ "`#{tbl_nm}`"
711
726
  elsif is_postgres || is_mssql
712
- "\"#{(hm.table_name).gsub('.', '"."')}\""
727
+ "\"#{(tbl_nm).gsub('.', '"."')}\""
713
728
  else
714
- hm.table_name
729
+ tbl_nm
715
730
  end
716
731
  end
717
732
  group_bys = ::Brick.is_oracle || is_mssql ? hm_selects : (1..hm_selects.length).to_a
@@ -790,7 +805,7 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
790
805
  alias _brick_find_sti_class find_sti_class
791
806
  def find_sti_class(type_name)
792
807
  if ::Brick.sti_models.key?(type_name ||= name)
793
- _brick_find_sti_class(type_name)
808
+ ::Brick.sti_models[type_name].fetch(:base, nil) || _brick_find_sti_class(type_name)
794
809
  else
795
810
  # This auto-STI is more of a brute-force approach, building modules where needed
796
811
  # The more graceful alternative is the overload of ActiveSupport::Dependencies#autoload_module! found below
@@ -1390,11 +1405,11 @@ class Object
1390
1405
  # (More information on https://docs.avohq.io/2.0/controllers.html)
1391
1406
  controller_base = Avo::ResourcesController
1392
1407
  end
1393
- table_name = ActiveSupport::Inflector.underscore(plural_class_name)
1394
- singular_table_name = ActiveSupport::Inflector.singularize(table_name)
1408
+ table_name = model&.table_name || ActiveSupport::Inflector.underscore(plural_class_name)
1409
+ singular_table_name = ActiveSupport::Inflector.singularize(ActiveSupport::Inflector.underscore(plural_class_name))
1395
1410
  pk = model&._brick_primary_key(relations.fetch(table_name, nil))
1396
1411
  is_postgres = ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
1397
- is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2'
1412
+ is_mysql = ['Mysql2', 'Trilogy'].include?(ActiveRecord::Base.connection.adapter_name)
1398
1413
 
1399
1414
  code = +"class #{class_name} < #{controller_base&.name || 'ApplicationController'}\n"
1400
1415
  built_controller = Class.new(controller_base || ActionController::Base) do |new_controller_class|
@@ -1576,7 +1591,6 @@ class Object
1576
1591
  order_by, _ = model._brick_calculate_ordering(ordering, true) # Don't do the txt part
1577
1592
 
1578
1593
  ar_relation = ActiveRecord.version < Gem::Version.new('4') ? model.preload : model.all
1579
-
1580
1594
  @_brick_params = ar_relation.brick_select(params, (selects ||= []), order_by,
1581
1595
  translations = {},
1582
1596
  join_array = ::Brick::JoinArray.new)
@@ -1592,12 +1606,13 @@ class Object
1592
1606
  end
1593
1607
  end
1594
1608
  ar_select = ar_relation.respond_to?(:_select!) ? ar_relation.dup._select!(*selects, *counts) : ar_relation.select(selects + counts)
1595
- instance_variable_set("@#{table_name.pluralize}".to_sym, ar_select)
1596
- if namespace && (idx = lookup_context.prefixes.index(table_name))
1609
+ instance_variable_set("@#{table_name.split('.').last}".to_sym, ar_select)
1610
+ table_name_no_schema = singular_table_name.pluralize
1611
+ if namespace && (idx = lookup_context.prefixes.index(table_name_no_schema))
1597
1612
  lookup_context.prefixes[idx] = "#{namespace.name.underscore}/#{lookup_context.prefixes[idx]}"
1598
1613
  end
1599
1614
  @_brick_excl = session[:_brick_exclude]&.split(',')&.each_with_object([]) do |excl, s|
1600
- if (excl_parts = excl.split('.')).first == table_name
1615
+ if (excl_parts = excl.split('.')).first == table_name_no_schema
1601
1616
  s << excl_parts.last
1602
1617
  end
1603
1618
  end
@@ -1609,6 +1624,18 @@ class Object
1609
1624
  end
1610
1625
 
1611
1626
  unless is_openapi || is_avo
1627
+ # Skip showing Bullet gem optimisation messages
1628
+ if Object.const_defined?('Bullet') && Bullet.respond_to?(:enable?)
1629
+ around_action :skip_bullet
1630
+ def skip_bullet
1631
+ bullet_enabled = Bullet.enable?
1632
+ Bullet.enable = false
1633
+ yield
1634
+ ensure
1635
+ Bullet.enable = bullet_enabled
1636
+ end
1637
+ end
1638
+
1612
1639
  _, order_by_txt = model._brick_calculate_ordering(default_ordering(table_name, pk)) if pk
1613
1640
  code << " def index\n"
1614
1641
  code << " @#{table_name.pluralize} = #{model.name}#{pk&.present? ? ".order(#{order_by_txt.join(', ')})" : '.all'}\n"
@@ -1728,7 +1755,12 @@ class Object
1728
1755
  val_part.gsub('^^sl^^', '/')
1729
1756
  end
1730
1757
  end
1731
- model.find(id.is_a?(Array) && id.length == 1 ? id.first : id)
1758
+ # Support friendly_id gem
1759
+ if Object.const_defined?('FriendlyId') && model.instance_variable_get(:@friendly_id_config)
1760
+ model.friendly.find(id.is_a?(Array) && id.length == 1 ? id.first : id)
1761
+ else
1762
+ model.find(id.is_a?(Array) && id.length == 1 ? id.first : id)
1763
+ end
1732
1764
  end
1733
1765
  end
1734
1766
 
@@ -1917,7 +1949,7 @@ end.class_exec do
1917
1949
  puts "*** In the brick.rb initializer the line \"::Brick.schema_behavior = ...\" refers to schema(s) called #{possible_schemas.map { |s| "\"#{s}\"" }.join(', ')}. No mentioned schema exists. ***"
1918
1950
  end
1919
1951
  end
1920
- when 'Mysql2'
1952
+ when 'Mysql2', 'Trilogy'
1921
1953
  ::Brick.default_schema = schema = ActiveRecord::Base.connection.current_database
1922
1954
  when 'OracleEnhanced'
1923
1955
  # ActiveRecord::Base.connection.current_database will be something like "XEPDB1"
@@ -2052,7 +2084,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
2052
2084
  # end
2053
2085
  # schema = ::Brick.default_schema # Reset back for this next round of fun
2054
2086
  case ActiveRecord::Base.connection.adapter_name
2055
- when 'PostgreSQL', 'Mysql2', 'SQLServer'
2087
+ when 'PostgreSQL', 'Mysql2', 'Trilogy', 'SQLServer'
2056
2088
  sql = "SELECT kcu1.CONSTRAINT_SCHEMA, kcu1.TABLE_NAME, kcu1.COLUMN_NAME,
2057
2089
  kcu2.CONSTRAINT_SCHEMA AS primary_schema, kcu2.TABLE_NAME AS primary_table, kcu1.CONSTRAINT_NAME AS CONSTRAINT_SCHEMA_FK
2058
2090
  FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS rc
@@ -197,7 +197,7 @@ function linkSchemas() {
197
197
  class BrickTitle
198
198
  def initialize(name, view_component)
199
199
  @vc = view_component
200
- @_name = name
200
+ @_name = name || ''
201
201
  end
202
202
  def to_s
203
203
  @_name.html_safe + @vc.instance_variable_get(:@__vc_helpers)&.link_to_brick(nil,
@@ -313,10 +313,10 @@ function linkSchemas() {
313
313
  end
314
314
 
315
315
  if @_brick_model
316
- pk = @_brick_model._brick_primary_key(::Brick.relations.fetch(@_brick_model&.table_name, nil))
316
+ pk = @_brick_model._brick_primary_key(::Brick.relations.fetch((table_name = @_brick_model.table_name.split('.').last), nil))
317
317
  obj_name = model_name.split('::').last.underscore
318
318
  path_obj_name = @_brick_model._brick_index(:singular)
319
- table_name = obj_name.pluralize
319
+ table_name ||= obj_name.pluralize
320
320
  template_link = nil
321
321
  bts, hms = ::Brick.get_bts_and_hms(@_brick_model) # This gets BT and HM and also has_many :through (HMT)
322
322
  hms_columns = [] # Used for 'index'
@@ -346,13 +346,16 @@ function linkSchemas() {
346
346
  end
347
347
  case args.first
348
348
  when 'index'
349
- unless skip_klass_hms.key?(assoc_name.to_sym) || hm_assoc.options[:source]
349
+ if !skip_klass_hms.key?(assoc_name.to_sym) && (
350
+ @_brick_model._br_hm_counts.key?(assoc_name) ||
351
+ @_brick_model._br_bt_descrip.key?(assoc_name) # Will end up here if it's a has_one
352
+ )
350
353
  hm_entry = +"'#{hm_assoc.name}' => [#{assoc_name.inspect}, "
351
- hm_entry << if hm_assoc.macro == :has_many
354
+ hm_entry << if hm_assoc.macro == :has_one
355
+ 'nil'
356
+ else # :has_many or :has_and_belongs_to_many
352
357
  # Postgres column names are limited to 63 characters
353
358
  "'" + "b_r_#{assoc_name}_ct"[0..62] + "'"
354
- else # has_one
355
- 'nil'
356
359
  end
357
360
  hm_entry << ", #{path_keys(hm_assoc, hm_fk_name, pk).inspect}]"
358
361
  hms_columns << hm_entry
@@ -564,17 +567,65 @@ def hide_bcrypt(val, max_len = 200)
564
567
  '(hidden)'
565
568
  else
566
569
  if val.is_a?(String)
567
- val = \"#\{val[0...max_len]}...\" if val.length > max_len
570
+ if (val = val.dup.strip).length > max_len
571
+ if val[0] == '<' # Seems to be HTML?
572
+ cur_len = 0
573
+ cur_idx = 0
574
+ # Find which HTML tags we might be inside so we can apply ending tags to balance
575
+ element_name = nil
576
+ in_closing = nil
577
+ elements = []
578
+ val.each_char do |ch|
579
+ case ch
580
+ when '<'
581
+ element_name = +''
582
+ when '/' # First character of tag is '/'?
583
+ in_closing = true if element_name == ''
584
+ when '>'
585
+ if element_name
586
+ if in_closing
587
+ if (idx = elements.index { |tag| tag.downcase == element_name.downcase })
588
+ elements.delete_at(idx)
589
+ end
590
+ elsif (tag_name = element_name.split.first).present?
591
+ elements.unshift(tag_name)
592
+ end
593
+ element_name = nil
594
+ in_closing = nil
595
+ end
596
+ else
597
+ element_name << ch if element_name
598
+ end
599
+ cur_idx += 1
600
+ # Unless it's inside wickets then this is real text content, and see if we're at the limit
601
+ break if element_name.nil? && ((cur_len += 1) > max_len)
602
+ end
603
+ val = val[0..cur_idx]
604
+ # Somehow still in the middle of an opening tag right at the end? (Should never happen)
605
+ if !in_closing && (tag_name = element_name&.split&.first)&.present?
606
+ elements.unshift(tag_name)
607
+ val << '>'
608
+ end
609
+ elements.each do |closing_tag|
610
+ val << \"</#\{closing_tag}>\"
611
+ end
612
+ else # Not HTML, just cut it at the length
613
+ val = val[0...max_len]
614
+ end
615
+ val = \"#\{val}...\"
616
+ end
568
617
  val = val.dup.force_encoding('UTF-8') unless val.encoding.name == 'UTF-8'
618
+ val
619
+ else
620
+ val.to_s
569
621
  end
570
- val
571
622
  end
572
623
  end
573
624
  def display_value(col_type, val)
574
625
  case col_type
575
626
  when 'geometry', 'geography'
576
627
  if Object.const_defined?('RGeo')
577
- @is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2' if @is_mysql.nil?
628
+ @is_mysql = ['Mysql2', 'Trilogy'].include?(ActiveRecord::Base.connection.adapter_name) if @is_mysql.nil?
578
629
  @is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer' if @is_mssql.nil?
579
630
  val_err = nil
580
631
  if @is_mysql || @is_mssql
@@ -608,6 +659,8 @@ def display_value(col_type, val)
608
659
  else
609
660
  '(Add RGeo gem to parse geometry detail)'
610
661
  end
662
+ when :binary
663
+ display_binary(val) if val
611
664
  else
612
665
  if col_type
613
666
  hide_bcrypt(val)
@@ -616,6 +669,40 @@ def display_value(col_type, val)
616
669
  end
617
670
  end
618
671
  end
672
+
673
+ def image_signatures
674
+ @image_signatures ||= { \"\\xFF\\xD8\\xFF\\xEE\" => 'jpeg',
675
+ \"\\xFF\\xD8\\xFF\\xE0\\x00\\x10\\x4A\\x46\\x49\\x46\\x00\\x01\" => 'jpeg',
676
+ \"\\x89PNG\\r\\n\\x1A\\n\" => 'png',
677
+ '<svg' => 'svg+xml', # %%% Not yet very good detection for SVG
678
+ 'BM' => 'bmp',
679
+ 'GIF87a' => 'gif',
680
+ 'GIF89a' => 'gif' }
681
+ end
682
+ def display_binary(val)
683
+ if val[0..1] == \"\\x15\\x1C\" # One of those goofy Microsoft OLE containers?
684
+ package_header_length = val[2..3].bytes.reverse.inject(0) {|m, b| (m << 8) + b }
685
+ # This will often be just FF FF FF FF
686
+ # object_size = val[16..19].bytes.reverse.inject(0) {|m, b| (m << 8) + b }
687
+ friendly_and_class_names = val[20...package_header_length].split(\"\\0\")
688
+ object_type_name_length = val[package_header_length + 8..package_header_length+11].bytes.reverse.inject(0) {|m, b| (m << 8) + b }
689
+ friendly_and_class_names << val[package_header_length + 12...package_header_length + 12 + object_type_name_length].strip
690
+ # friendly_and_class_names will now be something like: ['Bitmap Image', 'Paint.Picture', 'PBrush']
691
+ real_object_size = val[package_header_length + 20 + object_type_name_length..package_header_length + 23 + object_type_name_length].bytes.reverse.inject(0) {|m, b| (m << 8) + b }
692
+ object_start = package_header_length + 24 + object_type_name_length
693
+ val = val[object_start...object_start + real_object_size]
694
+ end
695
+ if (signature = image_signatures.find { |k, _v| val[0...k.length] == k })
696
+ if val.length < 500_000
697
+ \"<img src=\\\"data:image/#\{signature.last};base64,#\{Base64.encode64(val)}\\\">\"
698
+ else
699
+ \"&lt;&nbsp;#\{signature.last} image, #\{val.length} bytes&nbsp;>\"
700
+ end
701
+ else
702
+ \"&lt;&nbsp;Binary, #\{val.length} bytes&nbsp;>\"
703
+ end
704
+ end
705
+
619
706
  # Accommodate composite primary keys that include strings with forward-slash characters
620
707
  def slashify(*vals)
621
708
  vals.map { |val_part| val_part.is_a?(String) ? val_part.gsub('/', '^^sl^^') : val_part }
@@ -994,8 +1081,12 @@ erDiagram
994
1081
  +"<html>
995
1082
  <head>
996
1083
  #{css}
997
- <title>#{model_name} <%
998
- if (description = (relation = Brick.relations[#{model_name}.table_name])&.fetch(:description, nil)).present?
1084
+ <title><% model = #{model_name}
1085
+ if sub_model = @_brick_params&.fetch(type_col = model.inheritance_column, nil)&.first
1086
+ model = Object.const_get(sub_model.to_sym)
1087
+ end
1088
+ %><%= model.name %><%
1089
+ if (description = (relation = Brick.relations[model.table_name])&.fetch(:description, nil)).present?
999
1090
  %> - <%= description
1000
1091
  %><% end
1001
1092
  %></title>
@@ -1005,15 +1096,15 @@ erDiagram
1005
1096
  #{schema_options}" if schema_options}
1006
1097
  <select id=\"tbl\">#{table_options}</select>
1007
1098
  <table id=\"resourceName\"><tr>
1008
- <td><h1>#{model_name}</h1></td>
1099
+ <td><h1><%= model.name %></h1></td>
1009
1100
  <td id=\"imgErd\" title=\"Show ERD\"></td>
1010
1101
  <% if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) %>
1011
1102
  <td><%= link_to_brick(
1012
1103
  avo_svg,
1013
- { index_proc: Proc.new do |model|
1014
- ::Avo.railtie_routes_url_helpers.send(\"resources_#\{model.base_class.model_name.route_key}_path\".to_sym)
1104
+ { index_proc: Proc.new do |avo_model|
1105
+ ::Avo.railtie_routes_url_helpers.send(\"resources_#\{model.model_name.route_key}_path\".to_sym)
1015
1106
  end,
1016
- title: '#{model_name} in Avo' }
1107
+ title: \"#\{model.name} in Avo\" }
1017
1108
  ) %></td>
1018
1109
  <% end %>
1019
1110
  </tr></table>#{template_link}<%
@@ -1025,13 +1116,13 @@ erDiagram
1025
1116
  <% if @_brick_params.length == 1 # %%% Does not yet work with composite keys
1026
1117
  k, id = @_brick_params.first
1027
1118
  id = id.first if id.is_a?(Array) && id.length == 1
1028
- origin = (key_parts = k.split('.')).length == 1 ? #{model_name} : #{model_name}.reflect_on_association(key_parts.first).klass
1119
+ origin = (key_parts = k.split('.')).length == 1 ? model : model.reflect_on_association(key_parts.first).klass
1029
1120
  if (destination_fk = Brick.relations[origin.table_name][:fks].values.find { |fk| fk[:fk] == key_parts.last }) &&
1030
1121
  (obj = (destination = origin.reflect_on_association(destination_fk[:assoc_name])&.klass)&.find(id)) %>
1031
1122
  <h3>for <%= link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination._brick_index(:singular)\}_path\".to_sym, id) %></h3><%
1032
1123
  end
1033
1124
  end %>
1034
- (<%= link_to 'See all #{model_name.split('::').last.pluralize}', #{@_brick_model._brick_index}_path %>)
1125
+ (<%= link_to \"See all #\{model.base_class.name.split('::').last.pluralize}\", #{@_brick_model._brick_index}_path %>)
1035
1126
  <% end
1036
1127
  # COLUMN EXCLUSIONS
1037
1128
  if @_brick_excl&.present? %>
@@ -1058,10 +1149,10 @@ erDiagram
1058
1149
  end.join(', ')}}
1059
1150
 
1060
1151
  # If the resource is missing, has the user simply created an inappropriately pluralised name for a table?
1061
- @#{table_name} ||= if dym_list = instance_variables.reject do |entry|
1062
- entry.to_s.start_with?('@_') ||
1063
- ['@cache_hit', '@marked_for_same_origin_verification', '@view_renderer', '@view_flow', '@output_buffer', '@virtual_path'].include?(entry.to_s)
1064
- end
1152
+ @#{table_name} ||= if (dym_list = instance_variables.reject do |entry|
1153
+ entry.to_s.start_with?('@_') ||
1154
+ ['@cache_hit', '@marked_for_same_origin_verification', '@view_renderer', '@view_flow', '@output_buffer', '@virtual_path'].include?(entry.to_s)
1155
+ end).present?
1065
1156
  msg = \"Can't find resource \\\"#{table_name}\\\".\"
1066
1157
  # Can't be sure otherwise of what is up, so check DidYouMean and offer a suggestion.
1067
1158
  if (dym = DidYouMean::SpellChecker.new(dictionary: dym_list).correct('@#{table_name}')).present?
@@ -1078,7 +1169,7 @@ erDiagram
1078
1169
 
1079
1170
  # Write out the mega-grid
1080
1171
  brick_grid(@#{table_name}, @_brick_bt_descrip, @_brick_sequence, @_brick_incl, @_brick_excl,
1081
- cols, poly_cols, bts, #{hms_keys.inspect}, {#{hms_columns.join(', ')}}) %>
1172
+ cols, poly_cols, bts, #{hms_keys.inspect}, {#{hms_columns.join(', ')}}) %>
1082
1173
 
1083
1174
  #{"<hr><%= link_to \"New #{obj_name}\", new_#{path_obj_name}_path %>" unless @_brick_model.is_view?}
1084
1175
  #{script}
@@ -1521,11 +1612,10 @@ document.querySelectorAll(\"input, select\").forEach(function (inp) {
1521
1612
  private
1522
1613
 
1523
1614
  alias _brick_render_template render_template
1524
- def render_template(view, template, *args)
1525
- result = _brick_render_template(view, template, *args)
1526
- if template.instance_variable_get(:@is_brick)
1527
- Apartment::Tenant.switch!(::Brick.apartment_default_tenant) if ::Brick.apartment_multitenant
1528
- end
1615
+ def render_template(view, template, layout_name, *args)
1616
+ layout_name = nil if (is_brick = template.instance_variable_get(:@is_brick)) && layout_name.is_a?(Proc)
1617
+ result = _brick_render_template(view, template, layout_name, *args)
1618
+ Apartment::Tenant.switch!(::Brick.apartment_default_tenant) if is_brick && ::Brick.apartment_multitenant
1529
1619
  result
1530
1620
  end
1531
1621
  end # TemplateRenderer
@@ -3,7 +3,7 @@ module Brick::Rails::FormTags
3
3
  def brick_grid(relation, bt_descrip, sequence = nil, inclusions, exclusions,
4
4
  cols, poly_cols, bts, hms_keys, hms_cols)
5
5
  out = "<table id=\"headerTop\"></table>
6
- <table id=\"#{relation.table_name}\" class=\"shadow\">
6
+ <table id=\"#{relation.table_name.split('.').last}\" class=\"shadow\">
7
7
  <thead><tr>"
8
8
  pk = (klass = relation.klass).primary_key || []
9
9
  pk = [pk] unless pk.is_a?(Array)
@@ -11,7 +11,7 @@ module Brick::Rails::FormTags
11
11
  out << "<th x-order=\"#{pk.join(',')}\"></th>"
12
12
  end
13
13
 
14
- col_keys ||= relation.columns.each_with_object([]) do |col, s|
14
+ col_keys = relation.columns.each_with_object([]) do |col, s|
15
15
  col_name = col.name
16
16
  next if inclusions&.exclude?(col_name) ||
17
17
  (pk.include?(col_name) && [:integer, :uuid].include?(col.type) && !bts.key?(col_name)) ||
@@ -39,8 +39,10 @@ module Brick::Rails::FormTags
39
39
  "#{' x-order="' + col_name + '"' if true}>#{col_name}"
40
40
  end
41
41
  elsif col # HM column
42
+ options = {}
43
+ options[col[1].inheritance_column] = col[1].name unless col[1] == col[1].base_class
42
44
  s << "<th#{' x-order="' + col_name + '"' if true}>#{col[2]} "
43
- s << (col.first ? "#{col[3]}" : "#{link_to(col[3], send("#{col[1]._brick_index}_path"))}")
45
+ s << (col.first ? "#{col[3]}" : "#{link_to(col[3], send("#{col[1]._brick_index}_path", options))}")
44
46
  elsif cust_cols.key?(col_name) # Custom column
45
47
  s << "<th x-order=\"#{col_name}\">#{col_name}"
46
48
  elsif col_name.is_a?(Symbol) && (hot = bts[col_name]) # has_one :through
@@ -88,6 +90,7 @@ module Brick::Rails::FormTags
88
90
  # 0..62 because Postgres column names are limited to 63 characters
89
91
  obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, bt_id_col
90
92
  )
93
+ bt_txt = display_binary(bt_txt).html_safe if bt_txt&.encoding&.name == 'ASCII-8BIT'
91
94
  bt_txt ||= "<span class=\"orphan\">&lt;&lt; Orphaned ID: #{val} >></span>" if val
92
95
  bt_id = bt_id_col&.map { |id_col| obj.respond_to?(id_sym = id_col.to_sym) ? obj.send(id_sym) : id_col }
93
96
  out << (bt_id&.first ? link_to(bt_txt, send("#{bt_class.base_class._brick_index(:singular)}_path".to_sym, bt_id)) : bt_txt || '')
@@ -96,17 +99,17 @@ module Brick::Rails::FormTags
96
99
  if hms_col.length == 1
97
100
  out << hms_col.first
98
101
  else
99
- klass = (col = cols[col_name])[1]
102
+ hm_klass = (col = cols[col_name])[1]
100
103
  if col[2] == 'HO'
101
- descrips = bt_descrip[col_name.to_sym][klass]
104
+ descrips = bt_descrip[col_name.to_sym][hm_klass]
102
105
  if (ho_id = (ho_id_col = descrips.last).map { |id_col| obj.send(id_col.to_sym) })&.first
103
- ho_txt = klass.brick_descrip(obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, ho_id_col)
104
- out << link_to(ho_txt, send("#{klass.base_class._brick_index(:singular)}_path".to_sym, ho_id))
106
+ ho_txt = hm_klass.brick_descrip(obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, ho_id_col)
107
+ out << link_to(ho_txt, send("#{hm_klass.base_class._brick_index(:singular)}_path".to_sym, ho_id))
105
108
  end
106
109
  else
107
110
  if (ct = obj.send(hms_col[1].to_sym)&.to_i)&.positive?
108
111
  out << "#{link_to("#{ct || 'View'} #{hms_col.first}",
109
- send("#{klass._brick_index}_path".to_sym,
112
+ send("#{hm_klass._brick_index}_path".to_sym,
110
113
  hms_col[2].each_with_object({}) { |v, s| s[v.first] = v.last.is_a?(String) ? v.last : obj.send(v.last) })
111
114
  )}\n"
112
115
  end
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 100
8
+ TINY = 102
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
@@ -26,7 +26,7 @@ end
26
26
  require 'brick/util'
27
27
 
28
28
  # Allow ActiveRecord < 3.2 to work with Ruby 2.7 and later
29
- if (ruby_version = ::Gem::Version.new(RUBY_VERSION)) >= ::Gem::Version.new('2.7')
29
+ if (is_ruby_2_7 = (ruby_version = ::Gem::Version.new(RUBY_VERSION)) >= ::Gem::Version.new('2.7'))
30
30
  if ActiveRecord.version < ::Gem::Version.new('3.2')
31
31
  # Remove circular reference for "now"
32
32
  ::Brick::Util._patch_require(
@@ -640,50 +640,62 @@ In config/initializers/brick.rb appropriate entries would look something like:
640
640
  table_class_length = 38 # Length of "Classes that can be built from tables:"
641
641
  view_class_length = 37 # Length of "Classes that can be built from views:"
642
642
 
643
- brick_routes_create = lambda do |schema_name, controller_name, v, options|
643
+ brick_routes_create = lambda do |schema_name, res_name, options|
644
644
  if schema_name # && !Object.const_defined('Apartment')
645
645
  send(:namespace, schema_name) do
646
- send(:resources, v[:resource].to_sym, **options)
646
+ send(:resources, res_name.to_sym, **options)
647
647
  end
648
648
  else
649
- send(:resources, v[:resource].to_sym, **options)
649
+ send(:resources, res_name.to_sym, **options)
650
650
  end
651
651
  end
652
652
 
653
653
  # %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
654
654
  # If auto-controllers and auto-models are both enabled then this makes sense:
655
655
  controller_prefix = (path_prefix ? "#{path_prefix}/" : '')
656
+ sti_subclasses = ::Brick.config.sti_namespace_prefixes.each_with_object(Hash.new { |h, k| h[k] = [] }) do |v, s|
657
+ # Turn something like {"::Spouse"=>"Person", "::Friend"=>"Person"} into {"Person"=>["Spouse", "Friend"]}
658
+ s[v.last] << v.first[2..-1] unless v.first.end_with?('::')
659
+ end
656
660
  ::Brick.relations.each do |k, v|
657
- unless !(controller_name = v.fetch(:resource, nil)&.pluralize) || existing_controllers.key?(controller_name)
658
- options = {}
659
- options[:only] = [:index, :show] if v.key?(:isView)
660
- # First do the API routes
661
- full_resource = nil
662
- if (schema_name = v.fetch(:schema, nil))
663
- full_resource = "#{schema_name}/#{v[:resource]}"
664
- send(:get, "#{::Brick.api_root}#{full_resource}", { to: "#{controller_prefix}#{schema_name}/#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
661
+ next if !(controller_name = v.fetch(:resource, nil)&.pluralize) || existing_controllers.key?(controller_name)
662
+
663
+ options = {}
664
+ options[:only] = [:index, :show] if v.key?(:isView)
665
+ # First do the API routes
666
+ full_resource = nil
667
+ if (schema_name = v.fetch(:schema, nil))
668
+ full_resource = "#{schema_name}/#{v[:resource]}"
669
+ send(:get, "#{::Brick.api_root}#{full_resource}", { to: "#{controller_prefix}#{schema_name}/#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
670
+ else
671
+ # Normally goes to something like: /api/v1/employees
672
+ send(:get, "#{::Brick.api_root}#{v[:resource]}", { to: "#{controller_prefix}#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
673
+ end
674
+
675
+ # Track routes being built
676
+ if (class_name = v.fetch(:class_name, nil))
677
+ if v.key?(:isView)
678
+ view_class_length = class_name.length if class_name.length > view_class_length
679
+ views
665
680
  else
666
- # Normally goes to something like: /api/v1/employees
667
- send(:get, "#{::Brick.api_root}#{v[:resource]}", { to: "#{controller_prefix}#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
668
- end
669
- # Now the normal routes
670
- if path_prefix
671
- # Was: send(:scope, path: path_prefix) do
672
- send(:namespace, path_prefix) do
673
- brick_routes_create.call(schema_name, controller_name, v, options)
681
+ table_class_length = class_name.length if class_name.length > table_class_length
682
+ tables
683
+ end << [class_name, full_resource || v[:resource]]
684
+ end
685
+
686
+ # Now the normal routes
687
+ if path_prefix
688
+ # Was: send(:scope, path: path_prefix) do
689
+ send(:namespace, path_prefix) do
690
+ brick_routes_create.call(schema_name, v[:resource], options)
691
+ sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
692
+ brick_routes_create.call(schema_name, sc.underscore.tr('/', '_').pluralize, options)
674
693
  end
675
- else
676
- brick_routes_create.call(schema_name, controller_name, v, options)
677
694
  end
678
-
679
- if (class_name = v.fetch(:class_name, nil))
680
- if v.key?(:isView)
681
- view_class_length = class_name.length if class_name.length > view_class_length
682
- views
683
- else
684
- table_class_length = class_name.length if class_name.length > table_class_length
685
- tables
686
- end << [class_name, full_resource || v[:resource]]
695
+ else
696
+ brick_routes_create.call(schema_name, v[:resource], options)
697
+ sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
698
+ brick_routes_create.call(schema_name, sc.underscore.tr('/', '_').pluralize, options)
687
699
  end
688
700
  end
689
701
  end
@@ -1031,6 +1043,18 @@ ActiveSupport.on_load(:active_record) do
1031
1043
  end
1032
1044
  # rubocop:enable Lint/ConstantDefinitionInBlock
1033
1045
 
1046
+ arsc = ::ActiveRecord::StatementCache
1047
+ if is_ruby_2_7 && (params = arsc.method(:create).parameters).length == 2 && params.last == [:opt, :block]
1048
+ arsc.class_exec do
1049
+ def self.create(connection, callable = nil, &block)
1050
+ relation = (callable || block).call ::ActiveRecord::StatementCache::Params.new
1051
+ bind_map = ::ActiveRecord::StatementCache::BindMap.new relation.bound_attributes
1052
+ query_builder = connection.cacheable_query(self, relation.arel)
1053
+ new query_builder, bind_map
1054
+ end
1055
+ end
1056
+ end
1057
+
1034
1058
  # Rails < 4.2 is not innately compatible with Ruby 2.4 and later, and comes up with:
1035
1059
  # "TypeError: Cannot visit Integer" unless we patch like this:
1036
1060
  if ruby_version >= ::Gem::Version.new('2.4') &&
@@ -1069,11 +1093,54 @@ ActiveSupport.on_load(:active_record) do
1069
1093
  end
1070
1094
  end
1071
1095
 
1096
+ if Psych.respond_to?(:unsafe_load) && ActiveRecord.version < ::Gem::Version.new('6.1')
1097
+ Psych.class_exec do
1098
+ class << self
1099
+ alias _original_load load
1100
+ def load(yaml, *args, **kwargs)
1101
+ if caller.first.end_with?("`database_configuration'") && kwargs[:aliases].nil?
1102
+ kwargs[:aliases] = true
1103
+ end
1104
+ _original_load(yaml, *args, **kwargs)
1105
+ end
1106
+ end
1107
+ end
1108
+ end
1109
+
1110
+ # def aliased_table_for(arel_table, table_name = nil)
1111
+ # table_name ||= arel_table.name
1112
+
1113
+ # if aliases[table_name] == 0
1114
+ # # If it's zero, we can have our table_name
1115
+ # aliases[table_name] = 1
1116
+ # arel_table = arel_table.alias(table_name) if arel_table.name != table_name
1117
+ # else
1118
+ # # Otherwise, we need to use an alias
1119
+ # aliased_name = @connection.table_alias_for(yield)
1120
+
1121
+ # # Update the count
1122
+ # count = aliases[aliased_name] += 1
1123
+
1124
+ # aliased_name = "#{truncate(aliased_name)}_#{count}" if count > 1
1125
+
1126
+ # arel_table = arel_table.alias(aliased_name)
1127
+ # end
1128
+
1129
+ # arel_table
1130
+ # end
1131
+ # def aliased_table_for(table_name, aliased_name, type_caster)
1132
+
1072
1133
  class ActiveRecord::Associations::JoinDependency
1073
- if JoinBase.instance_method(:initialize).arity == 2 # Older ActiveRecord 4.x?
1074
- def initialize(base, associations, joins)
1075
- @alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(base.connection, joins)
1076
- @alias_tracker.aliased_table_for(base.table_name, base.table_name) # Updates the count for base.table_name to 1
1134
+ if JoinBase.instance_method(:initialize).arity == 2 # Older ActiveRecord <= 5.1?
1135
+ def initialize(base, associations, joins, eager_loading: true)
1136
+ araat = ::ActiveRecord::Associations::AliasTracker
1137
+ if araat.respond_to?(:create_with_joins) # Rails 5.0 and 5.1
1138
+ @alias_tracker = araat.create_with_joins(base.connection, base.table_name, joins)
1139
+ @eager_loading = eager_loading # (Unused in Rails 5.0)
1140
+ else # Rails 4.2
1141
+ @alias_tracker = araat.create(base.connection, joins)
1142
+ @alias_tracker.aliased_table_for(base, base.table_name) # Updates the count for base.table_name to 1
1143
+ end
1077
1144
  tree = self.class.make_tree associations
1078
1145
 
1079
1146
  # Provide a way to find the original relation that this tree is being used for
@@ -1086,7 +1153,7 @@ ActiveSupport.on_load(:active_record) do
1086
1153
  @join_root.children.each { |child| construct_tables! @join_root, child }
1087
1154
  end
1088
1155
 
1089
- else # For ActiveRecord 5.0 - 7.1
1156
+ else # For ActiveRecord 5.2 - 7.1
1090
1157
 
1091
1158
  def initialize(base, table, associations, join_type = nil)
1092
1159
  tree = self.class.make_tree associations
@@ -1233,13 +1300,13 @@ module ActiveRecord
1233
1300
 
1234
1301
  if private_instance_methods.include?(:build_join_query)
1235
1302
  alias _brick_build_join_query build_join_query
1236
- def build_join_query(manager, buckets, *args)
1303
+ def build_join_query(manager, buckets, *args) # , **kwargs)
1237
1304
  # %%% Better way to bring relation into the mix
1238
1305
  if (aj = buckets.fetch(:association_join, nil))
1239
1306
  aj.instance_variable_set(:@relation, self)
1240
1307
  end
1241
1308
 
1242
- _brick_build_join_query(manager, buckets, *args)
1309
+ _brick_build_join_query(manager, buckets, *args) # , **kwargs)
1243
1310
  end
1244
1311
 
1245
1312
  else
@@ -1322,24 +1389,26 @@ module ActiveRecord
1322
1389
  private
1323
1390
 
1324
1391
  # %%% Pretty much have to flat-out replace this guy (I think anyway)
1325
- # Good with Rails 5.24 and 7 on this
1326
- def build(associations, base_klass, root = nil, path = '')
1327
- root ||= associations
1328
- associations.map do |name, right|
1329
- reflection = find_reflection base_klass, name
1330
- reflection.check_validity!
1331
- reflection.check_eager_loadable!
1332
-
1333
- if reflection.polymorphic?
1334
- raise EagerLoadPolymorphicError.new(reflection)
1335
- end
1392
+ # Good with Rails 5.24 through 7 on this
1393
+ # Ransack gem includes Polyamorous which replaces #build in a different way (which we handle below)
1394
+ unless Gem::Specification.all_names.any? { |g| g.start_with?('ransack-') }
1395
+ def build(associations, base_klass, root = nil, path = '')
1396
+ root ||= associations
1397
+ associations.map do |name, right|
1398
+ reflection = find_reflection base_klass, name
1399
+ reflection.check_validity!
1400
+ reflection.check_eager_loadable!
1401
+
1402
+ if reflection.polymorphic?
1403
+ raise EagerLoadPolymorphicError.new(reflection)
1404
+ end
1336
1405
 
1337
- # %%% The path
1338
- link_path = path.blank? ? name.to_s : path + ".#{name}"
1339
- ja = JoinAssociation.new(reflection, build(right, reflection.klass, root, link_path))
1340
- ja.instance_variable_set(:@link_path, link_path) # Make note on the JoinAssociation of its AR path
1341
- ja.instance_variable_set(:@assocs, root)
1342
- ja
1406
+ link_path = path.blank? ? name.to_s : path + ".#{name}"
1407
+ ja = JoinAssociation.new(reflection, build(right, reflection.klass, root, link_path))
1408
+ ja.instance_variable_set(:@link_path, link_path) # Make note on the JoinAssociation of its AR path
1409
+ ja.instance_variable_set(:@assocs, root)
1410
+ ja
1411
+ end
1343
1412
  end
1344
1413
  end
1345
1414
 
@@ -1348,10 +1417,9 @@ module ActiveRecord
1348
1417
  alias _brick_table_aliases_for table_aliases_for
1349
1418
  def table_aliases_for(parent, node)
1350
1419
  result = _brick_table_aliases_for(parent, node)
1351
-
1352
1420
  # Capture the table alias name that was chosen
1353
- link_path = node.instance_variable_get(:@link_path)
1354
1421
  if (relation = node.instance_variable_get(:@assocs)&.instance_variable_get(:@relation))
1422
+ link_path = node.instance_variable_get(:@link_path)
1355
1423
  relation.brick_links[link_path] = result.first.table_alias || result.first.table_name
1356
1424
  end
1357
1425
  result
@@ -1360,11 +1428,14 @@ module ActiveRecord
1360
1428
  alias _brick_make_constraints make_constraints
1361
1429
  def make_constraints(parent, child, join_type)
1362
1430
  result = _brick_make_constraints(parent, child, join_type)
1363
-
1364
1431
  # Capture the table alias name that was chosen
1365
- link_path = child.instance_variable_get(:@link_path)
1366
1432
  if (relation = child.instance_variable_get(:@assocs)&.instance_variable_get(:@relation))
1367
- relation.brick_links[link_path] = result.first.left.table_alias || result.first.left.table_name
1433
+ link_path = child.instance_variable_get(:@link_path)
1434
+ relation.brick_links[link_path] = if child.table.is_a?(Arel::Nodes::TableAlias)
1435
+ child.table.right
1436
+ else
1437
+ result.first&.left&.table_alias || child.table_name
1438
+ end
1368
1439
  end
1369
1440
  result
1370
1441
  end
@@ -1374,4 +1445,128 @@ module ActiveRecord
1374
1445
  end
1375
1446
  end
1376
1447
 
1448
+ # Now the Ransack Polyamorous version of #build
1449
+ if Gem::Specification.all_names.any? { |g| g.start_with?('ransack-') }
1450
+ require "polyamorous/activerecord_#{::ActiveRecord::VERSION::STRING[0, 3]}_ruby_2/join_dependency"
1451
+ module Polyamorous::JoinDependencyExtensions
1452
+ def build(associations, base_klass, root = nil, path = '')
1453
+ root ||= associations
1454
+ puts associations.map(&:first)
1455
+
1456
+ associations.map do |name, right|
1457
+ link_path = path.blank? ? name.to_s : path + ".#{name}"
1458
+ ja = if name.is_a? ::Polyamorous::Join
1459
+ reflection = find_reflection base_klass, name.name
1460
+ reflection.check_validity!
1461
+ reflection.check_eager_loadable!
1462
+
1463
+ klass = if reflection.polymorphic?
1464
+ name.klass || base_klass
1465
+ else
1466
+ reflection.klass
1467
+ end
1468
+ ::ActiveRecord::Associations::JoinDependency::JoinAssociation.new(
1469
+ reflection, build(right, klass, root, link_path), name.klass, name.type
1470
+ )
1471
+ else
1472
+ reflection = find_reflection base_klass, name
1473
+ reflection.check_validity!
1474
+ reflection.check_eager_loadable!
1475
+
1476
+ if reflection.polymorphic?
1477
+ raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
1478
+ end
1479
+ ::ActiveRecord::Associations::JoinDependency::JoinAssociation.new(
1480
+ reflection, build(right, reflection.klass, root, link_path)
1481
+ )
1482
+ end
1483
+ ja.instance_variable_set(:@link_path, link_path) # Make note on the JoinAssociation of its AR path
1484
+ ja.instance_variable_set(:@assocs, root)
1485
+ ja
1486
+ end
1487
+ end
1488
+ end
1489
+ end
1490
+
1491
+ # Keyword arguments updates for Rails <= 5.2.x and Ruby >= 3.0
1492
+ if ActiveRecord.version < ::Gem::Version.new('6.0') && ruby_version >= ::Gem::Version.new('3.0')
1493
+ admsm = ActionDispatch::MiddlewareStack::Middleware
1494
+ admsm.class_exec do
1495
+ # redefine #build
1496
+ def build(app, **kwargs)
1497
+ # puts klass.name
1498
+ if args.length > 1 && args.last.is_a?(Hash)
1499
+ kwargs.merge!(args.pop)
1500
+ end
1501
+ # binding.pry if klass == ActionDispatch::Static # ActionDispatch::Reloader
1502
+ klass.new(app, *args, **kwargs, &block)
1503
+ end
1504
+ end
1505
+
1506
+ require 'active_model'
1507
+ require 'active_model/type'
1508
+ require 'active_model/type/value'
1509
+ class ActiveModel::Type::Value
1510
+ def initialize(*args, precision: nil, limit: nil, scale: nil)
1511
+ @precision = precision
1512
+ @scale = scale
1513
+ @limit = limit
1514
+ end
1515
+ end
1516
+
1517
+ if Object.const_defined?('I18n')
1518
+ module I18n::Base
1519
+ alias _brick_translate translate
1520
+ def translate(key = nil, *args, throw: false, raise: false, locale: nil, **options)
1521
+ options.merge!(args.pop) if args.length > 0 && args.last.is_a?(Hash)
1522
+ _brick_translate(key = nil, throw: false, raise: false, locale: nil, **options)
1523
+ end
1524
+ end
1525
+ end
1526
+
1527
+ module ActionController::RequestForgeryProtection
1528
+ private
1529
+
1530
+ # Creates the authenticity token for the current request.
1531
+ def form_authenticity_token(*args, form_options: {}) # :doc:
1532
+ form_options.merge!(args.pop) if args.length > 0 && args.last.is_a?(Hash)
1533
+ masked_authenticity_token(session, form_options: form_options)
1534
+ end
1535
+ end
1536
+
1537
+ module ActiveSupport
1538
+ class MessageEncryptor
1539
+ def encrypt_and_sign(value, *args, expires_at: nil, expires_in: nil, purpose: nil)
1540
+ encrypted = if method(:_encrypt).arity == 1
1541
+ _encrypt(value) # Rails <= 5.1
1542
+ else
1543
+ if args.length > 0 && args.last.is_a?(Hash)
1544
+ expires_at ||= args.last[:expires_at]
1545
+ expires_in ||= args.last[:expires_in]
1546
+ purpose ||= args.last[:purpose]
1547
+ end
1548
+ _encrypt(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose)
1549
+ end
1550
+ verifier.generate(encrypted)
1551
+ end
1552
+ end
1553
+ if const_defined?('Messages')
1554
+ class Messages::Metadata
1555
+ def self.wrap(message, *args, expires_at: nil, expires_in: nil, purpose: nil)
1556
+ if args.length > 0 && args.last.is_a?(Hash)
1557
+ expires_at ||= args.last[:expires_at]
1558
+ expires_in ||= args.last[:expires_in]
1559
+ purpose ||= args.last[:purpose]
1560
+ end
1561
+ if expires_at || expires_in || purpose
1562
+ JSON.encode new(encode(message), pick_expiry(expires_at, expires_in), purpose)
1563
+ else
1564
+ message
1565
+ end
1566
+ end
1567
+ end
1568
+ end
1569
+ end
1570
+ end
1571
+
1377
1572
  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.100
4
+ version: 1.0.102
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-12-09 00:00:00.000000000 Z
11
+ date: 2023-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
166
  version: 1.42.0
167
+ - !ruby/object:Gem::Dependency
168
+ name: mysql2
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '0.5'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '0.5'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: pg
169
183
  requirement: !ruby/object:Gem::Requirement