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 +4 -4
- data/lib/brick/config.rb +8 -0
- data/lib/brick/extensions.rb +90 -71
- data/lib/brick/frameworks/rails/engine.rb +20 -9
- data/lib/brick/frameworks/rails/form_builder.rb +3 -3
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +29 -17
- data/lib/generators/brick/migrations_generator.rb +2 -2
- data/lib/generators/brick/models_generator.rb +3 -0
- data/lib/generators/brick/seeds_generator.rb +39 -15
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 995a300b0971af02a39ed75857ce349c99720068555cbb1d6311a75d9a13f2b8
|
4
|
+
data.tar.gz: e9153dac19552fc8dd581774f89b9ed682ac040b5a4dc0d47f4b28fe079ca36d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
@@ -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
|
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
|
1191
|
+
elsif split_self_name&.length&.> 1 # Classic mode
|
1191
1192
|
begin
|
1192
|
-
|
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
|
-
|
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
|
-
|
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,
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
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
|
-
|
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)
|
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]}
|
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
|
-
|
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
|
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.
|
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
|
|
@@ -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)
|
@@ -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
|
-
|
59
|
-
|
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}."
|
@@ -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
|
-
|
19
|
-
|
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
|
-
|
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 |
|
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.
|
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
|
78
|
+
end
|
79
|
+
).present?
|
57
80
|
seeds << "\n"
|
58
|
-
fringe.each do |
|
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 =
|
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: #{
|
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)} = #{
|
135
|
+
seeds << "#{tbl.gsub('.', '__')}_#{brick_escape(pk_val)} = #{seed_model.klass.name}.create(#{(fk_vals + data).join(', ')})\n"
|
112
136
|
end
|
113
|
-
seeds << " # (Skipping #{
|
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.
|
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
|
+
date: 2023-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|