brick 1.0.8 → 1.0.10
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 +27 -2
- data/lib/brick/extensions.rb +207 -137
- data/lib/brick/frameworks/rails/engine.rb +164 -46
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +29 -13
- data/lib/generators/brick/install_generator.rb +79 -8
- 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: cfc0c2e81700a407de1fe154d337dd83d756a29fba0914db5eeb1974198720d2
|
4
|
+
data.tar.gz: 0051e6b4fe55d1e9f4cc4614aabd941a6947f66af5ac41f35725df594217fd2d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 445acf196541b17429bb7c5f93b64ebd51cc4c3b1fcc4cec7fcb43cca6e47ed86a027bc0010b3053a763414809a390509d9da9c296c4eab85bbd794a3553ba90
|
7
|
+
data.tar.gz: 110cd25429c21410effe398a61c4284876d49e222267987e0c5e0e09b5ab8e84bd4991fbd36257077f5178fa3fcba661970162e574744c2f8d2b80ec4a1e2d51
|
data/lib/brick/config.rb
CHANGED
@@ -65,13 +65,30 @@ module Brick
|
|
65
65
|
@mutex.synchronize { @additional_references = references }
|
66
66
|
end
|
67
67
|
|
68
|
+
# Skip creating a has_many association for these
|
69
|
+
def skip_hms
|
70
|
+
@mutex.synchronize { @skip_hms }
|
71
|
+
end
|
72
|
+
|
73
|
+
def skip_hms=(skips)
|
74
|
+
@mutex.synchronize { @skip_hms = skips }
|
75
|
+
end
|
76
|
+
|
68
77
|
# Associations to treat as a has_one
|
69
78
|
def has_ones
|
70
79
|
@mutex.synchronize { @has_ones }
|
71
80
|
end
|
72
81
|
|
73
|
-
def has_ones=(
|
74
|
-
@mutex.synchronize { @has_ones =
|
82
|
+
def has_ones=(hos)
|
83
|
+
@mutex.synchronize { @has_ones = hos }
|
84
|
+
end
|
85
|
+
|
86
|
+
def model_descrips
|
87
|
+
@mutex.synchronize { @model_descrips }
|
88
|
+
end
|
89
|
+
|
90
|
+
def model_descrips=(descrips)
|
91
|
+
@mutex.synchronize { @model_descrips = descrips }
|
75
92
|
end
|
76
93
|
|
77
94
|
def skip_database_views
|
@@ -90,6 +107,14 @@ module Brick
|
|
90
107
|
@mutex.synchronize { @exclude_tables = value }
|
91
108
|
end
|
92
109
|
|
110
|
+
def table_name_prefixes
|
111
|
+
@mutex.synchronize { @table_name_prefixes }
|
112
|
+
end
|
113
|
+
|
114
|
+
def table_name_prefixes=(value)
|
115
|
+
@mutex.synchronize { @table_name_prefixes = value }
|
116
|
+
end
|
117
|
+
|
93
118
|
def metadata_columns
|
94
119
|
@mutex.synchronize { @metadata_columns }
|
95
120
|
end
|
data/lib/brick/extensions.rb
CHANGED
@@ -46,7 +46,56 @@ module ActiveRecord
|
|
46
46
|
# Used to show a little prettier name for an object
|
47
47
|
def brick_descrip
|
48
48
|
klass = self.class
|
49
|
-
|
49
|
+
# If available, parse simple DSL attached to a model in order to provide a friendlier name.
|
50
|
+
# Object property names can be referenced in square brackets like this:
|
51
|
+
# { 'User' => '[profile.firstname] [profile.lastname]' }
|
52
|
+
|
53
|
+
# If there's no DSL yet specified, just try to find the first usable column on this model
|
54
|
+
unless ::Brick.config.model_descrips[klass.name]
|
55
|
+
descrip_col = (klass.columns.map(&:name) - klass._brick_get_fks -
|
56
|
+
(::Brick.config.metadata_columns || []) -
|
57
|
+
[klass.primary_key]).first
|
58
|
+
::Brick.config.model_descrips[klass.name] = "[#{descrip_col}]" if descrip_col
|
59
|
+
end
|
60
|
+
if (dsl ||= ::Brick.config.model_descrips[klass.name])
|
61
|
+
caches = {}
|
62
|
+
output = +''
|
63
|
+
is_brackets_have_content = false
|
64
|
+
bracket_name = nil
|
65
|
+
dsl.each_char do |ch|
|
66
|
+
if bracket_name
|
67
|
+
if ch == ']' # Time to process a bracketed thing?
|
68
|
+
obj_name = +''
|
69
|
+
obj = self
|
70
|
+
bracket_name.split('.').each do |part|
|
71
|
+
obj_name += ".#{part}"
|
72
|
+
obj = if caches.key?(obj_name)
|
73
|
+
caches[obj_name]
|
74
|
+
else
|
75
|
+
(caches[obj_name] = obj&.send(part.to_sym))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
is_brackets_have_content = true unless (obj&.to_s).blank?
|
79
|
+
output << (obj&.to_s || '')
|
80
|
+
bracket_name = nil
|
81
|
+
else
|
82
|
+
bracket_name << ch
|
83
|
+
end
|
84
|
+
elsif ch == '['
|
85
|
+
bracket_name = +''
|
86
|
+
else
|
87
|
+
output << ch
|
88
|
+
end
|
89
|
+
end
|
90
|
+
output += bracket_name if bracket_name
|
91
|
+
end
|
92
|
+
if is_brackets_have_content
|
93
|
+
output
|
94
|
+
elsif klass.primary_key
|
95
|
+
"#{klass.name} ##{send(klass.primary_key)}"
|
96
|
+
else
|
97
|
+
to_s
|
98
|
+
end
|
50
99
|
end
|
51
100
|
|
52
101
|
private
|
@@ -123,12 +172,13 @@ class Object
|
|
123
172
|
# checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
124
173
|
plural_class_name = ActiveSupport::Inflector.pluralize(model_name = class_name)
|
125
174
|
singular_table_name = ActiveSupport::Inflector.underscore(model_name)
|
126
|
-
table_name = ActiveSupport::Inflector.pluralize(singular_table_name)
|
127
175
|
|
128
176
|
# Adjust for STI if we know of a base model for the requested model name
|
129
|
-
if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
|
130
|
-
|
131
|
-
|
177
|
+
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
|
178
|
+
base_model.table_name
|
179
|
+
else
|
180
|
+
ActiveSupport::Inflector.pluralize(singular_table_name)
|
181
|
+
end
|
132
182
|
|
133
183
|
# Maybe, just maybe there's a database table that will satisfy this need
|
134
184
|
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(m) })
|
@@ -155,7 +205,11 @@ class Object
|
|
155
205
|
if table_name == singular_table_name && !ActiveSupport::Inflector.inflections.uncountable.include?(table_name)
|
156
206
|
raise NameError.new("Class name for a model that references table \"#{matching}\" should be \"#{ActiveSupport::Inflector.singularize(model_name)}\".")
|
157
207
|
end
|
158
|
-
base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil)
|
208
|
+
if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
|
209
|
+
is_sti = true
|
210
|
+
else
|
211
|
+
base_model = ActiveRecord::Base
|
212
|
+
end
|
159
213
|
code = +"class #{model_name} < #{base_model.name}\n"
|
160
214
|
built_model = Class.new(base_model) do |new_model_class|
|
161
215
|
Object.const_set(model_name.to_sym, new_model_class)
|
@@ -193,93 +247,96 @@ class Object
|
|
193
247
|
code << " # Could not identify any column(s) to use as a primary key\n" unless is_view
|
194
248
|
end
|
195
249
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
assoc_name, need_class_name = _brick_get_hm_assoc_name(relation, assoc)
|
221
|
-
need_fk = "#{ActiveSupport::Inflector.singularize(assoc[:inverse][:inverse_table])}_id" != assoc[:fk]
|
222
|
-
# fks[table_name].find { |other_assoc| other_assoc.object_id != assoc.object_id && other_assoc[:assoc_name] == assoc[assoc_name] }
|
223
|
-
if (has_ones = ::Brick.config.has_ones&.fetch(model_name, nil))&.key?(singular_assoc_name = ActiveSupport::Inflector.singularize(assoc_name))
|
224
|
-
assoc_name = if has_ones[singular_assoc_name]
|
225
|
-
need_class_name = true
|
226
|
-
has_ones[singular_assoc_name]
|
227
|
-
else
|
228
|
-
singular_assoc_name
|
229
|
-
end
|
230
|
-
:has_one
|
250
|
+
unless is_sti
|
251
|
+
fks = relation[:fks] || {}
|
252
|
+
# Do the bulk of the has_many / belongs_to processing, and store details about HMT so they can be done at the very last
|
253
|
+
hmts = fks.each_with_object(Hash.new { |h, k| h[k] = [] }) do |fk, hmts|
|
254
|
+
# The key in each hash entry (fk.first) is the constraint name
|
255
|
+
assoc_name = (assoc = fk.last)[:assoc_name]
|
256
|
+
inverse_assoc_name = assoc[:inverse]&.fetch(:assoc_name, nil)
|
257
|
+
options = {}
|
258
|
+
singular_table_name = ActiveSupport::Inflector.singularize(assoc[:inverse_table])
|
259
|
+
macro = if assoc[:is_bt]
|
260
|
+
need_class_name = singular_table_name.underscore != assoc_name
|
261
|
+
need_fk = "#{assoc_name}_id" != assoc[:fk]
|
262
|
+
if (inverse = assoc[:inverse])
|
263
|
+
inverse_assoc_name, _x = _brick_get_hm_assoc_name(relations[assoc[:inverse_table]], inverse)
|
264
|
+
if (has_ones = ::Brick.config.has_ones&.fetch(inverse[:alternate_name].camelize, nil))&.key?(singular_inv_assoc_name = ActiveSupport::Inflector.singularize(inverse_assoc_name))
|
265
|
+
inverse_assoc_name = if has_ones[singular_inv_assoc_name]
|
266
|
+
need_inverse_of = true
|
267
|
+
has_ones[singular_inv_assoc_name]
|
268
|
+
else
|
269
|
+
singular_inv_assoc_name
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
:belongs_to
|
231
274
|
else
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
assoc_fk = assoc[:fk].uniq
|
242
|
-
assoc_fk.length < 2 ? assoc_fk.first : assoc_fk
|
275
|
+
# need_class_name = ActiveSupport::Inflector.singularize(assoc_name) == ActiveSupport::Inflector.singularize(table_name.underscore)
|
276
|
+
# Are there multiple foreign keys out to the same table?
|
277
|
+
assoc_name, need_class_name = _brick_get_hm_assoc_name(relation, assoc)
|
278
|
+
need_fk = "#{ActiveSupport::Inflector.singularize(assoc[:inverse][:inverse_table])}_id" != assoc[:fk]
|
279
|
+
# fks[table_name].find { |other_assoc| other_assoc.object_id != assoc.object_id && other_assoc[:assoc_name] == assoc[assoc_name] }
|
280
|
+
if (has_ones = ::Brick.config.has_ones&.fetch(model_name, nil))&.key?(singular_assoc_name = ActiveSupport::Inflector.singularize(assoc_name))
|
281
|
+
assoc_name = if has_ones[singular_assoc_name]
|
282
|
+
need_class_name = true
|
283
|
+
has_ones[singular_assoc_name]
|
243
284
|
else
|
244
|
-
|
285
|
+
singular_assoc_name
|
245
286
|
end
|
246
|
-
|
247
|
-
|
287
|
+
:has_one
|
288
|
+
else
|
289
|
+
:has_many
|
290
|
+
end
|
291
|
+
end
|
292
|
+
# Figure out if we need to specially call out the class_name and/or foreign key
|
293
|
+
# (and if either of those then definitely also a specific inverse_of)
|
294
|
+
options[:class_name] = singular_table_name.camelize if need_class_name
|
295
|
+
# Work around a bug in CPK where self-referencing belongs_to associations double up their foreign keys
|
296
|
+
if need_fk # Funky foreign key?
|
297
|
+
options[:foreign_key] = if assoc[:fk].is_a?(Array)
|
298
|
+
assoc_fk = assoc[:fk].uniq
|
299
|
+
assoc_fk.length < 2 ? assoc_fk.first : assoc_fk
|
300
|
+
else
|
301
|
+
assoc[:fk].to_sym
|
302
|
+
end
|
303
|
+
end
|
304
|
+
options[:inverse_of] = inverse_assoc_name.to_sym if inverse_assoc_name && (need_class_name || need_fk || need_inverse_of)
|
248
305
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
306
|
+
# Prepare a list of entries for "has_many :through"
|
307
|
+
if macro == :has_many
|
308
|
+
relations[assoc[:inverse_table]][:hmt_fks].each do |k, hmt_fk|
|
309
|
+
next if k == assoc[:fk]
|
253
310
|
|
254
|
-
|
311
|
+
hmts[ActiveSupport::Inflector.pluralize(hmt_fk.last)] << [assoc, hmt_fk.first]
|
312
|
+
end
|
255
313
|
end
|
256
|
-
end
|
257
314
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
315
|
+
# And finally create a has_one, has_many, or belongs_to for this association
|
316
|
+
assoc_name = assoc_name.to_sym
|
317
|
+
code << " #{macro} #{assoc_name.inspect}#{options.map { |k, v| ", #{k}: #{v.inspect}" }.join}\n"
|
318
|
+
self.send(macro, assoc_name, **options)
|
319
|
+
hmts
|
320
|
+
end
|
321
|
+
hmts.each do |hmt_fk, fks|
|
322
|
+
fks.each do |fk|
|
323
|
+
source = nil
|
324
|
+
this_hmt_fk = if fks.length > 1
|
325
|
+
singular_assoc_name = ActiveSupport::Inflector.singularize(fk.first[:inverse][:assoc_name])
|
326
|
+
source = fk.last
|
327
|
+
through = ActiveSupport::Inflector.pluralize(fk.first[:alternate_name])
|
328
|
+
"#{singular_assoc_name}_#{hmt_fk}"
|
329
|
+
else
|
330
|
+
through = fk.first[:assoc_name]
|
331
|
+
hmt_fk
|
332
|
+
end
|
333
|
+
code << " has_many :#{this_hmt_fk}, through: #{(assoc_name = through.to_sym).to_sym.inspect}#{", source: :#{source}" if source}\n"
|
334
|
+
options = { through: assoc_name }
|
335
|
+
options[:source] = source.to_sym if source
|
336
|
+
self.send(:has_many, this_hmt_fk.to_sym, **options)
|
337
|
+
end
|
280
338
|
end
|
281
339
|
end
|
282
|
-
|
283
340
|
code << "end # model #{model_name}\n\n"
|
284
341
|
end # class definition
|
285
342
|
[built_model, code]
|
@@ -298,6 +355,7 @@ class Object
|
|
298
355
|
code << " @#{table_name}.brick_where(params)\n"
|
299
356
|
code << " end\n"
|
300
357
|
self.define_method :index do
|
358
|
+
::Brick.set_db_schema(params)
|
301
359
|
ar_relation = model.primary_key ? model.order(model.primary_key) : model.all
|
302
360
|
instance_variable_set(:@_brick_params, ar_relation.brick_where(params))
|
303
361
|
instance_variable_set("@#{table_name}".to_sym, ar_relation)
|
@@ -308,6 +366,7 @@ class Object
|
|
308
366
|
code << " @#{singular_table_name} = #{model.name}.find(params[:id].split(','))\n"
|
309
367
|
code << " end\n"
|
310
368
|
self.define_method :show do
|
369
|
+
::Brick.set_db_schema(params)
|
311
370
|
instance_variable_set("@#{singular_table_name}".to_sym, model.find(params[:id].split(',')))
|
312
371
|
end
|
313
372
|
end
|
@@ -323,6 +382,7 @@ class Object
|
|
323
382
|
end
|
324
383
|
|
325
384
|
def _brick_get_hm_assoc_name(relation, hm_assoc)
|
385
|
+
binding.pry if hm_assoc.nil?
|
326
386
|
if relation[:hm_counts][hm_assoc[:assoc_name]]&.> 1
|
327
387
|
[ActiveSupport::Inflector.pluralize(hm_assoc[:alternate_name]), true]
|
328
388
|
else
|
@@ -349,9 +409,11 @@ module ActiveRecord::ConnectionHandling
|
|
349
409
|
# Only for Postgres? (Doesn't work in sqlite3)
|
350
410
|
# puts ActiveRecord::Base.connection.execute("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
351
411
|
|
352
|
-
|
412
|
+
schema_sql = 'SELECT NULL AS table_schema;'
|
413
|
+
case ActiveRecord::Base.connection.adapter_name
|
353
414
|
when 'PostgreSQL'
|
354
415
|
schema = 'public'
|
416
|
+
schema_sql = 'SELECT DISTINCT table_schema FROM INFORMATION_SCHEMA.tables;'
|
355
417
|
when 'Mysql2'
|
356
418
|
schema = ActiveRecord::Base.connection.current_database
|
357
419
|
when 'SQLite'
|
@@ -459,6 +521,10 @@ module ActiveRecord::ConnectionHandling
|
|
459
521
|
else
|
460
522
|
end
|
461
523
|
if sql
|
524
|
+
::Brick.db_schemas = ActiveRecord::Base.connection.execute(schema_sql)
|
525
|
+
::Brick.db_schemas = ::Brick.db_schemas.to_a unless ::Brick.db_schemas.is_a?(Array)
|
526
|
+
::Brick.db_schemas.map! { |row| row['table_schema'] } unless ::Brick.db_schemas.empty? || ::Brick.db_schemas.first.is_a?(String)
|
527
|
+
::Brick.db_schemas -= ['information_schema', 'pg_catalog']
|
462
528
|
ActiveRecord::Base.connection.execute(sql).each do |fk|
|
463
529
|
fk = fk.values unless fk.is_a?(Array)
|
464
530
|
::Brick._add_bt_and_hm(fk, relations)
|
@@ -499,65 +565,69 @@ module Brick
|
|
499
565
|
end # module Extensions
|
500
566
|
# rubocop:enable Style/CommentedKeyword
|
501
567
|
|
502
|
-
|
503
|
-
relations
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
568
|
+
class << self
|
569
|
+
def _add_bt_and_hm(fk, relations = nil)
|
570
|
+
relations ||= ::Brick.relations
|
571
|
+
bt_assoc_name = fk[1].underscore
|
572
|
+
bt_assoc_name = bt_assoc_name[0..-4] if bt_assoc_name.end_with?('_id')
|
573
|
+
|
574
|
+
bts = (relation = relations.fetch(fk[0], nil))&.fetch(:fks) { relation[:fks] = {} }
|
575
|
+
hms = (relation = relations.fetch(fk[2], nil))&.fetch(:fks) { relation[:fks] = {} }
|
576
|
+
|
577
|
+
unless (cnstr_name = fk[3])
|
578
|
+
# For any appended references (those that come from config), arrive upon a definitely unique constraint name
|
579
|
+
cnstr_base = cnstr_name = "(brick) #{fk[0]}_#{fk[2]}"
|
580
|
+
cnstr_added_num = 1
|
581
|
+
cnstr_name = "#{cnstr_base}_#{cnstr_added_num += 1}" while bts&.key?(cnstr_name) || hms&.key?(cnstr_name)
|
582
|
+
missing = []
|
583
|
+
missing << fk[0] unless relations.key?(fk[0])
|
584
|
+
missing << fk[2] unless relations.key?(fk[2])
|
585
|
+
unless missing.empty?
|
586
|
+
tables = relations.reject { |k, v| v.fetch(:isView, nil) }.keys.sort
|
587
|
+
puts "Brick: Additional reference #{fk.inspect} refers to non-existent #{'table'.pluralize(missing.length)} #{missing.join(' and ')}. (Available tables include #{tables.join(', ')}.)"
|
588
|
+
return
|
589
|
+
end
|
590
|
+
unless (cols = relations[fk[0]][:cols]).key?(fk[1])
|
591
|
+
columns = cols.map { |k, v| "#{k} (#{v.first.split(' ').first})" }
|
592
|
+
puts "Brick: Additional reference #{fk.inspect} refers to non-existent column #{fk[1]}. (Columns present in #{fk[0]} are #{columns.join(', ')}.)"
|
593
|
+
return
|
594
|
+
end
|
595
|
+
if (redundant = bts.find { |k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[0] && v[:fk] == fk[1] && v[:inverse_table] == fk[2] })
|
596
|
+
puts "Brick: Additional reference #{fk.inspect} is redundant and can be removed. (Already established by #{redundant.first}.)"
|
597
|
+
return
|
598
|
+
end
|
522
599
|
end
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
600
|
+
if (assoc_bt = bts[cnstr_name])
|
601
|
+
assoc_bt[:fk] = assoc_bt[:fk].is_a?(String) ? [assoc_bt[:fk], fk[1]] : assoc_bt[:fk].concat(fk[1])
|
602
|
+
assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[1]}"
|
603
|
+
else
|
604
|
+
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[1], assoc_name: bt_assoc_name, inverse_table: fk[2] }
|
527
605
|
end
|
528
|
-
|
529
|
-
|
530
|
-
|
606
|
+
|
607
|
+
unless ::Brick.config.skip_hms&.any? { |skip| fk[0] == skip[0] && fk[1] == skip[1] && fk[2] == skip[2] }
|
608
|
+
cnstr_name = "hm_#{cnstr_name}"
|
609
|
+
if (assoc_hm = hms.fetch(cnstr_name, nil))
|
610
|
+
assoc_hm[:fk] = assoc_hm[:fk].is_a?(String) ? [assoc_hm[:fk], fk[1]] : assoc_hm[:fk].concat(fk[1])
|
611
|
+
assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
|
612
|
+
assoc_hm[:inverse] = assoc_bt
|
613
|
+
else
|
614
|
+
assoc_hm = hms[cnstr_name] = { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0], inverse: assoc_bt }
|
615
|
+
hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
|
616
|
+
hm_counts[fk[0]] = hm_counts.fetch(fk[0]) { 0 } + 1
|
617
|
+
end
|
618
|
+
assoc_bt[:inverse] = assoc_hm
|
531
619
|
end
|
620
|
+
# hms[cnstr_name] << { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0] }
|
532
621
|
end
|
533
622
|
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
else
|
538
|
-
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[1], assoc_name: bt_assoc_name, inverse_table: fk[2] }
|
623
|
+
# Rails < 4.0 doesn't have ActiveRecord::RecordNotUnique, so use the more generic ActiveRecord::ActiveRecordError instead
|
624
|
+
ar_not_unique_error = ActiveRecord.const_defined?('RecordNotUnique') ? ActiveRecord::RecordNotUnique : ActiveRecord::ActiveRecordError
|
625
|
+
class NoUniqueColumnError < ar_not_unique_error
|
539
626
|
end
|
540
627
|
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
assoc_hm[:inverse] = assoc_bt
|
545
|
-
else
|
546
|
-
assoc_hm = hms[cnstr_name] = { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0], inverse: assoc_bt }
|
547
|
-
hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
|
548
|
-
hm_counts[fk[0]] = hm_counts.fetch(fk[0]) { 0 } + 1
|
628
|
+
# Rails < 4.2 doesn't have ActiveRecord::RecordInvalid, so use the more generic ActiveRecord::ActiveRecordError instead
|
629
|
+
ar_invalid_error = ActiveRecord.const_defined?('RecordInvalid') ? ActiveRecord::RecordInvalid : ActiveRecord::ActiveRecordError
|
630
|
+
class LessThanHalfAreMatchingColumnsError < ar_invalid_error
|
549
631
|
end
|
550
|
-
assoc_bt[:inverse] = assoc_hm
|
551
|
-
# hms[cnstr_name] << { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0] }
|
552
|
-
end
|
553
|
-
|
554
|
-
# Rails < 4.0 doesn't have ActiveRecord::RecordNotUnique, so use the more generic ActiveRecord::ActiveRecordError instead
|
555
|
-
ar_not_unique_error = ActiveRecord.const_defined?('RecordNotUnique') ? ActiveRecord::RecordNotUnique : ActiveRecord::ActiveRecordError
|
556
|
-
class NoUniqueColumnError < ar_not_unique_error
|
557
|
-
end
|
558
|
-
|
559
|
-
# Rails < 4.2 doesn't have ActiveRecord::RecordInvalid, so use the more generic ActiveRecord::ActiveRecordError instead
|
560
|
-
ar_invalid_error = ActiveRecord.const_defined?('RecordInvalid') ? ActiveRecord::RecordInvalid : ActiveRecord::ActiveRecordError
|
561
|
-
class LessThanHalfAreMatchingColumnsError < ar_invalid_error
|
562
632
|
end
|
563
633
|
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
|
+
# When table names have specific prefixes, automatically place them in their own module with a table_name_prefix.
|
20
|
+
::Brick.table_name_prefixes = app.config.brick.fetch(:table_name_prefixes, [])
|
21
|
+
|
19
22
|
# Columns to treat as being metadata for purposes of identifying associative tables for has_many :through
|
20
23
|
::Brick.metadata_columns = app.config.brick.fetch(:metadata_columns, ['created_at', 'updated_at', 'deleted_at'])
|
21
24
|
|
22
25
|
# Additional references (virtual foreign keys)
|
23
26
|
::Brick.additional_references = app.config.brick.fetch(:additional_references, nil)
|
24
27
|
|
28
|
+
# Skip creating a has_many association for these
|
29
|
+
::Brick.skip_hms = app.config.brick.fetch(:skip_hms, nil)
|
30
|
+
|
25
31
|
# Has one relationships
|
26
32
|
::Brick.has_ones = app.config.brick.fetch(:has_ones, nil)
|
27
33
|
end
|
@@ -62,78 +68,148 @@ module Brick
|
|
62
68
|
# This gets has_many as well as has_many :through
|
63
69
|
# %%% weed out ones that don't have an available model to reference
|
64
70
|
bts, hms = ::Brick.get_bts_and_hms(@_brick_model)
|
65
|
-
#
|
66
|
-
|
67
|
-
|
71
|
+
# Mark has_manys that go to an associative ("join") table so that they are skipped in the UI,
|
72
|
+
# as well as any possible polymorphic associations
|
73
|
+
skip_hms = {}
|
74
|
+
associatives = hms.each_with_object({}) do |hmt, s|
|
75
|
+
if (through = hmt.last.options[:through])
|
76
|
+
skip_hms[through] = nil
|
77
|
+
s[hmt.first] = hms[through] # End up with a hash of HMT names pointing to join-table associations
|
78
|
+
elsif hmt.last.inverse_of.nil?
|
79
|
+
puts "SKIPPING #{hmt.last.name.inspect}"
|
80
|
+
# %%% If we don't do this then below associative.name will find that associative is nil
|
81
|
+
skip_hms[hmt.last.name] = nil
|
82
|
+
end
|
68
83
|
end
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
84
|
+
|
85
|
+
schema_options = ::Brick.db_schemas.each_with_object(+'') { |v, s| s << "<option value=\"#{v}\">#{v}</option>" }.html_safe
|
86
|
+
hms_columns = +'' # Used for 'index'
|
87
|
+
# puts skip_hms.inspect
|
88
|
+
hms_headers = hms.each_with_object([]) do |hm, s|
|
89
|
+
next if skip_hms.key?(hm.last.name)
|
90
|
+
|
91
|
+
if args.first == 'index'
|
92
|
+
hm_fk_name = if hm.last.options[:through]
|
93
|
+
associative = associatives[hm.last.name]
|
94
|
+
"'#{associative.name}.#{associative.foreign_key}'"
|
95
|
+
else
|
96
|
+
hm.last.foreign_key
|
97
|
+
end
|
98
|
+
hms_columns << if hm.last.macro == :has_many
|
78
99
|
"<td>
|
79
100
|
<%= link_to \"#\{#{obj_name}.#{hm.first}.count\} #{hm.first}\", #{hm.last.klass.name.underscore.pluralize}_path({ #{hm_fk_name}: #{obj_name}.#{pk} }) unless #{obj_name}.#{hm.first}.count.zero? %>
|
80
101
|
</td>\n"
|
81
|
-
|
102
|
+
else # has_one
|
82
103
|
"<td>
|
83
104
|
<%= obj = #{obj_name}.#{hm.first}; link_to(obj.brick_descrip, obj) if obj %>
|
84
105
|
</td>\n"
|
85
|
-
|
106
|
+
end
|
107
|
+
end
|
108
|
+
s << [hm.last, "H#{hm.last.macro == :has_one ? 'O' : 'M'}#{'T' if hm.last.options[:through]} #{hm.first}"]
|
86
109
|
end
|
87
110
|
|
111
|
+
css = "<style>
|
112
|
+
table {
|
113
|
+
border-collapse: collapse;
|
114
|
+
margin: 25px 0;
|
115
|
+
font-size: 0.9em;
|
116
|
+
font-family: sans-serif;
|
117
|
+
min-width: 400px;
|
118
|
+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
|
119
|
+
}
|
120
|
+
|
121
|
+
table thead tr th, table tr th {
|
122
|
+
background-color: #009879;
|
123
|
+
color: #ffffff;
|
124
|
+
text-align: left;
|
125
|
+
}
|
126
|
+
|
127
|
+
table th, table td {
|
128
|
+
padding: 0.2em 0.5em;
|
129
|
+
}
|
130
|
+
|
131
|
+
.show-field {
|
132
|
+
background-color: #004998;
|
133
|
+
}
|
134
|
+
|
135
|
+
table tbody tr {
|
136
|
+
border-bottom: thin solid #dddddd;
|
137
|
+
}
|
138
|
+
|
139
|
+
table tbody tr:nth-of-type(even) {
|
140
|
+
background-color: #f3f3f3;
|
141
|
+
}
|
142
|
+
|
143
|
+
table tbody tr:last-of-type {
|
144
|
+
border-bottom: 2px solid #009879;
|
145
|
+
}
|
146
|
+
|
147
|
+
table tbody tr.active-row {
|
148
|
+
font-weight: bold;
|
149
|
+
color: #009879;
|
150
|
+
}
|
151
|
+
|
152
|
+
a.show-arrow {
|
153
|
+
font-size: 2.5em;
|
154
|
+
text-decoration: none;
|
155
|
+
}
|
156
|
+
</style>"
|
157
|
+
|
158
|
+
script = "<script>
|
159
|
+
var schemaSelect = document.getElementById(\"schema\");
|
160
|
+
if (schemaSelect) {
|
161
|
+
var brickSchema = changeout(location.href, \"_brick_schema\");
|
162
|
+
if (brickSchema) {
|
163
|
+
[... document.getElementsByTagName(\"A\")].forEach(function (a) { a.href = changeout(a.href, \"_brick_schema\", brickSchema); });
|
164
|
+
}
|
165
|
+
schemaSelect.value = brickSchema || \"public\";
|
166
|
+
schemaSelect.focus();
|
167
|
+
schemaSelect.addEventListener(\"change\", function () {
|
168
|
+
location.href = changeout(location.href, \"_brick_schema\", this.value);
|
169
|
+
});
|
170
|
+
}
|
171
|
+
function changeout(href, param, value) {
|
172
|
+
var hrefParts = href.split(\"?\");
|
173
|
+
var params = hrefParts.length > 1 ? hrefParts[1].split(\"&\") : [];
|
174
|
+
params = params.reduce(function (s, v) { var parts = v.split(\"=\"); s[parts[0]] = parts[1]; return s; }, {});
|
175
|
+
if (value === undefined) return params[param];
|
176
|
+
params[param] = value;
|
177
|
+
return hrefParts[0] + \"?\" + Object.keys(params).reduce(function (s, v) { s.push(v + \"=\" + params[v]); return s; }, []).join(\"&\");
|
178
|
+
}
|
179
|
+
</script>"
|
180
|
+
|
88
181
|
inline = case args.first
|
89
182
|
when 'index'
|
90
|
-
|
91
|
-
|
183
|
+
"#{css}
|
184
|
+
<p style=\"color: green\"><%= notice %></p>#{"
|
185
|
+
<select id=\"schema\">#{schema_options}</select>" if ::Brick.db_schemas.length > 1}
|
92
186
|
<h1>#{model_name.pluralize}</h1>
|
93
187
|
<% if @_brick_params&.present? %><h3>where <%= @_brick_params.each_with_object([]) { |v, s| s << \"#\{v.first\} = #\{v.last.inspect\}\" }.join(', ') %></h3><% end %>
|
94
|
-
|
95
188
|
<table id=\"#{table_name}\">
|
96
|
-
<tr>
|
97
|
-
<%
|
98
|
-
bts = { #{bts.each_with_object([]) { |v, s| s << "#{v.first.inspect} => [#{v.last.first.inspect}, #{v.last[1].name}, #{v.last[1].primary_key.inspect}]"}.join(', ')} }
|
189
|
+
<thead><tr>#{"<th></th>" if pk }
|
190
|
+
<% bts = { #{bts.each_with_object([]) { |v, s| s << "#{v.first.inspect} => [#{v.last.first.inspect}, #{v.last[1].name}, #{v.last[1].primary_key.inspect}]"}.join(', ')} }
|
99
191
|
@#{table_name}.columns.map(&:name).each do |col| %>
|
100
192
|
<% next if col == '#{pk}' || ::Brick.config.metadata_columns.include?(col) %>
|
101
193
|
<th>
|
102
|
-
<% if bt = bts[col]
|
103
|
-
if is_first
|
104
|
-
is_first = false
|
105
|
-
is_need_id_col = true %>
|
106
|
-
</th><th>
|
107
|
-
<% end %>
|
194
|
+
<% if (bt = bts[col]) %>
|
108
195
|
BT <%= \"#\{bt.first\}-\" unless bt[1].name.underscore == bt.first.to_s %><%= bt[1].name %>
|
109
|
-
<% else
|
110
|
-
is_first = false %>
|
196
|
+
<% else %>
|
111
197
|
<%= col %>
|
112
198
|
<% end %>
|
113
199
|
</th>
|
114
200
|
<% end %>
|
115
|
-
|
116
|
-
|
117
|
-
is_need_id_col = true %>
|
118
|
-
<th></th>
|
119
|
-
<% end %>
|
120
|
-
#{hms_headers}
|
121
|
-
</tr>
|
201
|
+
#{hms_headers.map { |h| "<th>#{h.last}</th>\n" }.join}
|
202
|
+
</tr></thead>
|
122
203
|
|
204
|
+
<tbody>
|
123
205
|
<% @#{table_name}.each do |#{obj_name}| %>
|
124
|
-
<tr
|
125
|
-
|
126
|
-
if is_need_id_col
|
127
|
-
is_first = false %>
|
128
|
-
<td><%= link_to \"#\{#{obj_name}.class.name\} ##\{#{obj_name}.id\}\", #{obj_name} %></td>
|
129
|
-
<% end %>
|
206
|
+
<tr>#{"
|
207
|
+
<td><%= link_to '⇛', #{obj_name}_path(#{obj_name}.#{pk}), { class: 'show-arrow' } %></td>" if pk }
|
130
208
|
<% #{obj_name}.attributes.each do |k, val| %>
|
131
209
|
<% next if k == '#{pk}' || ::Brick.config.metadata_columns.include?(k) %>
|
132
210
|
<td>
|
133
211
|
<% if (bt = bts[k]) %>
|
134
|
-
<%=
|
135
|
-
<% elsif is_first %>
|
136
|
-
<%= is_first = false; link_to val, #{obj_name}_path(#{obj_name}.#{pk}) %>
|
212
|
+
<%= bt_obj = bt[1].find_by(bt.last => val); link_to(bt_obj.brick_descrip, bt_obj) if bt_obj %>
|
137
213
|
<% else %>
|
138
214
|
<%= val %>
|
139
215
|
<% end %>
|
@@ -142,13 +218,55 @@ module Brick
|
|
142
218
|
#{hms_columns}
|
143
219
|
<!-- td>X</td -->
|
144
220
|
</tr>
|
221
|
+
</tbody>
|
145
222
|
<% end %>
|
146
223
|
</table>
|
147
224
|
|
148
225
|
#{"<hr><%= link_to \"New #{obj_name}\", new_#{obj_name}_path %>" unless @_brick_model.is_view?}
|
149
|
-
"
|
226
|
+
#{script}"
|
150
227
|
when 'show'
|
151
|
-
|
228
|
+
"#{css}
|
229
|
+
<p style=\"color: green\"><%= notice %></p>#{"
|
230
|
+
<select id=\"schema\">#{schema_options}</select>" if ::Brick.db_schemas.length > 1}
|
231
|
+
<h1>#{model_name}: <%= (obj = @#{obj_name}.first).brick_descrip %></h1>
|
232
|
+
<%= link_to '(See all #{obj_name.pluralize})', #{table_name}_path %>
|
233
|
+
<table>
|
234
|
+
<% bts = { #{bts.each_with_object([]) { |v, s| s << "#{v.first.inspect} => [#{v.last.first.inspect}, #{v.last[1].name}, #{v.last[1].primary_key.inspect}]"}.join(', ')} }
|
235
|
+
@#{obj_name}.first.attributes.each do |k, val| %>
|
236
|
+
<tr>
|
237
|
+
<% next if k == '#{pk}' || ::Brick.config.metadata_columns.include?(k) %>
|
238
|
+
<th class=\"show-field\">
|
239
|
+
<% if (bt = bts[k]) %>
|
240
|
+
BT <%= \"#\{bt.first\}-\" unless bt[1].name.underscore == bt.first.to_s %><%= bt[1].name %>
|
241
|
+
<% else %>
|
242
|
+
<%= k %>
|
243
|
+
<% end %>
|
244
|
+
</th>
|
245
|
+
<td>
|
246
|
+
<% if (bt = bts[k]) %>
|
247
|
+
<%= bt_obj = bt[1].find_by(bt.last => val); link_to(bt_obj.brick_descrip, bt_obj) if bt_obj %>
|
248
|
+
<% else %>
|
249
|
+
<%= val %>
|
250
|
+
<% end %>
|
251
|
+
</td>
|
252
|
+
</tr>
|
253
|
+
<% end %>
|
254
|
+
</table>
|
255
|
+
|
256
|
+
#{hms_headers.map do |hm|
|
257
|
+
next unless (pk = hm.first.klass.primary_key)
|
258
|
+
"<table id=\"#{hm_name = hm.first.name.to_s}\">
|
259
|
+
<tr><th>#{hm.last}</th></tr>
|
260
|
+
<% if (collection = @#{obj_name}.first.#{hm_name}).empty? %>
|
261
|
+
<tr><td>(none)</td></tr>
|
262
|
+
<% else %>
|
263
|
+
<% collection.order(#{pk.inspect}).uniq.each do |#{hm_singular_name = hm_name.singularize}| %>
|
264
|
+
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm_singular_name}_path(#{hm_singular_name}.#{pk})) %></td></tr>
|
265
|
+
<% end %>
|
266
|
+
<% end %>
|
267
|
+
</table>" end.join}
|
268
|
+
#{script}"
|
269
|
+
|
152
270
|
end
|
153
271
|
# As if it were an inline template (see #determine_template in actionview-5.2.6.2/lib/action_view/renderer/template_renderer.rb)
|
154
272
|
keys = options.has_key?(:locals) ? options[:locals].keys : []
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -86,6 +86,13 @@ module Brick
|
|
86
86
|
end
|
87
87
|
|
88
88
|
class << self
|
89
|
+
attr_accessor :db_schemas
|
90
|
+
|
91
|
+
def set_db_schema(params)
|
92
|
+
schema = params['_brick_schema'] || 'public'
|
93
|
+
ActiveRecord::Base.connection.execute("SET SEARCH_PATH='#{schema}';") if schema && ::Brick.db_schemas&.include?(schema)
|
94
|
+
end
|
95
|
+
|
89
96
|
# All tables and views (what Postgres calls "relations" including column and foreign key info)
|
90
97
|
def relations
|
91
98
|
connections = Brick.instance_variable_get(:@relations) ||
|
@@ -98,23 +105,10 @@ module Brick
|
|
98
105
|
model.reflect_on_all_associations.each_with_object([{}, {}]) do |a, s|
|
99
106
|
case a.macro
|
100
107
|
when :belongs_to
|
101
|
-
# Build #brick_descrip if needed
|
102
|
-
if a.klass.instance_methods(false).exclude?(:brick_descrip)
|
103
|
-
descrip_col = (a.klass.columns.map(&:name) - a.klass._brick_get_fks -
|
104
|
-
(::Brick.config.metadata_columns || []) -
|
105
|
-
[a.klass.primary_key]).first&.to_sym
|
106
|
-
if descrip_col
|
107
|
-
a.klass.define_method :brick_descrip do
|
108
|
-
send(descrip_col)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
108
|
s.first[a.foreign_key] = [a.name, a.klass]
|
114
109
|
when :has_many, :has_one
|
115
110
|
s.last[a.name] = a
|
116
111
|
end
|
117
|
-
s
|
118
112
|
end
|
119
113
|
end
|
120
114
|
|
@@ -180,6 +174,11 @@ module Brick
|
|
180
174
|
Brick.config.exclude_tables = value
|
181
175
|
end
|
182
176
|
|
177
|
+
# @api public
|
178
|
+
def table_name_prefixes=(value)
|
179
|
+
Brick.config.table_name_prefixes = value
|
180
|
+
end
|
181
|
+
|
183
182
|
# @api public
|
184
183
|
def metadata_columns=(value)
|
185
184
|
Brick.config.metadata_columns = value
|
@@ -196,6 +195,18 @@ module Brick
|
|
196
195
|
end
|
197
196
|
end
|
198
197
|
|
198
|
+
# Skip creating a has_many association for these
|
199
|
+
# (Uses the same exact three-part format as would define an additional_reference)
|
200
|
+
# @api public
|
201
|
+
def skip_hms=(skips)
|
202
|
+
if skips
|
203
|
+
skips = skips.call if skips.is_a?(Proc)
|
204
|
+
skips = skips.to_a unless skips.is_a?(Array)
|
205
|
+
skips = [skips] unless skips.empty? || skips.first.is_a?(Array)
|
206
|
+
Brick.config.skip_hms = skips
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
199
210
|
# Associations to treat as a has_one
|
200
211
|
# @api public
|
201
212
|
def has_ones=(hos)
|
@@ -211,6 +222,11 @@ module Brick
|
|
211
222
|
end
|
212
223
|
end
|
213
224
|
|
225
|
+
# DSL templates for individual models to provide prettier descriptions of objects
|
226
|
+
# @api public
|
227
|
+
def model_descrips=(descrips)
|
228
|
+
Brick.config.model_descrips = descrips
|
229
|
+
end
|
214
230
|
|
215
231
|
# Returns Brick's `::Gem::Version`, convenient for comparisons. This is
|
216
232
|
# recommended over `::Brick::VERSION::STRING`.
|
@@ -19,6 +19,62 @@ module Brick
|
|
19
19
|
|
20
20
|
def create_initializer_file
|
21
21
|
unless File.exists?(filename = 'config/initializers/brick.rb')
|
22
|
+
# See if we can make suggestions for additional_references
|
23
|
+
resembles_fks = []
|
24
|
+
possible_additional_references = (relations = ::Brick.relations).each_with_object([]) do |v, s|
|
25
|
+
v.last[:cols].each do |col, _type|
|
26
|
+
col_down = col.downcase
|
27
|
+
is_possible = true
|
28
|
+
if col_down.end_with?('_id')
|
29
|
+
col_down = col_down[0..-4]
|
30
|
+
elsif col_down.end_with?('id')
|
31
|
+
col_down = col_down[0..-3]
|
32
|
+
is_possible = false if col_down.length < 3 # Was it simply called "id" or something else really short?
|
33
|
+
elsif col_down.start_with?('id_')
|
34
|
+
col_down = col_down[3..-1]
|
35
|
+
elsif col_down.start_with?('id')
|
36
|
+
col_down = col_down[2..-1]
|
37
|
+
else
|
38
|
+
is_possible = false
|
39
|
+
end
|
40
|
+
if col_down.start_with?('fk_')
|
41
|
+
is_possible = true
|
42
|
+
col_down = col_down[3..-1]
|
43
|
+
end
|
44
|
+
# This possible key not really a primary key and not yet used as a foreign key?
|
45
|
+
if is_possible && !(relation = relations.fetch(v.first, {}))[:pkey].first&.last&.include?(col) &&
|
46
|
+
!relations.fetch(v.first, {})[:fks]&.any? { |_k, v| v[:is_bt] && v[:fk] == col }
|
47
|
+
if (relations.fetch(f_table = col_down, nil) ||
|
48
|
+
relations.fetch(f_table = ActiveSupport::Inflector.pluralize(col_down), nil)) &&
|
49
|
+
# Looks pretty promising ... just make sure a model file isn't present
|
50
|
+
!File.exists?("app/models/#{ActiveSupport::Inflector.singularize(v.first)}.rb")
|
51
|
+
s << "['#{v.first}', '#{col}', '#{f_table}']"
|
52
|
+
else
|
53
|
+
resembles_fks << "#{v.first}.#{col}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
s
|
58
|
+
end
|
59
|
+
|
60
|
+
bar = case possible_additional_references.length
|
61
|
+
when 0
|
62
|
+
+"# Brick.additional_references = [['orders', 'customer_id', 'customer'],
|
63
|
+
# ['customer', 'region_id', 'regions']]"
|
64
|
+
when 1
|
65
|
+
+"# # Here is a possible additional reference that has been auto-identified for the #{ActiveRecord::Base.connection.current_database} database:
|
66
|
+
# Brick.additional_references = [[#{possible_additional_references.first}]"
|
67
|
+
else
|
68
|
+
+"# # Here are possible additional references that have been auto-identified for the #{ActiveRecord::Base.connection.current_database} database:
|
69
|
+
# Brick.additional_references = [
|
70
|
+
# #{possible_additional_references.join(",\n# ")}
|
71
|
+
# ]"
|
72
|
+
end
|
73
|
+
if resembles_fks.length > 0
|
74
|
+
bar << "\n# # Columns named somewhat like a foreign key which you may want to consider:
|
75
|
+
# # #{resembles_fks.join(', ')}"
|
76
|
+
end
|
77
|
+
|
22
78
|
create_file filename, "# frozen_string_literal: true
|
23
79
|
|
24
80
|
# # Settings for the Brick gem
|
@@ -30,12 +86,15 @@ module Brick
|
|
30
86
|
# Brick.enable_controllers = false
|
31
87
|
# Brick.enable_views = false
|
32
88
|
|
33
|
-
# # By default models are auto-created
|
89
|
+
# # By default models are auto-created for database views, and set to be read-only. This can be skipped.
|
34
90
|
# Brick.skip_database_views = true
|
35
91
|
|
36
92
|
# # Any tables or views you'd like to skip when auto-creating models
|
37
93
|
# Brick.exclude_tables = ['custom_metadata', 'version_info']
|
38
94
|
|
95
|
+
# # When table names have specific prefixes automatically place them in their own module with a table_name_prefix.
|
96
|
+
# Brick.table_name_prefixes = { 'nav_' => 'Navigation' }
|
97
|
+
|
39
98
|
# # Additional table references which are used to create has_many / belongs_to associations inside auto-created
|
40
99
|
# # models. (You can consider these to be \"virtual foreign keys\" if you wish)... You only have to add these
|
41
100
|
# # in cases where your database for some reason does not have foreign key constraints defined. Sometimes for
|
@@ -44,9 +103,13 @@ module Brick
|
|
44
103
|
# # foreign table name / foreign key column / primary table name.
|
45
104
|
# # (We boldly expect that the primary key identified by ActiveRecord on the primary table will be accurate,
|
46
105
|
# # usually this is \"id\" but there are some good smarts that are used in case some other column has been set
|
47
|
-
# # to be the primary key.
|
48
|
-
#
|
49
|
-
|
106
|
+
# # to be the primary key.)
|
107
|
+
#{bar}
|
108
|
+
|
109
|
+
# # Skip creating a has_many association for these
|
110
|
+
# # (Uses the same exact three-part format as would define an additional_reference)
|
111
|
+
# # Say for instance that we didn't care to display the favourite colours that users have:
|
112
|
+
# Brick.skip_hms = [['users', 'favourite_colour_id', 'colours']]
|
50
113
|
|
51
114
|
# # By default primary tables involved in a foreign key relationship will indicate a \"has_many\" relationship pointing
|
52
115
|
# # back to the foreign table. In order to represent a \"has_one\" association instead, an override can be provided
|
@@ -56,12 +119,20 @@ module Brick
|
|
56
119
|
# # instead of \"user_profile\", then apply that as a third parameter like this:
|
57
120
|
# Brick.has_ones = [['User', 'user_profile', 'profile']]
|
58
121
|
|
59
|
-
# # We normally don't
|
60
|
-
# #
|
61
|
-
# # part of a has_many :through association. If you want to use different exclusion columns than our defaults
|
62
|
-
# # then this setting resets that list. For instance, here is
|
122
|
+
# # We normally don't show the timestamp columns \"created_at\", \"updated_at\", and \"deleted_at\", and also do
|
123
|
+
# # not consider them when finding associative tables to support an N:M association. (That is, ones that can be a
|
124
|
+
# # part of a has_many :through association.) If you want to use different exclusion columns than our defaults
|
125
|
+
# # then this setting resets that list. For instance, here is an override that is useful in the Sakila sample
|
126
|
+
# # database:
|
63
127
|
# Brick.metadata_columns = ['last_updated']
|
64
128
|
|
129
|
+
# # A simple DSL is available to allow more user-friendly display of objects. Normally a user object might be shown
|
130
|
+
# # as its first non-metadata column, or if that is not available then something like \"User #45\" where 45 is that
|
131
|
+
# # object's ID. If there is no primary key then even that is not possible, so the object's .to_s method is called.
|
132
|
+
# # To override these defaults and specify exactly what you want shown, such as first names and last names for a
|
133
|
+
# # user, then you can use model_descrips like this, putting expressions with property references in square brackets:
|
134
|
+
# Brick.model_descrips = { 'User' => '[profile.firstname] [profile.lastname]' }
|
135
|
+
|
65
136
|
# # If a default route is not supplied, Brick attempts to find the most \"central\" table and wires up the default
|
66
137
|
# # route to go to the :index action for what would be a controller for that table. You can specify any controller
|
67
138
|
# # 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.10
|
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-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|