brick 1.0.156 → 1.0.158
Sign up to get free protection for your applications and to get access to all the features.
- 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
|