brick 1.0.101 → 1.0.103

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: eba38d653370b0cc3e98f2adc82d8c475854440745bd35caab9a9a4253fc152c
4
- data.tar.gz: 18ca517b90266cf5db711040a7f40c8e848600135ef0a39ad91da9cd2a0b1c89
3
+ metadata.gz: 74743982216a9827513b4aaa68fe0887c9ba3e7b78c45ff52f0a2f2b99dc7b88
4
+ data.tar.gz: 3857a95e891e7561d285dd3ecbdeb6c4c16cc9a8205e7b733b55867ed7a69f64
5
5
  SHA512:
6
- metadata.gz: 859f48999fb0e8d1010a701e29c9f0275f716b20f54b4348576f4dbff7f68da1eb83709a51a1a7acd7debaf4dc1f82e863048ef16cda6b5f59862246e0c50787
7
- data.tar.gz: d93c3db79715f62705c0b8f2f5bd2640a2756e1fbcb6aa6a7cf017a18bc31fa5e98496ea41539611c149f84870f24596c105d146484519b7f040d2f36b25ce45
6
+ metadata.gz: 8b71aa5b1d4da8c4aa1ce88af6d0767c12bdf72b2457594575795d4b9b3f36d1bfb2adef82413a3bc80e041813e7bc6c05768016c5e4cfc197e3bbb730113934
7
+ data.tar.gz: 6399e040cb4b54eac4d7a09b907cfba11037a4b3391dd3a86b4845e79bd0c4d4e169586c095fdaaaa8506f346628072c9c4c9b6d2ed60c3306a58866e6f9d030
@@ -77,14 +77,13 @@ module ActiveRecord
77
77
  def self.brick_get_dsl
78
78
  # If there's no DSL yet specified, just try to find the first usable column on this model
79
79
  unless (dsl = ::Brick.config.model_descrips[name])
80
- descrip_col = (columns.map(&:name) - _brick_get_fks -
81
- (::Brick.config.metadata_columns || []) -
82
- [primary_key]).first
83
- dsl = ::Brick.config.model_descrips[name] = if descrip_col
84
- "[#{descrip_col}]"
85
- elsif (pk_parts = self.primary_key.is_a?(Array) ? self.primary_key : [self.primary_key])
86
- "#{name} ##{pk_parts.map { |pk_part| "[#{pk_part}]" }.join(', ')}"
87
- end
80
+ skip_columns = _brick_get_fks + (::Brick.config.metadata_columns || []) + [primary_key]
81
+ dsl = if (descrip_col = columns.find { |c| [:boolean, :binary, :xml].exclude?(c.type) && skip_columns.exclude?(c.name) })
82
+ "[#{descrip_col.name}]"
83
+ elsif (pk_parts = self.primary_key.is_a?(Array) ? self.primary_key : [self.primary_key])
84
+ "#{name} ##{pk_parts.map { |pk_part| "[#{pk_part}]" }.join(', ')}"
85
+ end
86
+ ::Brick.config.model_descrips[name] = dsl
88
87
  end
89
88
  dsl
90
89
  end
@@ -119,30 +118,32 @@ module ActiveRecord
119
118
  end
120
119
  s << part_sym
121
120
  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?
121
+ if first_parts
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
134
129
  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}]"
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?
134
+ 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}]"
144
+ end
145
+ members << parts
144
146
  end
145
- members << parts
146
147
  end
147
148
  bracket_name = nil
148
149
  else
@@ -409,7 +410,7 @@ module ActiveRecord
409
410
  model._brick_calculate_bts_hms(translations, join_array) if is_add_bts || is_add_hms
410
411
 
411
412
  is_postgres = ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
412
- is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2'
413
+ is_mysql = ['Mysql2', 'Trilogy'].include?(ActiveRecord::Base.connection.adapter_name)
413
414
  is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer'
414
415
  is_distinct = nil
415
416
  wheres = {}
@@ -557,7 +558,7 @@ module ActiveRecord
557
558
  brick_link = rel_dupe.brick_links[sel_col.first]
558
559
  field_tbl_name = brick_link&.split('.')&.last ||
559
560
  # ... so here's a best-effort guess for what the table name might be.
