brick 1.0.156 → 1.0.157

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: 995a300b0971af02a39ed75857ce349c99720068555cbb1d6311a75d9a13f2b8
4
+ data.tar.gz: e9153dac19552fc8dd581774f89b9ed682ac040b5a4dc0d47f4b28fe079ca36d
5
5
  SHA512:
6
- metadata.gz: 84fa7d9831dfca26b9b08ef46bd852c3d4f96e1f8420ff086526f97964090b3f95d71b5d66d9ff7ba47ec5f74e50938c9714731d21d0c59ac2365305ab8826bc
7
- data.tar.gz: cf790bbb66852142fe07cb1619517043dc3ef8c47b0f020b22bc635cd902fface9b52631507b2fdd9520b604c98eae00b58a1bad29ace185c8d342e79e99a062
6
+ metadata.gz: 4c0c8553d5ef266aa6e947d6f0dd9ee4ae23d64118cd3c69565345747be0afa4de63be999976bd8e4fed55ebec2f3551aa87a4f4b84dee0a3dced188d23260f6
7
+ data.tar.gz: a7e0dbdd27c5d0905d7acf0a7a3f6fb5e254556ef65a0633a3021c395664c65f152afd3cecc53992c0f5da0095a1246b10d14898c34d82c60fc1cec298e62a68
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
@@ -544,11 +544,9 @@ module ActiveRecord
544
544
  wheres = {}
545
545
  params.each do |k, v|
546
546
  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)
547
+ next unless k.start_with?('__')
551
548
 
549
+ k = k[2..-1] # Take off leading "__"
552
550
  if (where_col = (ks = k.split('.')).last)[-1] == '!'
553
551
  where_col = where_col[0..-2]
554
552
  end
@@ -1163,6 +1161,9 @@ if ActiveSupport::Dependencies.respond_to?(:autoload_module!) # %%% Only works w
1163
1161
  end
1164
1162
 
1165
1163
  ::Brick::ADD_CONST_MISSING = lambda do
1164
+ return if @_brick_const_missing_done
1165
+
1166
+ @_brick_const_missing_done = true
1166
1167
  alias _brick_const_missing const_missing
1167
1168
  def const_missing(*args)
1168
1169
  requested = args.first.to_s
@@ -1187,9 +1188,13 @@ end
1187
1188
  end
1188
1189
  base_module = if self < ActiveRecord::Migration || !self.name
1189
1190
  brick_root || Object
1190
- elsif (split_self_name || self.name.split('::')).length > 1 # Classic mode
1191
+ elsif split_self_name&.length&.> 1 # Classic mode
1191
1192
  begin
1192
- return self._brick_const_missing(*args)
1193
+ base = self
1194
+ unless (base_goal = requested.split('::')[0..-2].join('::')).empty?
1195
+ base = base.parent while base.name != base_goal && base != Object
1196
+ end
1197
+ return base._brick_const_missing(*args)
1193
1198
 
1194
1199
  rescue NameError # %%% Avoid the error "____ cannot be autoloaded from an anonymous class or module"
1195
1200
  return self.const_get(args.first) if self.const_defined?(args.first)
@@ -1294,10 +1299,16 @@ end
1294
1299
  # Build out a module for the schema if it's namespaced
1295
1300
  # schema_name = schema_name.camelize
1296
1301
  base_module.const_set(schema_name.to_sym, (built_module = Module.new))
1297
-
1298
1302
  [built_module, "module #{schema_name}; end\n"]
1299
1303
  # %%% Perhaps an option to use the first module just as schema, and additional modules as namespace with a table name prefix applied
1300
1304
 
1305
+ # MODULE (overrides from "treat_as_module")
1306
+ elsif (::Brick.enable_models? || ::Brick.enable_controllers?) &&
1307
+ (possible_module = (base_module == Object ? '' : "#{base_module.name}::") + class_name) &&
1308
+ ::Brick.config.treat_as_module.include?(possible_module)
1309
+ base_module.const_set(class_name.to_sym, (built_module = Module.new))
1310
+ [built_module, "module #{possible_module}; end\n"]
1311
+
1301
1312
  # AVO Resource
1302
1313
  elsif base_module == Object && Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) && requested.end_with?('Resource') &&
1303
1314
  # Expect that anything called MotorResource or SpinaResource could be from those administrative gems
