brick 1.0.12 → 1.0.15
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 +140 -69
- data/lib/brick/frameworks/rails/engine.rb +10 -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: 2dbd4fef7ab5c2796e75a0bce86316be00ebceb02aa5a0b8053f75b27d433c57
|
4
|
+
data.tar.gz: daa2802b2fec6533ccfed8fa9178129c6633f92b83fb092fefa48a657cb36e9b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33f97461748561fb21900ccb1b4b39c13803e68e04fea807851b467587907577a10bd5b5a2470cde5f50bca019d3cc986b512545bf700477028f68751bb95e6a
|
7
|
+
data.tar.gz: 97605afbe32e2509800f510ca2984651cc196c048a6267fa31d65363815bc6f4484955aacd45fae1553fe70453f1bee283766812a2f92678bc35e516ec594738
|
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,76 @@ 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
|
-
# Build missing prefix modules if they don't yet exist
|
157
|
-
this_module.const_set(module_prefixes.last.to_sym, klass = Class.new(self))
|
158
|
-
klass
|
159
173
|
end
|
160
174
|
end
|
161
175
|
end
|
162
176
|
end
|
163
177
|
end
|
164
178
|
|
165
|
-
|
179
|
+
module ActiveSupport::Dependencies
|
180
|
+
class << self
|
181
|
+
# %%% Probably a little more targeted than other approaches we've taken thusfar
|
182
|
+
# This happens before the whole parent check
|
183
|
+
alias _brick_autoload_module! autoload_module!
|
184
|
+
def autoload_module!(*args)
|
185
|
+
into, const_name, qualified_name, path_suffix = args
|
186
|
+
if (base_class = ::Brick.config.sti_namespace_prefixes&.fetch("::#{into.name}::", nil)&.constantize)
|
187
|
+
::Brick.sti_models[qualified_name] = { base: base_class }
|
188
|
+
# Build subclass and place it into the specially STI-namespaced module
|
189
|
+
into.const_set(const_name.to_sym, klass = Class.new(base_class))
|
190
|
+
# %%% used to also have: autoload_once_paths.include?(base_path) ||
|
191
|
+
autoloaded_constants << qualified_name unless autoloaded_constants.include?(qualified_name)
|
192
|
+
klass
|
193
|
+
elsif (base_class = ::Brick.config.sti_namespace_prefixes&.fetch("::#{const_name}", nil)&.constantize)
|
194
|
+
# Build subclass and place it into Object
|
195
|
+
Object.const_set(const_name.to_sym, klass = Class.new(base_class))
|
196
|
+
else
|
197
|
+
_brick_autoload_module!(*args)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
166
203
|
class Object
|
167
204
|
class << self
|
168
205
|
alias _brick_const_missing const_missing
|
169
206
|
def const_missing(*args)
|
170
|
-
return
|
207
|
+
return self.const_get(args.first) if self.const_defined?(args.first)
|
208
|
+
return Object.const_get(args.first) if Object.const_defined?(args.first) unless self == Object
|
171
209
|
|
172
210
|
class_name = args.first.to_s
|
173
211
|
# See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
@@ -175,43 +213,57 @@ class Object
|
|
175
213
|
# that is, checking #qualified_name_for with: from_mod, const_name
|
176
214
|
# If we want to support namespacing in the future, might have to utilise something like this:
|
177
215
|
# path_suffix = ActiveSupport::Dependencies.qualified_name_for(Object, args.first).underscore
|
178
|
-
# return
|
216
|
+
# return self._brick_const_missing(*args) if ActiveSupport::Dependencies.search_for_file(path_suffix)
|
179
217
|
# If the file really exists, go and snag it:
|
180
|
-
|
218
|
+
if !(is_found = ActiveSupport::Dependencies.search_for_file(class_name.underscore)) && (filepath = self.name&.split('::'))
|
219
|
+
filepath = (filepath[0..-2] + [class_name]).join('/').underscore + '.rb'
|
220
|
+
end
|
221
|
+
if is_found
|
222
|
+
return self._brick_const_missing(*args)
|
223
|
+
elsif ActiveSupport::Dependencies.search_for_file(filepath) # Last-ditch effort to pick this thing up before we fill in the gaps on our own
|
224
|
+
my_const = parent.const_missing(class_name) # ends up having: MyModule::MyClass
|
225
|
+
return my_const
|
226
|
+
end
|
181
227
|
|
182
228
|
relations = ::Brick.instance_variable_get(:@relations)[ActiveRecord::Base.connection_pool.object_id] || {}
|
183
|
-
is_controllers_enabled =
|
229
|
+
is_controllers_enabled = ::Brick.enable_controllers? || (ENV['RAILS_ENV'] || ENV['RACK_ENV']) == 'development'
|
184
230
|
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
|
-
|
231
|
+
# Otherwise now it's up to us to fill in the gaps
|
232
|
+
if (model = ActiveSupport::Inflector.singularize(plural_class_name).constantize)
|
233
|
+
# 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.
|
234
|
+
build_controller(class_name, plural_class_name, model, relations)
|
235
|
+
end
|
236
|
+
elsif ::Brick.enable_models?
|
237
|
+
# See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
238
|
+
# checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
239
|
+
plural_class_name = ActiveSupport::Inflector.pluralize(model_name = class_name)
|
240
|
+
singular_table_name = ActiveSupport::Inflector.underscore(model_name)
|
241
|
+
|
242
|
+
# Adjust for STI if we know of a base model for the requested model name
|
243
|
+
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
|
244
|
+
base_model.table_name
|
245
|
+
else
|
246
|
+
ActiveSupport::Inflector.pluralize(singular_table_name)
|
247
|
+
end
|
248
|
+
|
249
|
+
# Maybe, just maybe there's a database table that will satisfy this need
|
250
|
+
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(m) })
|
251
|
+
build_model(model_name, singular_table_name, table_name, relations, matching)
|
252
|
+
end
|
253
|
+
end
|
208
254
|
if result
|
209
255
|
built_class, code = result
|
210
256
|
puts "\n#{code}"
|
211
257
|
built_class
|
258
|
+
elsif ::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}")
|
259
|
+
# module_prefixes = type_name.split('::')
|
260
|
+
# path = self.name.split('::')[0..-2] + []
|
261
|
+
# module_prefixes.unshift('') unless module_prefixes.first.blank?
|
262
|
+
# candidate_file = Rails.root.join('app/models' + module_prefixes.map(&:underscore).join('/') + '.rb')
|
263
|
+
self._brick_const_missing(*args)
|
212
264
|
else
|
213
|
-
puts "MISSING! #{args.inspect} #{table_name}"
|
214
|
-
|
265
|
+
puts "MISSING! #{self.name} #{args.inspect} #{table_name}"
|
266
|
+
self._brick_const_missing(*args)
|
215
267
|
end
|
216
268
|
end
|
217
269
|
|
@@ -223,12 +275,15 @@ class Object
|
|
223
275
|
|
224
276
|
# Are they trying to use a pluralised class name such as "Employees" instead of "Employee"?
|
225
277
|
if table_name == singular_table_name && !ActiveSupport::Inflector.inflections.uncountable.include?(table_name)
|
226
|
-
|
278
|
+
unless ::Brick.config.sti_namespace_prefixes&.key?("::#{singular_table_name.titleize}::")
|
279
|
+
puts "Warning: Class name for a model that references table \"#{matching}\" should be \"#{ActiveSupport::Inflector.singularize(model_name)}\"."
|
280
|
+
end
|
281
|
+
return
|
227
282
|
end
|
228
283
|
if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
|
229
284
|
is_sti = true
|
230
285
|
else
|
231
|
-
base_model = ActiveRecord::Base
|
286
|
+
base_model = ::Brick.config.models_inherit_from || ActiveRecord::Base
|
232
287
|
end
|
233
288
|
code = +"class #{model_name} < #{base_model.name}\n"
|
234
289
|
built_model = Class.new(base_model) do |new_model_class|
|
@@ -277,6 +332,12 @@ class Object
|
|
277
332
|
options = {}
|
278
333
|
singular_table_name = ActiveSupport::Inflector.singularize(assoc[:inverse_table])
|
279
334
|
macro = if assoc[:is_bt]
|
335
|
+
# Try to take care of screwy names if this is a belongs_to going to an STI subclass
|
336
|
+
# binding.pry if assoc[:fk] == 'issue_severity_id'
|
337
|
+
if (primary_class = assoc.fetch(:primary_class, nil)) &&
|
338
|
+
(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 })
|
339
|
+
assoc_name = sti_inverse_assoc.options[:inverse_of].to_s || assoc_name
|
340
|
+
end
|
280
341
|
need_class_name = singular_table_name.underscore != assoc_name
|
281
342
|
need_fk = "#{assoc_name}_id" != assoc[:fk]
|
282
343
|
if (inverse = assoc[:inverse])
|
@@ -311,7 +372,7 @@ class Object
|
|
311
372
|
end
|
312
373
|
# Figure out if we need to specially call out the class_name and/or foreign key
|
313
374
|
# (and if either of those then definitely also a specific inverse_of)
|
314
|
-
options[:class_name] = singular_table_name.camelize if need_class_name
|
375
|
+
options[:class_name] = assoc[:primary_class]&.name || singular_table_name.camelize if need_class_name
|
315
376
|
# Work around a bug in CPK where self-referencing belongs_to associations double up their foreign keys
|
316
377
|
if need_fk # Funky foreign key?
|
317
378
|
options[:foreign_key] = if assoc[:fk].is_a?(Array)
|
@@ -356,6 +417,14 @@ class Object
|
|
356
417
|
self.send(:has_many, this_hmt_fk.to_sym, **options)
|
357
418
|
end
|
358
419
|
end
|
420
|
+
# Not NULLables
|
421
|
+
relation[:cols].each do |col, datatype|
|
422
|
+
if (datatype[3] && ar_pks.exclude?(col) && ::Brick.config.metadata_columns.exclude?(col)) ||
|
423
|
+
::Brick.config.not_nullables.include?("#{matching}.#{col}")
|
424
|
+
code << " validates :#{col}, presence: true\n"
|
425
|
+
self.send(:validates, col.to_sym, { presence: true })
|
426
|
+
end
|
427
|
+
end
|
359
428
|
end
|
360
429
|
code << "end # model #{model_name}\n\n"
|
361
430
|
end # class definition
|
@@ -451,7 +520,8 @@ module ActiveRecord::ConnectionHandling
|
|
451
520
|
"SELECT t.table_name AS relation_name, t.table_type,
|
452
521
|
c.column_name, c.data_type,
|
453
522
|
COALESCE(c.character_maximum_length, c.numeric_precision) AS max_length,
|
454
|
-
tc.constraint_type AS const, kcu.constraint_name AS \"key\"
|
523
|
+
tc.constraint_type AS const, kcu.constraint_name AS \"key\",
|
524
|
+
c.is_nullable
|
455
525
|
FROM INFORMATION_SCHEMA.tables AS t
|
456
526
|
LEFT OUTER JOIN INFORMATION_SCHEMA.columns AS c ON t.table_schema = c.table_schema
|
457
527
|
AND t.table_name = c.table_name
|
@@ -489,7 +559,7 @@ module ActiveRecord::ConnectionHandling
|
|
489
559
|
end
|
490
560
|
key << col_name if key
|
491
561
|
cols = relation[:cols] # relation.fetch(:cols) { relation[:cols] = [] }
|
492
|
-
cols[col_name] = [r['data_type'], r['max_length'], measures&.include?(col_name)]
|
562
|
+
cols[col_name] = [r['data_type'], r['max_length'], measures&.include?(col_name), r['is_nullable'] == 'NO']
|
493
563
|
# puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
|
494
564
|
end
|
495
565
|
else # MySQL2 acts a little differently, bringing back an array for each row
|
@@ -593,16 +663,17 @@ module Brick
|
|
593
663
|
bt_assoc_name = bt_assoc_name[0..-4] if bt_assoc_name.end_with?('_id')
|
594
664
|
|
595
665
|
bts = (relation = relations.fetch(fk[0], nil))&.fetch(:fks) { relation[:fks] = {} }
|
596
|
-
|
666
|
+
primary_table = (is_class = fk[2].is_a?(Hash) && fk[2].key?(:class)) ? (primary_class = fk[2][:class].constantize).table_name : fk[2]
|
667
|
+
hms = (relation = relations.fetch(primary_table, nil))&.fetch(:fks) { relation[:fks] = {} } unless is_class
|
597
668
|
|
598
669
|
unless (cnstr_name = fk[3])
|
599
670
|
# 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]}"
|
671
|
+
cnstr_base = cnstr_name = "(brick) #{fk[0]}_#{is_class ? fk[2][:class].underscore : fk[2]}"
|
601
672
|
cnstr_added_num = 1
|
602
673
|
cnstr_name = "#{cnstr_base}_#{cnstr_added_num += 1}" while bts&.key?(cnstr_name) || hms&.key?(cnstr_name)
|
603
674
|
missing = []
|
604
675
|
missing << fk[0] unless relations.key?(fk[0])
|
605
|
-
missing <<
|
676
|
+
missing << primary_table unless is_class || relations.key?(primary_table)
|
606
677
|
unless missing.empty?
|
607
678
|
tables = relations.reject { |k, v| v.fetch(:isView, nil) }.keys.sort
|
608
679
|
puts "Brick: Additional reference #{fk.inspect} refers to non-existent #{'table'.pluralize(missing.length)} #{missing.join(' and ')}. (Available tables include #{tables.join(', ')}.)"
|
@@ -613,8 +684,12 @@ module Brick
|
|
613
684
|
puts "Brick: Additional reference #{fk.inspect} refers to non-existent column #{fk[1]}. (Columns present in #{fk[0]} are #{columns.join(', ')}.)"
|
614
685
|
return
|
615
686
|
end
|
616
|
-
if (redundant = bts.find { |k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[0] && v[:fk] == fk[1] && v[:inverse_table] ==
|
617
|
-
|
687
|
+
if (redundant = bts.find { |k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[0] && v[:fk] == fk[1] && v[:inverse_table] == primary_table })
|
688
|
+
if is_class && !redundant.last.key?(:class)
|
689
|
+
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
|
690
|
+
else
|
691
|
+
puts "Brick: Additional reference #{fk.inspect} is redundant and can be removed. (Already established by #{redundant.first}.)"
|
692
|
+
end
|
618
693
|
return
|
619
694
|
end
|
620
695
|
end
|
@@ -622,10 +697,16 @@ module Brick
|
|
622
697
|
assoc_bt[:fk] = assoc_bt[:fk].is_a?(String) ? [assoc_bt[:fk], fk[1]] : assoc_bt[:fk].concat(fk[1])
|
623
698
|
assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[1]}"
|
624
699
|
else
|
625
|
-
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[1], assoc_name: bt_assoc_name, inverse_table:
|
700
|
+
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[1], assoc_name: bt_assoc_name, inverse_table: primary_table }
|
701
|
+
end
|
702
|
+
if is_class
|
703
|
+
# For use in finding the proper :source for a HMT association that references an STI subclass
|
704
|
+
assoc_bt[:primary_class] = primary_class
|
705
|
+
# For use in finding the proper :inverse_of for a BT association that references an STI subclass
|
706
|
+
# assoc_bt[:inverse_of] = primary_class.reflect_on_all_associations.find { |a| a.foreign_key == bt[1] }
|
626
707
|
end
|
627
708
|
|
628
|
-
unless ::Brick.config.skip_hms&.any? { |skip| fk[0] == skip[0] && fk[1] == skip[1] &&
|
709
|
+
unless is_class || ::Brick.config.skip_hms&.any? { |skip| fk[0] == skip[0] && fk[1] == skip[1] && primary_table == skip[2] }
|
629
710
|
cnstr_name = "hm_#{cnstr_name}"
|
630
711
|
if (assoc_hm = hms.fetch(cnstr_name, nil))
|
631
712
|
assoc_hm[:fk] = assoc_hm[:fk].is_a?(String) ? [assoc_hm[:fk], fk[1]] : assoc_hm[:fk].concat(fk[1])
|
@@ -640,15 +721,5 @@ module Brick
|
|
640
721
|
end
|
641
722
|
# hms[cnstr_name] << { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0] }
|
642
723
|
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
724
|
end
|
654
725
|
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
|
|
@@ -37,7 +43,7 @@ module Brick
|
|
37
43
|
# ====================================
|
38
44
|
# Dynamically create generic templates
|
39
45
|
# ====================================
|
40
|
-
if
|
46
|
+
if ::Brick.enable_views? || (ENV['RAILS_ENV'] || ENV['RACK_ENV']) == 'development'
|
41
47
|
ActionView::LookupContext.class_exec do
|
42
48
|
alias :_brick_template_exists? :template_exists?
|
43
49
|
def template_exists?(*args, **options)
|
@@ -283,26 +289,10 @@ function changeout(href, param, value) {
|
|
283
289
|
end
|
284
290
|
end
|
285
291
|
|
286
|
-
if
|
292
|
+
if ::Brick.enable_routes? || (ENV['RAILS_ENV'] || ENV['RACK_ENV']) == 'development'
|
287
293
|
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
|
294
|
+
# In order to defer auto-creation of any routes that already exist, calculate Brick routes only after having loaded all others
|
295
|
+
prepend ::Brick::RouteSet
|
306
296
|
end
|
307
297
|
end
|
308
298
|
|
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 for auto-generated models to 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.15
|
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
|