brick 1.0.156 → 1.0.158

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: e001ce24602c8fef817c24a0eb88e014e990d98828474acd66d868b50dd68692
4
- data.tar.gz: 8378593754641ffc86059672fe6148ba767d91a867361b21ec8377a82903af6b
3
+ metadata.gz: 326ec09b1c28cf7c54e54bae9ea6ceed7d7e476a177be0a41af14e04f7cc3d11
4
+ data.tar.gz: 0507c22a2acf86c58737ecfedeb1a1f5ed6979b5263b28c6d6e77b0e9500ab7c
5
5
  SHA512:
6
- metadata.gz: 84fa7d9831dfca26b9b08ef46bd852c3d4f96e1f8420ff086526f97964090b3f95d71b5d66d9ff7ba47ec5f74e50938c9714731d21d0c59ac2365305ab8826bc
7
- data.tar.gz: cf790bbb66852142fe07cb1619517043dc3ef8c47b0f020b22bc635cd902fface9b52631507b2fdd9520b604c98eae00b58a1bad29ace185c8d342e79e99a062
6
+ metadata.gz: d6803593a5c3e17ef40005c405017ed4e7519024ca172a077ad377c201a7b2e2caf2a8b4bc5a0ef63f091124a44199ffd3b386db7d6b5d51352f2e3327537d81
7
+ data.tar.gz: 486b0a15422b0d3150b575c8d19c24cc3434e3fd8f35335d2e490e1e69e423995a2032de913def73b4f4df0e2789c6f4af7d61930db5cc144d0a0cb76be3ebdc
data/lib/brick/config.rb CHANGED
@@ -332,6 +332,14 @@ module Brick
332
332
  @mutex.synchronize { @table_name_prefixes = value }
333
333
  end
334
334
 
335
+ def treat_as_module
336
+ @mutex.synchronize { @treat_as_module || [] }
337
+ end
338
+
339
+ def treat_as_module=(mod_names)
340
+ @mutex.synchronize { @treat_as_module = mod_names }
341
+ end
342
+
335
343
  def order
336
344
  @mutex.synchronize { @order || {} }
337
345
  end
@@ -59,7 +59,7 @@ module ActiveRecord
59
59
  end
60
60
 
61
61
  def is_brick?
62
- instance_variables.include?(:@_brick_built) && instance_variable_get(:@_brick_built)
62
+ instance_variables.include?(:@_brick_relation) && instance_variable_get(:@_brick_relation)
63
63
  end
64
64
 
65
65
  def _assoc_names
@@ -89,6 +89,13 @@ module ActiveRecord
89
89
  def brick_foreign_type(assoc)
90
90
  reflect_on_association(assoc).foreign_type || "#{assoc}_type"
91
91
  end
92
+
93
+ def _brick_all_fields
94
+ rtans = if respond_to?(:rich_text_association_names)
95
+ rich_text_association_names&.map { |rtan| rtan.to_s.start_with?('rich_text_') ? rtan[10..-1] : rtan }
96
+ end
97
+ columns_hash.keys.map(&:to_sym) + (rtans || [])
98
+ end
92
99
  end
93
100
 
94
101
  def self._brick_primary_key(relation = nil)
@@ -357,7 +364,7 @@ module ActiveRecord
357
364
  # Support nested attributes which use the friendly_id gem
358
365
  assoc.klass._brick_nested_friendly_id if Object.const_defined?('FriendlyId') &&
359
366
  assoc.klass.instance_variable_get(:@friendly_id_config)
360
- new_attrib_text = assoc.klass._brick_find_permits(assoc, (new_permits = assoc.klass.columns_hash.keys.map(&:to_sym)), done_permits)
367
+ new_attrib_text = assoc.klass._brick_find_permits(assoc, (new_permits = assoc.klass._brick_all_fields), done_permits)
361
368
  new_permits << :_destroy
362
369
  current_permits << { "#{assoc.name}_attributes".to_sym => new_permits }
363
370
  s << "#{assoc.name}_attributes: #{new_attrib_text}"
@@ -544,11 +551,9 @@ module ActiveRecord
544
551
  wheres = {}
545
552
  params.each do |k, v|
546
553
  k = k.to_s # Rails < 4.2 comes in as a symbol
547
- next if ['_brick_schema', '_brick_order',
548
- '_brick_erd', '_brick_exclude', '_brick_unexclude',
549
- '_brick_page', '_brick_page_size', '_brick_offset', '_brick_limit',
550
- '_brick_is_api', 'controller', 'action'].include?(k)
554
+ next unless k.start_with?('__')
551
555
 
556
+ k = k[2..-1] # Take off leading "__"
552
557
  if (where_col = (ks = k.split('.')).last)[-1] == '!'
553
558
  where_col = where_col[0..-2]