@@ -1469,6 +1480,7 @@ class Object
1469
1480
  code = +"class #{full_name} < #{base_model.name}\n"
1470
1481
  built_model = Class.new(base_model) do |new_model_class|
1471
1482
  (schema_module || Object).const_set((chosen_name = (inheritable_name || model_name)).to_sym, new_model_class)
1483
+ @_brick_relation = relation
1472
1484
  if inheritable_name
1473
1485
  new_model_class.define_singleton_method :inherited do |subclass|
1474
1486
  super(subclass)
@@ -1478,7 +1490,7 @@ class Object
1478
1490
  puts "should be \"class #{model_name} < #{inheritable_name}\"\n (not \"#{subclass.name} < #{inheritable_name}\")"
1479
1491
  end
1480
1492
  end
1481
- self.abstract_class = true
1493
+ new_model_class.abstract_class = true
1482
1494
  code << " self.abstract_class = true\n"
1483
1495
  elsif Object.const_defined?('BCrypt') && relation[:cols].include?('password_digest') &&
1484
1496
  !instance_methods.include?(:password) && respond_to?(:has_secure_password)
@@ -1489,7 +1501,7 @@ class Object
1489
1501
  # Accommodate singular or camel-cased table names such as "order_detail" or "OrderDetails"
1490
1502
  code << " self.table_name = '#{self.table_name = matching}'\n" if inheritable_name || self.table_name != matching
1491
1503
  if (inh_col = ::Brick.config.sti_type_column.find { |_k, v| v.include?(matching) }&.first)
1492
- self.inheritance_column = inh_col
1504
+ new_model_class.inheritance_column = inh_col
1493
1505
  code << " self.inheritance_column = '#{inh_col}'\n"
1494
1506
  end
1495
1507
 
@@ -1542,19 +1554,19 @@ class Object
1542
1554
  unless is_sti
1543
1555
  fks = relation[:fks] || {}
1544
1556
  # 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
1557
+ hmts = fks.each_with_object(Hash.new { |h, k| h[k] = [] }) do |fk, hmts2|
1558
+ # The key in each hash entry (fk.first) is the constraint name
1559
+ inverse_assoc_name = (assoc = fk.last)[:inverse]&.fetch(:assoc_name, nil)
1560
+ if (invs = assoc[:inverse_table]).is_a?(Array)
1561
+ if assoc[:is_bt]
1562
+ invs = invs.first # Just do the first one of what would be multiple identical polymorphic belongs_to
1563
+ else
1564
+ invs.each { |inv| build_bt_or_hm(full_name, relations, relation, hmts2, assoc, inverse_assoc_name, inv, code) }
1565
+ end
1566
+ else
1567
+ build_bt_or_hm(full_name, relations, relation, hmts2, assoc, inverse_assoc_name, invs, code)
1568
+ end
1569
+ end
1558
1570
  # # Not NULLables
1559
1571
  # # %%% For the minute we've had to pull this out because it's been troublesome implementing the NotNull validator
1560
1572
  # relation[:cols].each do |col, datatype|
@@ -1572,58 +1584,54 @@ class Object
1572
1584
  # self.broadcasts_to ->(model) { (model&.class&.name || chosen_name).underscore.pluralize.to_sym }
1573
1585
  # code << " broadcasts_to ->(#{chosen_name}) { #{chosen_name}&.class&.name&.underscore&.pluralize&.to_sym }\n"
1574
1586
  # 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)