560
- rel_dupe.klass.reflect_on_association(sel_col.first).klass.table_name
561
+ rel_dupe.klass.reflect_on_association(sel_col.first)&.klass&.table_name
561
562
  # If it's Oracle, quote any AREL aliases that had been applied
562
563
  field_tbl_name = "\"#{field_tbl_name}\"" if ::Brick.is_oracle && rel_dupe.brick_links.values.include?(field_tbl_name)
563
564
 
@@ -803,6 +804,7 @@ JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', '
803
804
  alias _brick_find_sti_class find_sti_class
804
805
  def find_sti_class(type_name)
805
806
  if ::Brick.sti_models.key?(type_name ||= name)
807
+ # Used to be: ::Brick.sti_models[type_name].fetch(:base, nil) || _brick_find_sti_class(type_name)
806
808
  _brick_find_sti_class(type_name)
807
809
  else
808
810
  # This auto-STI is more of a brute-force approach, building modules where needed
@@ -1407,7 +1409,7 @@ class Object
1407
1409
  singular_table_name = ActiveSupport::Inflector.singularize(ActiveSupport::Inflector.underscore(plural_class_name))
1408
1410
  pk = model&._brick_primary_key(relations.fetch(table_name, nil))
1409
1411
  is_postgres = ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
1410
- is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2'
1412
+ is_mysql = ['Mysql2', 'Trilogy'].include?(ActiveRecord::Base.connection.adapter_name)
1411
1413
 
1412
1414
  code = +"class #{class_name} < #{controller_base&.name || 'ApplicationController'}\n"
1413
1415
  built_controller = Class.new(controller_base || ActionController::Base) do |new_controller_class|
@@ -1622,6 +1624,18 @@ class Object
1622
1624
  end
1623
1625
 
1624
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
+
1625
1639
  _, order_by_txt = model._brick_calculate_ordering(default_ordering(table_name, pk)) if pk
1626
1640
  code << " def index\n"
