brick 1.0.58 → 1.0.61
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 +5 -0
- data/lib/brick/extensions.rb +190 -114
- data/lib/brick/frameworks/rails/engine.rb +53 -4
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +3 -0
- data/lib/generators/brick/migrations_generator.rb +4 -5
- data/lib/generators/brick/{model_generator.rb → models_generator.rb} +41 -32
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67de1c0cde3f28379388dfe0c2aa488197c5cfaad1a108408b506ba66ab64b38
|
4
|
+
data.tar.gz: 7d3e40960043e5e09e483dee791dd5f7c1d0f54f692a39ec2ef85b1b5cba1073
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90ae5f021e562d3188c4bde03e9b71a744417c5223f5736cff97cce90646b8b06860b245dcb718414fdce00df2c772bcbe1ad0d102310d7f3ff030641591387d
|
7
|
+
data.tar.gz: 704f2dde1c0043a141d96ff272346ac583e7f784df494bb4ce39f4bf5e2cd06ffadfa37783a1bfb0671c5a5fd120819c9f286e5c9bd0608764aa10338840d881
|
data/lib/brick/config.rb
CHANGED
@@ -230,6 +230,11 @@ module Brick
|
|
230
230
|
@mutex.synchronize { @not_nullables = columns }
|
231
231
|
end
|
232
232
|
|
233
|
+
# Add status page showing all resources and what files have been built out for them
|
234
|
+
def add_status
|
235
|
+
true
|
236
|
+
end
|
237
|
+
|
233
238
|
# Add a special page to show references to non-existent records ("orphans")
|
234
239
|
def add_orphans
|
235
240
|
true
|
data/lib/brick/extensions.rb
CHANGED
@@ -373,6 +373,7 @@ module ActiveRecord
|
|
373
373
|
end
|
374
374
|
|
375
375
|
def brick_select(params, selects = nil, order_by = nil, translations = {}, join_array = ::Brick::JoinArray.new)
|
376
|
+
is_mysql = ActiveRecord::Base.connection.adapter_name == 'Mysql2'
|
376
377
|
is_distinct = nil
|
377
378
|
wheres = {}
|
378
379
|
params.each do |k, v|
|
@@ -397,12 +398,14 @@ module ActiveRecord
|
|
397
398
|
if selects&.empty? # Default to all columns
|
398
399
|
tbl_no_schema = table.name.split('.').last
|
399
400
|
columns.each do |col|
|
400
|
-
if (col_name = col.name) == 'class'
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
401
|
+
col_alias = ' AS _class' if (col_name = col.name) == 'class'
|
402
|
+
selects << if is_mysql
|
403
|
+
"`#{tbl_no_schema}`.`#{col_name}`#{col_alias}"
|
404
|
+
else
|
405
|
+
# Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
|
406
|
+
cast_as_text = '::text' if is_distinct && Brick.relations[klass.table_name]&.[](:cols)&.[](col.name)&.first&.start_with?('xml')
|
407
|
+
"\"#{tbl_no_schema}\".\"#{col_name}\"#{cast_as_text}#{col_alias}"
|
408
|
+
end
|
406
409
|
end
|
407
410
|
end
|
408
411
|
|
@@ -430,7 +433,11 @@ module ActiveRecord
|
|
430
433
|
if used_col_aliases.key?(col_alias = "_brfk_#{v.first}__#{sel_col.last}")
|
431
434
|
col_alias = "_brfk_#{v.first}__#{v1[idx][-2..-1].map(&:to_s).join('__')}"
|
432
435
|
end
|
433
|
-
selects <<
|
436
|
+
selects << if is_mysql
|
437
|
+
"`#{field_tbl_name}`.`#{sel_col.last}` AS `#{col_alias}`"
|
438
|
+
else
|
439
|
+
"\"#{field_tbl_name}\".\"#{sel_col.last}\"#{'::text' if is_xml} AS \"#{col_alias}\""
|
440
|
+
end
|
434
441
|
used_col_aliases[col_alias] = nil
|
435
442
|
v1[idx] << col_alias
|
436
443
|
end
|
@@ -439,7 +446,11 @@ module ActiveRecord
|
|
439
446
|
# Accommodate composite primary key by allowing id_col to come in as an array
|
440
447
|
((id_col = k1.primary_key).is_a?(Array) ? id_col : [id_col]).each do |id_part|
|
441
448
|
id_for_tables[v.first] << if id_part
|
442
|
-
selects <<
|
449
|
+
selects << if is_mysql
|
450
|
+
"#{"`#{tbl_name}`.`#{id_part}`"} AS `#{(id_alias = "_brfk_#{v.first}__#{id_part}")}`"
|
451
|
+
else
|
452
|
+
"#{"\"#{tbl_name}\".\"#{id_part}\""} AS \"#{(id_alias = "_brfk_#{v.first}__#{id_part}")}\""
|
453
|
+
end
|
443
454
|
id_alias
|
444
455
|
end
|
445
456
|
end
|
@@ -646,7 +657,7 @@ Module.class_exec do
|
|
646
657
|
full_class_name << "::#{self.name}" unless self == Object
|
647
658
|
full_class_name << "::#{plural_class_name.underscore.singularize.camelize}"
|
648
659
|
if (plural_class_name == 'BrickSwagger' ||
|
649
|
-
(::Brick.config.add_orphans && plural_class_name == 'BrickGem') ||
|
660
|
+
((::Brick.config.add_status || ::Brick.config.add_orphans) && plural_class_name == 'BrickGem') ||
|
650
661
|
model = self.const_get(full_class_name))
|
651
662
|
# if it's a controller and no match or a model doesn't really use the same table name, eager load all models and try to find a model class of the right name.
|
652
663
|
Object.send(:build_controller, self, class_name, plural_class_name, model, relations)
|
@@ -667,45 +678,11 @@ Module.class_exec do
|
|
667
678
|
elsif ::Brick.enable_models?
|
668
679
|
# Custom inheritable Brick base model?
|
669
680
|
class_name = (inheritable_name = class_name)[5..-1] if class_name.start_with?('Brick')
|
670
|
-
|
671
|
-
# checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
672
|
-
|
673
|
-
if (base_model = ::Brick.config.sti_namespace_prefixes&.fetch("::#{base_module.name}::", nil)&.constantize) || # Are we part of an auto-STI namespace? ...
|
674
|
-
base_module != Object # ... or otherwise already in some namespace?
|
675
|
-
schema_name = [(singular_schema_name = name.underscore),
|
676
|
-
(schema_name = singular_schema_name.pluralize),
|
677
|
-
name,
|
678
|
-
name.pluralize].find { |s| Brick.db_schemas.include?(s) }
|
679
|
-
end
|
680
|
-
plural_class_name = ActiveSupport::Inflector.pluralize(model_name = class_name)
|
681
|
-
# If it's namespaced then we turn the first part into what would be a schema name
|
682
|
-
singular_table_name = ActiveSupport::Inflector.underscore(model_name).gsub('/', '.')
|
683
|
-
|
684
|
-
if base_model
|
685
|
-
schema_name = name.underscore # For the auto-STI namespace models
|
686
|
-
table_name = base_model.table_name
|
687
|
-
Object.send(:build_model, base_module, inheritable_name, model_name, singular_table_name, table_name, relations, table_name)
|
688
|
-
else
|
689
|
-
# Adjust for STI if we know of a base model for the requested model name
|
690
|
-
# %%% Does not yet work with namespaced model names. Perhaps prefix with plural_class_name when doing the lookups here.
|
691
|
-
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil) || ::Brick.existing_stis[model_name]&.constantize)
|
692
|
-
base_model.table_name
|
693
|
-
else
|
694
|
-
ActiveSupport::Inflector.pluralize(singular_table_name)
|
695
|
-
end
|
696
|
-
if ::Brick.apartment_multitenant &&
|
697
|
-
Apartment.excluded_models.include?(table_name.singularize.camelize)
|
698
|
-
schema_name = Apartment.default_schema
|
699
|
-
end
|
700
|
-
# Maybe, just maybe there's a database table that will satisfy this need
|
701
|
-
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(schema_name ? "#{schema_name}.#{m}" : m) })
|
702
|
-
Object.send(:build_model, schema_name, inheritable_name, model_name, singular_table_name, table_name, relations, matching)
|
703
|
-
end
|
704
|
-
end
|
681
|
+
Object.send(:build_model, relations, base_module, name, class_name, inheritable_name)
|
705
682
|
end
|
706
683
|
if result
|
707
684
|
built_class, code = result
|
708
|
-
puts "\n#{code}"
|
685
|
+
puts "\n#{code}\n"
|
709
686
|
built_class
|
710
687
|
elsif ::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}") && !schema_name
|
711
688
|
# module_prefixes = type_name.split('::')
|
@@ -727,7 +704,42 @@ class Object
|
|
727
704
|
|
728
705
|
private
|
729
706
|
|
730
|
-
def build_model(
|
707
|
+
def build_model(relations, base_module, base_name, class_name, inheritable_name = nil)
|
708
|
+
if (base_model = ::Brick.config.sti_namespace_prefixes&.fetch("::#{base_module.name}::", nil)&.constantize) || # Are we part of an auto-STI namespace? ...
|
709
|
+
base_module != Object # ... or otherwise already in some namespace?
|
710
|
+
schema_name = [(singular_schema_name = base_name.underscore),
|
711
|
+
(schema_name = singular_schema_name.pluralize),
|
712
|
+
base_name,
|
713
|
+
base_name.pluralize].find { |s| Brick.db_schemas.include?(s) }
|
714
|
+
end
|
715
|
+
plural_class_name = ActiveSupport::Inflector.pluralize(model_name = class_name)
|
716
|
+
# If it's namespaced then we turn the first part into what would be a schema name
|
717
|
+
singular_table_name = ActiveSupport::Inflector.underscore(model_name).gsub('/', '.')
|
718
|
+
|
719
|
+
if base_model
|
720
|
+
schema_name = base_name.underscore # For the auto-STI namespace models
|
721
|
+
table_name = base_model.table_name
|
722
|
+
build_model_worker(base_module, inheritable_name, model_name, singular_table_name, table_name, relations, table_name)
|
723
|
+
else
|
724
|
+
# Adjust for STI if we know of a base model for the requested model name
|
725
|
+
# %%% Does not yet work with namespaced model names. Perhaps prefix with plural_class_name when doing the lookups here.
|
726
|
+
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil) || ::Brick.existing_stis[model_name]&.constantize)
|
727
|
+
base_model.table_name
|
728
|
+
else
|
729
|
+
ActiveSupport::Inflector.pluralize(singular_table_name)
|
730
|
+
end
|
731
|
+
if ::Brick.apartment_multitenant &&
|
732
|
+
Apartment.excluded_models.include?(table_name.singularize.camelize)
|
733
|
+
schema_name = Apartment.default_schema
|
734
|
+
end
|
735
|
+
# Maybe, just maybe there's a database table that will satisfy this need
|
736
|
+
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(schema_name ? "#{schema_name}.#{m}" : m) })
|
737
|
+
build_model_worker(schema_name, inheritable_name, model_name, singular_table_name, table_name, relations, matching)
|
738
|
+
end
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
def build_model_worker(schema_name, inheritable_name, model_name, singular_table_name, table_name, relations, matching)
|
731
743
|
if ::Brick.apartment_multitenant &&
|
732
744
|
schema_name == Apartment.default_schema
|
733
745
|
relation = relations["#{schema_name}.#{matching}"]
|
@@ -893,7 +905,7 @@ class Object
|
|
893
905
|
end
|
894
906
|
end
|
895
907
|
end
|
896
|
-
code << "end # model #{full_name}\n
|
908
|
+
code << "end # model #{full_name}\n"
|
897
909
|
[built_model, code]
|
898
910
|
end
|
899
911
|
|
@@ -989,6 +1001,17 @@ class Object
|
|
989
1001
|
self.send(macro, assoc_name, **options)
|
990
1002
|
end
|
991
1003
|
|
1004
|
+
def default_ordering(table_name, pk)
|
1005
|
+
case (order_tbl = ::Brick.config.order[table_name]) && (order_default = order_tbl[:_brick_default])
|
1006
|
+
when Array
|
1007
|
+
order_default.map { |od_part| order_tbl[od_part] || od_part }
|
1008
|
+
when Symbol
|
1009
|
+
order_tbl[order_default] || order_default
|
1010
|
+
else
|
1011
|
+
pk.map(&:to_sym) # If it's not a custom ORDER BY, just use the key
|
1012
|
+
end
|
1013
|
+
end
|
1014
|
+
|
992
1015
|
def build_controller(namespace, class_name, plural_class_name, model, relations)
|
993
1016
|
table_name = ActiveSupport::Inflector.underscore(plural_class_name)
|
994
1017
|
singular_table_name = ActiveSupport::Inflector.singularize(table_name)
|
@@ -1002,6 +1025,9 @@ class Object
|
|
1002
1025
|
# Brick-specific pages
|
1003
1026
|
case plural_class_name
|
1004
1027
|
when 'BrickGem'
|
1028
|
+
self.define_method :status do
|
1029
|
+
instance_variable_set(:@resources, ::Brick.get_status_of_resources)
|
1030
|
+
end
|
1005
1031
|
self.define_method :orphans do
|
1006
1032
|
instance_variable_set(:@orphans, ::Brick.find_orphans(::Brick.set_db_schema(params)))
|
1007
1033
|
end
|
@@ -1073,27 +1099,9 @@ class Object
|
|
1073
1099
|
# Normal (non-swagger) request
|
1074
1100
|
|
1075
1101
|
# %%% Allow params to define which columns to use for order_by
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
order_default.map { |od_part| order_tbl[od_part] || od_part }
|
1080
|
-
when Symbol
|
1081
|
-
order_tbl[order_default] || order_default
|
1082
|
-
else
|
1083
|
-
pk
|
1084
|
-
end
|
1085
|
-
else
|
1086
|
-
pk # If it's not a custom ORDER BY, just use the key
|
1087
|
-
end
|
1088
|
-
order_by, order_by_txt = model._brick_calculate_ordering(ordering)
|
1089
|
-
if (order_params = params['_brick_order']&.split(',')&.map(&:to_sym)) # Overriding the default by providing a querystring param?
|
1090
|
-
order_by, _ = model._brick_calculate_ordering(order_params, true) # Don't do the txt part
|
1091
|
-
end
|
1092
|
-
|
1093
|
-
code << " def index\n"
|
1094
|
-
code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{order_by_txt.join(', ')})" : '.all'}\n"
|
1095
|
-
code << " @#{table_name}.brick_select(params)\n"
|
1096
|
-
code << " end\n"
|
1102
|
+
# Overriding the default by providing a querystring param?
|
1103
|
+
ordering = params['_brick_order']&.split(',')&.map(&:to_sym) || Object.send(:default_ordering, table_name, pk)
|
1104
|
+
order_by, _ = model._brick_calculate_ordering(ordering, true) # Don't do the txt part
|
1097
1105
|
|
1098
1106
|
::Brick.set_db_schema(params)
|
1099
1107
|
if request.format == :csv # Asking for a template?
|
@@ -1126,6 +1134,12 @@ class Object
|
|
1126
1134
|
@_brick_join_array = join_array
|
1127
1135
|
end
|
1128
1136
|
|
1137
|
+
_, order_by_txt = model._brick_calculate_ordering(default_ordering(table_name, pk))
|
1138
|
+
code << " def index\n"
|
1139
|
+
code << " @#{table_name} = #{model.name}#{pk&.present? ? ".order(#{order_by_txt.join(', ')})" : '.all'}\n"
|
1140
|
+
code << " @#{table_name}.brick_select(params)\n"
|
1141
|
+
code << " end\n"
|
1142
|
+
|
1129
1143
|
is_pk_string = nil
|
1130
1144
|
if (pk_col = model&.primary_key)
|
1131
1145
|
code << " def show\n"
|
@@ -1255,7 +1269,7 @@ class Object
|
|
1255
1269
|
# Get column names for params from relations[model.table_name][:cols].keys
|
1256
1270
|
end
|
1257
1271
|
# end
|
1258
|
-
code << "end # #{namespace_name}#{class_name}\n
|
1272
|
+
code << "end # #{namespace_name}#{class_name}\n"
|
1259
1273
|
end # class definition
|
1260
1274
|
[built_controller, code]
|
1261
1275
|
end
|
@@ -1312,7 +1326,7 @@ module ActiveRecord::ConnectionHandling
|
|
1312
1326
|
load apartment_initializer
|
1313
1327
|
apartment_excluded = Apartment.excluded_models
|
1314
1328
|
end
|
1315
|
-
# Only for Postgres
|
1329
|
+
# Only for Postgres (Doesn't work in sqlite3 or MySQL)
|
1316
1330
|
# puts ActiveRecord::Base.execute_sql("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
1317
1331
|
|
1318
1332
|
is_postgres = nil
|
@@ -1347,7 +1361,7 @@ module ActiveRecord::ConnectionHandling
|
|
1347
1361
|
puts "Unfamiliar with connection adapter #{ActiveRecord::Base.connection.adapter_name}"
|
1348
1362
|
end
|
1349
1363
|
|
1350
|
-
::Brick.db_schemas ||=
|
1364
|
+
::Brick.db_schemas ||= {}
|
1351
1365
|
|
1352
1366
|
if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
1353
1367
|
if (possible_schema = ::Brick.config.schema_behavior&.[](:multitenant)&.[](:schema_to_analyse))
|
@@ -1363,41 +1377,11 @@ module ActiveRecord::ConnectionHandling
|
|
1363
1377
|
# %%% Retrieve internal ActiveRecord table names like this:
|
1364
1378
|
# ActiveRecord::Base.internal_metadata_table_name, ActiveRecord::Base.schema_migrations_table_name
|
1365
1379
|
# For if it's not SQLite -- so this is the Postgres and MySQL version
|
1366
|
-
sql ||= "SELECT t.table_schema AS schema, t.table_name AS relation_name, t.table_type,#{"
|
1367
|
-
pg_catalog.obj_description((t.table_schema || '.' || t.table_name)::regclass, 'pg_class') AS table_description," if is_postgres}
|
1368
|
-
c.column_name, c.data_type,
|
1369
|
-
COALESCE(c.character_maximum_length, c.numeric_precision) AS max_length,
|
1370
|
-
tc.constraint_type AS const, kcu.constraint_name AS \"key\",
|
1371
|
-
c.is_nullable
|
1372
|
-
FROM INFORMATION_SCHEMA.tables AS t
|
1373
|
-
LEFT OUTER JOIN INFORMATION_SCHEMA.columns AS c ON t.table_schema = c.table_schema
|
1374
|
-
AND t.table_name = c.table_name
|
1375
|
-
LEFT OUTER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu ON
|
1376
|
-
-- ON kcu.CONSTRAINT_CATALOG = t.table_catalog AND
|
1377
|
-
kcu.CONSTRAINT_SCHEMA = c.table_schema
|
1378
|
-
AND kcu.TABLE_NAME = c.table_name
|
1379
|
-
AND kcu.position_in_unique_constraint IS NULL
|
1380
|
-
AND kcu.ordinal_position = c.ordinal_position
|
1381
|
-
LEFT OUTER JOIN INFORMATION_SCHEMA.table_constraints AS tc
|
1382
|
-
ON kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
|
1383
|
-
AND kcu.TABLE_NAME = tc.TABLE_NAME
|
1384
|
-
AND kcu.CONSTRAINT_NAME = tc.constraint_name
|
1385
|
-
WHERE t.table_schema NOT IN ('information_schema', 'pg_catalog')#{"
|
1386
|
-
AND t.table_schema = COALESCE(current_setting('SEARCH_PATH'), 'public')" if schema }
|
1387
|
-
-- AND t.table_type IN ('VIEW') -- 'BASE TABLE', 'FOREIGN TABLE'
|
1388
|
-
AND t.table_name NOT IN ('pg_stat_statements', ?, ?)
|
1389
|
-
ORDER BY 1, t.table_type DESC, c.ordinal_position"
|
1390
1380
|
measures = []
|
1391
1381
|
case ActiveRecord::Base.connection.adapter_name
|
1392
1382
|
when 'PostgreSQL', 'SQLite' # These bring back a hash for each row because the query uses column aliases
|
1393
1383
|
# schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
1394
|
-
|
1395
|
-
ActiveRecord::Base.schema_migrations_table_name
|
1396
|
-
else
|
1397
|
-
'schema_migrations'
|
1398
|
-
end
|
1399
|
-
ar_imtn = ActiveRecord.version >= ::Gem::Version.new('5.0') ? ActiveRecord::Base.internal_metadata_table_name : ''
|
1400
|
-
ActiveRecord::Base.execute_sql(sql, ar_smtn, ar_imtn).each do |r|
|
1384
|
+
ActiveRecord::Base.retrieve_schema_and_tables(sql, is_postgres, schema).each do |r|
|
1401
1385
|
# If Apartment gem lists the table as being associated with a non-tenanted model then use whatever it thinks
|
1402
1386
|
# is the default schema, usually 'public'.
|
1403
1387
|
schema_name = if ::Brick.config.schema_behavior[:multitenant]
|
@@ -1424,23 +1408,23 @@ module ActiveRecord::ConnectionHandling
|
|
1424
1408
|
# puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
|
1425
1409
|
end
|
1426
1410
|
else # MySQL2 acts a little differently, bringing back an array for each row
|
1427
|
-
ActiveRecord::Base.
|
1428
|
-
relation = relations[(relation_name = r[
|
1429
|
-
relation[:isView] = true if r[
|
1430
|
-
col_name = r[
|
1431
|
-
key = case r[
|
1411
|
+
ActiveRecord::Base.retrieve_schema_and_tables(sql).each do |r|
|
1412
|
+
relation = relations[(relation_name = r[1])] # here relation represents a table or view from the database
|
1413
|
+
relation[:isView] = true if r[2] == 'VIEW' # table_type
|
1414
|
+
col_name = r[3]
|
1415
|
+
key = case r[6] # constraint type
|
1432
1416
|
when 'PRIMARY KEY'
|
1433
1417
|
# key
|
1434
|
-
relation[:pkey][r[
|
1418
|
+
relation[:pkey][r[7] || relation_name] ||= []
|
1435
1419
|
when 'UNIQUE'
|
1436
|
-
relation[:ukeys][r[
|
1420
|
+
relation[:ukeys][r[7] || "#{relation_name}.#{col_name}"] ||= []
|
1437
1421
|
# key = (relation[:ukeys] = Hash.new { |h, k| h[k] = [] }) if key.is_a?(Array)
|
1438
1422
|
# key[r['key']]
|
1439
1423
|
end
|
1440
1424
|
key << col_name if key
|
1441
1425
|
cols = relation[:cols] # relation.fetch(:cols) { relation[:cols] = [] }
|
1442
1426
|
# 'data_type', 'max_length'
|
1443
|
-
cols[col_name] = [r[
|
1427
|
+
cols[col_name] = [r[4], r[5], measures&.include?(col_name)]
|
1444
1428
|
# puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
|
1445
1429
|
end
|
1446
1430
|
end
|
@@ -1476,9 +1460,11 @@ module ActiveRecord::ConnectionHandling
|
|
1476
1460
|
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu2
|
1477
1461
|
ON kcu2.CONSTRAINT_CATALOG = rc.UNIQUE_CONSTRAINT_CATALOG
|
1478
1462
|
AND kcu2.CONSTRAINT_SCHEMA = rc.UNIQUE_CONSTRAINT_SCHEMA
|
1479
|
-
AND kcu2.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
|
1463
|
+
AND kcu2.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME#{"
|
1464
|
+
AND kcu2.TABLE_NAME = kcu1.REFERENCED_TABLE_NAME
|
1465
|
+
AND kcu2.COLUMN_NAME = kcu1.REFERENCED_COLUMN_NAME" unless is_postgres }
|
1480
1466
|
AND kcu2.ORDINAL_POSITION = kcu1.ORDINAL_POSITION#{"
|
1481
|
-
WHERE kcu1.CONSTRAINT_SCHEMA = COALESCE(current_setting('SEARCH_PATH'), 'public')" if schema }"
|
1467
|
+
WHERE kcu1.CONSTRAINT_SCHEMA = COALESCE(current_setting('SEARCH_PATH'), 'public')" if is_postgres && schema }"
|
1482
1468
|
# AND kcu2.TABLE_NAME = ?;", Apartment::Tenant.current, table_name
|
1483
1469
|
when 'SQLite'
|
1484
1470
|
sql = "SELECT m.name, fkl.\"from\", fkl.\"table\", m.name || '_' || fkl.\"from\" AS constraint_name
|
@@ -1494,12 +1480,14 @@ module ActiveRecord::ConnectionHandling
|
|
1494
1480
|
# Multitenancy makes things a little more general overall, except for non-tenanted tables
|
1495
1481
|
if apartment_excluded&.include?(fk[1].singularize.camelize)
|
1496
1482
|
fk[0] = Apartment.default_schema
|
1497
|
-
elsif fk[0] == 'public' || (is_multitenant && fk[0] == schema)
|
1483
|
+
elsif is_postgres && (fk[0] == 'public' || (is_multitenant && fk[0] == schema)) ||
|
1484
|
+
!is_postgres && ['mysql', 'performance_schema', 'sys'].exclude?(fk[0])
|
1498
1485
|
fk[0] = nil
|
1499
1486
|
end
|
1500
1487
|
if apartment_excluded&.include?(fk[4].singularize.camelize)
|
1501
1488
|
fk[3] = Apartment.default_schema
|
1502
|
-
elsif fk[3] == 'public' || (is_multitenant && fk[3] == schema)
|
1489
|
+
elsif is_postgres && (fk[3] == 'public' || (is_multitenant && fk[3] == schema)) ||
|
1490
|
+
!is_postgres && ['mysql', 'performance_schema', 'sys'].exclude?(fk[3])
|
1503
1491
|
fk[3] = nil
|
1504
1492
|
end
|
1505
1493
|
::Brick._add_bt_and_hm(fk, relations)
|
@@ -1533,6 +1521,41 @@ module ActiveRecord::ConnectionHandling
|
|
1533
1521
|
|
1534
1522
|
::Brick.load_additional_references if initializer_loaded
|
1535
1523
|
end
|
1524
|
+
|
1525
|
+
def retrieve_schema_and_tables(sql = nil, is_postgres = nil, schema = nil)
|
1526
|
+
sql ||= "SELECT t.table_schema AS \"schema\", t.table_name AS relation_name, t.table_type,#{"
|
1527
|
+
pg_catalog.obj_description((t.table_schema || '.' || t.table_name)::regclass, 'pg_class') AS table_description," if is_postgres}
|
1528
|
+
c.column_name, c.data_type,
|
1529
|
+
COALESCE(c.character_maximum_length, c.numeric_precision) AS max_length,
|
1530
|
+
tc.constraint_type AS const, kcu.constraint_name AS \"key\",
|
1531
|
+
c.is_nullable
|
1532
|
+
FROM INFORMATION_SCHEMA.tables AS t
|
1533
|
+
LEFT OUTER JOIN INFORMATION_SCHEMA.columns AS c ON t.table_schema = c.table_schema
|
1534
|
+
AND t.table_name = c.table_name
|
1535
|
+
LEFT OUTER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu ON
|
1536
|
+
-- ON kcu.CONSTRAINT_CATALOG = t.table_catalog AND
|
1537
|
+
kcu.CONSTRAINT_SCHEMA = c.table_schema
|
1538
|
+
AND kcu.TABLE_NAME = c.table_name
|
1539
|
+
AND kcu.position_in_unique_constraint IS NULL
|
1540
|
+
AND kcu.ordinal_position = c.ordinal_position
|
1541
|
+
LEFT OUTER JOIN INFORMATION_SCHEMA.table_constraints AS tc
|
1542
|
+
ON kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
|
1543
|
+
AND kcu.TABLE_NAME = tc.TABLE_NAME
|
1544
|
+
AND kcu.CONSTRAINT_NAME = tc.constraint_name
|
1545
|
+
WHERE t.table_schema #{
|
1546
|
+
is_postgres ? "NOT IN ('information_schema', 'pg_catalog')" : "= '#{ActiveRecord::Base.connection.current_database.tr("'", "''")}'"}#{"
|
1547
|
+
AND t.table_schema = COALESCE(current_setting('SEARCH_PATH'), 'public')" if is_postgres && schema }
|
1548
|
+
-- AND t.table_type IN ('VIEW') -- 'BASE TABLE', 'FOREIGN TABLE'
|
1549
|
+
AND t.table_name NOT IN ('pg_stat_statements', ?, ?)
|
1550
|
+
ORDER BY 1, t.table_type DESC, 2, c.ordinal_position"
|
1551
|
+
ar_smtn = if ActiveRecord::Base.respond_to?(:schema_migrations_table_name)
|
1552
|
+
ActiveRecord::Base.schema_migrations_table_name
|
1553
|
+
else
|
1554
|
+
'schema_migrations'
|
1555
|
+
end
|
1556
|
+
ar_imtn = ActiveRecord.version >= ::Gem::Version.new('5.0') ? ActiveRecord::Base.internal_metadata_table_name : ''
|
1557
|
+
ActiveRecord::Base.execute_sql(sql, ar_smtn, ar_imtn)
|
1558
|
+
end
|
1536
1559
|
end
|
1537
1560
|
|
1538
1561
|
# ==========================================
|
@@ -1680,6 +1703,59 @@ module Brick
|
|
1680
1703
|
assoc_bt[:inverse] = assoc_hm
|
1681
1704
|
end
|
1682
1705
|
|
1706
|
+
# Identify built out routes, migrations, models,
|
1707
|
+
# (and also soon controllers and views!)
|
1708
|
+
# for each resource
|
1709
|
+
def get_status_of_resources
|
1710
|
+
rails_root = ::Rails.root.to_s
|
1711
|
+
migrations = if Dir.exist?(mig_path = ActiveRecord::Migrator.migrations_paths.first || "#{rails_root}/db/migrate")
|
1712
|
+
Dir["#{mig_path}/**/*.rb"].each_with_object(Hash.new { |h, k| h[k] = [] }) do |v, s|
|
1713
|
+
File.read(v).split("\n").each do |line|
|
1714
|
+
# For all non-commented lines, look for any that have "create_table", "alter_table", or "drop_table"
|
1715
|
+
if !line.lstrip.start_with?('#') &&
|
1716
|
+
(idx = (line.index('create_table ') || line.index('create_table('))&.+(13)) ||
|
1717
|
+
(idx = (line.index('alter_table ') || line.index('alter_table('))&.+(12)) ||
|
1718
|
+
(idx = (line.index('drop_table ') || line.index('drop_table('))&.+(11))
|
1719
|
+
tbl = line[idx..-1].match(/([:'"\w\.]+)/)&.captures&.first
|
1720
|
+
if tbl
|
1721
|
+
s[tbl.tr(':\'"', '').pluralize] << v
|
1722
|
+
break
|
1723
|
+
end
|
1724
|
+
end
|
1725
|
+
end
|
1726
|
+
end
|
1727
|
+
end
|
1728
|
+
if ::ActiveSupport.version < ::Gem::Version.new('6') ||
|
1729
|
+
::Rails.configuration.instance_variable_get(:@autoloader) == :classic
|
1730
|
+
Rails.configuration.eager_load_namespaces.select { |ns| ns < Rails::Application }.each(&:eager_load!)
|
1731
|
+
else
|
1732
|
+
Zeitwerk::Loader.eager_load_all
|
1733
|
+
end
|
1734
|
+
abstract_activerecord_bases = ActiveRecord::Base.descendants.select { |ar| ar.abstract_class? }.map(&:name)
|
1735
|
+
# abstract_activerecord_bases << ActiveRecord::Base
|
1736
|
+
models = if Dir.exist?(model_path = "#{rails_root}/app/models")
|
1737
|
+
Dir["#{model_path}/**/*.rb"].each_with_object({}) do |v, s|
|
1738
|
+
File.read(v).split("\n").each do |line|
|
1739
|
+
# For all non-commented lines, look for any that start with "class " and also "< ApplicationRecord"
|
1740
|
+
if line.lstrip.start_with?('class') && (idx = line.index('class'))
|
1741
|
+
model = line[idx + 5..-1].match(/[\s:]+([\w:]+)/)&.captures&.first
|
1742
|
+
if model && abstract_activerecord_bases.exclude?(model)
|
1743
|
+
klass = begin
|
1744
|
+
model.constantize
|
1745
|
+
rescue
|
1746
|
+
end
|
1747
|
+
s[model.underscore.tr('/', '.').pluralize] = [
|
1748
|
+
v.start_with?(rails_root) ? v[rails_root.length + 1..-1] : v,
|
1749
|
+
klass
|
1750
|
+
]
|
1751
|
+
end
|
1752
|
+
end
|
1753
|
+
end
|
1754
|
+
end
|
1755
|
+
end
|
1756
|
+
::Brick.relations.keys.map { |v| [(r = v.pluralize), (model = models[r])&.last&.table_name || v, migrations&.fetch(r, nil), model&.first] }
|
1757
|
+
end
|
1758
|
+
|
1683
1759
|
# Locate orphaned records
|
1684
1760
|
def find_orphans(multi_schema)
|
1685
1761
|
is_default_schema = multi_schema&.==(Apartment.default_schema)
|
@@ -56,6 +56,7 @@ module Brick
|
|
56
56
|
# Used by Rails 5.0 and above
|
57
57
|
alias :_brick_template_exists? :template_exists?
|
58
58
|
def template_exists?(*args, **options)
|
59
|
+
(::Brick.config.add_status && args.first == 'status') ||
|
59
60
|
(::Brick.config.add_orphans && args.first == 'orphans') ||
|
60
61
|
_brick_template_exists?(*args, **options) ||
|
61
62
|
# Do not auto-create a template when it's searching for an application.html.erb, which comes in like: ["edit", ["games", "application"]]
|
@@ -96,11 +97,12 @@ module Brick
|
|
96
97
|
@_brick_model ||
|
97
98
|
(ActionView.version < ::Gem::Version.new('5.0') && args[1].is_a?(Array) ? set_brick_model(args) : nil)
|
98
99
|
)&.name) ||
|
100
|
+
(is_status = ::Brick.config.add_status && args[0..1] == ['status', ['brick_gem']]) ||
|
99
101
|
(is_orphans = ::Brick.config.add_orphans && args[0..1] == ['orphans', ['brick_gem']])
|
100
102
|
return _brick_find_template(*args, **options)
|
101
103
|
end
|
102
104
|
|
103
|
-
unless is_orphans
|
105
|
+
unless is_status || is_orphans
|
104
106
|
pk = @_brick_model._brick_primary_key(::Brick.relations.fetch(model_name, nil))
|
105
107
|
obj_name = model_name.split('::').last.underscore
|
106
108
|
path_obj_name = model_name.underscore.tr('/', '_')
|
@@ -148,7 +150,6 @@ module Brick
|
|
148
150
|
", nil, #{path_keys(hm_assoc, hm_fk_name, obj_name, pk)}"
|
149
151
|
end
|
150
152
|
hm_entry << ']'
|
151
|
-
puts hm_entry
|
152
153
|
hms_columns << hm_entry
|
153
154
|
when 'show', 'update'
|
154
155
|
hm_stuff << if hm_fk_name
|
@@ -613,8 +614,9 @@ if (headerTop) {
|
|
613
614
|
});
|
614
615
|
});
|
615
616
|
</script>
|
616
|
-
<% end
|
617
|
-
|
617
|
+
<% end
|
618
|
+
|
619
|
+
%><table id=\"headerTop\">
|
618
620
|
<table id=\"#{table_name}\">
|
619
621
|
<thead><tr>#{"<th x-order=\"#{pk.join(',')}\"></th>" if pk.present?}<%=
|
620
622
|
# Consider getting the name from the association -- hm.first.name -- if a more \"friendly\" alias should be used for a screwy table name
|
@@ -708,6 +710,53 @@ if (headerTop) {
|
|
708
710
|
|
709
711
|
#{"<hr><%= link_to \"New #{obj_name}\", new_#{path_obj_name}_path %>" unless @_brick_model.is_view?}
|
710
712
|
#{script}"
|
713
|
+
|
714
|
+
when 'status'
|
715
|
+
# Status page - list of all resources and 5 things they do or don't have present, and what is turned on and off
|
716
|
+
# Must load all models, and then find what table names are represented
|
717
|
+
# Easily could be multiple files involved (STI for instance)
|
718
|
+
+"#{css}
|
719
|
+
<p style=\"color: green\"><%= notice %></p>#{"
|
720
|
+
<select id=\"schema\">#{schema_options}</select>" if ::Brick.config.schema_behavior[:multitenant] && ::Brick.db_schemas.length > 1}
|
721
|
+
<select id=\"tbl\">#{table_options}</select>
|
722
|
+
<h1>Status</h1>
|
723
|
+
<table id=\"status\"><thead><tr>
|
724
|
+
<th>Resource</th>
|
725
|
+
<th>Table</th>
|
726
|
+
<th>Migration</th>
|
727
|
+
<th>Model</th>
|
728
|
+
<th>Route</th>
|
729
|
+
<th>Controller</th>
|
730
|
+
<th>Views</th>
|
731
|
+
</tr></thead>
|
732
|
+
<tbody>
|
733
|
+
<% # (listing in schema.rb)
|
734
|
+
# Solid colour if file or route entry is present
|
735
|
+
@resources.each do |r|
|
736
|
+
%>
|
737
|
+
<tr>
|
738
|
+
<td><%= link_to(r[0], \"/#\{r[0].tr('.', '/')}\") %></td>
|
739
|
+
<td<%= if r[1]
|
740
|
+
' class=\"orphan\"' unless ::Brick.relations.key?(r[1])
|
741
|
+
else
|
742
|
+
' class=\"dimmed\"'
|
743
|
+
end&.html_safe %>><%= # Table
|
744
|
+
r[1] %></td>
|
745
|
+
<td<%= ' class=\"dimmed\"'.html_safe unless r[2] %>><%= # Migration
|
746
|
+
r[2]&.join('<br>')&.html_safe %></td>
|
747
|
+
<td<%= ' class=\"dimmed\"'.html_safe unless r[3] %>><%= # Model
|
748
|
+
r[3] %></td>
|
749
|
+
<td<%= ' class=\"dimmed\"'.html_safe unless r[4] %>><%= # Route
|
750
|
+
%></td>
|
751
|
+
<td<%= ' class=\"dimmed\"'.html_safe unless r[5] %>><%= # Controller
|
752
|
+
%></td>
|
753
|
+
<td<%= ' class=\"dimmed\"'.html_safe unless r[6] %>><%= # Views
|
754
|
+
%></td>
|
755
|
+
<tr>
|
756
|
+
<% end %>
|
757
|
+
</tbody><table>
|
758
|
+
#{script}"
|
759
|
+
|
711
760
|
when 'orphans'
|
712
761
|
if is_orphans
|
713
762
|
+"#{css}
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -484,6 +484,9 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
484
484
|
send(:resources, controller_name.to_sym, **options)
|
485
485
|
end
|
486
486
|
end
|
487
|
+
if ::Brick.config.add_status && instance_variable_get(:@set).named_routes.names.exclude?(:brick_status)
|
488
|
+
get('/brick_status', to: 'brick_gem#status', as: 'brick_status')
|
489
|
+
end
|
487
490
|
if ::Brick.config.add_orphans && instance_variable_get(:@set).named_routes.names.exclude?(:brick_orphans)
|
488
491
|
get('/brick_orphans', to: 'brick_gem#orphans', as: 'brick_orphans')
|
489
492
|
end
|
@@ -85,10 +85,9 @@ module Brick
|
|
85
85
|
# Generate a list of tables that can be chosen
|
86
86
|
chosen = gets_list(list: tables, chosen: tables.dup)
|
87
87
|
schemas = chosen.each_with_object({}) do |v, s|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
s[schema] = nil if schema && [::Brick.default_schema, 'public'].exclude?(schema)
|
88
|
+
if (v_parts = v.split('.')).length > 1
|
89
|
+
s[v_parts.first] = nil unless [::Brick.default_schema, 'public'].include?(v_parts.first)
|
90
|
+
end
|
92
91
|
end
|
93
92
|
# Start the timestamps back the same number of minutes from now as expected number of migrations to create
|
94
93
|
current_mig_time = Time.now - (schemas.length + chosen.length).minutes
|
@@ -130,7 +129,7 @@ module Brick
|
|
130
129
|
tbl_parts.first
|
131
130
|
end
|
132
131
|
end
|
133
|
-
unless built_schemas.key?(schema)
|
132
|
+
unless schema.blank? || built_schemas.key?(schema)
|
134
133
|
mig = +" def change\n create_schema(:#{schema}) unless schema_exists?(:#{schema})\n end\n"
|
135
134
|
migration_file_write(mig_path, "create_db_schema_#{schema}", current_mig_time += 1.minute, ar_version, mig)
|
136
135
|
built_schemas[schema] = nil
|
@@ -6,63 +6,72 @@ require 'fancy_gets'
|
|
6
6
|
|
7
7
|
module Brick
|
8
8
|
# Auto-generates models, controllers, or views
|
9
|
-
class
|
9
|
+
class ModelsGenerator < ::Rails::Generators::Base
|
10
10
|
include FancyGets
|
11
11
|
# include ::Rails::Generators::Migration
|
12
12
|
|
13
|
-
# # source_root File.expand_path('templates', __dir__)
|
14
|
-
# class_option(
|
15
|
-
# :with_changes,
|
16
|
-
# type: :boolean,
|
17
|
-
# default: false,
|
18
|
-
# desc: 'Add IMPORT_TEMPLATE to model'
|
19
|
-
# )
|
20
|
-
|
21
13
|
desc 'Auto-generates models, controllers, or views.'
|
22
14
|
|
23
|
-
def
|
24
|
-
# %%% If Apartment is active, ask which schema they want
|
15
|
+
def brick_models
|
16
|
+
# %%% If Apartment is active and there's no schema_to_analyse, ask which schema they want
|
25
17
|
|
26
18
|
# Load all models
|
27
|
-
|
19
|
+
if ::ActiveSupport.version < ::Gem::Version.new('6') ||
|
20
|
+
::Rails.configuration.instance_variable_get(:@autoloader) == :classic
|
21
|
+
Rails.configuration.eager_load_namespaces.select { |ns| ns < Rails::Application }.each(&:eager_load!)
|
22
|
+
else
|
23
|
+
Zeitwerk::Loader.eager_load_all
|
24
|
+
end
|
28
25
|
|
29
26
|
# Generate a list of viable models that can be chosen
|
30
27
|
longest_length = 0
|
31
28
|
model_info = Hash.new { |h, k| h[k] = {} }
|
32
29
|
tableless = Hash.new { |h, k| h[k] = [] }
|
33
|
-
|
34
|
-
|
35
|
-
true
|
36
|
-
elsif !m.table_exists?
|
37
|
-
tableless[m.table_name] << m.name
|
38
|
-
' (No Table)'
|
39
|
-
else
|
40
|
-
this_f_keys = (model_info[m][:f_keys] = m.reflect_on_all_associations.select { |a| a.macro == :belongs_to }) || []
|
41
|
-
column_names = (model_info[m][:column_names] = m.columns.map(&:name) - [m.primary_key, 'created_at', 'updated_at', 'deleted_at'] - this_f_keys.map(&:foreign_key))
|
42
|
-
if column_names.empty? && this_f_keys && !this_f_keys.empty?
|
43
|
-
fk_message = ", although #{this_f_keys.length} foreign keys"
|
44
|
-
" (No columns#{fk_message})"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
# puts "#{m.name}#{trouble}" if trouble&.is_a?(String)
|
48
|
-
trouble
|
30
|
+
existing_models = ActiveRecord::Base.descendants.reject do |m|
|
31
|
+
m.abstract_class? || !m.table_exists? || ::Brick.relations.key?(m.table_name)
|
49
32
|
end
|
33
|
+
models = ::Brick.relations.keys.map do |tbl|
|
34
|
+
tbl_parts = tbl.split('.')
|
35
|
+
tbl_parts.shift if [::Brick.default_schema, 'public'].include?(tbl_parts.first)
|
36
|
+
tbl_parts[-1] = tbl_parts[-1].singularize
|
37
|
+
tbl_parts.join('/').camelize
|
38
|
+
end - existing_models.map(&:name)
|
50
39
|
models.sort! do |a, b| # Sort first to separate namespaced stuff from the rest, then alphabetically
|
51
|
-
is_a_namespaced = a.
|
52
|
-
is_b_namespaced = b.
|
40
|
+
is_a_namespaced = a.include?('::')
|
41
|
+
is_b_namespaced = b.include?('::')
|
53
42
|
if is_a_namespaced && !is_b_namespaced
|
54
43
|
1
|
55
44
|
elsif !is_a_namespaced && is_b_namespaced
|
56
45
|
-1
|
57
46
|
else
|
58
|
-
a
|
47
|
+
a <=> b
|
59
48
|
end
|
60
49
|
end
|
61
50
|
models.each do |m| # Find longest name in the list for future use to show lists on the right side of the screen
|
62
|
-
if longest_length < (len = m.
|
51
|
+
if longest_length < (len = m.length)
|
63
52
|
longest_length = len
|
64
53
|
end
|
65
54
|
end
|
55
|
+
chosen = gets_list(list: models, chosen: models.dup)
|
56
|
+
relations = ::Brick.relations
|
57
|
+
chosen.each do |model_name|
|
58
|
+
# %%% If we're in a schema then make sure the module file exists
|
59
|
+
base_module = if (model_parts = model_name.split('::')).length > 1
|
60
|
+
"::#{model_parts.first}".constantize
|
61
|
+
else
|
62
|
+
Object
|
63
|
+
end
|
64
|
+
_built_model, code = Object.send(:build_model, relations, base_module, base_module.name, model_parts.last)
|
65
|
+
path = ['models']
|
66
|
+
path.concat(model_parts.map(&:underscore))
|
67
|
+
dir = +"#{::Rails.root}/app"
|
68
|
+
path[0..-2].each do |path_part|
|
69
|
+
dir << "/#{path_part}"
|
70
|
+
Dir.mkdir(dir) unless Dir.exists?(dir)
|
71
|
+
end
|
72
|
+
File.open("#{dir}/#{path.last}.rb", 'w') { |f| f.write code } unless code.blank?
|
73
|
+
end
|
74
|
+
puts "\n*** Created #{chosen.length} model files under app/models ***"
|
66
75
|
end
|
67
76
|
|
68
77
|
private
|
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.61
|
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-08-
|
11
|
+
date: 2022-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -239,7 +239,7 @@ files:
|
|
239
239
|
- lib/generators/brick/USAGE
|
240
240
|
- lib/generators/brick/install_generator.rb
|
241
241
|
- lib/generators/brick/migrations_generator.rb
|
242
|
-
- lib/generators/brick/
|
242
|
+
- lib/generators/brick/models_generator.rb
|
243
243
|
- lib/generators/brick/templates/add_object_changes_to_versions.rb.erb
|
244
244
|
- lib/generators/brick/templates/create_versions.rb.erb
|
245
245
|
homepage: https://github.com/lorint/brick
|