1587
+
1588
+ hmts&.each do |hmt_fk, hms|
1589
+ hmt_fk = hmt_fk.tr('.', '_')
1590
+ hms.each do |hm|
1591
+ # %%% Need to confirm that HMTs work when they are built from has_manys with custom names
1592
+ through = ::Brick.config.schema_behavior[:multitenant] ? hm.first[:assoc_name] : hm.first[:inverse_table].tr('.', '_').pluralize
1593
+ options = {}
1594
+ hmt_name = if hms.length > 1
1595
+ if hms[0].first[:inverse][:assoc_name] == hms[1].first[:inverse][:assoc_name] # Same BT names pointing back to us? (Most common scenario)
1596
+ "#{hmt_fk}_through_#{hm.first[:assoc_name]}"
1597
+ else # Use BT names to provide uniqueness
1598
+ if self.name.underscore.singularize == hm.first[:alternate_name]
1599
+ # Has previously been:
1600
+ # # If it folds back on itself then look at the other side
1601
+ # # (At this point just infer the source be the inverse of the first has_many that
1602
+ # # we find that is not ourselves. If there are more than two then uh oh, can't
1603
+ # # yet handle that rare circumstance!)
1604
+ # other = hms.find { |hm1| hm1 != hm } # .first[:fk]
1605
+ # options[:source] = other.first[:inverse][:assoc_name].to_sym
1606
+ # And also has been:
1607
+ # hm.first[:inverse][:assoc_name].to_sym
1608
+ options[:source] = hm.last.to_sym
1609
+ else
1610
+ through = hm.first.fetch(:alternate_chosen_name, hm.first[:alternate_name])
1611
+ end
1612
+ singular_assoc_name = hm.first[:inverse][:assoc_name].singularize
1613
+ "#{singular_assoc_name}_#{hmt_fk}"
1614
+ end
1615
+ else
1616
+ hmt_fk
1617
+ end
1618
+ options[:through] = through.to_sym
1619
+ if relation[:fks].any? { |k, v| v[:assoc_name] == hmt_name }
1620
+ hmt_name = "#{hmt_name.singularize}_#{hm.first[:assoc_name]}"
1621
+ # Was:
1622
+ # options[:class_name] = hm.first[:inverse_table].singularize.camelize
1623
+ # options[:foreign_key] = hm.first[:fk].to_sym
1624
+ far_assoc = relations[hm.first[:inverse_table]][:fks].find { |_k, v| v[:assoc_name] == hm.last }
1625
+ options[:class_name] = far_assoc.last[:inverse_table].singularize.camelize
1626
+ options[:foreign_key] = far_assoc.last[:fk].to_sym
1623
1627
  end
1628
+ options[:source] ||= hm.last.to_sym unless hmt_name.singularize == hm.last
1629
+ code << " has_many :#{hmt_name}#{options.map { |opt| ", #{opt.first}: #{opt.last.inspect}" }.join}\n"
1630
+ new_model_class.send(:has_many, hmt_name.to_sym, **options)
1624
1631
  end
1625
1632
  end
1626
1633
  code << "end # model #{full_name}\n"
1634
+ end # model class definition
1627
1635
  [built_model, code]
1628
1636
  end
1629
1637
 
@@ -2150,6 +2158,17 @@ class Object
2150
2158
  new_obj.send("#{k}=", ActiveStorage::Filename.new('')) if v.is_a?(ActiveStorage::Filename) && !v.instance_variable_get(:@filename)
2151
2159
  end if Object.const_defined?('ActiveStorage')
2152
2160
  end
2161
+ new_obj.attribute_names.each do |a|
2162
+ if (val = params["__#{a}"])
2163
+ # val = case new_obj.class.column_for_attribute(a).type
2164
+ # when :datetime, :date, :time, :timestamp
2165
+ # val.
2166
+ # else
2167
+ # val
2168
+ # end
2169
+ new_obj.send("#{a}=", val)
2170
+ end
2171
+ end
2153
2172
  instance_variable_set("@#{singular_table_name}".to_sym, new_obj)
2154
2173
  add_csp_hash
2155
2174
  end
