brick 1.0.76 → 1.0.77
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 +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
|