1627
1641
  code << " @#{table_name.pluralize} = #{model.name}#{pk&.present? ? ".order(#{order_by_txt.join(', ')})" : '.all'}\n"
@@ -1935,7 +1949,7 @@ end.class_exec do
1935
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. ***"
1936
1950
  end
1937
1951
  end
1938
- when 'Mysql2'
1952
+ when 'Mysql2', 'Trilogy'
1939
1953
  ::Brick.default_schema = schema = ActiveRecord::Base.connection.current_database
1940
1954
  when 'OracleEnhanced'
1941
1955
  # ActiveRecord::Base.connection.current_database will be something like "XEPDB1"
@@ -2070,7 +2084,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
2070
2084
  # end
2071
2085
  # schema = ::Brick.default_schema # Reset back for this next round of fun
2072
2086
  case ActiveRecord::Base.connection.adapter_name
2073
- when 'PostgreSQL', 'Mysql2', 'SQLServer'
2087
+ when 'PostgreSQL', 'Mysql2', 'Trilogy', 'SQLServer'
2074
2088
  sql = "SELECT kcu1.CONSTRAINT_SCHEMA, kcu1.TABLE_NAME, kcu1.COLUMN_NAME,
2075
2089
  kcu2.CONSTRAINT_SCHEMA AS primary_schema, kcu2.TABLE_NAME AS primary_table, kcu1.CONSTRAINT_NAME AS CONSTRAINT_SCHEMA_FK
2076
2090
  FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS rc
@@ -29,6 +29,54 @@ module Brick
29
29
  var finalParams = Object.keys(params).reduce(function (s, v) { if (params[v]) s.push(v + \"=\" + params[v]); return s; }, []).join(\"&\");
30
30
  return hrefParts[0] + (finalParams.length > 0 ? \"?\" + finalParams : \"\");
31
31
  }
32
+
33
+ // This PageTransitionEvent fires when the page first loads, as well as after any other history
34
+ // transition such as when using the browser's Back and Forward buttons.
35
+ window.addEventListener(\"pageshow\", linkSchemas);
36
+ var brickSchema;
37
+ function linkSchemas() {
38
+ var schemaSelect = document.getElementById(\"schema\");
39
+ var tblSelect = document.getElementById(\"tbl\");
40
+ if (tblSelect) { // Always present
41
+ // Used to be: var i = # {::Brick.config.path_prefix ? '0' : 'schemaSelect ? 1 : 0'},
42
+ var changeoutList = changeout(location.href);
43
+ for (var i = 0; i < changeoutList.length; ++i) {
44
+ tblSelect.value = changeoutList[i];
45
+ if (tblSelect.value !== \"\") break;
46
+ }
47
+
48
+ tblSelect.addEventListener(\"change\", function () {
49
+ var lhr = changeout(location.href, null, this.value);
50
+ if (brickSchema) lhr = changeout(lhr, \"_brick_schema\", schemaSelect.value);
51
+ location.href = lhr;
52
+ });
53
+ }
54
+
55
+ if (schemaSelect) { // First drop-down is only present if multitenant
56
+ if (brickSchema = changeout(location.href, \"_brick_schema\")) {
57
+ [... document.getElementsByTagName(\"A\")].forEach(function (a) { a.href = changeout(a.href, \"_brick_schema\", brickSchema); });
58
+ }
59
+ if (schemaSelect.options.length > 1) {
60
+ schemaSelect.value = brickSchema || \"public\";
61
+ schemaSelect.addEventListener(\"change\", function () {
62
+ // If there's an ID then remove it (trim after selected table)
63
+ location.href = changeout(location.href, \"_brick_schema\", this.value, tblSelect.value);
64
+ });
65
+ }
66
+ }
67
+ tblSelect.focus();
68
+
69
+ [... document.getElementsByTagName(\"FORM\")].forEach(function (form) {
70
+ if (brickSchema)
71
+ form.action = changeout(form.action, \"_brick_schema\", brickSchema);
72
+ form.addEventListener('submit', function (ev) {
73
+ [... ev.target.getElementsByTagName(\"SELECT\")].forEach(function (select) {
74
+ if (select.value === \"^^^brick_NULL^^^\") select.value = null;
75
+ });
76
+ return true;
77
+ });
78
+ });
79
+ };
32
80
  "
33
81
 
34
82
  # paths['app/models'] << 'lib/brick/frameworks/active_record/models'
@@ -168,17 +216,9 @@ module Brick
168
216
  end
169
217
  "<script>
170
218
  #{JS_CHANGEOUT}
171
- window.addEventListener(\"load\", linkSchemas);
172
219
  document.addEventListener(\"turbo:render\", linkSchemas);
173
220
  window.addEventListener(\"popstate\", linkSchemas);
174
221
  // [... document.getElementsByTagName('turbo-frame')].forEach(function (a) { a.addEventListener(\"turbo:frame-render\", linkSchemas); });
175
- function linkSchemas() {
176
- brickSchema = changeout(location.href, \"_brick_schema\");
177
- if (brickSchema) {
178
- [... document.getElementsByTagName(\"A\")].forEach(function (a) { a.href = changeout(a.href, \"_brick_schema\", brickSchema); });
179
- [... document.getElementsByTagName(\"FORM\")].forEach(function (form) { form.action = changeout(form.action, \"_brick_schema\", brickSchema); });
180
- }
181
- }
182
222
  </script>
183
223
  #{_brick_content}".html_safe
184
224
  end
@@ -288,7 +328,14 @@ function linkSchemas() {
288
328
  else
289
329
  [[fk_name, pk.length == 1 ? pk.first : pk.inspect]]
290
330
  end
291
- keys << [hm_assoc.inverse_of.foreign_type, hm_assoc.active_record.name] if hm_assoc.options.key?(:as)
331
+ if hm_assoc.options.key?(:as)
332
+ poly_type = if hm_assoc.active_record.column_names.include?(hm_assoc.active_record.inheritance_column)
333
+ '[sti_type]'
334
+ else
335
+ hm_assoc.active_record.name
336
+ end
337
+ keys << [hm_assoc.inverse_of.foreign_type, poly_type]
338
+ end
292
339
  keys.to_h
293
340
  end
294
341
 
@@ -362,9 +409,14 @@ function linkSchemas() {
362
409
  end
363
410
  when 'show', 'new', 'update'
364
411
  hm_stuff << if hm_fk_name
365
- if hm_assoc.klass.column_names.include?(hm_fk_name)
412
+ if hm_assoc.klass.column_names.include?(hm_fk_name) ||
413
+ (hm_fk_name.is_a?(String) && hm_fk_name.include?('.')) # HMT? (Could do a better check for this)
366
414
  predicates = path_keys(hm_assoc, hm_fk_name, pk).map do |k, v|
367
- v.is_a?(String) ? "#{k}: '#{v}'" : "#{k}: @#{obj_name}.#{v}"
415
+ if v == '[sti_type]'
416
+ "'#{k}': @#{obj_name}.#{hm_assoc.active_record.inheritance_column}"
417
+ else
418
+ v.is_a?(String) ? "'#{k}': '#{v}'" : "'#{k}': @#{obj_name}.#{v}"
419
+ end
368
420
  end.join(', ')
369
421
  "<%= link_to '#{assoc_name}', #{hm_assoc.klass._brick_index}_path({ #{predicates} }) %>\n"
370
422
  else
@@ -622,13 +674,24 @@ def hide_bcrypt(val, max_len = 200)
622
674
  end
623
675
  end
624
676
  def display_value(col_type, val)
677
+ is_mssql_geography = nil
678
+ # Some binary thing that really looks like a Microsoft-encoded WGS84 point? (With the first two bytes, E6 10, indicating an EPSG code of 4326)
679
+ if col_type == :binary && val && val.length < 31 && (val.length - 6) % 8 == 0 && val[0..5].bytes == [230, 16, 0, 0, 1, 12]
680
+ col_type = 'geography'
681
+ is_mssql_geography = true
682
+ end
625
683
  case col_type
626
684
  when 'geometry', 'geography'
627
685
  if Object.const_defined?('RGeo')
628
- @is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2' if @is_mysql.nil?
686
+ @is_mysql = ['Mysql2', 'Trilogy'].include?(ActiveRecord::Base.connection.adapter_name) if @is_mysql.nil?
629
687
  @is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer' if @is_mssql.nil?
630
688
  val_err = nil
631
- if @is_mysql || @is_mssql
689
+
690
+ if @is_mysql || (is_mssql_geography ||=
691
+ (@is_mssql ||
692
+ (val && val.length < 31 && (val.length - 6) % 8 == 0 && val[0..5].bytes == [230, 16, 0, 0, 1, 12])
693
+ )
694
+ )
632
695
  # MySQL's \"Internal Geometry Format\" and MSSQL's Geography are like WKB, but with an initial 4 bytes that indicates the SRID.
633
696
  if (srid = val&.[](0..3)&.unpack('I'))
634
697
  val = val.dup.force_encoding('BINARY')[4..-1].bytes
@@ -642,23 +705,30 @@ def display_value(col_type, val)
642
705
  # xx1x xxxx = IsWholeGlobe
643
706
  # Convert Microsoft's unique geography binary to standard WKB
644
707
  # (MSSQL point usually has two doubles, lng / lat, and can also have Z)
645
- if @is_mssql
708
+ if is_mssql_geography
646
709
  if val[0] == 1 && (val[1] & 8 > 0) && # Single point?
647
- (val.length - 2) % 8 == 0 && val.length < 27 # And containing up to three 8-byte values?
648
- idx = 2
649
- new_val = [0, 0, 0, 0, 1]
650
- new_val.concat(val[idx - 8...idx].reverse) while (idx += 8) <= val.length
651
- val = new_val
710
+ (val.length - 2) % 8 == 0 && val.length < 27 # And containing up to three 8-byte values?
711
+ val = [0, 0, 0, 0, 1] + val[2..-1].reverse
652
712
  else
653
713
  val_err = '(Microsoft internal SQL geography type)'
654
714
  end
655
715
  end
656
716
  end
657
717
  end
658
- val_err || (val ? RGeo::WKRep::WKBParser.new.parse(val.pack('c*')) : nil)
718
+ unless val_err || val.nil?
719
+ if (geometry = RGeo::WKRep::WKBParser.new.parse(val.pack('c*'))).is_a?(RGeo::Cartesian::PointImpl) &&
720
+ !(geometry.y == 0.0 && geometry.x == 0.0)
721
+ # Create a POINT link to this style of Google maps URL: https://www.google.com/maps/place/38.7071296+-121.2810649/@38.7071296,-121.2810649,12z
722
+ geometry = \"<a href=\\\"https://www.google.com/maps/place/#\{geometry.y}+#\{geometry.x}/@#\{geometry.y},#\{geometry.x},12z\\\" target=\\\"blank\\\">#\{geometry.to_s}</a>\"
723
+ end
724
+ val = geometry
725
+ end
726
+ val_err || val
659
727
  else
660
728
  '(Add RGeo gem to parse geometry detail)'
661
729
  end
730
+ when :binary
731
+ display_binary(val) if val
662
732
  else
663
733
  if col_type
664
734
  hide_bcrypt(val)
@@ -667,6 +737,40 @@ def display_value(col_type, val)
667
737
  end
668
738
  end
669
739
  end
740
+
741
+ def image_signatures
742
+ @image_signatures ||= { \"\\xFF\\xD8\\xFF\\xEE\".force_encoding('ASCII-8BIT') => 'jpeg',
743
+ \"\\xFF\\xD8\\xFF\\xE0\\x00\\x10\\x4A\\x46\\x49\\x46\\x00\\x01\".force_encoding('ASCII-8BIT') => 'jpeg',
744
+ \"\\x89PNG\\r\\n\\x1A\\n\".force_encoding('ASCII-8BIT') => 'png',
745
+ '<svg' => 'svg+xml', # %%% Not yet very good detection for SVG
746
+ 'BM'.force_encoding('ASCII-8BIT') => 'bmp',
747
+ 'GIF87a'.force_encoding('ASCII-8BIT') => 'gif',
748
+ 'GIF89a'.force_encoding('ASCII-8BIT') => 'gif' }
749
+ end
750
+ def display_binary(val)
751
+ if val[0..1] == \"\\x15\\x1C\" # One of those goofy Microsoft OLE containers?
752
+ package_header_length = val[2..3].bytes.reverse.inject(0) {|m, b| (m << 8) + b }
753
+ # This will often be just FF FF FF FF
754
+ # object_size = val[16..19].bytes.reverse.inject(0) {|m, b| (m << 8) + b }
755
+ friendly_and_class_names = val[20...package_header_length].split(\"\\0\")
756
+ object_type_name_length = val[package_header_length + 8..package_header_length+11].bytes.reverse.inject(0) {|m, b| (m << 8) + b }
757
+ friendly_and_class_names << val[package_header_length + 12...package_header_length + 12 + object_type_name_length].strip
758
+ # friendly_and_class_names will now be something like: ['Bitmap Image', 'Paint.Picture', 'PBrush']
759
+ 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 }
760
+ object_start = package_header_length + 24 + object_type_name_length
761
+ val = val[object_start...object_start + real_object_size]
762
+ end
763
+ if (signature = image_signatures.find { |k, _v| val[0...k.length] == k })
764
+ if val.length < 500_000
765
+ \"<img src=\\\"data:image/#\{signature.last};base64,#\{Base64.encode64(val)}\\\">\"
766
+ else
767
+ \"&lt;&nbsp;#\{signature.last} image, #\{val.length} bytes&nbsp;>\"
768
+ end
769
+ else
770
+ \"&lt;&nbsp;Binary, #\{val.length} bytes&nbsp;>\"
771
+ end
772
+ end
773
+
670
774
  # Accommodate composite primary keys that include strings with forward-slash characters
671
775
  def slashify(*vals)
672
776
  vals.map { |val_part| val_part.is_a?(String) ? val_part.gsub('/', '^^sl^^') : val_part }
@@ -705,56 +809,8 @@ callbacks = {} %>
705
809
 
706
810
  # %%% When doing schema select, if we're on a new page go to index
707
811
  script = "<script>
708
- var schemaSelect = document.getElementById(\"schema\");
709
- var tblSelect = document.getElementById(\"tbl\");
710
- var brickSchema;
711
812
  var #{table_name}HtColumns;
712
813
 
713
- // This PageTransitionEvent fires when the page first loads, as well as after any other history
714
- // transition such as when using the browser's Back and Forward buttons.
715
- window.addEventListener(\"pageshow\", function() {
716
- if (tblSelect) { // Always present
717
- var i = #{::Brick.config.path_prefix ? '0' : 'schemaSelect ? 1 : 0'},
718
- changeoutList = changeout(location.href);
719
- for (; i < changeoutList.length; ++i) {
720
- tblSelect.value = changeoutList[i];
721
- if (tblSelect.value !== \"\") break;
722
- }
723
-
724
- tblSelect.addEventListener(\"change\", function () {
725
- var lhr = changeout(location.href, null, this.value);
726
- if (brickSchema)
727
- lhr = changeout(lhr, \"_brick_schema\", schemaSelect.value);
728
- location.href = lhr;
729
- });
730
- }
731
-
732
- if (schemaSelect && schemaSelect.options.length > 1) { // First drop-down is only present if multitenant
733
- brickSchema = changeout(location.href, \"_brick_schema\");
734
- if (brickSchema) {
735
- [... document.getElementsByTagName(\"A\")].forEach(function (a) { a.href = changeout(a.href, \"_brick_schema\", brickSchema); });
736
- }
737
- schemaSelect.value = brickSchema || \"public\";
738
- schemaSelect.focus();
739
- schemaSelect.addEventListener(\"change\", function () {
740
- // If there's an ID then remove it (trim after selected table)
741
- location.href = changeout(location.href, \"_brick_schema\", this.value, tblSelect.value);
742
- });
743
- }
744
-
745
- [... document.getElementsByTagName(\"FORM\")].forEach(function (form) {
746
- if (brickSchema)
747
- form.action = changeout(form.action, \"_brick_schema\", brickSchema);
748
- form.addEventListener('submit', function (ev) {
749
- [... ev.target.getElementsByTagName(\"SELECT\")].forEach(function (select) {
750
- if (select.value === \"^^^brick_NULL^^^\")
751
- select.value = null;
752
- });
753
- return true;
754
- });
755
- });
756
- });
757
-
758
814
  // Add \"Are you sure?\" behaviour to any data-confirm buttons out there
759
815
  document.querySelectorAll(\"input[type=submit][data-confirm]\").forEach(function (btn) {
760
816
  btn.addEventListener(\"click\", function (evt) {
@@ -1082,8 +1138,11 @@ erDiagram
1082
1138
  id = id.first if id.is_a?(Array) && id.length == 1
1083
1139
  origin = (key_parts = k.split('.')).length == 1 ? model : model.reflect_on_association(key_parts.first).klass
1084
1140
  if (destination_fk = Brick.relations[origin.table_name][:fks].values.find { |fk| fk[:fk] == key_parts.last }) &&
1085
- (obj = (destination = origin.reflect_on_association(destination_fk[:assoc_name])&.klass)&.find(id)) %>
1086
- <h3>for <%= link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination._brick_index(:singular)\}_path\".to_sym, id) %></h3><%
1141
+ (objs = (destination = origin.reflect_on_association(destination_fk[:assoc_name])&.klass)&.find(id))
1142
+ objs = [objs] unless objs.is_a?(Array) %>
1143
+ <h3>for <% objs.each do |obj| %><%=
1144
+ link_to \"#{"#\{obj.brick_descrip\} (#\{destination.name\})\""}, send(\"#\{destination._brick_index(:singular)\}_path\".to_sym, id)
1145
+ %><% end %></h3><%
1087
1146
  end
1088
1147
  end %>
1089
1148
  (<%= link_to \"See all #\{model.base_class.name.split('::').last.pluralize}\", #{@_brick_model._brick_index}_path %>)
@@ -1227,7 +1286,9 @@ erDiagram
1227
1286
  <head>
1228
1287
  #{css}
1229
1288
  <title><%=
1230
- page_title = (\"#{model_name}: #\{(obj = @#{obj_name})&.brick_descrip || controller_name}\")
1289
+ model = (obj = @#{obj_name})&.class
1290
+ model_name = @#{obj_name}.#{inh_col = @_brick_model.inheritance_column} if obj.respond_to?(:#{inh_col})
1291
+ page_title = (\"#\{model_name ||= model.name}: #\{obj&.brick_descrip || controller_name}\")
1231
1292
  %></title>
1232
1293
  </head>
1233
1294
  <body>
@@ -1261,7 +1322,7 @@ end
1261
1322
  <% if obj
1262
1323
  # path_options = [obj.#{pk}]
1263
1324
  # path_options << { '_brick_schema': } if
1264
- # url = send(:#\{model_name._brick_index(:singular)}_path, obj.#{pk})
1325
+ # url = send(:#\{model._brick_index(:singular)}_path, obj.#{pk})
1265
1326
  options = {}
1266
1327
  options[:url] = send(\"#\{#{model_name}._brick_index(:singular)}_path\".to_sym, obj) if ::Brick.config.path_prefix
1267
1328
  %>
@@ -1357,7 +1418,17 @@ end
1357
1418
  # In Postgres labels of data stored in a hierarchical tree-like structure
1358
1419
  # If it's not yet enabled then: create extension ltree;
1359
1420
  val %>
1360
- <% when :binary, :primary_key
1421
+ <% when :binary %>
1422
+ <%= is_revert = false
1423
+ if val
1424
+ # %%% This same kind of geography check is done two other times above ... would be great to DRY it up.
1425
+ if val.length < 31 && (val.length - 6) % 8 == 0 && val[0..5].bytes == [230, 16, 0, 0, 1, 12]
1426
+ display_value('geography', val)
1427
+ else
1428
+ display_binary(val)
1429
+ end.html_safe
1430
+ end %>
1431
+ <% when :primary_key
1361
1432
  is_revert = false %>
1362
1433
  <% else %>
1363
1434
  <%= is_revert = false
@@ -1381,8 +1452,8 @@ end
1381
1452
  <% end %>
1382
1453
 
1383
1454
  #{unless args.first == 'new'
1384
- # Was: confirm_are_you_sure = ActionView.version < ::Gem::Version.new('7.0') ? "data: { confirm: 'Delete #{model_name} -- Are you sure?' }" : "form: { data: { turbo_confirm: 'Delete #{model_name} -- Are you sure?' } }"
1385
- confirm_are_you_sure = "data: { confirm: 'Delete #{model_name} -- Are you sure?' }"
1455
+ # Was: confirm_are_you_sure = ActionView.version < ::Gem::Version.new('7.0') ? "data: { confirm: 'Delete #\{model_name} -- Are you sure?' }" : "form: { data: { turbo_confirm: 'Delete #\{model_name} -- Are you sure?' } }"
1456
+ confirm_are_you_sure = "data: { confirm: 'Delete #\{model_name} -- Are you sure?' }"
1386
1457
  hms_headers.each_with_object(+'') do |hm, s|
1387
1458
  # %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
1388
1459
  next if hm.first.options[:through] && !hm.first.through_reflection
@@ -1390,11 +1461,24 @@ end
1390
1461
  if (pk = hm.first.klass.primary_key)
1391
1462
  hm_singular_name = (hm_name = hm.first.name.to_s).singularize.underscore
1392
1463
  obj_pk = (pk.is_a?(Array) ? pk : [pk]).each_with_object([]) { |pk_part, s| s << "#{hm_singular_name}.#{pk_part}" }.join(', ')
1464
+ poly_fix = if (poly_type = (hm.first.options[:as] && hm.first.type))
1465
+ "
1466
+ # Let's fix an unexpected \"feature\" of AR -- when going through a polymorphic has_many
1467
+ # association that points to an STI model then filtering for the __able_type column is done
1468
+ # with a .where(). And the polymorphic class name it points to is the base class name of
1469
+ # the STI model instead of its subclass.
1470
+ if (poly_type = #{poly_type.inspect}) &&
1471
+ @#{obj_name}.respond_to?(:#{@_brick_model.inheritance_column}) &&
1472
+ (base_type = collection.where_values_hash[poly_type])
1473
+ collection = collection.rewhere(poly_type => [base_type, @#{obj_name}.#{@_brick_model.inheritance_column}])
1474
+ end"
1475
+ end
1393
1476
  s << "<table id=\"#{hm_name}\" class=\"shadow\">
1394
- <tr><th>#{hm[3]}</th></tr>
1477
+ <tr><th>#{hm[1]}#{' poly' if hm[0].options[:as]} #{hm[3]}</th></tr>
1395
1478
  <% collection = @#{obj_name}.#{hm_name}
1396
1479
  collection = case collection
1397
- when ActiveRecord::Associations::CollectionProxy
1480
+ when ActiveRecord::Associations::CollectionProxy#{
1481
+ poly_fix}
1398
1482
  collection.order(#{pk.inspect})
1399
1483
  when ActiveRecord::Base # Object from a has_one
1400
1484
  [collection]
@@ -1576,11 +1660,10 @@ document.querySelectorAll(\"input, select\").forEach(function (inp) {
1576
1660
  private
1577
1661
 
1578
1662
  alias _brick_render_template render_template
1579
- def render_template(view, template, *args)
1580
- result = _brick_render_template(view, template, *args)
1581
- if template.instance_variable_get(:@is_brick)
1582
- Apartment::Tenant.switch!(::Brick.apartment_default_tenant) if ::Brick.apartment_multitenant
1583
- end
1663
+ def render_template(view, template, layout_name, *args)
1664
+ layout_name = nil if (is_brick = template.instance_variable_get(:@is_brick)) && layout_name.is_a?(Proc)
1665
+ result = _brick_render_template(view, template, layout_name, *args)
1666
+ Apartment::Tenant.switch!(::Brick.apartment_default_tenant) if is_brick && ::Brick.apartment_multitenant
1584
1667
  result
1585
1668
  end
1586
1669
  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)
@@ -90,6 +90,7 @@ module Brick::Rails::FormTags
90
90
  # 0..62 because Postgres column names are limited to 63 characters
91
91
  obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, bt_id_col
92
92
  )
93
+ bt_txt = display_binary(bt_txt).html_safe if bt_txt&.encoding&.name == 'ASCII-8BIT'
93
94
  bt_txt ||= "<span class=\"orphan\">&lt;&lt; Orphaned ID: #{val} >></span>" if val
94
95
  bt_id = bt_id_col&.map { |id_col| obj.respond_to?(id_sym = id_col.to_sym) ? obj.send(id_sym) : id_col }
95
96
  out << (bt_id&.first ? link_to(bt_txt, send("#{bt_class.base_class._brick_index(:singular)}_path".to_sym, bt_id)) : bt_txt || '')
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 101
8
+ TINY = 103
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(
@@ -1043,6 +1043,18 @@ ActiveSupport.on_load(:active_record) do
1043
1043
  end
1044
1044
  # rubocop:enable Lint/ConstantDefinitionInBlock
1045
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
+
1046
1058
  # Rails < 4.2 is not innately compatible with Ruby 2.4 and later, and comes up with:
1047
1059
  # "TypeError: Cannot visit Integer" unless we patch like this:
1048
1060
  if ruby_version >= ::Gem::Version.new('2.4') &&
@@ -1081,11 +1093,54 @@ ActiveSupport.on_load(:active_record) do
1081
1093
  end
1082
1094
  end
1083
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
+
1084
1133
  class ActiveRecord::Associations::JoinDependency
1085
- if JoinBase.instance_method(:initialize).arity == 2 # Older ActiveRecord 4.x?
1086
- def initialize(base, associations, joins)
1087
- @alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(base.connection, joins)
1088
- @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
1089
1144
  tree = self.class.make_tree associations
1090
1145
 
1091
1146
  # Provide a way to find the original relation that this tree is being used for
@@ -1098,7 +1153,7 @@ ActiveSupport.on_load(:active_record) do
1098
1153
  @join_root.children.each { |child| construct_tables! @join_root, child }
1099
1154
  end
1100
1155
 
1101
- else # For ActiveRecord 5.0 - 7.1
1156
+ else # For ActiveRecord 5.2 - 7.1
1102
1157
 
1103
1158
  def initialize(base, table, associations, join_type = nil)
1104
1159
  tree = self.class.make_tree associations
@@ -1245,13 +1300,13 @@ module ActiveRecord
1245
1300
 
1246
1301
  if private_instance_methods.include?(:build_join_query)
1247
1302
  alias _brick_build_join_query build_join_query
1248
- def build_join_query(manager, buckets, *args)
1303
+ def build_join_query(manager, buckets, *args) # , **kwargs)
1249
1304
  # %%% Better way to bring relation into the mix
1250
1305
  if (aj = buckets.fetch(:association_join, nil))
1251
1306
  aj.instance_variable_set(:@relation, self)
1252
1307
  end
1253
1308
 
1254
- _brick_build_join_query(manager, buckets, *args)
1309
+ _brick_build_join_query(manager, buckets, *args) # , **kwargs)
1255
1310
  end
1256
1311
 
1257
1312
  else
@@ -1433,4 +1488,85 @@ if Gem::Specification.all_names.any? { |g| g.start_with?('ransack-') }
1433
1488
  end
1434
1489
  end
1435
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
+
1436
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.101
4
+ version: 1.0.103
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-17 00:00:00.000000000 Z
11
+ date: 2023-01-09 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