brick 1.0.8 → 1.0.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|