554
559
  end
@@ -1163,6 +1168,9 @@ if ActiveSupport::Dependencies.respond_to?(:autoload_module!) # %%% Only works w
1163
1168
  end
1164
1169
 
1165
1170
  ::Brick::ADD_CONST_MISSING = lambda do
1171
+ return if @_brick_const_missing_done
1172
+
1173
+ @_brick_const_missing_done = true
1166
1174
  alias _brick_const_missing const_missing
1167
1175
  def const_missing(*args)
1168
1176
  requested = args.first.to_s
@@ -1187,9 +1195,13 @@ end
1187
1195
  end
1188
1196
  base_module = if self < ActiveRecord::Migration || !self.name
1189
1197
  brick_root || Object
1190
- elsif (split_self_name || self.name.split('::')).length > 1 # Classic mode
1198
+ elsif split_self_name&.length&.> 1 # Classic mode
1191
1199
  begin
1192
- return self._brick_const_missing(*args)
1200
+ base = self
1201
+ unless (base_goal = requested.split('::')[0..-2].join('::')).empty?
1202
+ base = base.parent while base.name != base_goal && base != Object
1203
+ end
1204
+ return base._brick_const_missing(*args)
1193
1205
 
1194
1206
  rescue NameError # %%% Avoid the error "____ cannot be autoloaded from an anonymous class or module"
1195
1207
  return self.const_get(args.first) if self.const_defined?(args.first)
@@ -1294,10 +1306,16 @@ end
1294
1306
  # Build out a module for the schema if it's namespaced
1295
1307
  # schema_name = schema_name.camelize
1296
1308
  base_module.const_set(schema_name.to_sym, (built_module = Module.new))
1297
-
1298
1309
  [built_module, "module #{schema_name}; end\n"]
1299
1310
  # %%% Perhaps an option to use the first module just as schema, and additional modules as namespace with a table name prefix applied
1300
1311
 
1312
+ # MODULE (overrides from "treat_as_module")
1313
+ elsif (::Brick.enable_models? || ::Brick.enable_controllers?) &&
1314
+ (possible_module = (base_module == Object ? '' : "#{base_module.name}::") + class_name) &&
1315
+ ::Brick.config.treat_as_module.include?(possible_module)
1316
+ base_module.const_set(class_name.to_sym, (built_module = Module.new))
1317
+ [built_module, "module #{possible_module}; end\n"]
1318
+
1301
1319
  # AVO Resource
1302
1320
  elsif base_module == Object && Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) && requested.end_with?('Resource') &&
1303
1321
  # Expect that anything called MotorResource or SpinaResource could be from those administrative gems
@@ -1469,6 +1487,7 @@ class Object
1469
1487
  code = +"class #{full_name} < #{base_model.name}\n"
1470
1488
  built_model = Class.new(base_model) do |new_model_class|
1471
1489
  (schema_module || Object).const_set((chosen_name = (inheritable_name || model_name)).to_sym, new_model_class)
1490
+ @_brick_relation = relation
1472
1491
  if inheritable_name
1473
1492
  new_model_class.define_singleton_method :inherited do |subclass|
1474
1493
  super(subclass)
@@ -1478,7 +1497,7 @@ class Object
1478
1497
  puts "should be \"class #{model_name} < #{inheritable_name}\"\n (not \"#{subclass.name} < #{inheritable_name}\")"
1479
1498
  end
1480
1499
  end
1481
- self.abstract_class = true
1500
+ new_model_class.abstract_class = true
1482
1501
  code << " self.abstract_class = true\n"
1483
1502
  elsif Object.const_defined?('BCrypt') && relation[:cols].include?('password_digest') &&
1484
1503
  !instance_methods.include?(:password) && respond_to?(:has_secure_password)
@@ -1489,7 +1508,7 @@ class Object
1489
1508
  # Accommodate singular or camel-cased table names such as "order_detail" or "OrderDetails"
1490
1509
  code << " self.table_name = '#{self.table_name = matching}'\n" if inheritable_name || self.table_name != matching
1491
1510
  if (inh_col = ::Brick.config.sti_type_column.find { |_k, v| v.include?(matching) }&.first)
1492
- self.inheritance_column = inh_col
1511
+ new_model_class.inheritance_column = inh_col
1493
1512
  code << " self.inheritance_column = '#{inh_col}'\n"
1494
1513
  end
1495
1514
 
@@ -1542,19 +1561,19 @@ class Object
1542
1561
  unless is_sti
1543
1562
  fks = relation[:fks] || {}
1544
1563
  # Do the bulk of the has_many / belongs_to processing, and store details about HMT so they can be done at the very last
