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 +4 -4
- data/lib/brick/config.rb +8 -0
- data/lib/brick/extensions.rb +100 -74
- data/lib/brick/frameworks/rails/engine.rb +39 -21
- data/lib/brick/frameworks/rails/form_builder.rb +12 -5
- data/lib/brick/frameworks/rails/form_tags.rb +1 -1
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +31 -19
- data/lib/generators/brick/install_generator.rb +1 -1
- data/lib/generators/brick/migration_builder.rb +341 -0
- data/lib/generators/brick/migrations_generator.rb +6 -327
- data/lib/generators/brick/models_generator.rb +3 -0
- data/lib/generators/brick/seeds_generator.rb +39 -15
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 326ec09b1c28cf7c54e54bae9ea6ceed7d7e476a177be0a41af14e04f7cc3d11
|
4
|
+
data.tar.gz: 0507c22a2acf86c58737ecfedeb1a1f5ed6979b5263b28c6d6e77b0e9500ab7c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/brick/extensions.rb
CHANGED
@@ -59,7 +59,7 @@ module ActiveRecord
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def is_brick?
|
62
|
-
instance_variables.include?(:@
|
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.
|
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
|
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
|
1198
|
+
elsif split_self_name&.length&.> 1 # Classic mode
|
1191
1199
|
begin
|
1192
|
-
|
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
|
-
|
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
|
-
|
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,
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
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
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
hms.
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
options[:
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
|
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
|
-
|
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.
|
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
|
-
|
717
|
-
|
718
|
-
|
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
|
-
|
1677
|
-
|
1678
|
-
|
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
|
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 { |
|
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]}
|
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
|
-
|
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
|
-
|
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
|
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
|
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"
|
data/lib/brick/version_number.rb
CHANGED
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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
|
-
|
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
|
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
|