brick 1.0.76 → 1.0.77
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 +9 -0
- data/lib/brick/extensions.rb +126 -82
- data/lib/brick/frameworks/rails/engine.rb +52 -34
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +30 -7
- data/lib/generators/brick/install_generator.rb +4 -0
- 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: 7e113470d473585f716cfa5b835f503448a0d160b60de20f884bf3ce2cc14c08
|
4
|
+
data.tar.gz: 40064f980dd6893cb4e7e47861e00b184983d1a4bbc11b7cbd16fd6caddfab87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4219dfee478a845a949b1ac9b03d0a135504723a892b571ca068af70c2b58ca52025f5e1f2fedb6a97a5b2dcd97280c5f54132b854e285e6daf7bc95d0c2d357
|
7
|
+
data.tar.gz: 67ae65b0de3801bbb2a1817d16b4023116ee7d41491b6d45927bd8197a8f800151f8ce79de34732a55f3dfcabbe301dc69b276899cfd544604d6a64e61220c99
|
data/lib/brick/config.rb
CHANGED
@@ -20,6 +20,15 @@ module Brick
|
|
20
20
|
@serializer = Brick::Serializers::YAML
|
21
21
|
end
|
22
22
|
|
23
|
+
# Any path prefixing to apply to all auto-generated Brick routes
|
24
|
+
def path_prefix
|
25
|
+
@mutex.synchronize { @path_prefix }
|
26
|
+
end
|
27
|
+
|
28
|
+
def path_prefix=(path)
|
29
|
+
@mutex.synchronize { @path_prefix = path }
|
30
|
+
end
|
31
|
+
|
23
32
|
# Indicates whether Brick models are on or off. Default: true.
|
24
33
|
def enable_models
|
25
34
|
@mutex.synchronize { !!@enable_models }
|
data/lib/brick/extensions.rb
CHANGED
@@ -256,12 +256,13 @@ module ActiveRecord
|
|
256
256
|
table_name == assoc_name ? link : "#{assoc_name}-#{link}".html_safe
|
257
257
|
end
|
258
258
|
|
259
|
-
def self._brick_index
|
260
|
-
tbl_parts = table_name.split('.')
|
259
|
+
def self._brick_index(mode = nil)
|
260
|
+
tbl_parts = ((mode == :singular) ? table_name.singularize : table_name).split('.')
|
261
261
|
tbl_parts.shift if ::Brick.apartment_multitenant && tbl_parts.first == Apartment.default_schema
|
262
|
-
|
263
|
-
|
264
|
-
|
262
|
+
tbl_parts.unshift(::Brick.config.path_prefix) if ::Brick.config.path_prefix
|
263
|
+
index = tbl_parts.map(&:underscore).join('_')
|
264
|
+
# Rails applies an _index suffix to that route when the resource name is singular
|
265
|
+
index << '_index' if mode != :singular && index == index.singularize
|
265
266
|
index
|
266
267
|
end
|
267
268
|
|
@@ -783,50 +784,60 @@ end
|
|
783
784
|
Module.class_exec do
|
784
785
|
alias _brick_const_missing const_missing
|
785
786
|
def const_missing(*args)
|
786
|
-
|
787
|
+
requested = args.first.to_s
|
788
|
+
is_controller = requested.end_with?('Controller')
|
789
|
+
# self.name is nil when a model name is requested in an .erb file
|
790
|
+
if self.name && ::Brick.config.path_prefix
|
791
|
+
camelize_prefix = ::Brick.config.path_prefix.camelize
|
792
|
+
# Asking for the prefix module?
|
793
|
+
if self == Object && requested == camelize_prefix
|
794
|
+
Object.const_set(args.first, (built_module = Module.new))
|
795
|
+
puts "module #{camelize_prefix}; end\n"
|
796
|
+
return built_module
|
797
|
+
end
|
798
|
+
split_self_name.shift if (split_self_name = self.name.split('::')).first.blank?
|
799
|
+
if split_self_name.first == camelize_prefix
|
800
|
+
split_self_name.shift # Remove the identified path prefix from the split name
|
801
|
+
if is_controller
|
802
|
+
brick_root = split_self_name.empty? ? self : camelize_prefix.constantize
|
803
|
+
end
|
804
|
+
end
|
805
|
+
end
|
806
|
+
base_module = if self < ActiveRecord::Migration || !self.name
|
807
|
+
brick_root || Object
|
808
|
+
elsif (split_self_name || self.name.split('::')).length > 1
|
809
|
+
return self._brick_const_missing(*args)
|
810
|
+
else
|
811
|
+
self
|
812
|
+
end
|
813
|
+
desired_classname = (self == Object) ? requested : "#{name}::#{requested}"
|
787
814
|
if ((is_defined = self.const_defined?(args.first)) && (possible = self.const_get(args.first)) && possible.name == desired_classname) ||
|
788
815
|
# Try to require the respective Ruby file
|
789
816
|
((filename = ActiveSupport::Dependencies.search_for_file(desired_classname.underscore) ||
|
790
|
-
(self != Object && ActiveSupport::Dependencies.search_for_file((desired_classname =
|
817
|
+
(self != Object && ActiveSupport::Dependencies.search_for_file((desired_classname = requested).underscore))
|
791
818
|
) && (require_dependency(filename) || true) &&
|
792
819
|
((possible = self.const_get(args.first)) && possible.name == desired_classname)
|
793
820
|
) ||
|
794
821
|
# If any class has turned up so far (and we're not in the middle of eager loading)
|
795
822
|
# then return what we've found.
|
796
|
-
(is_defined && !::Brick.is_eager_loading)
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
# checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
804
|
-
# that is, checking #qualified_name_for with: from_mod, const_name
|
805
|
-
# If we want to support namespacing in the future, might have to utilise something like this:
|
806
|
-
# path_suffix = ActiveSupport::Dependencies.qualified_name_for(Object, args.first).underscore
|
807
|
-
# return self._brick_const_missing(*args) if ActiveSupport::Dependencies.search_for_file(path_suffix)
|
808
|
-
# If the file really exists, go and snag it:
|
809
|
-
if ActiveSupport::Dependencies.search_for_file(class_name.underscore)
|
810
|
-
return base_module._brick_const_missing(*args)
|
811
|
-
# elsif ActiveSupport::Dependencies.search_for_file(filepath) # Last-ditch effort to pick this thing up before we fill in the gaps on our own
|
812
|
-
# my_const = parent.const_missing(class_name) # ends up having: MyModule::MyClass
|
813
|
-
# return my_const
|
814
|
-
else
|
815
|
-
filepath = base_module.name&.split('::')&.[](0..-2) unless base_module == Object
|
816
|
-
filepath = ((filepath || []) + [class_name]).join('/').underscore + '.rb'
|
817
|
-
if ActiveSupport::Dependencies.search_for_file(filepath) # Last-ditch effort to pick this thing up before we fill in the gaps on our own
|
818
|
-
return base_module._brick_const_missing(*args)
|
823
|
+
(is_defined && !::Brick.is_eager_loading) # Used to also have: && possible != self
|
824
|
+
if (!brick_root && (filename || possible.instance_of?(Class))) ||
|
825
|
+
(possible.instance_of?(Module) &&
|
826
|
+
((possible.respond_to?(:module_parent) ? possible.module_parent : possible.parent) == self)
|
827
|
+
) ||
|
828
|
+
(possible.instance_of?(Class) && possible == self) # Are we simply searching for ourselves?
|
829
|
+
return possible
|
819
830
|
end
|
820
831
|
end
|
821
|
-
|
832
|
+
class_name = ::Brick.namify(requested)
|
822
833
|
relations = ::Brick.relations
|
823
|
-
|
824
|
-
|
834
|
+
result = if ::Brick.enable_controllers? &&
|
835
|
+
is_controller && (plural_class_name = class_name[0..-11]).length.positive?
|
825
836
|
# Otherwise now it's up to us to fill in the gaps
|
837
|
+
full_class_name = +''
|
838
|
+
full_class_name << "::#{(split_self_name&.first && split_self_name.join('::')) || self.name}" unless self == Object
|
826
839
|
# (Go over to underscores for a moment so that if we have something come in like VABCsController then the model name ends up as
|
827
840
|
# Vabc instead of VABC)
|
828
|
-
full_class_name = +''
|
829
|
-
full_class_name << "::#{self.name}" unless self == Object
|
830
841
|
singular_class_name = ::Brick.namify(plural_class_name, :underscore).singularize.camelize
|
831
842
|
full_class_name << "::#{singular_class_name}"
|
832
843
|
if plural_class_name == 'BrickSwagger' ||
|
@@ -929,7 +940,9 @@ class Object
|
|
929
940
|
schema_name
|
930
941
|
else
|
931
942
|
matching = "#{schema_name}.#{matching}"
|
932
|
-
|
943
|
+
# %%% Coming up with integers when tables are in schemas
|
944
|
+
# ::Brick.db_schemas[schema_name] ||= self.const_get(schema_name.camelize.to_sym)
|
945
|
+
self.const_get(schema_name.camelize)
|
933
946
|
end
|
934
947
|
"#{schema_module&.name}::#{inheritable_name || model_name}"
|
935
948
|
end
|
@@ -1197,8 +1210,7 @@ class Object
|
|
1197
1210
|
is_postgres = ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
1198
1211
|
is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2'
|
1199
1212
|
|
1200
|
-
|
1201
|
-
code = +"class #{namespace_name}#{class_name} < ApplicationController\n"
|
1213
|
+
code = +"class #{class_name} < ApplicationController\n"
|
1202
1214
|
built_controller = Class.new(ActionController::Base) do |new_controller_class|
|
1203
1215
|
(namespace || Object).const_set(class_name.to_sym, new_controller_class)
|
1204
1216
|
|
@@ -1341,13 +1353,6 @@ class Object
|
|
1341
1353
|
code << " end\n"
|
1342
1354
|
self.define_method :show do
|
1343
1355
|
::Brick.set_db_schema(params)
|
1344
|
-
id = if model.columns_hash[pk.first]&.type == :string
|
1345
|
-
is_pk_string = true
|
1346
|
-
params[:id]
|
1347
|
-
else
|
1348
|
-
params[:id]&.split(/[\/,_]/)
|
1349
|
-
end
|
1350
|
-
id = id.first if id.is_a?(Array) && id.length == 1
|
1351
1356
|
instance_variable_set("@#{singular_table_name}".to_sym, find_obj)
|
1352
1357
|
end
|
1353
1358
|
end
|
@@ -1442,7 +1447,14 @@ class Object
|
|
1442
1447
|
@#{singular_table_name} = #{model.name}.find(id.is_a?(Array) && id.length == 1 ? id.first : id)
|
1443
1448
|
end\n"
|
1444
1449
|
self.define_method :find_obj do
|
1445
|
-
id =
|
1450
|
+
id = if model.columns_hash[pk.first]&.type == :string
|
1451
|
+
is_pk_string = true
|
1452
|
+
params[:id].gsub('^^sl^^', '/')
|
1453
|
+
else
|
1454
|
+
params[:id]&.split(/[\/,_]/).map do |val_part|
|
1455
|
+
val_part.gsub('^^sl^^', '/')
|
1456
|
+
end
|
1457
|
+
end
|
1446
1458
|
model.find(id.is_a?(Array) && id.length == 1 ? id.first : id)
|
1447
1459
|
end
|
1448
1460
|
end
|
@@ -1465,33 +1477,55 @@ class Object
|
|
1465
1477
|
# Get column names for params from relations[model.table_name][:cols].keys
|
1466
1478
|
end
|
1467
1479
|
end # unless is_swagger
|
1468
|
-
code << "end # #{
|
1480
|
+
code << "end # #{class_name}\n"
|
1469
1481
|
end # class definition
|
1470
1482
|
[built_controller, code]
|
1471
1483
|
end
|
1472
1484
|
|
1473
1485
|
def _brick_get_hm_assoc_name(relation, hm_assoc, source = nil)
|
1474
|
-
if (relation[:hm_counts][hm_assoc[:inverse_table]]&.> 1) &&
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1486
|
+
assoc_name, needs_class = if (relation[:hm_counts][hm_assoc[:inverse_table]]&.> 1) &&
|
1487
|
+
hm_assoc[:alternate_name] != (source || name.underscore)
|
1488
|
+
plural = "#{hm_assoc[:assoc_name]}_#{ActiveSupport::Inflector.pluralize(hm_assoc[:alternate_name])}"
|
1489
|
+
new_alt_name = (hm_assoc[:alternate_name] == name.underscore) ? "#{hm_assoc[:assoc_name].singularize}_#{plural}" : plural
|
1490
|
+
# uniq = 1
|
1491
|
+
# while same_name = relation[:fks].find { |x| x.last[:assoc_name] == hm_assoc[:assoc_name] && x.last != hm_assoc }
|
1492
|
+
# hm_assoc[:assoc_name] = "#{hm_assoc_name}_#{uniq += 1}"
|
1493
|
+
# end
|
1494
|
+
# puts new_alt_name
|
1495
|
+
# hm_assoc[:assoc_name] = new_alt_name
|
1496
|
+
[new_alt_name, true]
|
1497
|
+
else
|
1498
|
+
assoc_name = ::Brick.namify(hm_assoc[:inverse_table]).pluralize
|
1499
|
+
if (needs_class = assoc_name.include?('.')) # If there is a schema name present, use a downcased version for the :has_many
|
1500
|
+
assoc_parts = assoc_name.split('.')
|
1501
|
+
assoc_parts[0].downcase! if assoc_parts[0] =~ /^[A-Z0-9_]+$/
|
1502
|
+
assoc_name = assoc_parts.join('.')
|
1503
|
+
end
|
1504
|
+
# hm_assoc[:assoc_name] = assoc_name
|
1505
|
+
[assoc_name, needs_class]
|
1506
|
+
end
|
1507
|
+
# Already have the HM class around?
|
1508
|
+
begin
|
1509
|
+
if (hm_class = Object._brick_const_missing(hm_class_name = relation[:class_name].to_sym))
|
1510
|
+
existing_hm_assocs = hm_class.reflect_on_all_associations.select do |assoc|
|
1511
|
+
assoc.macro != :belongs_to && assoc.klass == self && assoc.foreign_key == hm_assoc[:fk]
|
1512
|
+
end
|
1513
|
+
# Missing a has_many in an existing class?
|
1514
|
+
if existing_hm_assocs.empty?
|
1515
|
+
options = { inverse_of: hm_assoc[:inverse][:assoc_name].to_sym }
|
1516
|
+
# Add class_name and foreign_key where necessary
|
1517
|
+
unless hm_assoc[:alternate_name] == (source || name.underscore)
|
1518
|
+
options[:class_name] = self.name
|
1519
|
+
options[:foreign_key] = hm_assoc[:fk].to_sym
|
1520
|
+
end
|
1521
|
+
hm_class.send(:has_many, assoc_name.to_sym, options)
|
1522
|
+
puts "# ** Adding a missing has_many to #{hm_class.name}:\nclass #{hm_class.name} < #{hm_class.superclass.name}"
|
1523
|
+
puts " has_many :#{assoc_name}, #{options.inspect}\nend\n"
|
1524
|
+
end
|
1491
1525
|
end
|
1492
|
-
|
1493
|
-
[assoc_name, needs_class]
|
1526
|
+
rescue NameError
|
1494
1527
|
end
|
1528
|
+
[assoc_name, needs_class]
|
1495
1529
|
end
|
1496
1530
|
end
|
1497
1531
|
end
|
@@ -1504,6 +1538,7 @@ module ActiveRecord::ConnectionHandling
|
|
1504
1538
|
alias _brick_establish_connection establish_connection
|
1505
1539
|
def establish_connection(*args)
|
1506
1540
|
conn = _brick_establish_connection(*args)
|
1541
|
+
begin
|
1507
1542
|
# Overwrite SQLite's #begin_db_transaction so it opens in IMMEDIATE mode instead of
|
1508
1543
|
# the default DEFERRED mode.
|
1509
1544
|
# https://discuss.rubyonrails.org/t/failed-write-transaction-upgrades-in-sqlite3/81480/2
|
@@ -1525,9 +1560,10 @@ module ActiveRecord::ConnectionHandling
|
|
1525
1560
|
end
|
1526
1561
|
end
|
1527
1562
|
end
|
1528
|
-
|
1563
|
+
# ::Brick.is_db_present = true
|
1529
1564
|
_brick_reflect_tables
|
1530
1565
|
rescue ActiveRecord::NoDatabaseError
|
1566
|
+
# ::Brick.is_db_present = false
|
1531
1567
|
end
|
1532
1568
|
conn
|
1533
1569
|
end
|
@@ -1535,6 +1571,8 @@ module ActiveRecord::ConnectionHandling
|
|
1535
1571
|
# This is done separately so that during testing it can be called right after a migration
|
1536
1572
|
# in order to make sure everything is good.
|
1537
1573
|
def _brick_reflect_tables
|
1574
|
+
# return if ActiveRecord::Base.connection.current_database == 'postgres'
|
1575
|
+
|
1538
1576
|
initializer_loaded = false
|
1539
1577
|
if (relations = ::Brick.relations).empty?
|
1540
1578
|
# If there's schema things configured then we only expect our initializer to be named exactly this
|
@@ -1543,8 +1581,8 @@ module ActiveRecord::ConnectionHandling
|
|
1543
1581
|
end
|
1544
1582
|
# Load the initializer for the Apartment gem a little early so that if .excluded_models and
|
1545
1583
|
# .default_schema are specified then we can work with non-tenanted models more appropriately
|
1546
|
-
apartment = Object.const_defined?('Apartment')
|
1547
|
-
|
1584
|
+
if (apartment = Object.const_defined?('Apartment')) &&
|
1585
|
+
File.exist?(apartment_initializer = ::Rails.root.join('config/initializers/apartment.rb'))
|
1548
1586
|
load apartment_initializer
|
1549
1587
|
apartment_excluded = Apartment.excluded_models
|
1550
1588
|
end
|
@@ -1556,19 +1594,21 @@ module ActiveRecord::ConnectionHandling
|
|
1556
1594
|
case ActiveRecord::Base.connection.adapter_name
|
1557
1595
|
when 'PostgreSQL', 'SQLServer'
|
1558
1596
|
is_postgres = !is_mssql
|
1559
|
-
db_schemas =
|
1597
|
+
db_schemas = if is_postgres
|
1598
|
+
ActiveRecord::Base.execute_sql('SELECT nspname AS table_schema, MAX(oid) AS dt FROM pg_namespace GROUP BY 1 ORDER BY 1;')
|
1599
|
+
else
|
1600
|
+
ActiveRecord::Base.execute_sql('SELECT DISTINCT table_schema, NULL AS dt FROM INFORMATION_SCHEMA.tables;')
|
1601
|
+
end
|
1560
1602
|
::Brick.db_schemas = db_schemas.each_with_object({}) do |row, s|
|
1561
1603
|
row = case row
|
1562
|
-
when String
|
1563
|
-
row
|
1564
1604
|
when Array
|
1565
|
-
row
|
1605
|
+
row
|
1566
1606
|
else
|
1567
|
-
row['table_schema']
|
1607
|
+
[row['table_schema'], row['dt']]
|
1568
1608
|
end
|
1569
1609
|
# Remove any system schemas
|
1570
|
-
s[row] =
|
1571
|
-
|
1610
|
+
s[row.first] = row.last unless ['information_schema', 'pg_catalog', 'pg_toast',
|
1611
|
+
'INFORMATION_SCHEMA', 'sys'].include?(row.first)
|
1572
1612
|
end
|
1573
1613
|
if (is_multitenant = (multitenancy = ::Brick.config.schema_behavior[:multitenant]) &&
|
1574
1614
|
(sta = multitenancy[:schema_to_analyse]) != 'public') &&
|
@@ -1602,6 +1642,10 @@ module ActiveRecord::ConnectionHandling
|
|
1602
1642
|
if ::Brick.db_schemas.key?(possible_schema)
|
1603
1643
|
::Brick.default_schema = schema = possible_schema
|
1604
1644
|
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", schema)
|
1645
|
+
elsif Rails.env == 'test' # When testing, just find the most recently-created schema
|
1646
|
+
::Brick.default_schema = schema = ::Brick.db_schemas.to_a.sort { |a, b| b.last <=> a.last }.first.first
|
1647
|
+
puts "While running tests, had noticed that in the brick.rb initializer the line \"::Brick.schema_behavior = ...\" refers to a schema called \"#{possible_schema}\" which does not exist. Reverting to instead use the most recently-created schema, #{schema}."
|
1648
|
+
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", schema)
|
1605
1649
|
else
|
1606
1650
|
puts "*** In the brick.rb initializer the line \"::Brick.schema_behavior = ...\" refers to a schema called \"#{possible_schema}\". This schema does not exist. ***"
|
1607
1651
|
end
|
@@ -1614,7 +1658,7 @@ module ActiveRecord::ConnectionHandling
|
|
1614
1658
|
measures = []
|
1615
1659
|
::Brick.is_oracle = true if ActiveRecord::Base.connection.adapter_name == 'OracleEnhanced'
|
1616
1660
|
case ActiveRecord::Base.connection.adapter_name
|
1617
|
-
when 'PostgreSQL', 'SQLite' # These bring back a hash for each row because the query uses column aliases
|
1661
|
+
when 'PostgreSQL', 'SQLite', 'SQLServer' # These bring back a hash for each row because the query uses column aliases
|
1618
1662
|
# schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
1619
1663
|
ActiveRecord::Base.retrieve_schema_and_tables(sql, is_postgres, is_mssql, schema).each do |r|
|
1620
1664
|
# If Apartment gem lists the table as being associated with a non-tenanted model then use whatever it thinks
|
@@ -1823,7 +1867,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1823
1867
|
LEFT OUTER JOIN INFORMATION_SCHEMA.columns AS c ON t.table_schema = c.table_schema
|
1824
1868
|
AND t.table_name = c.table_name
|
1825
1869
|
LEFT OUTER JOIN
|
1826
|
-
(SELECT kcu1.constraint_schema, kcu1.table_name, kcu1.ordinal_position,
|
1870
|
+
(SELECT kcu1.constraint_schema, kcu1.table_name, kcu1.column_name, kcu1.ordinal_position,
|
1827
1871
|
tc.constraint_type, kcu1.constraint_name
|
1828
1872
|
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu1
|
1829
1873
|
INNER JOIN INFORMATION_SCHEMA.table_constraints AS tc
|
@@ -1834,9 +1878,9 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1834
1878
|
) AS kcu ON
|
1835
1879
|
-- kcu.CONSTRAINT_CATALOG = t.table_catalog AND
|
1836
1880
|
kcu.CONSTRAINT_SCHEMA = c.table_schema
|
1837
|
-
AND kcu.TABLE_NAME = c.table_name
|
1881
|
+
AND kcu.TABLE_NAME = c.table_name
|
1882
|
+
AND kcu.column_name = c.column_name#{"
|
1838
1883
|
-- AND kcu.position_in_unique_constraint IS NULL" unless is_mssql}
|
1839
|
-
AND kcu.ordinal_position = c.ordinal_position
|
1840
1884
|
WHERE t.table_schema #{is_postgres || is_mssql ?
|
1841
1885
|
"NOT IN ('information_schema', 'pg_catalog',
|
1842
1886
|
'INFORMATION_SCHEMA', 'sys')"
|
@@ -1845,7 +1889,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
1845
1889
|
AND t.table_schema = COALESCE(current_setting('SEARCH_PATH'), 'public')" if is_postgres && schema }
|
1846
1890
|
-- AND t.table_type IN ('VIEW') -- 'BASE TABLE', 'FOREIGN TABLE'
|
1847
1891
|
AND t.table_name NOT IN ('pg_stat_statements', ?, ?)
|
1848
|
-
ORDER BY 1, t.table_type DESC, 2,
|
1892
|
+
ORDER BY 1, t.table_type DESC, 2, kcu.ordinal_position"
|
1849
1893
|
ActiveRecord::Base.execute_sql(sql, *ar_tables)
|
1850
1894
|
end
|
1851
1895
|
|
@@ -2090,7 +2134,7 @@ module Brick
|
|
2090
2134
|
end
|
2091
2135
|
end
|
2092
2136
|
end
|
2093
|
-
::Brick.relations.keys.map { |v| [(
|
2137
|
+
::Brick.relations.keys.map { |v| [(model = models[v])&.last, model&.last&.table_name, migrations&.fetch(v, nil), model&.first] }
|
2094
2138
|
end
|
2095
2139
|
|
2096
2140
|
def ensure_unique(name, *sources)
|
@@ -114,7 +114,7 @@ module Brick
|
|
114
114
|
if @_brick_model
|
115
115
|
pk = @_brick_model._brick_primary_key(::Brick.relations.fetch(@_brick_model&.table_name, nil))
|
116
116
|
obj_name = model_name.split('::').last.underscore
|
117
|
-
path_obj_name =
|
117
|
+
path_obj_name = @_brick_model._brick_index(:singular)
|
118
118
|
table_name = obj_name.pluralize
|
119
119
|
template_link = nil
|
120
120
|
bts, hms = ::Brick.get_bts_and_hms(@_brick_model) # This gets BT and HM and also has_many :through (HMT)
|
@@ -125,9 +125,11 @@ module Brick
|
|
125
125
|
"H#{hm_assoc.macro == :has_one ? 'O' : 'M'}#{'T' if hm_assoc.options[:through]}",
|
126
126
|
(assoc_name = hm.first)]
|
127
127
|
hm_fk_name = if (through = hm_assoc.options[:through])
|
128
|
-
|
128
|
+
# %%% How to deal with weird self_ref type has_many -> has_one polymorphic stuff?
|
129
|
+
# (or perhaps we don't need to!)
|
130
|
+
next unless @_brick_model.instance_methods.include?(through) &&
|
131
|
+
(associative = @_brick_model._br_associatives.fetch(hm.first, nil))
|
129
132
|
|
130
|
-
associative = @_brick_model._br_associatives[hm.first]
|
131
133
|
tbl_nm = if hm_assoc.options[:source]
|
132
134
|
associative.klass.reflect_on_association(hm_assoc.options[:source]).inverse_of&.name
|
133
135
|
else
|
@@ -177,6 +179,7 @@ module Brick
|
|
177
179
|
# %%% If we are not auto-creating controllers (or routes) then omit by default, and if enabled anyway, such as in a development
|
178
180
|
# environment or whatever, then get either the controllers or routes list instead
|
179
181
|
apartment_default_schema = ::Brick.apartment_multitenant && Apartment.default_schema
|
182
|
+
prefix = "#{::Brick.config.path_prefix}/" if ::Brick.config.path_prefix
|
180
183
|
table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables).each_with_object({}) do |tbl, s|
|
181
184
|
binding.pry if tbl.is_a?(Symbol)
|
182
185
|
if (tbl_parts = tbl.split('.')).first == apartment_default_schema
|
@@ -184,7 +187,7 @@ module Brick
|
|
184
187
|
end
|
185
188
|
s[tbl] = nil
|
186
189
|
end.keys.sort.each_with_object(+'') do |v, s|
|
187
|
-
s << "<option value=\"#{v.underscore.gsub('.', '/')}\">#{v}</option>"
|
190
|
+
s << "<option value=\"#{prefix}#{v.underscore.gsub('.', '/')}\">#{v}</option>"
|
188
191
|
end.html_safe
|
189
192
|
table_options << '<option value="brick_status">(Status)</option>'.html_safe if ::Brick.config.add_status
|
190
193
|
table_options << '<option value="brick_orphans">(Orphans)</option>'.html_safe if is_orphans
|
@@ -405,6 +408,11 @@ def display_value(col_type, val)
|
|
405
408
|
end
|
406
409
|
end
|
407
410
|
end
|
411
|
+
# Accommodate composite primary keys that include strings with forward-slash characters
|
412
|
+
def slashify(val)
|
413
|
+
val = [val] unless val.is_a?(Array)
|
414
|
+
val.map { |val_part| val_part.is_a?(String) ? val_part.gsub('/', '^^sl^^') : val_part }
|
415
|
+
end
|
408
416
|
callbacks = {} %>"
|
409
417
|
|
410
418
|
if ['index', 'show', 'new', 'update'].include?(args.first)
|
@@ -461,7 +469,7 @@ window.addEventListener(\"pageshow\", function() {
|
|
461
469
|
});
|
462
470
|
|
463
471
|
if (tblSelect) { // Always present
|
464
|
-
var i = schemaSelect ? 1 : 0,
|
472
|
+
var i = #{::Brick.config.path_prefix ? '0' : 'schemaSelect ? 1 : 0'},
|
465
473
|
changeoutList = changeout(location.href);
|
466
474
|
for (; i < changeoutList.length; ++i) {
|
467
475
|
tblSelect.value = changeoutList[i];
|
@@ -523,15 +531,19 @@ if (grid) {
|
|
523
531
|
function gridMove(evt) {
|
524
532
|
var lastHighCell = gridHighCell;
|
525
533
|
gridHighCell = document.elementFromPoint(evt.x, evt.y);
|
526
|
-
|
527
|
-
gridHighCell.
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
534
|
+
while (gridHighCell && gridHighCell.tagName !== \"TD\" && gridHighCell.tagName !== \"TH\")
|
535
|
+
gridHighCell = gridHighCell.parentElement;
|
536
|
+
if (gridHighCell) {
|
537
|
+
if (lastHighCell !== gridHighCell) {
|
538
|
+
gridHighCell.classList.add(\"highlight\");
|
539
|
+
if (lastHighCell) lastHighCell.classList.remove(\"highlight\");
|
540
|
+
}
|
541
|
+
var lastHighHeader = gridHighHeader;
|
542
|
+
gridHighHeader = headerCols[gridHighCell.cellIndex];
|
543
|
+
if (lastHighHeader !== gridHighHeader) {
|
544
|
+
if (gridHighHeader) gridHighHeader.classList.add(\"highlight\");
|
545
|
+
if (lastHighHeader) lastHighHeader.classList.remove(\"highlight\");
|
546
|
+
}
|
535
547
|
}
|
536
548
|
}
|
537
549
|
}
|
@@ -849,24 +861,25 @@ erDiagram
|
|
849
861
|
@#{table_name}.each do |#{obj_name}|
|
850
862
|
hms_cols = {#{hms_columns.join(', ')}} %>
|
851
863
|
<tr>#{"
|
852
|
-
<td><%= link_to '⇛', #{path_obj_name}_path(#{obj_pk}), { class: 'big-arrow' } %></td>" if obj_pk}
|
864
|
+
<td><%= link_to '⇛', #{path_obj_name}_path(slashify(#{obj_pk})), { class: 'big-arrow' } %></td>" if obj_pk}
|
853
865
|
<% @_brick_sequence.each do |col_name|
|
854
866
|
val = #{obj_name}.attributes[col_name] %>
|
855
867
|
<td<%= ' class=\"dimmed\"'.html_safe unless cols.key?(col_name) || (cust_col = cust_cols[col_name])%>><%
|
856
868
|
if (bt = bts[col_name])
|
857
869
|
if bt[2] # Polymorphic?
|
858
|
-
bt_class = #{obj_name}.send(\"#\{bt.first
|
859
|
-
|
860
|
-
poly_id = #{obj_name}.send(\"#\{bt.first
|
861
|
-
%><%= link_to(\"#\{bt_class
|
870
|
+
bt_class = #{obj_name}.send(\"#\{bt.first}_type\")
|
871
|
+
base_class_underscored = (::Brick.existing_stis[bt_class] || bt_class).constantize.base_class._brick_index(:singular)
|
872
|
+
poly_id = #{obj_name}.send(\"#\{bt.first}_id\")
|
873
|
+
%><%= link_to(\"#\{bt_class} ##\{poly_id}\", send(\"#\{base_class_underscored}_path\".to_sym, poly_id)) if poly_id %><%
|
862
874
|
else
|
875
|
+
# binding.pry if @_brick_bt_descrip[bt.first][bt[1].first.first].nil?
|
863
876
|
bt_txt = (bt_class = bt[1].first.first).brick_descrip(
|
864
877
|
# 0..62 because Postgres column names are limited to 63 characters
|
865
878
|
#{obj_name}, (descrips = @_brick_bt_descrip[bt.first][bt_class])[0..-2].map { |id| #{obj_name}.send(id.last[0..62]) }, (bt_id_col = descrips.last)
|
866
879
|
)
|
867
880
|
bt_txt ||= \"<span class=\\\"orphan\\\"><< Orphaned ID: #\{val} >></span>\".html_safe if val
|
868
881
|
bt_id = bt_id_col.map { |id_col| #{obj_name}.send(id_col.to_sym) } %>
|
869
|
-
<%= bt_id&.first ? link_to(bt_txt, send(\"#\{bt_class.base_class.
|
882
|
+
<%= bt_id&.first ? link_to(bt_txt, send(\"#\{bt_class.base_class._brick_index(:singular)}_path\".to_sym, bt_id)) : bt_txt %>
|
870
883
|
<% end
|
871
884
|
elsif (hms_col = hms_cols[col_name])
|
872
885
|
if hms_col.length == 1 %>
|
@@ -877,9 +890,9 @@ erDiagram
|
|
877
890
|
descrips = @_brick_bt_descrip[col_name.to_sym][klass]
|
878
891
|
ho_txt = klass.brick_descrip(#{obj_name}, descrips[0..-2].map { |id| #{obj_name}.send(id.last[0..62]) }, (ho_id_col = descrips.last))
|
879
892
|
ho_id = ho_id_col.map { |id_col| #{obj_name}.send(id_col.to_sym) }
|
880
|
-
ho_id&.first ? link_to(ho_txt, send(\"#\{klass.base_class.
|
893
|
+
ho_id&.first ? link_to(ho_txt, send(\"#\{klass.base_class._brick_index(:singular)}_path\".to_sym, ho_id)) : ho_txt
|
881
894
|
else
|
882
|
-
\"#\{hms_col[1] || 'View'
|
895
|
+
\"#\{hms_col[1] || 'View'} #\{hms_col.first}\"
|
883
896
|
end %>
|
884
897
|
<%= link_to txt, send(\"#\{klass._brick_index}_path\".to_sym, hms_col[2]) unless hms_col[1]&.zero? %>
|
885
898
|
<% end
|
@@ -927,7 +940,7 @@ erDiagram
|
|
927
940
|
@resources.each do |r|
|
928
941
|
%>
|
929
942
|
<tr>
|
930
|
-
<td><%= link_to(r[0], \"
|
943
|
+
<td><%= link_to(r[0], r[0] && send(\"#\{r[0]&._brick_index}_path\".to_sym)) %></td>
|
931
944
|
<td<%= if r[1]
|
932
945
|
' class=\"orphan\"' unless ::Brick.relations.key?(r[1])
|
933
946
|
else
|
@@ -984,12 +997,15 @@ if (description = (relation = Brick.relations[#{model_name}.table_name])&.fetch(
|
|
984
997
|
end
|
985
998
|
%><%= link_to '(See all #{obj_name.pluralize})', #{@_brick_model._brick_index}_path %>
|
986
999
|
#{erd_markup}
|
987
|
-
<% if obj
|
1000
|
+
<% if obj
|
1001
|
+
# path_options = [obj.#{pk}]
|
1002
|
+
# path_options << { '_brick_schema': } if
|
1003
|
+
# url = send(:#\{model_name._brick_index(:singular)}_path, obj.#{pk})
|
1004
|
+
options = {}
|
1005
|
+
options[:url] = send(\"#\{#{model_name}._brick_index(:singular)}_path\".to_sym, obj) if ::Brick.config.path_prefix
|
1006
|
+
%>
|
988
1007
|
<br><br>
|
989
|
-
<%= #
|
990
|
-
# path_options << { '_brick_schema': } if
|
991
|
-
# url = send(:#{model_name.underscore}_path, obj.#{pk})
|
992
|
-
form_for(obj.becomes(#{model_name})) do |f| %>
|
1008
|
+
<%= form_for(obj.becomes(#{model_name}), options) do |f| %>
|
993
1009
|
<table class=\"shadow\">
|
994
1010
|
<% has_fields = false
|
995
1011
|
@#{obj_name}.attributes.each do |k, val|
|
@@ -1008,7 +1024,7 @@ end
|
|
1008
1024
|
bt_pair = nil
|
1009
1025
|
loop do
|
1010
1026
|
bt_pair = bt[1].find { |pair| pair.first.name == poly_class_name }
|
1011
|
-
#
|
1027
|
+
# Accommodate any valid STI by going up the chain of inheritance
|
1012
1028
|
break unless bt_pair.nil? && poly_class_name = ::Brick.existing_stis[poly_class_name]
|
1013
1029
|
end
|
1014
1030
|
puts \"*** Might be missing an STI class called #\{orig_poly_name\} whose base class should have this:
|
@@ -1043,7 +1059,7 @@ end
|
|
1043
1059
|
html_options[:prompt] = \"Select #\{bt_name\}\" %>
|
1044
1060
|
<%= f.select k.to_sym, bt[3], { value: val || '^^^brick_NULL^^^' }, html_options %>
|
1045
1061
|
<%= if (bt_obj = bt_class&.find_by(bt_pair[1] => val))
|
1046
|
-
link_to('⇛', send(\"#\{bt_class.base_class.
|
1062
|
+
link_to('⇛', send(\"#\{bt_class.base_class._brick_index(:singular)\}_path\".to_sym, bt_obj.send(bt_class.primary_key.to_sym)), { class: 'show-arrow' })
|
1047
1063
|
elsif val
|
1048
1064
|
\"<span class=\\\"orphan\\\">Orphaned ID: #\{val}</span>\".html_safe
|
1049
1065
|
end %>
|
@@ -1101,9 +1117,10 @@ end
|
|
1101
1117
|
<tr><td colspan=\"2\">(No displayable fields)</td></tr>
|
1102
1118
|
<% end %>
|
1103
1119
|
</table>
|
1104
|
-
|
1120
|
+
<% end %>
|
1105
1121
|
|
1106
|
-
|
1122
|
+
#{unless args.first == 'new'
|
1123
|
+
hms_headers.each_with_object(+'') do |hm, s|
|
1107
1124
|
# %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
|
1108
1125
|
next if hm.first.options[:through] && !hm.first.through_reflection
|
1109
1126
|
|
@@ -1118,14 +1135,15 @@ end
|
|
1118
1135
|
<tr><td>(none)</td></tr>
|
1119
1136
|
<% else %>
|
1120
1137
|
<% collection.uniq.each do |#{hm_singular_name}| %>
|
1121
|
-
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm.first.klass.
|
1138
|
+
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm.first.klass._brick_index(:singular)}_path(slashify(#{obj_pk}))) %></td></tr>
|
1122
1139
|
<% end %>
|
1123
1140
|
<% end %>
|
1124
1141
|
</table>"
|
1125
1142
|
else
|
1126
1143
|
s
|
1127
1144
|
end
|
1128
|
-
end
|
1145
|
+
end
|
1146
|
+
end}
|
1129
1147
|
<% end %>
|
1130
1148
|
#{script}"
|
1131
1149
|
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -216,6 +216,12 @@ module Brick
|
|
216
216
|
true
|
217
217
|
end
|
218
218
|
|
219
|
+
# Any path prefixing to apply to all auto-generated Brick routes
|
220
|
+
# @api public
|
221
|
+
def path_prefix=(path)
|
222
|
+
Brick.config.path_prefix = path
|
223
|
+
end
|
224
|
+
|
219
225
|
# Switches Brick auto-models on or off, for all threads
|
220
226
|
# @api public
|
221
227
|
def enable_models=(value)
|
@@ -538,23 +544,40 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
538
544
|
view_class_length = 37 # Length of "Classes that can be built from views:"
|
539
545
|
existing_controllers = routes.each_with_object({}) { |r, s| c = r.defaults[:controller]; s[c] = nil if c }
|
540
546
|
::Rails.application.routes.append do
|
547
|
+
brick_routes_create = lambda do |schema_name, controller_name, v, options|
|
548
|
+
if schema_name # && !Object.const_defined('Apartment')
|
549
|
+
send(:namespace, schema_name) do
|
550
|
+
send(:resources, v[:resource].to_sym, **options)
|
551
|
+
end
|
552
|
+
else
|
553
|
+
send(:resources, v[:resource].to_sym, **options)
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
541
557
|
# %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
|
542
558
|
# If auto-controllers and auto-models are both enabled then this makes sense:
|
543
559
|
::Brick.relations.each do |k, v|
|
544
560
|
unless !(controller_name = v.fetch(:resource, nil)&.pluralize) || existing_controllers.key?(controller_name)
|
545
561
|
options = {}
|
546
562
|
options[:only] = [:index, :show] if v.key?(:isView)
|
563
|
+
# First do the API routes
|
547
564
|
full_resource = nil
|
548
|
-
|
549
|
-
|
550
|
-
send(:resources, v[:resource].to_sym, **options)
|
551
|
-
end
|
565
|
+
controller_prefix = (::Brick.config.path_prefix ? "#{::Brick.config.path_prefix}/" : '')
|
566
|
+
if (schema_name = v.fetch(:schema, nil))
|
552
567
|
full_resource = "#{schema_name}/#{v[:resource]}"
|
553
|
-
send(:get, "#{::Brick.api_root}#{full_resource}", { to: "#{schema_name}/#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
|
568
|
+
send(:get, "#{::Brick.api_root}#{full_resource}", { to: "#{controller_prefix}#{schema_name}/#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
|
554
569
|
else
|
555
|
-
send(:resources, v[:resource].to_sym, **options)
|
556
570
|
# Normally goes to something like: /api/v1/employees
|
557
|
-
send(:get, "#{::Brick.api_root}#{v[:resource]}", { to: "#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
|
571
|
+
send(:get, "#{::Brick.api_root}#{v[:resource]}", { to: "#{controller_prefix}#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
|
572
|
+
end
|
573
|
+
# Now the normal routes
|
574
|
+
if ::Brick.config.path_prefix
|
575
|
+
# Was: send(:scope, path: ::Brick.config.path_prefix) do
|
576
|
+
send(:namespace, ::Brick.config.path_prefix) do
|
577
|
+
brick_routes_create.call(schema_name, controller_name, v, options)
|
578
|
+
end
|
579
|
+
else
|
580
|
+
brick_routes_create.call(schema_name, controller_name, v, options)
|
558
581
|
end
|
559
582
|
|
560
583
|
if (class_name = v.fetch(:class_name, nil))
|
@@ -139,6 +139,10 @@ module Brick
|
|
139
139
|
# # Settings for the Brick gem
|
140
140
|
# # (By default this auto-creates models, controllers, views, and routes on-the-fly.)
|
141
141
|
|
142
|
+
# # Custom path prefix to apply to all auto-generated Brick routes. Also causes auto-generated controllers
|
143
|
+
# # to be created inside a module with the same name.
|
144
|
+
# ::Brick.path_prefix = 'admin'
|
145
|
+
|
142
146
|
# # Normally all are enabled in development mode, and for security reasons only models are enabled in production
|
143
147
|
# # and test. This allows you to either (a) turn off models entirely, or (b) enable controllers, views, and routes
|
144
148
|
# # in production.
|
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.77
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lorin Thwaits
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-10-
|
11
|
+
date: 2022-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|