1545
- fks.each_with_object(Hash.new { |h, k| h[k] = [] }) do |fk, hmts|
1546
- # The key in each hash entry (fk.first) is the constraint name
1547
- inverse_assoc_name = (assoc = fk.last)[:inverse]&.fetch(:assoc_name, nil)
1548
- if (invs = assoc[:inverse_table]).is_a?(Array)
1549
- if assoc[:is_bt]
1550
- invs = invs.first # Just do the first one of what would be multiple identical polymorphic belongs_to
1551
- else
1552
- invs.each { |inv| build_bt_or_hm(full_name, relations, relation, hmts, assoc, inverse_assoc_name, inv, code) }
1553
- end
1554
- end
1555
- build_bt_or_hm(full_name, relations, relation, hmts, assoc, inverse_assoc_name, invs, code) unless invs.is_a?(Array)
1556
- hmts
1557
- end
1564
+ hmts = fks.each_with_object(Hash.new { |h, k| h[k] = [] }) do |fk, hmts2|
1565
+ # The key in each hash entry (fk.first) is the constraint name
1566
+ inverse_assoc_name = (assoc = fk.last)[:inverse]&.fetch(:assoc_name, nil)
1567
+ if (invs = assoc[:inverse_table]).is_a?(Array)
1568
+ if assoc[:is_bt]
1569
+ invs = invs.first # Just do the first one of what would be multiple identical polymorphic belongs_to
1570
+ else
1571
+ invs.each { |inv| build_bt_or_hm(full_name, relations, relation, hmts2, assoc, inverse_assoc_name, inv, code) }
1572
+ end
1573
+ else
1574
+ build_bt_or_hm(full_name, relations, relation, hmts2, assoc, inverse_assoc_name, invs, code)
1575
+ end
1576
+ end
1558
1577
  # # Not NULLables
1559
1578
  # # %%% For the minute we've had to pull this out because it's been troublesome implementing the NotNull validator
1560
1579
  # relation[:cols].each do |col, datatype|
@@ -1572,58 +1591,54 @@ class Object
1572
1591
  # self.broadcasts_to ->(model) { (model&.class&.name || chosen_name).underscore.pluralize.to_sym }
1573
1592
  # code << " broadcasts_to ->(#{chosen_name}) { #{chosen_name}&.class&.name&.underscore&.pluralize&.to_sym }\n"
1574
1593
  # end
