brick 1.0.100 → 1.0.102

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