brick 1.0.0 → 1.0.3
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 +60 -0
- data/lib/brick/extensions.rb +147 -120
- data/lib/brick/frameworks/rails/engine.rb +214 -7
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +69 -11
- data/lib/generators/brick/install_generator.rb +112 -66
- 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: ff255b32c913dbb0e02d3f197d035faf43dc44afc4041cda3f57e92fc9df893a
|
4
|
+
data.tar.gz: 763b5579402a7668abf3d266665879643b8d44ed04686a35f2df145b1ddfee72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f513e360dcba221bbd5061eb2ef3ef349a7c18ec6babae32273e877ba5b8cd0071f337f11c63460157afaa55cb0e8eea7dc99d80f64231270fc76218ef84397e
|
7
|
+
data.tar.gz: eb684612e551f8c884b618985884ad88b5ed827680fb4056f91f6c575fb479d18b92e6d4fa6de222ec24a7991f419723447313780fdbe5b7a0a903561dcb3dd7
|
data/lib/brick/config.rb
CHANGED
@@ -20,6 +20,33 @@ module Brick
|
|
20
20
|
@serializer = Brick::Serializers::YAML
|
21
21
|
end
|
22
22
|
|
23
|
+
# Indicates whether Brick models are on or off. Default: true.
|
24
|
+
def enable_models
|
25
|
+
@mutex.synchronize { !!@enable_models }
|
26
|
+
end
|
27
|
+
|
28
|
+
def enable_models=(enable)
|
29
|
+
@mutex.synchronize { @enable_models = enable }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Indicates whether Brick controllers are on or off. Default: true.
|
33
|
+
def enable_controllers
|
34
|
+
@mutex.synchronize { !!@enable_controllers }
|
35
|
+
end
|
36
|
+
|
37
|
+
def enable_controllers=(enable)
|
38
|
+
@mutex.synchronize { @enable_controllers = enable }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Indicates whether Brick views are on or off. Default: true.
|
42
|
+
def enable_views
|
43
|
+
@mutex.synchronize { !!@enable_views }
|
44
|
+
end
|
45
|
+
|
46
|
+
def enable_views=(enable)
|
47
|
+
@mutex.synchronize { @enable_views = enable }
|
48
|
+
end
|
49
|
+
|
23
50
|
# Indicates whether Brick routes are on or off. Default: true.
|
24
51
|
def enable_routes
|
25
52
|
@mutex.synchronize { !!@enable_routes }
|
@@ -28,5 +55,38 @@ module Brick
|
|
28
55
|
def enable_routes=(enable)
|
29
56
|
@mutex.synchronize { @enable_routes = enable }
|
30
57
|
end
|
58
|
+
|
59
|
+
# Additional table associations to use (Think of these as virtual foreign keys perhaps)
|
60
|
+
def additional_references
|
61
|
+
@mutex.synchronize { @additional_references }
|
62
|
+
end
|
63
|
+
|
64
|
+
def additional_references=(references)
|
65
|
+
@mutex.synchronize { @additional_references = references }
|
66
|
+
end
|
67
|
+
|
68
|
+
def skip_database_views
|
69
|
+
@mutex.synchronize { @skip_database_views }
|
70
|
+
end
|
71
|
+
|
72
|
+
def skip_database_views=(disable)
|
73
|
+
@mutex.synchronize { @skip_database_views = disable }
|
74
|
+
end
|
75
|
+
|
76
|
+
def exclude_tables
|
77
|
+
@mutex.synchronize { @exclude_tables }
|
78
|
+
end
|
79
|
+
|
80
|
+
def exclude_tables=(value)
|
81
|
+
@mutex.synchronize { @exclude_tables = value }
|
82
|
+
end
|
83
|
+
|
84
|
+
def metadata_columns
|
85
|
+
@mutex.synchronize { @metadata_columns }
|
86
|
+
end
|
87
|
+
|
88
|
+
def metadata_columns=(columns)
|
89
|
+
@mutex.synchronize { @metadata_columns = columns }
|
90
|
+
end
|
31
91
|
end
|
32
92
|
end
|
data/lib/brick/extensions.rb
CHANGED
@@ -28,56 +28,64 @@
|
|
28
28
|
|
29
29
|
# Drag TmfModel#name onto the rows and have it automatically add five columns -- where type=zone / where type = sectionn / etc
|
30
30
|
|
31
|
+
# ==========================================================
|
32
|
+
# Dynamically create model or controller classes when needed
|
33
|
+
# ==========================================================
|
31
34
|
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
(!matching.key?(:isView) && ['new', 'create', 'edit', 'update', 'destroy'].include?(args.first))
|
50
|
-
)
|
51
|
-
)
|
52
|
-
instance_variable_set(:@_brick_match, matching)
|
53
|
-
end
|
35
|
+
# By default all models indicate that they are not views
|
36
|
+
module ActiveRecord
|
37
|
+
class Base
|
38
|
+
def self.is_view?
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
# Used to show a little prettier name for an object
|
43
|
+
def brick_descrip
|
44
|
+
klass = self.class
|
45
|
+
klass.primary_key ? "#{klass.name} ##{send(klass.primary_key)}" : to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def self._brick_get_fks
|
51
|
+
@_brick_get_fks ||= reflect_on_all_associations.select { |a2| a2.macro == :belongs_to }.map(&:foreign_key)
|
54
52
|
end
|
55
|
-
x
|
56
53
|
end
|
57
54
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
55
|
+
class Relation
|
56
|
+
def brick_where(params)
|
57
|
+
wheres = {}
|
58
|
+
rel_joins = []
|
59
|
+
params.each do |k, v|
|
60
|
+
if (ks = k.split('.')).length > 1
|
61
|
+
assoc_name = ks.first.to_sym
|
62
|
+
# Make sure it's a good association name and that the model has that column name
|
63
|
+
next unless klass.reflect_on_association(assoc_name)&.klass&.columns&.map(&:name)&.include?(ks.last)
|
64
|
+
|
65
|
+
rel_joins << assoc_name unless rel_joins.include?(assoc_name)
|
66
|
+
else
|
67
|
+
next unless klass._brick_get_fks.include?(k)
|
68
|
+
end
|
69
|
+
wheres[k] = v.split(',')
|
70
|
+
end
|
71
|
+
unless wheres.empty?
|
72
|
+
where!(wheres)
|
73
|
+
joins!(rel_joins) unless rel_joins.empty?
|
74
|
+
wheres # Return the specific parameters that we did use
|
67
75
|
end
|
68
|
-
# As if it were an inline template (see #determine_template in actionview-5.2.6.2/lib/action_view/renderer/template_renderer.rb)
|
69
|
-
keys = options.has_key?(:locals) ? options[:locals].keys : []
|
70
|
-
handler = ActionView::Template.handler_for_extension(options[:type] || 'erb')
|
71
|
-
ActionView::Template.new(inline, "auto-generated #{args.first} template", handler, locals: keys)
|
72
|
-
else
|
73
|
-
_brick_find_template(*args, **options)
|
74
76
|
end
|
75
77
|
end
|
76
78
|
end
|
77
79
|
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
80
|
+
# Rails::Application.class_exec do
|
81
|
+
# alias _brick_initialize! initialize!
|
82
|
+
# # Run Before initialize make sure our settings
|
83
|
+
# def initialize!
|
84
|
+
# # initialization code goes here
|
85
|
+
# puts "BEFORE1"
|
86
|
+
# _brick_initialize!
|
87
|
+
# end
|
88
|
+
# end
|
81
89
|
|
82
90
|
# Object.class_exec do
|
83
91
|
class Object
|
@@ -96,29 +104,28 @@ class Object
|
|
96
104
|
# If the file really exists, go and snag it:
|
97
105
|
return Object._brick_const_missing(*args) if ActiveSupport::Dependencies.search_for_file(class_name.underscore)
|
98
106
|
|
99
|
-
|
107
|
+
relations = ::Brick.instance_variable_get(:@relations)[ActiveRecord::Base.connection_pool.object_id] || {}
|
108
|
+
result = if ::Brick.enable_controllers? && class_name.end_with?('Controller') && (plural_class_name = class_name[0..-11]).length.positive?
|
100
109
|
# Otherwise now it's up to us to fill in the gaps
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
110
|
+
if (model = ActiveSupport::Inflector.singularize(plural_class_name).constantize)
|
111
|
+
# 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.
|
112
|
+
build_controller(class_name, plural_class_name, model, relations)
|
113
|
+
end
|
114
|
+
elsif ::Brick.enable_models?
|
106
115
|
# See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
107
116
|
# checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
108
117
|
plural_class_name = ActiveSupport::Inflector.pluralize(model_name = class_name)
|
109
118
|
singular_table_name = ActiveSupport::Inflector.underscore(model_name)
|
110
119
|
table_name = ActiveSupport::Inflector.pluralize(singular_table_name)
|
120
|
+
|
121
|
+
# Maybe, just maybe there's a database table that will satisfy this need
|
122
|
+
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(m) })
|
123
|
+
build_model(model_name, singular_table_name, table_name, relations, matching)
|
124
|
+
end
|
111
125
|
end
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
if matching = matches.find { |m| relations.key?(m) }
|
116
|
-
built_class, code = if is_controller
|
117
|
-
build_controller(class_name, model_name, singular_table_name, table_name, relations, matching)
|
118
|
-
else
|
119
|
-
build_model(model_name, singular_table_name, table_name, relations, matching)
|
120
|
-
end
|
121
|
-
puts "#{code}end # #{ is_controller ? 'controller' : 'model' }\n\n"
|
126
|
+
if result
|
127
|
+
built_class, code = result
|
128
|
+
puts "\n#{code}"
|
122
129
|
built_class
|
123
130
|
else
|
124
131
|
puts "MISSING! #{args.inspect} #{table_name}"
|
@@ -129,9 +136,12 @@ class Object
|
|
129
136
|
private
|
130
137
|
|
131
138
|
def build_model(model_name, singular_table_name, table_name, relations, matching)
|
139
|
+
return if ((is_view = (relation = relations[matching]).key?(:isView)) && ::Brick.config.skip_database_views) ||
|
140
|
+
::Brick.config.exclude_tables.include?(matching)
|
141
|
+
|
132
142
|
# Are they trying to use a pluralised class name such as "Employees" instead of "Employee"?
|
133
143
|
if table_name == singular_table_name && !ActiveSupport::Inflector.inflections.uncountable.include?(table_name)
|
134
|
-
raise NameError.new("Class name for a model that references table \"#{matching}\" should be \"#{ActiveSupport::Inflector.singularize(
|
144
|
+
raise NameError.new("Class name for a model that references table \"#{matching}\" should be \"#{ActiveSupport::Inflector.singularize(model_name)}\".")
|
135
145
|
end
|
136
146
|
code = +"class #{model_name} < ActiveRecord::Base\n"
|
137
147
|
built_model = Class.new(ActiveRecord::Base) do |new_model_class|
|
@@ -139,11 +149,13 @@ class Object
|
|
139
149
|
# Accommodate singular or camel-cased table names such as "order_detail" or "OrderDetails"
|
140
150
|
code << " self.table_name = '#{self.table_name = matching}'\n" unless table_name == matching
|
141
151
|
|
142
|
-
#
|
143
|
-
|
144
|
-
|
152
|
+
# Override models backed by a view so they return true for #is_view?
|
153
|
+
# (Dynamically-created controllers and view templates for such models will then act in a read-only way)
|
154
|
+
if is_view
|
155
|
+
new_model_class.define_singleton_method :'is_view?' do
|
145
156
|
true
|
146
157
|
end
|
158
|
+
code << " def self.is_view?; true; end\n"
|
147
159
|
end
|
148
160
|
|
149
161
|
# Missing a primary key column? (Usually "id")
|
@@ -165,7 +177,7 @@ class Object
|
|
165
177
|
code << " self.primary_key = #{pk_sym.inspect}\n"
|
166
178
|
end
|
167
179
|
else
|
168
|
-
code << " # Could not identify any column(s) to use as a primary key\n"
|
180
|
+
code << " # Could not identify any column(s) to use as a primary key\n" unless is_view
|
169
181
|
end
|
170
182
|
|
171
183
|
# if relation[:cols].key?('last_update')
|
@@ -217,28 +229,32 @@ class Object
|
|
217
229
|
end
|
218
230
|
end
|
219
231
|
end
|
232
|
+
code << "end # model #{model_name}\n\n"
|
220
233
|
end # class definition
|
221
234
|
[built_model, code]
|
222
235
|
end
|
223
236
|
|
237
|
+
def build_controller(class_name, plural_class_name, model, relations)
|
238
|
+
table_name = ActiveSupport::Inflector.underscore(plural_class_name)
|
239
|
+
singular_table_name = ActiveSupport::Inflector.singularize(table_name)
|
224
240
|
|
225
|
-
def build_controller(class_name, model_name, singular_table_name, table_name, relations, matching)
|
226
241
|
code = +"class #{class_name} < ApplicationController\n"
|
227
242
|
built_controller = Class.new(ActionController::Base) do |new_controller_class|
|
228
243
|
Object.const_set(class_name.to_sym, new_controller_class)
|
229
244
|
|
230
|
-
model = model_name.constantize
|
231
245
|
code << " def index\n"
|
232
|
-
code << " @#{table_name} = #{
|
246
|
+
code << " @#{table_name} = #{model.name}#{model.primary_key ? ".order(#{model.primary_key.inspect}" : '.all'})\n"
|
247
|
+
code << " @#{table_name}.brick_where(params)\n"
|
233
248
|
code << " end\n"
|
234
249
|
self.define_method :index do
|
235
|
-
|
236
|
-
instance_variable_set(
|
250
|
+
ar_relation = model.primary_key ? model.order(model.primary_key) : model.all
|
251
|
+
instance_variable_set(:@_brick_params, ar_relation.brick_where(params))
|
252
|
+
instance_variable_set("@#{table_name}".to_sym, ar_relation)
|
237
253
|
end
|
238
254
|
|
239
255
|
if model.primary_key
|
240
256
|
code << " def show\n"
|
241
|
-
code << " @#{singular_table_name} = #{
|
257
|
+
code << " @#{singular_table_name} = #{model.name}.find(params[:id].split(','))\n"
|
242
258
|
code << " end\n"
|
243
259
|
self.define_method :show do
|
244
260
|
instance_variable_set("@#{singular_table_name}".to_sym, model.find(params[:id].split(',')))
|
@@ -246,9 +262,11 @@ class Object
|
|
246
262
|
end
|
247
263
|
|
248
264
|
# By default, views get marked as read-only
|
249
|
-
unless (relation = relations[
|
265
|
+
unless (relation = relations[model.table_name]).key?(:isView)
|
250
266
|
code << " # (Define :new, :create, :edit, :update, and :destroy)\n"
|
267
|
+
# Get column names for params from relations[model.table_name][:cols].keys
|
251
268
|
end
|
269
|
+
code << "end # #{class_name}\n\n"
|
252
270
|
end # class definition
|
253
271
|
[built_controller, code]
|
254
272
|
end
|
@@ -268,16 +286,11 @@ end
|
|
268
286
|
# ==========================================================
|
269
287
|
|
270
288
|
module ActiveRecord::ConnectionHandling
|
271
|
-
alias
|
289
|
+
alias _brick_establish_connection establish_connection
|
272
290
|
def establish_connection(*args)
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
x = old_establish_connection(*args)
|
277
|
-
# Key our list of relations for this connection off of the connection pool's object_id
|
278
|
-
relations = (connections[ActiveRecord::Base.connection_pool.object_id] ||= Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = {} } })
|
279
|
-
|
280
|
-
if relations.empty?
|
291
|
+
x = _brick_establish_connection(*args)
|
292
|
+
|
293
|
+
if (relations = ::Brick.relations).empty?
|
281
294
|
schema = 'public'
|
282
295
|
puts ActiveRecord::Base.connection.execute("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
283
296
|
sql = ActiveRecord::Base.send(:sanitize_sql_array, [
|
@@ -306,9 +319,6 @@ module ActiveRecord::ConnectionHandling
|
|
306
319
|
# next if internal_views.include?(r['relation_name']) # Skip internal views such as v_all_assessments
|
307
320
|
|
308
321
|
relation = relations[r['relation_name']]
|
309
|
-
relation[:index] = r['relation_name'].underscore
|
310
|
-
relation[:show] = relation[:index].singularize
|
311
|
-
relation[:index] = relation[:index].pluralize
|
312
322
|
relation[:isView] = true if r['table_type'] == 'VIEW'
|
313
323
|
col_name = r['column_name']
|
314
324
|
cols = relation[:cols] # relation.fetch(:cols) { relation[:cols] = [] }
|
@@ -326,7 +336,7 @@ module ActiveRecord::ConnectionHandling
|
|
326
336
|
end
|
327
337
|
|
328
338
|
sql = ActiveRecord::Base.send(:sanitize_sql_array, [
|
329
|
-
"SELECT kcu1.
|
339
|
+
"SELECT kcu1.TABLE_NAME, kcu1.COLUMN_NAME, kcu2.TABLE_NAME, kcu1.CONSTRAINT_NAME
|
330
340
|
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS rc
|
331
341
|
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu1
|
332
342
|
ON kcu1.CONSTRAINT_CATALOG = rc.CONSTRAINT_CATALOG
|
@@ -340,45 +350,12 @@ module ActiveRecord::ConnectionHandling
|
|
340
350
|
WHERE kcu1.CONSTRAINT_SCHEMA = ? -- COALESCE(current_setting('SEARCH_PATH'), 'public')", schema
|
341
351
|
# AND kcu2.TABLE_NAME = ?;", Apartment::Tenant.current, table_name
|
342
352
|
])
|
343
|
-
ActiveRecord::Base.connection.execute(sql).values.each
|
344
|
-
bt_assoc_name = fk[2].underscore
|
345
|
-
bt_assoc_name = bt_assoc_name[0..-4] if bt_assoc_name.end_with?('_id')
|
346
|
-
|
347
|
-
bts = (relation = relations[fk[1]]).fetch(:fks) { relation[:fks] = {} }
|
348
|
-
if (assoc_bt = bts[fk[0]])
|
349
|
-
assoc_bt[:fk] = assoc_bt[:fk].is_a?(String) ? [assoc_bt[:fk], fk[2]] : assoc_bt[:fk].concat(fk[2])
|
350
|
-
assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[2]}"
|
351
|
-
else
|
352
|
-
assoc_bt = bts[fk[0]] = { is_bt: true, fk: fk[2], assoc_name: bt_assoc_name, inverse_table: fk[3] }
|
353
|
-
end
|
354
|
-
|
355
|
-
hms = (relation = relations[fk[3]]).fetch(:fks) { relation[:fks] = {} }
|
356
|
-
if (assoc_hm = hms[fk[0]])
|
357
|
-
assoc_hm[:fk] = assoc_hm[:fk].is_a?(String) ? [assoc_hm[:fk], fk[2]] : assoc_hm[:fk].concat(fk[2])
|
358
|
-
assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
|
359
|
-
assoc_hm[:inverse] = assoc_bt
|
360
|
-
else
|
361
|
-
assoc_hm = hms[fk[0]] = { is_bt: false, fk: fk[2], assoc_name: fk[1], alternate_name: bt_assoc_name, inverse_table: fk[1], inverse: assoc_bt }
|
362
|
-
hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
|
363
|
-
hm_counts[fk[1]] = hm_counts.fetch(fk[1]) { 0 } + 1
|
364
|
-
end
|
365
|
-
assoc_bt[:inverse] = assoc_hm
|
366
|
-
# hms[fk[0]] << { is_bt: false, fk: fk[2], assoc_name: fk[1], alternate_name: bt_assoc_name, inverse_table: fk[1] }
|
367
|
-
end
|
368
|
-
# Find associative tables that can be set up for has_many :through
|
369
|
-
relations.each do |_key, tbl|
|
370
|
-
tbl_cols = tbl[:cols].keys
|
371
|
-
fks = tbl[:fks].each_with_object({}) { |fk, s| s[fk.last[:fk]] = fk.last[:inverse_table] if fk.last[:is_bt]; s }
|
372
|
-
# Aside from the primary key and created_at, updated_at,This table has only foreign keys?
|
373
|
-
if fks.length > 1 && (tbl_cols - fks.keys - ['created_at', 'updated_at', 'deleted_at', 'last_update'] - tbl[:pkey].values.first).length.zero?
|
374
|
-
fks.each { |fk| tbl[:hmt_fks][fk.first] = fk.last }
|
375
|
-
end
|
376
|
-
end
|
353
|
+
ActiveRecord::Base.connection.execute(sql).values.each { |fk| ::Brick._add_bt_and_hm(fk, relations) }
|
377
354
|
end
|
378
355
|
|
379
|
-
puts "Classes built from tables:"
|
356
|
+
puts "Classes that can be built from tables:"
|
380
357
|
relations.select { |_k, v| !v.key?(:isView) }.keys.each { |k| puts ActiveSupport::Inflector.singularize(k).camelize }
|
381
|
-
puts "Classes built from views:"
|
358
|
+
puts "Classes that can be built from views:"
|
382
359
|
relations.select { |_k, v| v.key?(:isView) }.keys.each { |k| puts ActiveSupport::Inflector.singularize(k).camelize }
|
383
360
|
# pp relations; nil
|
384
361
|
|
@@ -406,12 +383,62 @@ module Brick
|
|
406
383
|
|
407
384
|
private
|
408
385
|
|
409
|
-
def _create_class()
|
410
|
-
end
|
411
386
|
end
|
412
387
|
end # module Extensions
|
413
388
|
# rubocop:enable Style/CommentedKeyword
|
414
389
|
|
390
|
+
def self._add_bt_and_hm(fk, relations = nil)
|
391
|
+
relations ||= ::Brick.relations
|
392
|
+
bt_assoc_name = fk[1].underscore
|
393
|
+
bt_assoc_name = bt_assoc_name[0..-4] if bt_assoc_name.end_with?('_id')
|
394
|
+
|
395
|
+
bts = (relation = relations.fetch(fk[0], nil))&.fetch(:fks) { relation[:fks] = {} }
|
396
|
+
hms = (relation = relations.fetch(fk[2], nil))&.fetch(:fks) { relation[:fks] = {} }
|
397
|
+
|
398
|
+
unless (cnstr_name = fk[3])
|
399
|
+
# For any appended references (those that come from config), arrive upon a definitely unique constraint name
|
400
|
+
cnstr_base = cnstr_name = "(brick) #{fk[0]}_#{fk[2]}"
|
401
|
+
cnstr_added_num = 1
|
402
|
+
cnstr_name = "#{cnstr_base}_#{cnstr_added_num += 1}" while bts&.key?(cnstr_name) || hms&.key?(cnstr_name)
|
403
|
+
missing = []
|
404
|
+
missing << fk[0] unless relations.key?(fk[0])
|
405
|
+
missing << fk[2] unless relations.key?(fk[2])
|
406
|
+
unless missing.empty?
|
407
|
+
tables = relations.reject { |k, v| v.fetch(:isView, nil) }.keys.sort
|
408
|
+
puts "Brick: Additional reference #{fk.inspect} refers to non-existent #{'table'.pluralize(missing.length)} #{missing.join(' and ')}. (Available tables include #{tables.join(', ')}.)"
|
409
|
+
return
|
410
|
+
end
|
411
|
+
unless (cols = relations[fk[0]][:cols]).key?(fk[1])
|
412
|
+
columns = cols.map { |k, v| "#{k} (#{v.first.split(' ').first})" }
|
413
|
+
puts "Brick: Additional reference #{fk.inspect} refers to non-existent column #{fk[1]}. (Columns present in #{fk[0]} are #{columns.join(', ')}.)"
|
414
|
+
return
|
415
|
+
end
|
416
|
+
if (redundant = bts.find{|k, v| v[:inverse][:inverse_table] == fk[0] && v[:fk] == fk[1] && v[:inverse_table] == fk[2] })
|
417
|
+
puts "Brick: Additional reference #{fk.inspect} is redundant and can be removed. (Already established by #{redundant.first}.)"
|
418
|
+
return
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
if (assoc_bt = bts[cnstr_name])
|
423
|
+
assoc_bt[:fk] = assoc_bt[:fk].is_a?(String) ? [assoc_bt[:fk], fk[1]] : assoc_bt[:fk].concat(fk[1])
|
424
|
+
assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[1]}"
|
425
|
+
else
|
426
|
+
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[1], assoc_name: bt_assoc_name, inverse_table: fk[2] }
|
427
|
+
end
|
428
|
+
|
429
|
+
if (assoc_hm = hms[cnstr_name])
|
430
|
+
assoc_hm[:fk] = assoc_hm[:fk].is_a?(String) ? [assoc_hm[:fk], fk[1]] : assoc_hm[:fk].concat(fk[1])
|
431
|
+
assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
|
432
|
+
assoc_hm[:inverse] = assoc_bt
|
433
|
+
else
|
434
|
+
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 }
|
435
|
+
hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
|
436
|
+
hm_counts[fk[0]] = hm_counts.fetch(fk[0]) { 0 } + 1
|
437
|
+
end
|
438
|
+
assoc_bt[:inverse] = assoc_hm
|
439
|
+
# hms[cnstr_name] << { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0] }
|
440
|
+
end
|
441
|
+
|
415
442
|
# Rails < 4.0 doesn't have ActiveRecord::RecordNotUnique, so use the more generic ActiveRecord::ActiveRecordError instead
|
416
443
|
ar_not_unique_error = ActiveRecord.const_defined?('RecordNotUnique') ? ActiveRecord::RecordNotUnique : ActiveRecord::ActiveRecordError
|
417
444
|
class NoUniqueColumnError < ar_not_unique_error
|
@@ -5,15 +5,222 @@ module Brick
|
|
5
5
|
# See http://guides.rubyonrails.org/engines.html
|
6
6
|
class Engine < ::Rails::Engine
|
7
7
|
# paths['app/models'] << 'lib/brick/frameworks/active_record/models'
|
8
|
+
puts "BEFORE - engine set config"
|
8
9
|
config.brick = ActiveSupport::OrderedOptions.new
|
9
|
-
initializer 'brick.initialisation' do |app|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
# initializer 'brick.initialisation' do |app|
|
11
|
+
ActiveSupport.on_load(:before_initialize) do |app|
|
12
|
+
puts "BEFORE - engine initialisation"
|
13
|
+
::Brick.enable_models = app.config.brick.fetch(:enable_models, true)
|
14
|
+
::Brick.enable_controllers = app.config.brick.fetch(:enable_controllers, true)
|
15
|
+
::Brick.enable_views = app.config.brick.fetch(:enable_views, true)
|
16
|
+
::Brick.enable_routes = app.config.brick.fetch(:enable_routes, true)
|
17
|
+
::Brick.skip_database_views = app.config.brick.fetch(:skip_database_views, false)
|
18
|
+
|
19
|
+
# Specific database tables and views to omit when auto-creating models
|
20
|
+
::Brick.exclude_tables = app.config.brick.fetch(:exclude_tables, [])
|
21
|
+
|
22
|
+
# Columns to treat as being metadata for purposes of identifying associative tables for has_many :through
|
23
|
+
::Brick.metadata_columns = app.config.brick.fetch(:metadata_columns, ['created_at', 'updated_at', 'deleted_at'])
|
24
|
+
|
25
|
+
# Additional references (virtual foreign keys)
|
26
|
+
::Brick.additional_references = app.config.brick.fetch(:additional_references, nil)
|
27
|
+
|
28
|
+
# After we're initialized and before running the rest of stuff, put our configuration in place
|
29
|
+
ActiveSupport.on_load(:after_initialize) do |xyz|
|
30
|
+
puts "AFTER - engine initialisation"
|
31
|
+
# ====================================
|
32
|
+
# Dynamically create generic templates
|
33
|
+
# ====================================
|
34
|
+
if ::Brick.enable_views?
|
35
|
+
ActionView::LookupContext.class_exec do
|
36
|
+
alias :_brick_template_exists? :template_exists?
|
37
|
+
def template_exists?(*args, **options)
|
38
|
+
unless (is_template_exists = _brick_template_exists?(*args, **options))
|
39
|
+
# Need to return true if we can fill in the blanks for a missing one
|
40
|
+
# args will be something like: ["index", ["categories"]]
|
41
|
+
model = args[1].map(&:camelize).join('::').singularize.constantize
|
42
|
+
if (
|
43
|
+
is_template_exists = model && (
|
44
|
+
['index', 'show'].include?(args.first) || # Everything has index and show
|
45
|
+
# Only CRU stuff has create / update / destroy
|
46
|
+
(!model.is_view? && ['new', 'create', 'edit', 'update', 'destroy'].include?(args.first))
|
47
|
+
)
|
48
|
+
)
|
49
|
+
instance_variable_set(:@_brick_model, model)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
is_template_exists
|
53
|
+
end
|
54
|
+
|
55
|
+
alias :_brick_find_template :find_template
|
56
|
+
def find_template(*args, **options)
|
57
|
+
if @_brick_model
|
58
|
+
model_name = @_brick_model.name
|
59
|
+
pk = @_brick_model.primary_key
|
60
|
+
obj_name = model_name.underscore
|
61
|
+
table_name = model_name.pluralize.underscore
|
62
|
+
# This gets has_many as well as has_many :through
|
63
|
+
# %%% weed out ones that don't have an available model to reference
|
64
|
+
bts, hms = @_brick_model.reflect_on_all_associations.each_with_object([{}, {}]) do |a, s|
|
65
|
+
case a.macro
|
66
|
+
when :belongs_to
|
67
|
+
# Build #brick_descrip if needed
|
68
|
+
unless a.klass.instance_methods(false).include?(:brick_descrip)
|
69
|
+
descrip_col = (a.klass.columns.map(&:name) - a.klass._brick_get_fks -
|
70
|
+
(::Brick.config.metadata_columns || []) -
|
71
|
+
[a.klass.primary_key]).first&.to_sym
|
72
|
+
if descrip_col
|
73
|
+
a.klass.define_method :brick_descrip do
|
74
|
+
send(descrip_col)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
s.first[a.foreign_key] = [a.name, a.klass]
|
80
|
+
when :has_many
|
81
|
+
s.last[a.name] = a
|
82
|
+
end
|
83
|
+
s
|
84
|
+
end
|
85
|
+
# Weed out has_manys that go to an associative table
|
86
|
+
associatives = hms.select { |k, v| v.options[:through] }.each_with_object({}) do |hmt, s|
|
87
|
+
s[hmt.first] = hms.delete(hmt.last.options[:through]) # End up with a hash of HMT names pointing to join-table associations
|
88
|
+
end
|
89
|
+
hms_headers = hms.each_with_object(+'') { |hm, s| s << "<th>HM#{'T' if hm.last.options[:through]} #{hm.first}</th>\n" }
|
90
|
+
hms_columns = hms.each_with_object(+'') do |hm, s|
|
91
|
+
hm_fk_name = if hm.last.options[:through]
|
92
|
+
associative = associatives[hm.last.name]
|
93
|
+
"'#{associative.name}.#{associative.foreign_key}'"
|
94
|
+
else
|
95
|
+
hm.last.foreign_key
|
96
|
+
end
|
97
|
+
s << "<td>
|
98
|
+
<%= link_to \"#\{#{obj_name}.#{hm.first}.count\} #{hm.first}\", #{hm.last.klass.name.underscore.pluralize}_path({ #{hm_fk_name}: #{obj_name}.#{pk} }) %>
|
99
|
+
</td>\n"
|
100
|
+
end
|
101
|
+
|
102
|
+
inline = case args.first
|
103
|
+
when 'index'
|
104
|
+
"<p style=\"color: green\"><%= notice %></p>
|
105
|
+
|
106
|
+
<h1>#{model_name.pluralize}</h1>
|
107
|
+
<% if @_brick_params&.present? %><h3>where <%= @_brick_params.each_with_object([]) { |v, s| s << \"#\{v.first\} = #\{v.last.inspect\}\" }.join(', ') %></h3><% end %>
|
108
|
+
|
109
|
+
<table id=\"#{table_name}\">
|
110
|
+
<tr>
|
111
|
+
<% is_first = true; is_need_id_col = nil
|
112
|
+
bts = { #{bts.each_with_object([]) { |v, s| s << "#{v.first.inspect} => [#{v.last.first.inspect}, #{v.last.last.name}, #{v.last.last.primary_key.inspect}]"}.join(', ')} }
|
113
|
+
@#{table_name}.columns.map(&:name).each do |col| %>
|
114
|
+
<% next if col == '#{pk}' || ::Brick.config.metadata_columns.include?(col) %>
|
115
|
+
<th>
|
116
|
+
<% if bt = bts[col]
|
117
|
+
if is_first
|
118
|
+
is_first = false
|
119
|
+
is_need_id_col = true %>
|
120
|
+
</th><th>
|
121
|
+
<% end %>
|
122
|
+
BT <%= bt[1].name %>
|
123
|
+
<% else
|
124
|
+
is_first = false %>
|
125
|
+
<%= col %>
|
126
|
+
<% end %>
|
127
|
+
</th>
|
128
|
+
<% end %>
|
129
|
+
<% if is_first # STILL haven't been able to write a first non-key / non-metadata column?
|
130
|
+
is_first = false
|
131
|
+
is_need_id_col = true %>
|
132
|
+
<th></th>
|
133
|
+
<% end %>
|
134
|
+
#{hms_headers}
|
135
|
+
</tr>
|
136
|
+
|
137
|
+
<% @#{table_name}.each do |#{obj_name}| %>
|
138
|
+
<tr>
|
139
|
+
<% is_first = true
|
140
|
+
if is_need_id_col
|
141
|
+
is_first = false %>
|
142
|
+
<td><%= link_to \"#\{#{obj_name}.class.name\} ##\{#{obj_name}.id\}\", #{obj_name} %></td>
|
143
|
+
<% end %>
|
144
|
+
<% #{obj_name}.attributes.each do |k, val| %>
|
145
|
+
<% next if k == '#{pk}' || ::Brick.config.metadata_columns.include?(k) %>
|
146
|
+
<td>
|
147
|
+
<% if (bt = bts[k]) %>
|
148
|
+
<%= obj = bt[1].find_by(bt.last => val); link_to obj.brick_descrip, obj %>
|
149
|
+
<% elsif is_first %>
|
150
|
+
<%= is_first = false; link_to val, #{obj_name} %>
|
151
|
+
<% else %>
|
152
|
+
<%= val %>
|
153
|
+
<% end %>
|
154
|
+
</td>
|
155
|
+
<% end %>
|
156
|
+
#{hms_columns}
|
157
|
+
<!-- td>X</td -->
|
158
|
+
</tr>
|
159
|
+
<% end %>
|
160
|
+
</table>
|
161
|
+
|
162
|
+
<%= link_to \"New #{obj_name}\", new_#{obj_name}_path %>
|
163
|
+
"
|
164
|
+
# "<%= @#{@_brick_model.name.underscore.pluralize}.inspect %>"
|
165
|
+
when 'show'
|
166
|
+
"<%= @#{@_brick_model.name.underscore}.inspect %>"
|
167
|
+
end
|
168
|
+
# As if it were an inline template (see #determine_template in actionview-5.2.6.2/lib/action_view/renderer/template_renderer.rb)
|
169
|
+
keys = options.has_key?(:locals) ? options[:locals].keys : []
|
170
|
+
handler = ActionView::Template.handler_for_extension(options[:type] || 'erb')
|
171
|
+
ActionView::Template.new(inline, "auto-generated #{args.first} template", handler, locals: keys)
|
172
|
+
else
|
173
|
+
_brick_find_template(*args, **options)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
if ::Brick.enable_routes?
|
180
|
+
ActionDispatch::Routing::RouteSet.class_exec do
|
181
|
+
alias _brick_finalize_routeset! finalize!
|
182
|
+
def finalize!(*args, **options)
|
183
|
+
unless @finalized
|
184
|
+
existing_controllers = routes.each_with_object({}) { |r, s| c = r.defaults[:controller]; s[c] = nil if c }
|
185
|
+
::Rails.application.routes.append do
|
186
|
+
# %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
|
187
|
+
# If auto-controllers and auto-models are both enabled then this makes sense:
|
188
|
+
relations = (::Brick.instance_variable_get(:@relations) || {})[ActiveRecord::Base.connection_pool.object_id] || {}
|
189
|
+
relations.each do |k, v|
|
190
|
+
unless existing_controllers.key?(controller_name = k.underscore.pluralize)
|
191
|
+
options = {}
|
192
|
+
options[:only] = [:index, :show] if v.key?(:isView)
|
193
|
+
send(:resources, controller_name.to_sym, **options)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
_brick_finalize_routeset!(*args, **options)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Additional references (virtual foreign keys)
|
204
|
+
if (ars = ::Brick.config.additional_references)
|
205
|
+
ars = ars.call if ars.is_a?(Proc)
|
206
|
+
ars = ars.to_a unless ars.is_a?(Array)
|
207
|
+
ars = [ars] unless ars.empty? || ars.first.is_a?(Array)
|
208
|
+
ars.each do |fk|
|
209
|
+
::Brick._add_bt_and_hm(fk[0..2])
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Find associative tables that can be set up for has_many :through
|
214
|
+
::Brick.relations.each do |_key, tbl|
|
215
|
+
tbl_cols = tbl[:cols].keys
|
216
|
+
fks = tbl[:fks].each_with_object({}) { |fk, s| s[fk.last[:fk]] = fk.last[:inverse_table] if fk.last[:is_bt]; s }
|
217
|
+
# Aside from the primary key and the metadata columns created_at, updated_at, and deleted_at, if this table only has
|
218
|
+
# foreign keys then it can act as an associative table and thus be used with has_many :through.
|
219
|
+
if fks.length > 1 && (tbl_cols - fks.keys - (::Brick.config.metadata_columns || []) - tbl[:pkey].values.first).length.zero?
|
220
|
+
fks.each { |fk| tbl[:hmt_fks][fk.first] = fk.last }
|
221
|
+
end
|
222
|
+
end
|
13
223
|
end
|
14
|
-
# Brick.enable_models = app.config.brick.fetch(:enable_models, true)
|
15
|
-
# Brick.enable_controllers = app.config.brick.fetch(:enable_controllers, true)
|
16
|
-
# Brick.enable_views = app.config.brick.fetch(:enable_views, true)
|
17
224
|
end
|
18
225
|
end
|
19
226
|
end
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -79,15 +79,51 @@ require 'brick/config'
|
|
79
79
|
require 'brick/frameworks/rails'
|
80
80
|
module Brick
|
81
81
|
class << self
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
relations
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
82
|
+
# All tables and views (what Postgres calls "relations" including column and foreign key info)
|
83
|
+
def relations
|
84
|
+
connections = Brick.instance_variable_get(:@relations) ||
|
85
|
+
Brick.instance_variable_set(:@relations, (connections = {}))
|
86
|
+
# Key our list of relations for this connection off of the connection pool's object_id
|
87
|
+
(connections[ActiveRecord::Base.connection_pool.object_id] ||= Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = {} } })
|
88
|
+
end
|
89
|
+
|
90
|
+
# Switches Brick auto-models on or off, for all threads
|
91
|
+
# @api public
|
92
|
+
def enable_models=(value)
|
93
|
+
Brick.config.enable_models = value
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns `true` if Brick models are on, `false` otherwise. This affects all
|
97
|
+
# threads. Enabled by default.
|
98
|
+
# @api public
|
99
|
+
def enable_models?
|
100
|
+
!!Brick.config.enable_models
|
101
|
+
end
|
102
|
+
|
103
|
+
# Switches Brick auto-controllers on or off, for all threads
|
104
|
+
# @api public
|
105
|
+
def enable_controllers=(value)
|
106
|
+
Brick.config.enable_controllers = value
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns `true` if Brick controllers are on, `false` otherwise. This affects all
|
110
|
+
# threads. Enabled by default.
|
111
|
+
# @api public
|
112
|
+
def enable_controllers?
|
113
|
+
!!Brick.config.enable_controllers
|
114
|
+
end
|
115
|
+
|
116
|
+
# Switches Brick auto-views on or off, for all threads
|
117
|
+
# @api public
|
118
|
+
def enable_views=(value)
|
119
|
+
Brick.config.enable_views = value
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns `true` if Brick views are on, `false` otherwise. This affects all
|
123
|
+
# threads. Enabled by default.
|
124
|
+
# @api public
|
125
|
+
def enable_views?
|
126
|
+
!!Brick.config.enable_views
|
91
127
|
end
|
92
128
|
|
93
129
|
# Switches Brick auto-routes on or off, for all threads
|
@@ -96,13 +132,35 @@ module Brick
|
|
96
132
|
Brick.config.enable_routes = value
|
97
133
|
end
|
98
134
|
|
99
|
-
# Returns `true` if Brick routes are on, `false` otherwise. This
|
100
|
-
#
|
135
|
+
# Returns `true` if Brick routes are on, `false` otherwise. This affects all
|
136
|
+
# threads. Enabled by default.
|
101
137
|
# @api public
|
102
138
|
def enable_routes?
|
103
139
|
!!Brick.config.enable_routes
|
104
140
|
end
|
105
141
|
|
142
|
+
# @api public
|
143
|
+
def skip_database_views=(value)
|
144
|
+
Brick.config.skip_database_views = value
|
145
|
+
end
|
146
|
+
|
147
|
+
# @api public
|
148
|
+
def exclude_tables=(value)
|
149
|
+
Brick.config.exclude_tables = value
|
150
|
+
end
|
151
|
+
|
152
|
+
# @api public
|
153
|
+
def metadata_columns=(value)
|
154
|
+
Brick.config.metadata_columns = value
|
155
|
+
end
|
156
|
+
|
157
|
+
# Additional table associations to use (Think of these as virtual foreign keys perhaps)
|
158
|
+
# @api public
|
159
|
+
def additional_references=(value)
|
160
|
+
Brick.config.additional_references = value
|
161
|
+
end
|
162
|
+
|
163
|
+
|
106
164
|
# Returns Brick's `::Gem::Version`, convenient for comparisons. This is
|
107
165
|
# recommended over `::Brick::VERSION::STRING`.
|
108
166
|
#
|
@@ -1,12 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'rails/generators'
|
4
|
-
require 'rails/generators/active_record'
|
4
|
+
# require 'rails/generators/active_record'
|
5
5
|
|
6
6
|
module Brick
|
7
|
-
# Auto-generates an IMPORT_TEMPLATE entry for one or more models
|
8
7
|
class InstallGenerator < ::Rails::Generators::Base
|
9
|
-
include ::Rails::Generators::Migration
|
8
|
+
# include ::Rails::Generators::Migration
|
10
9
|
|
11
10
|
source_root File.expand_path('templates', __dir__)
|
12
11
|
class_option(
|
@@ -16,81 +15,128 @@ module Brick
|
|
16
15
|
desc: 'Store changeset (diff) with each version'
|
17
16
|
)
|
18
17
|
|
19
|
-
desc 'Generates
|
20
|
-
' Also generates an initializer file for configuring Brick'
|
18
|
+
desc 'Generates an initializer file for configuring Brick'
|
21
19
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
end
|
20
|
+
def create_initializer_file
|
21
|
+
unless File.exists?(filename = 'config/initializers/brick.rb')
|
22
|
+
create_file filename, "# frozen_string_literal: true
|
26
23
|
|
27
|
-
|
28
|
-
|
29
|
-
end
|
24
|
+
# # Settings for the Brick gem
|
25
|
+
# # (By default this auto-creates models, controllers, views, and routes on-the-fly.)
|
30
26
|
|
31
|
-
|
27
|
+
# # Normally these all start out as being enabled, but can be selectively disabled:
|
28
|
+
# Brick.enable_routes = false
|
29
|
+
# Brick.enable_models = false
|
30
|
+
# Brick.enable_controllers = false
|
31
|
+
# Brick.enable_views = false
|
32
|
+
|
33
|
+
# # By default models are auto-created from database views, and set to be read-only. This can be skipped.
|
34
|
+
# Brick.skip_database_views = true
|
35
|
+
|
36
|
+
# # Any tables or views you'd like to skip when auto-creating models
|
37
|
+
# Brick.exclude_tables = ['custom_metadata', 'version_info']
|
32
38
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
# # Additional table references which are used to create has_many / belongs_to associations inside auto-created
|
40
|
+
# # models. (You can consider these to be \"virtual foreign keys\" if you wish)... You only have to add these
|
41
|
+
# # in cases where your database for some reason does not have foreign key constraints defined. Sometimes for
|
42
|
+
# # performance reasons or just out of sheer laziness these might be missing.
|
43
|
+
# # Each of these virtual foreign keys is defined as an array having three values:
|
44
|
+
# # foreign table name / foreign key column / primary table name.
|
45
|
+
# # (We boldly expect that the primary key identified by ActiveRecord on the primary table will be accurate,
|
46
|
+
# # 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
|
+
# Brick.additional_references = [['orders', 'customer_id', 'customer'],
|
49
|
+
# ['customer', 'region_id', 'regions']]
|
50
|
+
|
51
|
+
# # We normally don't consider the timestamp columns \"created_at\", \"updated_at\", and \"deleted_at\" to count when
|
52
|
+
# # finding tables which can serve as associative tables in an N:M association. That is, ones that can be a
|
53
|
+
# # part of a has_many :through association. If you want to use different exclusion columns than our defaults
|
54
|
+
# # then this setting resets that list. For instance, here is the override for the Sakila sample database:
|
55
|
+
# Brick.metadata_columns = ['last_updated']
|
56
|
+
|
57
|
+
# # If a default route is not supplied, Brick attempts to find the most \"central\" table and wires up the default
|
58
|
+
# # route to go to the :index action for what would be a controller for that table. You can specify any controller
|
59
|
+
# # name and action you wish in order to override this and have that be the default route when none other has been
|
60
|
+
# # specified in routes.rb or elsewhere. (Or just use an empty string in order to disable this behaviour.)
|
61
|
+
# Brick.default_route_fallback = 'customers' # This defaults to \"customers/index\"
|
62
|
+
# Brick.default_route_fallback = 'orders/outstanding' # Example of a non-RESTful route
|
63
|
+
# Brick.default_route_fallback = '' # Omits setting a default route in the absence of any other
|
64
|
+
"
|
45
65
|
end
|
46
66
|
end
|
47
67
|
|
68
|
+
# def create_migration_file
|
69
|
+
# add_brick_migration('create_versions')
|
70
|
+
# add_brick_migration('add_object_changes_to_versions') if options.with_changes?
|
71
|
+
# end
|
72
|
+
|
73
|
+
# def self.next_migration_number(dirname)
|
74
|
+
# ::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
75
|
+
# end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
# def add_brick_migration(template)
|
80
|
+
# migration_dir = File.expand_path('db/migrate')
|
81
|
+
# if self.class.migration_exists?(migration_dir, template)
|
82
|
+
# ::Kernel.warn "Migration already exists: #{template}"
|
83
|
+
# else
|
84
|
+
# migration_template(
|
85
|
+
# "#{template}.rb.erb",
|
86
|
+
# "db/migrate/#{template}.rb",
|
87
|
+
# item_type_options: item_type_options,
|
88
|
+
# migration_version: migration_version,
|
89
|
+
# versions_table_options: versions_table_options
|
90
|
+
# )
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
|
48
94
|
private
|
49
95
|
|
50
|
-
# MySQL 5.6 utf8mb4 limit is 191 chars for keys used in indexes.
|
51
|
-
def item_type_options
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
96
|
+
# # MySQL 5.6 utf8mb4 limit is 191 chars for keys used in indexes.
|
97
|
+
# def item_type_options
|
98
|
+
# opt = { null: false }
|
99
|
+
# opt[:limit] = 191 if mysql?
|
100
|
+
# ", #{opt}"
|
101
|
+
# end
|
56
102
|
|
57
|
-
def migration_version
|
58
|
-
|
103
|
+
# def migration_version
|
104
|
+
# return unless (major = ActiveRecord::VERSION::MAJOR) >= 5
|
59
105
|
|
60
|
-
|
61
|
-
end
|
106
|
+
# "[#{major}.#{ActiveRecord::VERSION::MINOR}]"
|
107
|
+
# end
|
62
108
|
|
63
|
-
# Class names of MySQL adapters.
|
64
|
-
# - `MysqlAdapter` - Used by gems: `mysql`, `activerecord-jdbcmysql-adapter`.
|
65
|
-
# - `Mysql2Adapter` - Used by `mysql2` gem.
|
66
|
-
def mysql?
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
109
|
+
# # Class names of MySQL adapters.
|
110
|
+
# # - `MysqlAdapter` - Used by gems: `mysql`, `activerecord-jdbcmysql-adapter`.
|
111
|
+
# # - `Mysql2Adapter` - Used by `mysql2` gem.
|
112
|
+
# def mysql?
|
113
|
+
# [
|
114
|
+
# 'ActiveRecord::ConnectionAdapters::MysqlAdapter',
|
115
|
+
# 'ActiveRecord::ConnectionAdapters::Mysql2Adapter'
|
116
|
+
# ].freeze.include?(ActiveRecord::Base.connection.class.name)
|
117
|
+
# end
|
72
118
|
|
73
|
-
# Even modern versions of MySQL still use `latin1` as the default character
|
74
|
-
# encoding. Many users are not aware of this, and run into trouble when they
|
75
|
-
# try to use Brick in apps that otherwise tend to use UTF-8. Postgres, by
|
76
|
-
# comparison, uses UTF-8 except in the unusual case where the OS is configured
|
77
|
-
# with a custom locale.
|
78
|
-
#
|
79
|
-
# - https://dev.mysql.com/doc/refman/5.7/en/charset-applications.html
|
80
|
-
# - http://www.postgresql.org/docs/9.4/static/multibyte.html
|
81
|
-
#
|
82
|
-
# Furthermore, MySQL's original implementation of UTF-8 was flawed, and had
|
83
|
-
# to be fixed later by introducing a new charset, `utf8mb4`.
|
84
|
-
#
|
85
|
-
# - https://mathiasbynens.be/notes/mysql-utf8mb4
|
86
|
-
# - https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
|
87
|
-
#
|
88
|
-
def versions_table_options
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
119
|
+
# # Even modern versions of MySQL still use `latin1` as the default character
|
120
|
+
# # encoding. Many users are not aware of this, and run into trouble when they
|
121
|
+
# # try to use Brick in apps that otherwise tend to use UTF-8. Postgres, by
|
122
|
+
# # comparison, uses UTF-8 except in the unusual case where the OS is configured
|
123
|
+
# # with a custom locale.
|
124
|
+
# #
|
125
|
+
# # - https://dev.mysql.com/doc/refman/5.7/en/charset-applications.html
|
126
|
+
# # - http://www.postgresql.org/docs/9.4/static/multibyte.html
|
127
|
+
# #
|
128
|
+
# # Furthermore, MySQL's original implementation of UTF-8 was flawed, and had
|
129
|
+
# # to be fixed later by introducing a new charset, `utf8mb4`.
|
130
|
+
# #
|
131
|
+
# # - https://mathiasbynens.be/notes/mysql-utf8mb4
|
132
|
+
# # - https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
|
133
|
+
# #
|
134
|
+
# def versions_table_options
|
135
|
+
# if mysql?
|
136
|
+
# ', { options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }'
|
137
|
+
# else
|
138
|
+
# ''
|
139
|
+
# end
|
140
|
+
# end
|
95
141
|
end
|
96
142
|
end
|
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.3
|
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-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|