1575
- end # class definition
1576
- # Having this separate -- will this now work out better?
1577
- built_model.class_exec do
1578
- @_brick_built = true
1579
- @_brick_relation = relation
1580
- hmts&.each do |hmt_fk, hms|
1581
- hmt_fk = hmt_fk.tr('.', '_')
1582
- hms.each do |hm|
1583
- # %%% Need to confirm that HMTs work when they are built from has_manys with custom names
1584
- through = ::Brick.config.schema_behavior[:multitenant] ? hm.first[:assoc_name] : hm.first[:inverse_table].tr('.', '_').pluralize
1585
- options = {}
1586
- hmt_name = if hms.length > 1
1587
- if hms[0].first[:inverse][:assoc_name] == hms[1].first[:inverse][:assoc_name] # Same BT names pointing back to us? (Most common scenario)
1588
- "#{hmt_fk}_through_#{hm.first[:assoc_name]}"
1589
- else # Use BT names to provide uniqueness
1590
- if self.name.underscore.singularize == hm.first[:alternate_name]
1591
- # Has previously been:
1592
- # # If it folds back on itself then look at the other side
1593
- # # (At this point just infer the source be the inverse of the first has_many that
1594
- # # we find that is not ourselves. If there are more than two then uh oh, can't
1595
- # # yet handle that rare circumstance!)
1596
- # other = hms.find { |hm1| hm1 != hm } # .first[:fk]
1597
- # options[:source] = other.first[:inverse][:assoc_name].to_sym
1598
- # And also has been:
1599
- # hm.first[:inverse][:assoc_name].to_sym
1600
- options[:source] = hm.last.to_sym
1601
- else
1602
- through = hm.first.fetch(:alternate_chosen_name, hm.first[:alternate_name])
1603
- end
1604
- singular_assoc_name = hm.first[:inverse][:assoc_name].singularize
1605
- "#{singular_assoc_name}_#{hmt_fk}"
1606
- end
1607
- else
1608
- hmt_fk
1609
- end
1610
- options[:through] = through.to_sym
1611
- if relation[:fks].any? { |k, v| v[:assoc_name] == hmt_name }
1612
- hmt_name = "#{hmt_name.singularize}_#{hm.first[:assoc_name]}"
1613
- # Was:
1614
- # options[:class_name] = hm.first[:inverse_table].singularize.camelize
1615
- # options[:foreign_key] = hm.first[:fk].to_sym
1616
- far_assoc = relations[hm.first[:inverse_table]][:fks].find { |_k, v| v[:assoc_name] == hm.last }
1617
- options[:class_name] = far_assoc.last[:inverse_table].singularize.camelize
1618
- options[:foreign_key] = far_assoc.last[:fk].to_sym
1619
- end
1620
- options[:source] ||= hm.last.to_sym unless hmt_name.singularize == hm.last
1621
- code << " has_many :#{hmt_name}#{options.map { |opt| ", #{opt.first}: #{opt.last.inspect}" }.join}\n"
1622
- self.send(:has_many, hmt_name.to_sym, **options)
1594
+
1595
+ hmts&.each do |hmt_fk, hms|
1596
+ hmt_fk = hmt_fk.tr('.', '_')
1597
+ hms.each do |hm|
1598
+ # %%% Need to confirm that HMTs work when they are built from has_manys with custom names
1599
+ through = ::Brick.config.schema_behavior[:multitenant] ? hm.first[:assoc_name] : hm.first[:inverse_table].tr('.', '_').pluralize
1600
+ options = {}
1601
+ hmt_name = if hms.length > 1
1602
+ if hms[0].first[:inverse][:assoc_name] == hms[1].first[:inverse][:assoc_name] # Same BT names pointing back to us? (Most common scenario)
1603
+ "#{hmt_fk}_through_#{hm.first[:assoc_name]}"
1604
+ else # Use BT names to provide uniqueness
1605
+ if self.name.underscore.singularize == hm.first[:alternate_name]
1606
+ # Has previously been:
1607
+ # # If it folds back on itself then look at the other side
1608
+ # # (At this point just infer the source be the inverse of the first has_many that
1609
+ # # we find that is not ourselves. If there are more than two then uh oh, can't
1610
+ # # yet handle that rare circumstance!)
1611
+ # other = hms.find { |hm1| hm1 != hm } # .first[:fk]
1612
+ # options[:source] = other.first[:inverse][:assoc_name].to_sym
1613
+ # And also has been:
1614
+ # hm.first[:inverse][:assoc_name].to_sym
1615
+ options[:source] = hm.last.to_sym
1616
+ else
1617
+ through = hm.first.fetch(:alternate_chosen_name, hm.first[:alternate_name])
1618
+ end
1619
+ singular_assoc_name = hm.first[:inverse][:assoc_name].singularize
1620
+ "#{singular_assoc_name}_#{hmt_fk}"
1621
+ end
1622
+ else
1623
+ hmt_fk
1624
+ end
1625
+ options[:through] = through.to_sym
1626
+ if relation[:fks].any? { |k, v| v[:assoc_name] == hmt_name }
1627
+ hmt_name = "#{hmt_name.singularize}_#{hm.first[:assoc_name]}"
1628
+ # Was:
1629
+ # options[:class_name] = hm.first[:inverse_table].singularize.camelize
1630
+ # options[:foreign_key] = hm.first[:fk].to_sym
1631
+ far_assoc = relations[hm.first[:inverse_table]][:fks].find { |_k, v| v[:assoc_name] == hm.last }
1632
+ options[:class_name] = far_assoc.last[:inverse_table].singularize.camelize
1633
+ options[:foreign_key] = far_assoc.last[:fk].to_sym
1623
1634
  end
1635
+ options[:source] ||= hm.last.to_sym unless hmt_name.singularize == hm.last
1636
+ code << " has_many :#{hmt_name}#{options.map { |opt| ", #{opt.first}: #{opt.last.inspect}" }.join}\n"
1637
+ new_model_class.send(:has_many, hmt_name.to_sym, **options)
1624
1638
  end
1625
1639
  end
1626
1640
  code << "end # model #{full_name}\n"
1641
+ end # model class definition
1627
1642
  [built_model, code]
1628
1643
  end
1629
1644
 
@@ -2144,7 +2159,18 @@ class Object
2144
2159
  code << " end\n"
2145
2160
  self.define_method :new do
2146
2161
  _schema, @_is_show_schema_list = ::Brick.set_db_schema(params)
2147
- if (new_obj = model.new).respond_to?(:serializable_hash)
2162
+ new_params = model.attribute_names.each_with_object({}) do |a, s|
2163
+ if (val = params["__#{a}"])
2164
+ # val = case new_obj.class.column_for_attribute(a).type
2165
+ # when :datetime, :date, :time, :timestamp
2166
+ # val.
2167
+ # else
2168
+ # val
2169
+ # end
2170
+ s[a] = val
2171
+ end
2172
+ end
2173
+ if (new_obj = model.new(new_params)).respond_to?(:serializable_hash)
2148
2174
  # Convert any Filename objects with nil into an empty string so that #encode can be called on them
2149
2175
  new_obj.serializable_hash.each do |k, v|
2150
2176
  new_obj.send("#{k}=", ActiveStorage::Filename.new('')) if v.is_a?(ActiveStorage::Filename) && !v.instance_variable_get(:@filename)