@@ -750,12 +750,12 @@ window.addEventListener(\"popstate\", linkSchemas);
750
750
  (hm_fk_name.is_a?(String) && hm_fk_name.include?('.')) # HMT? (Could do a better check for this)
751
751
  predicates = path_keys(hm_assoc, hm_fk_name, pk).map do |k, v|
752
752
  if v == '[sti_type]'
753
- "'#{k}': (@#{obj_name}.#{hm_assoc.active_record.inheritance_column})&.constantize&.base_class&.name"
753
+ "'__#{k}': (@#{obj_name}.#{hm_assoc.active_record.inheritance_column})&.constantize&.base_class&.name"
754
754
  else
755
- v.is_a?(String) ? "'#{k}': '#{v}'" : "'#{k}': @#{obj_name}.#{v}"
755
+ v.is_a?(String) ? "'__#{k}': '#{v}'" : "'__#{k}': @#{obj_name}.#{v}"
756
756
  end
757
757
  end.join(', ')
758
- "<%= link_to '#{assoc_name}', #{hm_assoc.klass._brick_index}_path({ #{predicates} }) %>\n"
758
+ "<%= link_to '#{assoc_name}', #{hm_assoc.klass._brick_index}_path(predicates = { #{predicates} }) %>\n"
759
759
  else
760
760
  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
761
  "Instead it should use the clause \"foreign_key: :#{hm_assoc.inverse_of&.foreign_key}\"."
@@ -897,6 +897,9 @@ tr th {
897
897
  tr th a {
898
898
  color: #80FFB8;
899
899
  }
900
+ .add-hm-related {
901
+ float: right;
902
+ }
900
903
 
901
904
  tr th, tr td {
902
905
  padding: 0.2em 0.5em;
@@ -1169,7 +1172,10 @@ if (window.brickFontFamily) {
1169
1172
  x.style.fontFamily = brickFontFamily.toString();
1170
1173
  });
1171
1174
  }
1172
- </script>"
1175
+ </script>
1176
+ <% if (apartment_default_schema = ::Brick.apartment_multitenant && ::Brick.apartment_default_tenant)
1177
+ Apartment::Tenant.switch!(apartment_default_schema)
1178
+ end %>"
1173
1179
 
1174
1180
  erd_markup = if @_brick_model
1175
1181
  "<div id=\"mermaidErd\" class=\"mermaid\">
@@ -1658,7 +1664,7 @@ end
1658
1664
  # path_options = [obj.#{pk}]
1659
1665
  # path_options << { '_brick_schema': } if
1660
1666
  options = {}
1661
- if ::Brick.config.path_prefix
1667
+ if ::Brick.config.path_prefix || (obj.class.table_name.singularize == obj.class.table_name)
1662
1668
  path_helper = obj.new_record? ? #{model_name}._brick_index : #{model_name}._brick_index(:singular)
1663
1669
  options[:url] = send(\"#\{path_helper}_path\".to_sym, obj)
1664
1670
  end
@@ -1763,7 +1769,14 @@ end
1763
1769
  end"}"
1764
1770
  end
1765
1771
  s << "<table id=\"#{hm_name}\" class=\"shadow\">
1766
- <tr><th>#{hm[1]}#{' poly' if hm[0].options[:as]} #{hm[3]}</th></tr>
1772
+ <tr><th>#{hm[1]}#{' poly' if hm[0].options[:as]} #{hm[3]}
1773
+ <span class = \"add-hm-related\"><%=
1774
+ pk_val = (obj_pk = model.primary_key).is_a?(String) ? obj.send(obj_pk) : obj_pk.map { |pk_part| obj.send(pk_part) }
1775
+ pk_val_arr = [pk_val] unless pk_val.is_a?(Array)
1776
+ 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,
1777
+ new_#{hm.first.klass._brick_index(:singular)}_path(predicates))
1778
+ %></span>
1779
+ </th></tr>
1767
1780
  <% if (assoc = @#{obj_name}.class.reflect_on_association(:#{hm_name})).macro == :has_one &&
1768
1781
  assoc.options&.fetch(:through, nil).nil?
1769
1782
  # In order to apply DSL properly, evaluate this HO the other way around as if it were as a BT
@@ -2034,9 +2047,7 @@ document.querySelectorAll(\"input, select\").forEach(function (inp) {
2034
2047
  alias _brick_render_template render_template
2035
2048
  def render_template(view, template, layout_name, *args)
2036
2049
  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
2050
+ _brick_render_template(view, template, layout_name, *args)
2040
2051
  end
2041
2052
  end # TemplateRenderer
2042
2053
  end
@@ -71,11 +71,11 @@ module Brick::Rails::FormBuilder
71
71
  # Postgres naturally uses the +uuid_generate_v4()+ function from the uuid-ossp extension
72
72
  # If it's not yet enabled then: create extension \"uuid-ossp\";
73
73
  # ActiveUUID gem created a new :uuid type
74
- out << val
74
+ out << val if val
75
75
  when :ltree
76
76
  # In Postgres labels of data stored in a hierarchical tree-like structure
77
77
  # If it's not yet enabled then: create extension ltree;
78
- out << val
78
+ out << val if val
79
79
  when :binary
80
80
  is_revert = false
81
81
  if val
@@ -95,7 +95,7 @@ module Brick::Rails::FormBuilder
95
95
  else
96
96
  eheij = ActiveSupport::JSON::Encoding.escape_html_entities_in_json
97
97
  ActiveSupport::JSON::Encoding.escape_html_entities_in_json = false if eheij
98
- val_str = val.to_json
98
+ val_str = val&.to_json
99
99
  ActiveSupport::JSON::Encoding.escape_html_entities_in_json = eheij
100
100
  end
101
101
  # Because there are so danged many quotes in JSON, escape them specially by converting to backticks.
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 156
8
+ TINY = 157
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
 
@@ -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)
@@ -55,8 +55,8 @@ module Brick
55
55
  def brick_migrations
56
56
  # If Apartment is active, see if a default schema to analyse is indicated
57
57
 
58
- # # Load all models
59
- # ::Brick.eager_load_classes
58
+ ::Brick.mode = :on
59
+ ActiveRecord::Base.establish_connection
60
60
 
61
61
  if (tables = ::Brick.relations.reject { |k, v| v.key?(:isView) && v[:isView] == true }.map(&:first).sort).empty?
62
62
  puts "No tables found in database #{ActiveRecord::Base.connection.current_database}."
@@ -15,6 +15,9 @@ module Brick
15
15
  def brick_models
16
16
  # %%% If Apartment is active and there's no schema_to_analyse, ask which schema they want
17
17
 
18
+ ::Brick.mode = :on
19
+ ActiveRecord::Base.establish_connection
20
+
18
21
  # Load all models
19
22
  ::Brick.eager_load_classes
20
23
 
@@ -9,25 +9,46 @@ module Brick
9
9
 
10
10
  desc 'Auto-generates a seeds file from existing data.'
11
11
 
12
+ SeedModel = Struct.new(:table_name, :klass, :is_brick)
13
+ SeedModel.define_method(:to_s) do
14
+ "#{klass.name}#{' (brick-generated)' if is_brick}"
15
+ end
16
+
12
17
  def brick_seeds
13
18
  # %%% If Apartment is active and there's no schema_to_analyse, ask which schema they want
14
19
 
15
20
  ::Brick.mode = :on
16
21
  ActiveRecord::Base.establish_connection
17
22
 
18
- if (tables = ::Brick.relations.reject { |k, v| v.key?(:isView) && v[:isView] == true }.map(&:first).sort).empty?
19
- puts "No tables found in database #{ActiveRecord::Base.connection.current_database}."
23
+ # Load all models
24
+ ::Brick.eager_load_classes
25
+
26
+ # Generate a list of viable models that can be chosen
27
+ # First start with any existing models that have been defined ...
28
+ existing_models = ActiveRecord::Base.descendants.each_with_object({}) do |m, s|
29
+ s[m.table_name] = SeedModel.new(m.table_name, m, false) if !m.abstract_class? && m.table_exists?
30
+ end
31
+ models = (existing_models.values +
32
+ # ... then add models which can be auto-built by Brick
33
+ ::Brick.relations.reject do |k, v|
34
+ (v.key?(:isView) && v[:isView] == true) || existing_models.key?(k)
35
+ end.map { |k, v| SeedModel.new(k, v[:class_name].constantize, true) }
36
+ ).sort { |a, b| a.to_s <=> b.to_s }
37
+ if models.empty?
38
+ puts "No viable models found for database #{ActiveRecord::Base.connection.current_database}."
20
39
  return
21
40
  end
22
41
 
23
42
  if File.exist?(seed_file_path = "#{::Rails.root}/db/seeds.rb")
24
- puts "WARNING: seeds file #{seed_file_path} appears to already be present."
43
+ puts "WARNING: seeds file #{seed_file_path} appears to already be present.\nOverwrite?"
44
+ return unless gets_list(list: ['No', 'Yes']) == 'Yes'
45
+
46
+ puts "\n"
25
47
  end
26
48
 
27
- # Generate a list of tables that can be chosen
28
- chosen = gets_list(list: tables, chosen: tables.dup)
49
+ chosen = gets_list(list: models, chosen: models.dup)
29
50
  schemas = chosen.each_with_object({}) do |v, s|
30
- if (v_parts = v.split('.')).length > 1
51
+ if (v_parts = v.table_name.split('.')).length > 1
31
52
  s[v_parts.first] = nil unless [::Brick.default_schema, 'public'].include?(v_parts.first)
32
53
  end
33
54
  end
@@ -40,12 +61,13 @@ module Brick
40
61
  # Start by making entries for fringe models (those with no foreign keys).
41
62
  # Continue layer by layer, creating entries for models that reference ones already done, until
42
63
  # no more entries can be created. (At that point hopefully all models are accounted for.)
43
- while (fringe = chosen.reject do |tbl|
64
+ while (fringe = chosen.reject do |seed_model|
65
+ tbl = seed_model.table_name
44
66
  snag_fks = []
45
67
  snags = ::Brick.relations.fetch(tbl, nil)&.fetch(:fks, nil)&.select do |_k, v|
46
68
  v[:is_bt] && !v[:polymorphic] &&
47
69
  tbl != v[:inverse_table] && # Ignore self-referencing associations (stuff like "parent_id")
48
- !done.include?(v[:inverse_table]) &&
70
+ !done.any? { |done_seed_model| done_seed_model.table_name == v[:inverse_table] } &&
49
71
  ::Brick.config.ignore_migration_fks.exclude?(snag_fk = "#{tbl}.#{v[:fk]}") &&
50
72
  snag_fks << snag_fk
51
73
  end
@@ -53,12 +75,14 @@ module Brick
53
75
  # puts snag_fks.inspect
54
76
  stuck[tbl] = snags
55
77
  end
56
- end).present?
78
+ end
79
+ ).present?
57
80
  seeds << "\n"
58
- fringe.each do |tbl|
81
+ fringe.each do |seed_model|
82
+ tbl = seed_model.table_name
59
83
  next unless ::Brick.config.exclude_tables.exclude?(tbl) &&
60
84
  (relation = ::Brick.relations.fetch(tbl, nil))&.fetch(:cols, nil)&.present? &&
61
- (klass = Object.const_get(class_name = relation[:class_name])).table_exists?
85
+ (klass = seed_model.klass).table_exists?
62
86
 
63
87
  pkey_cols = (rpk = relation[:pkey].values.flatten) & (arpk = [ar_base.primary_key].flatten.sort)
64
88
  # In case things aren't as standard
@@ -88,7 +112,7 @@ module Brick
88
112
  klass.order(*pkey_cols).each do |obj|
89
113
  unless has_rows
90
114
  has_rows = true
91
- seeds << " puts 'Seeding: #{class_name}'\n"
115
+ seeds << " puts 'Seeding: #{seed_model.klass.name}'\n"
92
116
  end
93
117
  is_empty = false
94
118
  pk_val = obj.send(pkey_cols.first)
@@ -108,9 +132,9 @@ module Brick
108
132
  data << "#{col}: #{val.inspect}"
109
133
  end
110
134
  end
111
- seeds << "#{tbl.gsub('.', '__')}_#{brick_escape(pk_val)} = #{class_name}.create(#{(fk_vals + data).join(', ')})\n"
135
+ seeds << "#{tbl.gsub('.', '__')}_#{brick_escape(pk_val)} = #{seed_model.klass.name}.create(#{(fk_vals + data).join(', ')})\n"
112
136
  end
113
- seeds << " # (Skipping #{class_name} as it has no rows)\n" unless has_rows
137
+ seeds << " # (Skipping #{seed_model.klass.name} as it has no rows)\n" unless has_rows
114
138
  File.open(seed_file_path, "w") { |f| f.write seeds }
115
139
  end
116
140
  done.concat(fringe)
@@ -118,7 +142,7 @@ module Brick
118
142
  end
119
143
  stuck_counts = Hash.new { |h, k| h[k] = 0 }
120
144
  chosen.each do |leftover|
121
- puts "Can't do #{leftover} because:\n #{stuck[leftover].map do |snag|
145
+ puts "Can't do #{leftover.klass.name} because:\n #{stuck[leftover.table_name].map do |snag|
122
146
  stuck_counts[snag.last[:inverse_table]] += 1
123
147
  snag.last[:assoc_name]
124
148
  end.join(', ')}"
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.156
4
+ version: 1.0.157
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-11 00:00:00.000000000 Z
11
+ date: 2023-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord