brick 1.0.13 → 1.0.16
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 +24 -0
- data/lib/brick/extensions.rb +143 -70
- data/lib/brick/frameworks/rails/engine.rb +9 -20
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +36 -0
- data/lib/generators/brick/install_generator.rb +17 -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: 30eeeb5fb81003f790234885589b29f227b7f4f2d498accb07da5cd8ea425084
|
4
|
+
data.tar.gz: 1841386c2750c2aef744a7ee555b649e2fc0298367684d261faf64297b8430c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64c5747800aaa5469f2ff2b0be3ae5056fa383c6d246e7aff15b19eb4a588e15e72f5d1ecb384879b3b7f175cab28961fb4984e11d5994085b215f442d722389
|
7
|
+
data.tar.gz: ec3418092244d97b3006086aaf0484ee1ab4342cf2c16716239f7127c8e23056dc25e1714e51523bdb8f87fb6b88c322dabefb096fe7bf0b1f695568b636d4b8
|
data/lib/brick/config.rb
CHANGED
@@ -91,6 +91,14 @@ module Brick
|
|
91
91
|
@mutex.synchronize { @model_descrips = descrips }
|
92
92
|
end
|
93
93
|
|
94
|
+
def sti_namespace_prefixes
|
95
|
+
@mutex.synchronize { @sti_namespace_prefixes ||= {} }
|
96
|
+
end
|
97
|
+
|
98
|
+
def sti_namespace_prefixes=(prefixes)
|
99
|
+
@mutex.synchronize { @sti_namespace_prefixes = prefixes }
|
100
|
+
end
|
101
|
+
|
94
102
|
def skip_database_views
|
95
103
|
@mutex.synchronize { @skip_database_views }
|
96
104
|
end
|
@@ -107,6 +115,14 @@ module Brick
|
|
107
115
|
@mutex.synchronize { @exclude_tables = value }
|
108
116
|
end
|
109
117
|
|
118
|
+
def models_inherit_from
|
119
|
+
@mutex.synchronize { @models_inherit_from }
|
120
|
+
end
|
121
|
+
|
122
|
+
def models_inherit_from=(value)
|
123
|
+
@mutex.synchronize { @models_inherit_from = value }
|
124
|
+
end
|
125
|
+
|
110
126
|
def table_name_prefixes
|
111
127
|
@mutex.synchronize { @table_name_prefixes }
|
112
128
|
end
|
@@ -122,5 +138,13 @@ module Brick
|
|
122
138
|
def metadata_columns=(columns)
|
123
139
|
@mutex.synchronize { @metadata_columns = columns }
|
124
140
|
end
|
141
|
+
|
142
|
+
def not_nullables
|
143
|
+
@mutex.synchronize { @not_nullables }
|
144
|
+
end
|
145
|
+
|
146
|
+
def not_nullables=(columns)
|
147
|
+
@mutex.synchronize { @not_nullables = columns }
|
148
|
+
end
|
125
149
|
end
|
126
150
|
end
|
data/lib/brick/extensions.rb
CHANGED
@@ -136,38 +136,78 @@ module ActiveRecord
|
|
136
136
|
|
137
137
|
alias _brick_find_sti_class find_sti_class
|
138
138
|
def find_sti_class(type_name)
|
139
|
-
::Brick.sti_models
|
140
|
-
|
141
|
-
module_prefixes.unshift('') unless module_prefixes.first.blank?
|
142
|
-
candidate_file = Rails.root.join('app/models' + module_prefixes.map(&:underscore).join('/') + '.rb')
|
143
|
-
if File.exists?(candidate_file)
|
144
|
-
# Find this STI class normally
|
139
|
+
if ::Brick.sti_models.key?(type_name)
|
140
|
+
# puts ['X', self.name, type_name].inspect
|
145
141
|
_brick_find_sti_class(type_name)
|
146
142
|
else
|
147
|
-
#
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
143
|
+
# This auto-STI is more of a brute-force approach, building modules where needed
|
144
|
+
# The more graceful alternative is the overload of ActiveSupport::Dependencies#autoload_module! found below
|
145
|
+
::Brick.sti_models[type_name] = { base: self } unless type_name.blank?
|
146
|
+
module_prefixes = type_name.split('::')
|
147
|
+
module_prefixes.unshift('') unless module_prefixes.first.blank?
|
148
|
+
module_name = module_prefixes[0..-2].join('::')
|
149
|
+
if ::Brick.config.sti_namespace_prefixes&.key?("::#{module_name}::") ||
|
150
|
+
::Brick.config.sti_namespace_prefixes&.key?("#{module_name}::")
|
151
|
+
_brick_find_sti_class(type_name)
|
152
|
+
elsif File.exists?(candidate_file = Rails.root.join('app/models' + module_prefixes.map(&:underscore).join('/') + '.rb'))
|
153
|
+
_brick_find_sti_class(type_name) # Find this STI class normally
|
154
|
+
else
|
155
|
+
# Build missing prefix modules if they don't yet exist
|
156
|
+
this_module = Object
|
157
|
+
module_prefixes[1..-2].each do |module_name|
|
158
|
+
this_module = if this_module.const_defined?(module_name)
|
159
|
+
this_module.const_get(module_name)
|
160
|
+
else
|
161
|
+
this_module.const_set(module_name.to_sym, Module.new)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
if this_module.const_defined?(class_name = module_prefixes.last.to_sym)
|
165
|
+
this_module.const_get(class_name)
|
166
|
+
else
|
167
|
+
# Build STI subclass and place it into the namespace module
|
168
|
+
# %%% Does this ever get used???
|
169
|
+
puts [this_module.const_set(class_name, klass = Class.new(self)).name, class_name].inspect
|
170
|
+
klass
|
171
|
+
end
|
155
172
|
end
|
156
|
-
|
157
|
-
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
if ActiveSupport::Dependencies.respond_to?(:autoload_module!) # %%% Only works with previous non-zeitwerk auto-loading
|
180
|
+
module ActiveSupport::Dependencies
|
181
|
+
class << self
|
182
|
+
# %%% Probably a little more targeted than other approaches we've taken thusfar
|
183
|
+
# This happens before the whole parent check
|
184
|
+
alias _brick_autoload_module! autoload_module!
|
185
|
+
def autoload_module!(*args)
|
186
|
+
into, const_name, qualified_name, path_suffix = args
|
187
|
+
if (base_class = ::Brick.config.sti_namespace_prefixes&.fetch("::#{into.name}::", nil)&.constantize)
|
188
|
+
::Brick.sti_models[qualified_name] = { base: base_class }
|
189
|
+
# Build subclass and place it into the specially STI-namespaced module
|
190
|
+
into.const_set(const_name.to_sym, klass = Class.new(base_class))
|
191
|
+
# %%% used to also have: autoload_once_paths.include?(base_path) ||
|
192
|
+
autoloaded_constants << qualified_name unless autoloaded_constants.include?(qualified_name)
|
158
193
|
klass
|
194
|
+
elsif (base_class = ::Brick.config.sti_namespace_prefixes&.fetch("::#{const_name}", nil)&.constantize)
|
195
|
+
# Build subclass and place it into Object
|
196
|
+
Object.const_set(const_name.to_sym, klass = Class.new(base_class))
|
197
|
+
else
|
198
|
+
_brick_autoload_module!(*args)
|
159
199
|
end
|
160
200
|
end
|
161
201
|
end
|
162
202
|
end
|
163
203
|
end
|
164
204
|
|
165
|
-
# Object.class_exec do
|
166
205
|
class Object
|
167
206
|
class << self
|
168
207
|
alias _brick_const_missing const_missing
|
169
208
|
def const_missing(*args)
|
170
|
-
return
|
209
|
+
return self.const_get(args.first) if self.const_defined?(args.first)
|
210
|
+
return Object.const_get(args.first) if Object.const_defined?(args.first) unless self == Object
|
171
211
|
|
172
212
|
class_name = args.first.to_s
|
173
213
|
# See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
@@ -175,43 +215,57 @@ class Object
|
|
175
215
|
# that is, checking #qualified_name_for with: from_mod, const_name
|
176
216
|
# If we want to support namespacing in the future, might have to utilise something like this:
|
177
217
|
# path_suffix = ActiveSupport::Dependencies.qualified_name_for(Object, args.first).underscore
|
178
|
-
# return
|
218
|
+
# return self._brick_const_missing(*args) if ActiveSupport::Dependencies.search_for_file(path_suffix)
|
179
219
|
# If the file really exists, go and snag it:
|
180
|
-
|
220
|
+
if !(is_found = ActiveSupport::Dependencies.search_for_file(class_name.underscore)) && (filepath = self.name&.split('::'))
|
221
|
+
filepath = (filepath[0..-2] + [class_name]).join('/').underscore + '.rb'
|
222
|
+
end
|
223
|
+
if is_found
|
224
|
+
return self._brick_const_missing(*args)
|
225
|
+
elsif ActiveSupport::Dependencies.search_for_file(filepath) # Last-ditch effort to pick this thing up before we fill in the gaps on our own
|
226
|
+
my_const = parent.const_missing(class_name) # ends up having: MyModule::MyClass
|
227
|
+
return my_const
|
228
|
+
end
|
181
229
|
|
182
230
|
relations = ::Brick.instance_variable_get(:@relations)[ActiveRecord::Base.connection_pool.object_id] || {}
|
183
231
|
is_controllers_enabled = ::Brick.enable_controllers? || (ENV['RAILS_ENV'] || ENV['RACK_ENV']) == 'development'
|
184
232
|
result = if is_controllers_enabled && class_name.end_with?('Controller') && (plural_class_name = class_name[0..-11]).length.positive?
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
233
|
+
# Otherwise now it's up to us to fill in the gaps
|
234
|
+
if (model = ActiveSupport::Inflector.singularize(plural_class_name).constantize)
|
235
|
+
# 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.
|
236
|
+
build_controller(class_name, plural_class_name, model, relations)
|
237
|
+
end
|
238
|
+
elsif ::Brick.enable_models?
|
239
|
+
# See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
240
|
+
# checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
241
|
+
plural_class_name = ActiveSupport::Inflector.pluralize(model_name = class_name)
|
242
|
+
singular_table_name = ActiveSupport::Inflector.underscore(model_name)
|
243
|
+
|
244
|
+
# Adjust for STI if we know of a base model for the requested model name
|
245
|
+
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
|
246
|
+
base_model.table_name
|
247
|
+
else
|
248
|
+
ActiveSupport::Inflector.pluralize(singular_table_name)
|
249
|
+
end
|
250
|
+
|
251
|
+
# Maybe, just maybe there's a database table that will satisfy this need
|
252
|
+
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(m) })
|
253
|
+
build_model(model_name, singular_table_name, table_name, relations, matching)
|
254
|
+
end
|
255
|
+
end
|
208
256
|
if result
|
209
257
|
built_class, code = result
|
210
258
|
puts "\n#{code}"
|
211
259
|
built_class
|
260
|
+
elsif ::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}")
|
261
|
+
# module_prefixes = type_name.split('::')
|
262
|
+
# path = self.name.split('::')[0..-2] + []
|
263
|
+
# module_prefixes.unshift('') unless module_prefixes.first.blank?
|
264
|
+
# candidate_file = Rails.root.join('app/models' + module_prefixes.map(&:underscore).join('/') + '.rb')
|
265
|
+
self._brick_const_missing(*args)
|
212
266
|
else
|
213
|
-
puts "MISSING! #{args.inspect} #{table_name}"
|
214
|
-
|
267
|
+
puts "MISSING! #{self.name} #{args.inspect} #{table_name}"
|
268
|
+
self._brick_const_missing(*args)
|
215
269
|
end
|
216
270
|
end
|
217
271
|
|
@@ -223,12 +277,15 @@ class Object
|
|
223
277
|
|
224
278
|
# Are they trying to use a pluralised class name such as "Employees" instead of "Employee"?
|
225
279
|
if table_name == singular_table_name && !ActiveSupport::Inflector.inflections.uncountable.include?(table_name)
|
226
|
-
|
280
|
+
unless ::Brick.config.sti_namespace_prefixes&.key?("::#{singular_table_name.titleize}::")
|
281
|
+
puts "Warning: Class name for a model that references table \"#{matching}\" should be \"#{ActiveSupport::Inflector.singularize(model_name)}\"."
|
282
|
+
end
|
283
|
+
return
|
227
284
|
end
|
228
285
|
if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
|
229
286
|
is_sti = true
|
230
287
|
else
|
231
|
-
base_model = ActiveRecord::Base
|
288
|
+
base_model = ::Brick.config.models_inherit_from || ActiveRecord::Base
|
232
289
|
end
|
233
290
|
code = +"class #{model_name} < #{base_model.name}\n"
|
234
291
|
built_model = Class.new(base_model) do |new_model_class|
|
@@ -277,6 +334,11 @@ class Object
|
|
277
334
|
options = {}
|
278
335
|
singular_table_name = ActiveSupport::Inflector.singularize(assoc[:inverse_table])
|
279
336
|
macro = if assoc[:is_bt]
|
337
|
+
# Try to take care of screwy names if this is a belongs_to going to an STI subclass
|
338
|
+
if (primary_class = assoc.fetch(:primary_class, nil)) &&
|
339
|
+
(sti_inverse_assoc = primary_class.reflect_on_all_associations.find { |a| a.macro == :has_many && a.options[:class_name] == self.name && assoc[:fk] = a.foreign_key })
|
340
|
+
assoc_name = sti_inverse_assoc.options[:inverse_of].to_s || assoc_name
|
341
|
+
end
|
280
342
|
need_class_name = singular_table_name.underscore != assoc_name
|
281
343
|
need_fk = "#{assoc_name}_id" != assoc[:fk]
|
282
344
|
if (inverse = assoc[:inverse])
|
@@ -311,7 +373,7 @@ class Object
|
|
311
373
|
end
|
312
374
|
# Figure out if we need to specially call out the class_name and/or foreign key
|
313
375
|
# (and if either of those then definitely also a specific inverse_of)
|
314
|
-
options[:class_name] = singular_table_name.camelize if need_class_name
|
376
|
+
options[:class_name] = assoc[:primary_class]&.name || singular_table_name.camelize if need_class_name
|
315
377
|
# Work around a bug in CPK where self-referencing belongs_to associations double up their foreign keys
|
316
378
|
if need_fk # Funky foreign key?
|
317
379
|
options[:foreign_key] = if assoc[:fk].is_a?(Array)
|
@@ -342,12 +404,13 @@ class Object
|
|
342
404
|
fks.each do |fk|
|
343
405
|
source = nil
|
344
406
|
this_hmt_fk = if fks.length > 1
|
345
|
-
singular_assoc_name =
|
407
|
+
singular_assoc_name = fk.first[:inverse][:assoc_name].singularize
|
346
408
|
source = fk.last
|
347
|
-
through =
|
409
|
+
through = fk.first[:alternate_name].pluralize
|
348
410
|
"#{singular_assoc_name}_#{hmt_fk}"
|
349
411
|
else
|
350
|
-
|
412
|
+
source = fk.last unless hmt_fk.singularize == fk.last
|
413
|
+
through = fk.first[:assoc_name].pluralize
|
351
414
|
hmt_fk
|
352
415
|
end
|
353
416
|
code << " has_many :#{this_hmt_fk}, through: #{(assoc_name = through.to_sym).to_sym.inspect}#{", source: :#{source}" if source}\n"
|
@@ -356,6 +419,14 @@ class Object
|
|
356
419
|
self.send(:has_many, this_hmt_fk.to_sym, **options)
|
357
420
|
end
|
358
421
|
end
|
422
|
+
# Not NULLables
|
423
|
+
relation[:cols].each do |col, datatype|
|
424
|
+
if (datatype[3] && ar_pks.exclude?(col) && ::Brick.config.metadata_columns.exclude?(col)) ||
|
425
|
+
::Brick.config.not_nullables.include?("#{matching}.#{col}")
|
426
|
+
code << " validates :#{col}, presence: true\n"
|
427
|
+
self.send(:validates, col.to_sym, { presence: true })
|
428
|
+
end
|
429
|
+
end
|
359
430
|
end
|
360
431
|
code << "end # model #{model_name}\n\n"
|
361
432
|
end # class definition
|
@@ -451,7 +522,8 @@ module ActiveRecord::ConnectionHandling
|
|
451
522
|
"SELECT t.table_name AS relation_name, t.table_type,
|
452
523
|
c.column_name, c.data_type,
|
453
524
|
COALESCE(c.character_maximum_length, c.numeric_precision) AS max_length,
|
454
|
-
tc.constraint_type AS const, kcu.constraint_name AS \"key\"
|
525
|
+
tc.constraint_type AS const, kcu.constraint_name AS \"key\",
|
526
|
+
c.is_nullable
|
455
527
|
FROM INFORMATION_SCHEMA.tables AS t
|
456
528
|
LEFT OUTER JOIN INFORMATION_SCHEMA.columns AS c ON t.table_schema = c.table_schema
|
457
529
|
AND t.table_name = c.table_name
|
@@ -489,7 +561,7 @@ module ActiveRecord::ConnectionHandling
|
|
489
561
|
end
|
490
562
|
key << col_name if key
|
491
563
|
cols = relation[:cols] # relation.fetch(:cols) { relation[:cols] = [] }
|
492
|
-
cols[col_name] = [r['data_type'], r['max_length'], measures&.include?(col_name)]
|
564
|
+
cols[col_name] = [r['data_type'], r['max_length'], measures&.include?(col_name), r['is_nullable'] == 'NO']
|
493
565
|
# puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
|
494
566
|
end
|
495
567
|
else # MySQL2 acts a little differently, bringing back an array for each row
|
@@ -593,16 +665,17 @@ module Brick
|
|
593
665
|
bt_assoc_name = bt_assoc_name[0..-4] if bt_assoc_name.end_with?('_id')
|
594
666
|
|
595
667
|
bts = (relation = relations.fetch(fk[0], nil))&.fetch(:fks) { relation[:fks] = {} }
|
596
|
-
|
668
|
+
primary_table = (is_class = fk[2].is_a?(Hash) && fk[2].key?(:class)) ? (primary_class = fk[2][:class].constantize).table_name : fk[2]
|
669
|
+
hms = (relation = relations.fetch(primary_table, nil))&.fetch(:fks) { relation[:fks] = {} } unless is_class
|
597
670
|
|
598
671
|
unless (cnstr_name = fk[3])
|
599
672
|
# For any appended references (those that come from config), arrive upon a definitely unique constraint name
|
600
|
-
cnstr_base = cnstr_name = "(brick) #{fk[0]}_#{fk[2]}"
|
673
|
+
cnstr_base = cnstr_name = "(brick) #{fk[0]}_#{is_class ? fk[2][:class].underscore : fk[2]}"
|
601
674
|
cnstr_added_num = 1
|
602
675
|
cnstr_name = "#{cnstr_base}_#{cnstr_added_num += 1}" while bts&.key?(cnstr_name) || hms&.key?(cnstr_name)
|
603
676
|
missing = []
|
604
677
|
missing << fk[0] unless relations.key?(fk[0])
|
605
|
-
missing <<
|
678
|
+
missing << primary_table unless is_class || relations.key?(primary_table)
|
606
679
|
unless missing.empty?
|
607
680
|
tables = relations.reject { |k, v| v.fetch(:isView, nil) }.keys.sort
|
608
681
|
puts "Brick: Additional reference #{fk.inspect} refers to non-existent #{'table'.pluralize(missing.length)} #{missing.join(' and ')}. (Available tables include #{tables.join(', ')}.)"
|
@@ -613,8 +686,12 @@ module Brick
|
|
613
686
|
puts "Brick: Additional reference #{fk.inspect} refers to non-existent column #{fk[1]}. (Columns present in #{fk[0]} are #{columns.join(', ')}.)"
|
614
687
|
return
|
615
688
|
end
|
616
|
-
if (redundant = bts.find { |k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[0] && v[:fk] == fk[1] && v[:inverse_table] ==
|
617
|
-
|
689
|
+
if (redundant = bts.find { |k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[0] && v[:fk] == fk[1] && v[:inverse_table] == primary_table })
|
690
|
+
if is_class && !redundant.last.key?(:class)
|
691
|
+
redundant.last[:primary_class] = primary_class # Round out this BT so it can find the proper :source for a HMT association that references an STI subclass
|
692
|
+
else
|
693
|
+
puts "Brick: Additional reference #{fk.inspect} is redundant and can be removed. (Already established by #{redundant.first}.)"
|
694
|
+
end
|
618
695
|
return
|
619
696
|
end
|
620
697
|
end
|
@@ -622,10 +699,16 @@ module Brick
|
|
622
699
|
assoc_bt[:fk] = assoc_bt[:fk].is_a?(String) ? [assoc_bt[:fk], fk[1]] : assoc_bt[:fk].concat(fk[1])
|
623
700
|
assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[1]}"
|
624
701
|
else
|
625
|
-
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[1], assoc_name: bt_assoc_name, inverse_table:
|
702
|
+
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[1], assoc_name: bt_assoc_name, inverse_table: primary_table }
|
703
|
+
end
|
704
|
+
if is_class
|
705
|
+
# For use in finding the proper :source for a HMT association that references an STI subclass
|
706
|
+
assoc_bt[:primary_class] = primary_class
|
707
|
+
# For use in finding the proper :inverse_of for a BT association that references an STI subclass
|
708
|
+
# assoc_bt[:inverse_of] = primary_class.reflect_on_all_associations.find { |a| a.foreign_key == bt[1] }
|
626
709
|
end
|
627
710
|
|
628
|
-
unless ::Brick.config.skip_hms&.any? { |skip| fk[0] == skip[0] && fk[1] == skip[1] &&
|
711
|
+
unless is_class || ::Brick.config.skip_hms&.any? { |skip| fk[0] == skip[0] && fk[1] == skip[1] && primary_table == skip[2] }
|
629
712
|
cnstr_name = "hm_#{cnstr_name}"
|
630
713
|
if (assoc_hm = hms.fetch(cnstr_name, nil))
|
631
714
|
assoc_hm[:fk] = assoc_hm[:fk].is_a?(String) ? [assoc_hm[:fk], fk[1]] : assoc_hm[:fk].concat(fk[1])
|
@@ -640,15 +723,5 @@ module Brick
|
|
640
723
|
end
|
641
724
|
# hms[cnstr_name] << { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0] }
|
642
725
|
end
|
643
|
-
|
644
|
-
# Rails < 4.0 doesn't have ActiveRecord::RecordNotUnique, so use the more generic ActiveRecord::ActiveRecordError instead
|
645
|
-
ar_not_unique_error = ActiveRecord.const_defined?('RecordNotUnique') ? ActiveRecord::RecordNotUnique : ActiveRecord::ActiveRecordError
|
646
|
-
class NoUniqueColumnError < ar_not_unique_error
|
647
|
-
end
|
648
|
-
|
649
|
-
# Rails < 4.2 doesn't have ActiveRecord::RecordInvalid, so use the more generic ActiveRecord::ActiveRecordError instead
|
650
|
-
ar_invalid_error = ActiveRecord.const_defined?('RecordInvalid') ? ActiveRecord::RecordInvalid : ActiveRecord::ActiveRecordError
|
651
|
-
class LessThanHalfAreMatchingColumnsError < ar_invalid_error
|
652
|
-
end
|
653
726
|
end
|
654
727
|
end
|
@@ -16,12 +16,18 @@ module Brick
|
|
16
16
|
# Specific database tables and views to omit when auto-creating models
|
17
17
|
::Brick.exclude_tables = app.config.brick.fetch(:exclude_tables, [])
|
18
18
|
|
19
|
+
# Class for auto-generated models to inherit from
|
20
|
+
::Brick.models_inherit_from = app.config.brick.fetch(:models_inherit_from, ActiveRecord::Base)
|
21
|
+
|
19
22
|
# When table names have specific prefixes, automatically place them in their own module with a table_name_prefix.
|
20
23
|
::Brick.table_name_prefixes = app.config.brick.fetch(:table_name_prefixes, [])
|
21
24
|
|
22
25
|
# Columns to treat as being metadata for purposes of identifying associative tables for has_many :through
|
23
26
|
::Brick.metadata_columns = app.config.brick.fetch(:metadata_columns, ['created_at', 'updated_at', 'deleted_at'])
|
24
27
|
|
28
|
+
# Columns for which to add a validate presence: true even though the database doesn't have them marked as NOT NULL
|
29
|
+
::Brick.not_nullables = app.config.brick.fetch(:not_nullables, [])
|
30
|
+
|
25
31
|
# Additional references (virtual foreign keys)
|
26
32
|
::Brick.additional_references = app.config.brick.fetch(:additional_references, nil)
|
27
33
|
|
@@ -84,7 +90,6 @@ module Brick
|
|
84
90
|
|
85
91
|
schema_options = ::Brick.db_schemas.each_with_object(+'') { |v, s| s << "<option value=\"#{v}\">#{v}</option>" }.html_safe
|
86
92
|
hms_columns = +'' # Used for 'index'
|
87
|
-
# puts skip_hms.inspect
|
88
93
|
hms_headers = hms.each_with_object([]) do |hm, s|
|
89
94
|
next if skip_hms.key?(hm.last.name)
|
90
95
|
|
@@ -264,7 +269,7 @@ function changeout(href, param, value) {
|
|
264
269
|
<% if (collection = @#{obj_name}.first.#{hm_name}).empty? %>
|
265
270
|
<tr><td>(none)</td></tr>
|
266
271
|
<% else %>
|
267
|
-
<% collection.order(#{pk.inspect}).uniq.each do |#{hm_singular_name = hm_name.singularize}| %>
|
272
|
+
<% collection.order(#{pk.inspect}).uniq.each do |#{hm_singular_name = hm_name.singularize.underscore}| %>
|
268
273
|
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm_singular_name}_path(#{hm_singular_name}.#{pk})) %></td></tr>
|
269
274
|
<% end %>
|
270
275
|
<% end %>
|
@@ -285,24 +290,8 @@ function changeout(href, param, value) {
|
|
285
290
|
|
286
291
|
if ::Brick.enable_routes? || (ENV['RAILS_ENV'] || ENV['RACK_ENV']) == 'development'
|
287
292
|
ActionDispatch::Routing::RouteSet.class_exec do
|
288
|
-
|
289
|
-
|
290
|
-
unless @finalized
|
291
|
-
existing_controllers = routes.each_with_object({}) { |r, s| c = r.defaults[:controller]; s[c] = nil if c }
|
292
|
-
::Rails.application.routes.append do
|
293
|
-
# %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
|
294
|
-
# If auto-controllers and auto-models are both enabled then this makes sense:
|
295
|
-
::Brick.relations.each do |k, v|
|
296
|
-
unless existing_controllers.key?(controller_name = k.underscore.pluralize)
|
297
|
-
options = {}
|
298
|
-
options[:only] = [:index, :show] if v.key?(:isView)
|
299
|
-
send(:resources, controller_name.to_sym, **options)
|
300
|
-
end
|
301
|
-
end
|
302
|
-
end
|
303
|
-
end
|
304
|
-
_brick_finalize_routeset!(*args, **options)
|
305
|
-
end
|
293
|
+
# In order to defer auto-creation of any routes that already exist, calculate Brick routes only after having loaded all others
|
294
|
+
prepend ::Brick::RouteSet
|
306
295
|
end
|
307
296
|
end
|
308
297
|
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -174,6 +174,11 @@ module Brick
|
|
174
174
|
Brick.config.exclude_tables = value
|
175
175
|
end
|
176
176
|
|
177
|
+
# @api public
|
178
|
+
def models_inherit_from=(value)
|
179
|
+
Brick.config.models_inherit_from = value
|
180
|
+
end
|
181
|
+
|
177
182
|
# @api public
|
178
183
|
def table_name_prefixes=(value)
|
179
184
|
Brick.config.table_name_prefixes = value
|
@@ -184,6 +189,11 @@ module Brick
|
|
184
189
|
Brick.config.metadata_columns = value
|
185
190
|
end
|
186
191
|
|
192
|
+
# @api public
|
193
|
+
def not_nullables=(value)
|
194
|
+
Brick.config.not_nullables = value
|
195
|
+
end
|
196
|
+
|
187
197
|
# Additional table associations to use (Think of these as virtual foreign keys perhaps)
|
188
198
|
# @api public
|
189
199
|
def additional_references=(ars)
|
@@ -228,6 +238,13 @@ module Brick
|
|
228
238
|
Brick.config.model_descrips = descrips
|
229
239
|
end
|
230
240
|
|
241
|
+
# Module prefixes to build out and associate with specific base STI models
|
242
|
+
# @api public
|
243
|
+
def sti_namespace_prefixes=(snp)
|
244
|
+
Brick.config.sti_namespace_prefixes = snp
|
245
|
+
end
|
246
|
+
|
247
|
+
|
231
248
|
# Returns Brick's `::Gem::Version`, convenient for comparisons. This is
|
232
249
|
# recommended over `::Brick::VERSION::STRING`.
|
233
250
|
#
|
@@ -262,6 +279,25 @@ module Brick
|
|
262
279
|
VERSION::STRING
|
263
280
|
end
|
264
281
|
end
|
282
|
+
|
283
|
+
module RouteSet
|
284
|
+
def finalize!
|
285
|
+
existing_controllers = routes.each_with_object({}) { |r, s| c = r.defaults[:controller]; s[c] = nil if c }
|
286
|
+
::Rails.application.routes.append do
|
287
|
+
# %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
|
288
|
+
# If auto-controllers and auto-models are both enabled then this makes sense:
|
289
|
+
::Brick.relations.each do |k, v|
|
290
|
+
unless existing_controllers.key?(controller_name = k.underscore.pluralize)
|
291
|
+
options = {}
|
292
|
+
options[:only] = [:index, :show] if v.key?(:isView)
|
293
|
+
send(:resources, controller_name.to_sym, **options)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
super
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
265
301
|
end
|
266
302
|
|
267
303
|
require 'brick/version_number'
|
@@ -94,6 +94,9 @@ module Brick
|
|
94
94
|
# # Any tables or views you'd like to skip when auto-creating models
|
95
95
|
# Brick.exclude_tables = ['custom_metadata', 'version_info']
|
96
96
|
|
97
|
+
# # Class that auto-generated models should inherit from
|
98
|
+
# Brick.models_inherit_from = ApplicationRecord
|
99
|
+
|
97
100
|
# # When table names have specific prefixes automatically place them in their own module with a table_name_prefix.
|
98
101
|
# Brick.table_name_prefixes = { 'nav_' => 'Navigation' }
|
99
102
|
|
@@ -128,6 +131,10 @@ module Brick
|
|
128
131
|
# # database:
|
129
132
|
# Brick.metadata_columns = ['last_update']
|
130
133
|
|
134
|
+
# # Columns for which to add a validate presence: true even though the database doesn't have them marked as NOT NULL.
|
135
|
+
# # Designated by <table name>.<column name>
|
136
|
+
# Brick.not_nullables = ['users.name']
|
137
|
+
|
131
138
|
# # A simple DSL is available to allow more user-friendly display of objects. Normally a user object might be shown
|
132
139
|
# # as its first non-metadata column, or if that is not available then something like \"User #45\" where 45 is that
|
133
140
|
# # object's ID. If there is no primary key then even that is not possible, so the object's .to_s method is called.
|
@@ -135,6 +142,16 @@ module Brick
|
|
135
142
|
# # user, then you can use model_descrips like this, putting expressions with property references in square brackets:
|
136
143
|
# Brick.model_descrips = { 'User' => '[profile.firstname] [profile.lastname]' }
|
137
144
|
|
145
|
+
# # Specify STI subclasses either directly by name or as a general module prefix that should always relate to a specific
|
146
|
+
# # parent STI class. The prefixed :: here for these examples is mandatory. Also having a suffixed :: means instead of
|
147
|
+
# # a class reference, this is for a general namespace reference. So in this case requests for, say, either of the
|
148
|
+
# # non-existant classes Animals::Cat or Animals::Goat (or anything else with the module prefix of \"Animals::\" would
|
149
|
+
# # build a model that inherits from Animal. And a request specifically for the class Snake would build a new model
|
150
|
+
# # that inherits from Reptile, and no other request would do this -- only specifically for Snake. The ending ::
|
151
|
+
# # indicates that it's a module prefix instead of a specific class name.
|
152
|
+
# Brick.sti_namespace_prefixes = { '::Animals::' => 'Animal',
|
153
|
+
# '::Snake' => 'Reptile' }
|
154
|
+
|
138
155
|
# # If a default route is not supplied, Brick attempts to find the most \"central\" table and wires up the default
|
139
156
|
# # route to go to the :index action for what would be a controller for that table. You can specify any controller
|
140
157
|
# # name and action you wish in order to override this and have that be the default route when none other has been
|
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.16
|
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-03-
|
11
|
+
date: 2022-03-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|