@@ -2291,7 +2317,7 @@ class Object
2291
2317
 
2292
2318
  if is_need_params
2293
2319
  code << " def #{params_name}\n"
2294
- permits_txt = model._brick_find_permits(model, permits = model.columns_hash.keys.map(&:to_sym))
2320
+ permits_txt = model._brick_find_permits(model, permits = model._brick_all_fields)
2295
2321
  code << " params.require(:#{require_name = model.name.underscore.tr('/', '_')
2296
2322
  }).permit(#{permits_txt.map(&:inspect).join(', ')})\n"
2297
2323
  code << " end\n"
@@ -713,12 +713,9 @@ window.addEventListener(\"popstate\", linkSchemas);
713
713
  next unless @_brick_model.instance_methods.include?(through) &&
714
714
  (associative = @_brick_model._br_associatives.fetch(hm.first, nil))
715
715
 
716
- tbl_nm = if (source = hm_assoc.source_reflection).macro == :has_many
717
- source.inverse_of&.name # For HM -> HM style HMT
718
- else # belongs_to or has_one
719
- hm_assoc.through_reflection&.name # for standard HMT, which is HM -> BT
720
- end
721
- # If there is no inverse available for the source belongs_to association, make one based on the class name
716
+ # Should handle standard HMT, which is HM -> BT, as well as HM -> HM style HMT
717
+ tbl_nm = hm_assoc.source_reflection&.inverse_of&.name
718
+ # If there is no inverse available for the source belongs_to association, infer one based on the class name
722
719
  unless tbl_nm
723
720
  tbl_nm = associative.class_name.underscore
724
721
  tbl_nm.slice!(0) if tbl_nm[0] == '/'
@@ -750,12 +747,12 @@ window.addEventListener(\"popstate\", linkSchemas);
750
747
  (hm_fk_name.is_a?(String) && hm_fk_name.include?('.')) # HMT? (Could do a better check for this)
751
748
  predicates = path_keys(hm_assoc, hm_fk_name, pk).map do |k, v|
752
749
  if v == '[sti_type]'
753
- "'#{k}': (@#{obj_name}.#{hm_assoc.active_record.inheritance_column})&.constantize&.base_class&.name"
750
+ "'__#{k}': (@#{obj_name}.#{hm_assoc.active_record.inheritance_column})&.constantize&.base_class&.name"
754
751
  else
755
- v.is_a?(String) ? "'#{k}': '#{v}'" : "'#{k}': @#{obj_name}.#{v}"
752
+ v.is_a?(String) ? "'__#{k}': '#{v}'" : "'__#{k}': @#{obj_name}.#{v}"
756
753
  end
757
754
  end.join(', ')
758
- "<%= link_to '#{assoc_name}', #{hm_assoc.klass._brick_index}_path({ #{predicates} }) %>\n"
755
+ "<%= link_to '#{assoc_name}', #{hm_assoc.klass._brick_index}_path(predicates = { #{predicates} }) %>\n"
759
756
  else
760
757
  puts "Warning: has_many :#{hm_assoc.name} in model #{hm_assoc.active_record.name} currently looks for a foreign key called \"#{hm_assoc.foreign_key}\". "\
761
758
  "Instead it should use the clause \"foreign_key: :#{hm_assoc.inverse_of&.foreign_key}\"."
@@ -897,6 +894,9 @@ tr th {
897
894
  tr th a {
898
895
  color: #80FFB8;
899
896
  }
897
+ .add-hm-related {
898
+ float: right;
899
+ }
900
900
 
901
901
  tr th, tr td {
902
902
  padding: 0.2em 0.5em;
@@ -1169,7 +1169,10 @@ if (window.brickFontFamily) {
1169
1169
  x.style.fontFamily = brickFontFamily.toString();
1170
1170
  });
1171
1171
  }
1172
- </script>"
1172
+ </script>
1173
+ <% if (apartment_default_schema = ::Brick.apartment_multitenant && ::Brick.apartment_default_tenant)
1174
+ Apartment::Tenant.switch!(apartment_default_schema)
1175
+ end %>"
1173
1176
 
1174
1177
  erd_markup = if @_brick_model
1175
1178
  "<div id=\"mermaidErd\" class=\"mermaid\">
@@ -1658,7 +1661,7 @@ end
1658
1661
  # path_options = [obj.#{pk}]
1659
1662
  # path_options << { '_brick_schema': } if
1660
1663
  options = {}
1661
- if ::Brick.config.path_prefix
1664
+ if ::Brick.config.path_prefix || (obj.class.table_name.singularize == obj.class.table_name)
1662
1665
  path_helper = obj.new_record? ? #{model_name}._brick_index : #{model_name}._brick_index(:singular)
1663
1666
  options[:url] = send(\"#\{path_helper}_path\".to_sym, obj)
1664
1667
  end
@@ -1673,12 +1676,22 @@ end
1673
1676
  @#{obj_name}.send(\"#\{model.brick_foreign_type(v.first)}=\", v[1].first&.first&.name)
1674
1677
  end
1675
1678
  end if @#{obj_name}.new_record?
1676
- @#{obj_name}.attributes.each do |k, val|
1677
- next if !(col = #{model_name}.columns_hash[k]) ||
1678
- (#{(pk.map(&:to_s) || []).inspect}.include?(k) && !bts.key?(k)) ||
1679
- ::Brick.config.metadata_columns.include?(k) %>
1679
+ rtans = #{model_name}.rich_text_association_names if #{model_name}.respond_to?(:rich_text_association_names)
1680
+ (#{model_name}.column_names + (rtans || [])).each do |k|
1681
+ next if (#{(pk.map(&:to_s) || []).inspect}.include?(k) && !bts.key?(k)) ||
1682
+ ::Brick.config.metadata_columns.include?(k)
1683
+
1684
+ col = #{model_name}.columns_hash[k]
1685
+ if !col && rtans&.include?(k)
1686
+ k = k[10..-1] if k.start_with?('rich_text_')
1687
+ col = (rt_col ||= ActiveRecord::ConnectionAdapters::Column.new(
1688
+ '', nil, ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(sql_type: 'varchar', type: :text)
1689
+ )
1690
+ )
1691
+ end
1692
+ val = @#{obj_name}.attributes[k] %>
1680
1693
  <tr>
1681
- <th class=\"show-field\"<%= \" title=\\\"#\{col.comment}\\\"\".html_safe if col.respond_to?(:comment) && !col.comment.blank? %>>
1694
+ <th class=\"show-field\"<%= \" title=\\\"#\{col&.comment}\\\"\".html_safe if col&.respond_to?(:comment) && !col&.comment.blank? %>>
1682
1695
  <% has_fields = true
1683
1696
  if (bt = bts[k])
1684
1697
  # Add a final member in this array with descriptive options to be used in <select> drop-downs
@@ -1710,7 +1723,7 @@ end
1710
1723
  collection&.brick_(:each) do |obj|
1711
1724
  option_detail << [
1712
1725
  obj.brick_descrip(
1713
- descrip_cols&.first&.map { |col| obj.send(col.last) },
1726
+ descrip_cols&.first&.map { |col2| obj.send(col2.last) },
1714
1727
  obj_pk
1715
1728
  ), obj.send(obj_pk)
1716
1729
  ]
@@ -1763,7 +1776,14 @@ end
1763
1776
  end"}"
1764
1777
  end
1765
1778
  s << "<table id=\"#{hm_name}\" class=\"shadow\">
1766
- <tr><th>#{hm[1]}#{' poly' if hm[0].options[:as]} #{hm[3]}</th></tr>
1779
+ <tr><th>#{hm[1]}#{' poly' if hm[0].options[:as]} #{hm[3]}
1780
+ <span class = \"add-hm-related\"><%=
1781
+ pk_val = (obj_pk = model.primary_key).is_a?(String) ? obj.send(obj_pk) : obj_pk.map { |pk_part| obj.send(pk_part) }
1782
+ pk_val_arr = [pk_val] unless pk_val.is_a?(Array)
1783
+ link_to('<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"#fff\" d=\"M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z\"/></svg>'.html_safe,
1784
+ new_#{hm.first.klass._brick_index(:singular)}_path(predicates))
1785
+ %></span>
1786
+ </th></tr>
1767
1787
  <% if (assoc = @#{obj_name}.class.reflect_on_association(:#{hm_name})).macro == :has_one &&
1768
1788
  assoc.options&.fetch(:through, nil).nil?
1769
1789
  # In order to apply DSL properly, evaluate this HO the other way around as if it were as a BT
@@ -2034,9 +2054,7 @@ document.querySelectorAll(\"input, select\").forEach(function (inp) {
2034
2054
  alias _brick_render_template render_template
2035
2055
  def render_template(view, template, layout_name, *args)
2036
2056
  layout_name = nil if (is_brick = template.instance_variable_get(:@is_brick)) && layout_name.is_a?(Proc)
2037
- result = _brick_render_template(view, template, layout_name, *args)
2038
- Apartment::Tenant.switch!(::Brick.apartment_default_tenant) if is_brick && ::Brick.apartment_multitenant
2039
- result
2057
+ _brick_render_template(view, template, layout_name, *args)
2040
2058
  end
2041
2059
  end # TemplateRenderer
2042
2060
  end
@@ -20,7 +20,14 @@ module Brick::Rails::FormBuilder
20
20
 
21
21
  html_options[:prompt] = "Select #{bt_name}"
22
22
  out << self.select(method.to_sym, bt[3], { value: val || '^^^brick_NULL^^^' }, html_options)
23
- bt_link = if (bt_obj = bt_class&.find_by(bt_pair[1] => val))
23
+ bt_obj = nil
24
+ begin
25
+ bt_obj = bt_class&.find_by(bt_pair[1] => val)
26
+ rescue ActiveRecord::SubclassNotFound => e
27
+ # %%% Would be cool to indicate to the user that a subclass is missing.
28
+ # Its name starts at: e.message.index('failed to locate the subclass: ') + 31
29
+ end
30
+ bt_link = if bt_obj
24
31
  bt_path = template.send(
25
32
  "#{bt_class.base_class._brick_index(:singular)}_path".to_sym,
26
33
  bt_obj.send(bt_class.primary_key.to_sym)
@@ -71,11 +78,11 @@ module Brick::Rails::FormBuilder
71
78
  # Postgres naturally uses the +uuid_generate_v4()+ function from the uuid-ossp extension
72
79
  # If it's not yet enabled then: create extension \"uuid-ossp\";
73
80
  # ActiveUUID gem created a new :uuid type
74
- out << val
81
+ out << val if val
75
82
  when :ltree
76
83
  # In Postgres labels of data stored in a hierarchical tree-like structure
77
84
  # If it's not yet enabled then: create extension ltree;
78
- out << val
85
+ out << val if val
79
86
  when :binary
80
87
  is_revert = false
81
88
  if val
@@ -95,12 +102,12 @@ module Brick::Rails::FormBuilder
95
102
  else
96
103
  eheij = ActiveSupport::JSON::Encoding.escape_html_entities_in_json
97
104
  ActiveSupport::JSON::Encoding.escape_html_entities_in_json = false if eheij
98
- val_str = val.to_json
105
+ val_str = val&.to_json
99
106
  ActiveSupport::JSON::Encoding.escape_html_entities_in_json = eheij
100
107
  end
101
108
  # Because there are so danged many quotes in JSON, escape them specially by converting to backticks.
102
109
  # (and previous to this, escape backticks with our own goofy code of ^^br_btick__ )
103
- out << (json_field = self.hidden_field(method.to_sym, { class: 'jsonpicker', value: val_str.gsub('`', '^^br_btick__').tr('\"', '`').html_safe }))
110
+ out << (json_field = self.hidden_field(method.to_sym, { class: 'jsonpicker', value: val_str&.gsub('`', '^^br_btick__')&.tr('\"', '`')&.html_safe }))
104
111
  out << "<div id=\"_br_json_#{self.field_id(method)}\"></div>"
105
112
  else
106
113
  is_revert = false
@@ -157,7 +157,7 @@ module Brick::Rails::FormTags
157
157
  out << link_to(ho_txt, send("#{hm_klass.base_class._brick_index(:singular)}_path".to_sym, ho_id))
158
158
  end
159
159
  elsif obj.respond_to?(ct_col = hms_col[1].to_sym) && (ct = obj.send(ct_col)&.to_i)&.positive?
160
- predicates = hms_col[2].each_with_object({}) { |v, s| s[v.first] = v.last.is_a?(String) ? v.last : obj.send(v.last) }
160
+ predicates = hms_col[2].each_with_object({}) { |v, s| s["__#{v.first}"] = v.last.is_a?(String) ? v.last : obj.send(v.last) }
161
161
  predicates.each { |k, v| predicates[k] = klass.name if v == '[sti_type]' }
162
162
  out << "#{link_to("#{ct || 'View'} #{hms_col.first}",
163
163
  send("#{hm_klass._brick_index}_path".to_sym, predicates))}\n"
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 156
8
+ TINY = 158
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
@@ -113,21 +113,23 @@ module Brick
113
113
  def set_db_schema(params = nil)
114
114
  # If Apartment::Tenant.current is not still the default (usually 'public') then an elevator has brought us into
115
115
  # a different tenant. If so then don't allow schema navigation.
116
- chosen = if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' &&
117
- (current_schema = (ActiveRecord::Base.execute_sql('SELECT current_schemas(true)').first['current_schemas'][1..-2]
118
- .split(',') - ['pg_catalog', 'pg_toast', 'heroku_ext']).first) &&
119
- (is_show_schema_list = (apartment_multitenant && current_schema == ::Brick.default_schema)) &&
120
- (schema = (params ? params['_brick_schema'] : ::Brick.default_schema)) &&
121
- ::Brick.db_schemas&.key?(schema)
122
- Apartment::Tenant.switch!(schema)
123
- schema
124
- elsif ::Brick.test_schema
125
- is_show_schema_list = true
126
- Apartment::Tenant.switch!(::Brick.test_schema)
127
- ::Brick.test_schema
128
- else
129
- current_schema # Just return the current schema
130
- end
116
+ if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' && apartment_multitenant
117
+ current_schema = (ActiveRecord::Base.execute_sql('SELECT current_schemas(true)')
118
+ .first['current_schemas'][1..-2]
119
+ .split(',') - ['pg_catalog', 'pg_toast', 'heroku_ext']).first
120
+ is_show_schema_list = current_schema == ::Brick.default_schema
121
+ schema = (is_show_schema_list && params && params['_brick_schema']) || ::Brick.default_schema
122
+ chosen = if is_show_schema_list && ::Brick.db_schemas&.key?(schema)
123
+ Apartment::Tenant.switch!(schema)
124
+ schema
125
+ elsif ::Brick.test_schema
126
+ is_show_schema_list = true
127
+ Apartment::Tenant.switch!(::Brick.test_schema)
128
+ ::Brick.test_schema
129
+ else
130
+ current_schema # Just return the current schema
131
+ end
132
+ end
131
133
  [chosen == ::Brick.default_schema ? nil : chosen, is_show_schema_list]
132
134
  end
133
135
 
@@ -219,8 +221,8 @@ module Brick
219
221
  hm_models = ActiveRecord::Base.descendants.select do |m|
220
222
  m.reflect_on_all_associations.any? { |assoc| !assoc.belongs_to? && assoc.options[:as]&.to_sym == a.name }
221
223
  end
222
- # No need to include subclassed models if their parent is already in the list
223
- hm_models.reject! { |m| hm_models.any? { |parent| parent != m && m < parent } }
224
+ # No need to include models with no table, or subclassed models if their parent is already in the list
225
+ hm_models.reject! { |m| !m.table_exists? || hm_models.any? { |parent| parent != m && m < parent } }
224
226
  if hm_models.empty?
225
227
  puts "Missing any real indication as to which models \"has_many\" this polymorphic BT in model #{a.active_record.name}:"
226
228
  puts " belongs_to :#{a.name}, polymorphic: true"
@@ -416,6 +418,12 @@ module Brick
416
418
  Brick.config.table_name_prefixes = value
417
419
  end
418
420
 
421
+ # @api public
422
+ # Causes Decidim to work with this line: Brick.treat_as_module = ['Decidim::ContentBlocks']
423
+ def treat_as_module=(value)
424
+ Brick.config.treat_as_module = value
425
+ end
426
+
419
427
  # @api public
420
428
  def metadata_columns=(value)
421
429
  Brick.config.metadata_columns = value
@@ -1147,8 +1155,11 @@ ActiveSupport.on_load(:active_record) do
1147
1155
  class << self
1148
1156
  def execute_sql(sql, *param_array)
1149
1157
  param_array = param_array.first if param_array.length == 1 && param_array.first.is_a?(Array)
1150
- if ['OracleEnhanced', 'SQLServer'].include?(ActiveRecord::Base.connection.adapter_name)
1158
+ case ActiveRecord::Base.connection.adapter_name
1159
+ when 'OracleEnhanced', 'SQLServer'
1151
1160
  connection.exec_query(send(:sanitize_sql_array, [sql] + param_array)).rows
1161
+ when 'Trilogy'
1162
+ connection.execute(send(:sanitize_sql_array, [sql] + param_array)).rows
1152
1163
  else
1153
1164
  connection.execute(send(:sanitize_sql_array, [sql] + param_array))
1154
1165
  end
@@ -1452,7 +1463,8 @@ ActiveSupport.on_load(:active_record) do
1452
1463
  class << self
1453
1464
  alias _original_load load
1454
1465
  def load(yaml, *args, **kwargs)
1455
- if kwargs[:aliases].nil? && caller[0..4].any? { |line| line.end_with?("`database_configuration'") }
1466
+ if kwargs[:aliases].nil? && caller[0..4].any? { |line| line.end_with?("`database_configuration'") ||
1467
+ line.end_with?("`secrets'") }
1456
1468
  kwargs[:aliases] = true
1457
1469
  end
1458
1470
  _original_load(yaml, *args, **kwargs)
@@ -28,7 +28,7 @@ module Brick
28
28
  relations = ::Brick.relations
29
29
  if is_brick_file
30
30
  # Need to remove any currently-existing additional_references so that it doesn't cloud the discovery process:
31
- ::Brick.config.additional_references.each do |ar|
31
+ ::Brick.config.additional_references&.each do |ar|
32
32
  if (fks = relations.fetch(ar[0], nil)&.fetch(:fks, nil))
33
33
  fks.delete(fks.find { |k, v| v[:is_bt] && k.start_with?('(brick) ') && v[:fk] == ar[1] }&.first)
34
34
  end