brick 1.0.0 → 1.0.1
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 +36 -0
- data/lib/brick/extensions.rb +96 -107
- data/lib/brick/frameworks/rails/engine.rb +57 -3
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +56 -2
- 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: 1b4733a804fae7a4759eae48690541f1873cb81fd1b2a9986f849202c1c76c5d
|
4
|
+
data.tar.gz: d721f215cfed0473804f82969fd3c89dd7a94671d24b0f44b0e03cc73d787754
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b73c787a9c7aa6804d2cfce77921252dd77ce3e6e3710bd30875436d0f66011e6043e0ae4cbdaaeff6f4abb725d8051b5c1be139ee28b80648314f124ff7204f
|
7
|
+
data.tar.gz: 000b467728db85565d793df6e22429e3ff804bb7d39e5c4d6346becc24bc940ec9476fe00758d1e4a4fad868239e755ba7bdef596ecb115f358332656fd1be5e
|
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,14 @@ 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=(references)
|
61
|
+
@mutex.synchronize { @additional_references = references }
|
62
|
+
end
|
63
|
+
|
64
|
+
def additional_references
|
65
|
+
@mutex.synchronize { @additional_references }
|
66
|
+
end
|
31
67
|
end
|
32
68
|
end
|
data/lib/brick/extensions.rb
CHANGED
@@ -28,57 +28,17 @@
|
|
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
|
-
# ====================================
|
33
|
-
# Dynamically create generic templates
|
34
|
-
# ====================================
|
35
|
-
|
36
|
-
# For templates:
|
37
|
-
class ActionView::LookupContext
|
38
|
-
alias :_brick_template_exists? :template_exists?
|
39
|
-
def template_exists?(*args, **options)
|
40
|
-
x = _brick_template_exists?(*args, **options)
|
41
|
-
# Need to return true if we can fill in the blanks for a missing one
|
42
|
-
# args will be something like: ["index", ["categories"]]
|
43
|
-
unless x
|
44
|
-
relations = Brick.instance_variable_get(:@relations)[ActiveRecord::Base.connection_pool.object_id] || {}
|
45
|
-
matching = [views_name = args[1]&.first, views_name.singularize].find { |m| relations.key?(m) }
|
46
|
-
if (x = matching && (matching = relations[matching]) &&
|
47
|
-
(['index', 'show'].include?(args.first) || # Everything has index and show
|
48
|
-
# Only CRU stuff has create / update / destroy
|
49
|
-
(!matching.key?(:isView) && ['new', 'create', 'edit', 'update', 'destroy'].include?(args.first))
|
50
|
-
)
|
51
|
-
)
|
52
|
-
instance_variable_set(:@_brick_match, matching)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
x
|
56
|
-
end
|
57
|
-
|
58
|
-
alias :_brick_find_template :find_template
|
59
|
-
def find_template(*args, **options)
|
60
|
-
if @_brick_match
|
61
|
-
inline = case args.first
|
62
|
-
when 'index'
|
63
|
-
# Something like: <%= @categories.inspect %>
|
64
|
-
"<%= @#{@_brick_match[:index]}.inspect %>"
|
65
|
-
when 'show'
|
66
|
-
"<%= @#{@_brick_match[:show]}.inspect %>"
|
67
|
-
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
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
31
|
# ==========================================================
|
79
32
|
# Dynamically create model or controller classes when needed
|
80
33
|
# ==========================================================
|
81
34
|
|
35
|
+
# By default all models indicate that they are not views
|
36
|
+
class ActiveRecord::Base
|
37
|
+
def self.is_view?
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
82
42
|
# Object.class_exec do
|
83
43
|
class Object
|
84
44
|
class << self
|
@@ -96,29 +56,28 @@ class Object
|
|
96
56
|
# If the file really exists, go and snag it:
|
97
57
|
return Object._brick_const_missing(*args) if ActiveSupport::Dependencies.search_for_file(class_name.underscore)
|
98
58
|
|
99
|
-
|
59
|
+
relations = ::Brick.instance_variable_get(:@relations)[ActiveRecord::Base.connection_pool.object_id] || {}
|
60
|
+
result = if ::Brick.enable_controllers? && class_name.end_with?('Controller') && (plural_class_name = class_name[0..-11]).length.positive?
|
100
61
|
# Otherwise now it's up to us to fill in the gaps
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
62
|
+
if (model = ActiveSupport::Inflector.singularize(plural_class_name).constantize)
|
63
|
+
# 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.
|
64
|
+
build_controller(class_name, plural_class_name, model, relations)
|
65
|
+
end
|
66
|
+
elsif ::Brick.enable_models?
|
106
67
|
# See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
|
107
68
|
# checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
|
108
69
|
plural_class_name = ActiveSupport::Inflector.pluralize(model_name = class_name)
|
109
70
|
singular_table_name = ActiveSupport::Inflector.underscore(model_name)
|
110
71
|
table_name = ActiveSupport::Inflector.pluralize(singular_table_name)
|
72
|
+
|
73
|
+
# Maybe, just maybe there's a database table that will satisfy this need
|
74
|
+
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(m) })
|
75
|
+
build_model(model_name, singular_table_name, table_name, relations, matching)
|
76
|
+
end
|
111
77
|
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"
|
78
|
+
if result
|
79
|
+
built_class, code = result
|
80
|
+
puts "\n#{code}"
|
122
81
|
built_class
|
123
82
|
else
|
124
83
|
puts "MISSING! #{args.inspect} #{table_name}"
|
@@ -131,7 +90,7 @@ class Object
|
|
131
90
|
def build_model(model_name, singular_table_name, table_name, relations, matching)
|
132
91
|
# Are they trying to use a pluralised class name such as "Employees" instead of "Employee"?
|
133
92
|
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(
|
93
|
+
raise NameError.new("Class name for a model that references table \"#{matching}\" should be \"#{ActiveSupport::Inflector.singularize(model_name)}\".")
|
135
94
|
end
|
136
95
|
code = +"class #{model_name} < ActiveRecord::Base\n"
|
137
96
|
built_model = Class.new(ActiveRecord::Base) do |new_model_class|
|
@@ -139,11 +98,13 @@ class Object
|
|
139
98
|
# Accommodate singular or camel-cased table names such as "order_detail" or "OrderDetails"
|
140
99
|
code << " self.table_name = '#{self.table_name = matching}'\n" unless table_name == matching
|
141
100
|
|
142
|
-
#
|
143
|
-
|
144
|
-
|
101
|
+
# Override models backed by a view so they return true for #is_view?
|
102
|
+
# (Dynamically-created controllers and view templates for such models will then act in a read-only way)
|
103
|
+
if (is_view = (relation = relations[matching]).key?(:isView))
|
104
|
+
new_model_class.define_singleton_method :'is_view?' do
|
145
105
|
true
|
146
106
|
end
|
107
|
+
code << " def self.is_view?; true; end\n"
|
147
108
|
end
|
148
109
|
|
149
110
|
# Missing a primary key column? (Usually "id")
|
@@ -165,7 +126,7 @@ class Object
|
|
165
126
|
code << " self.primary_key = #{pk_sym.inspect}\n"
|
166
127
|
end
|
167
128
|
else
|
168
|
-
code << " # Could not identify any column(s) to use as a primary key\n"
|
129
|
+
code << " # Could not identify any column(s) to use as a primary key\n" unless is_view
|
169
130
|
end
|
170
131
|
|
171
132
|
# if relation[:cols].key?('last_update')
|
@@ -217,28 +178,31 @@ class Object
|
|
217
178
|
end
|
218
179
|
end
|
219
180
|
end
|
181
|
+
code << "end # model #{model_name}\n\n"
|
220
182
|
end # class definition
|
221
183
|
[built_model, code]
|
222
184
|
end
|
223
185
|
|
224
186
|
|
225
|
-
def build_controller(class_name,
|
187
|
+
def build_controller(class_name, plural_class_name, model, relations)
|
188
|
+
table_name = ActiveSupport::Inflector.underscore(plural_class_name)
|
189
|
+
singular_table_name = ActiveSupport::Inflector.singularize(table_name)
|
190
|
+
|
226
191
|
code = +"class #{class_name} < ApplicationController\n"
|
227
192
|
built_controller = Class.new(ActionController::Base) do |new_controller_class|
|
228
193
|
Object.const_set(class_name.to_sym, new_controller_class)
|
229
194
|
|
230
|
-
model = model_name.constantize
|
231
195
|
code << " def index\n"
|
232
|
-
code << " @#{table_name} = #{
|
196
|
+
code << " @#{table_name} = #{model.name}#{model.primary_key ? ".order(#{model.primary_key.inspect}" : '.all'})\n"
|
233
197
|
code << " end\n"
|
234
198
|
self.define_method :index do
|
235
|
-
|
236
|
-
instance_variable_set("@#{table_name}".to_sym,
|
199
|
+
ar_relation = model.primary_key ? model.order(model.primary_key) : model.all
|
200
|
+
instance_variable_set("@#{table_name}".to_sym, ar_relation)
|
237
201
|
end
|
238
202
|
|
239
203
|
if model.primary_key
|
240
204
|
code << " def show\n"
|
241
|
-
code << " @#{singular_table_name} = #{
|
205
|
+
code << " @#{singular_table_name} = #{model.name}.find(params[:id].split(','))\n"
|
242
206
|
code << " end\n"
|
243
207
|
self.define_method :show do
|
244
208
|
instance_variable_set("@#{singular_table_name}".to_sym, model.find(params[:id].split(',')))
|
@@ -246,9 +210,11 @@ class Object
|
|
246
210
|
end
|
247
211
|
|
248
212
|
# By default, views get marked as read-only
|
249
|
-
unless (relation = relations[
|
213
|
+
unless (relation = relations[model.table_name]).key?(:isView)
|
250
214
|
code << " # (Define :new, :create, :edit, :update, and :destroy)\n"
|
215
|
+
# Get column names for params from relations[model.table_name][:cols].keys
|
251
216
|
end
|
217
|
+
code << "end # #{class_name}\n\n"
|
252
218
|
end # class definition
|
253
219
|
[built_controller, code]
|
254
220
|
end
|
@@ -270,14 +236,10 @@ end
|
|
270
236
|
module ActiveRecord::ConnectionHandling
|
271
237
|
alias old_establish_connection establish_connection
|
272
238
|
def establish_connection(*args)
|
273
|
-
connections = Brick.instance_variable_get(:@relations) ||
|
274
|
-
Brick.instance_variable_set(:@relations, (connections = {}))
|
275
239
|
# puts connections.inspect
|
276
240
|
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
241
|
|
280
|
-
if relations.empty?
|
242
|
+
if (relations = ::Brick.relations).empty?
|
281
243
|
schema = 'public'
|
282
244
|
puts ActiveRecord::Base.connection.execute("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
283
245
|
sql = ActiveRecord::Base.send(:sanitize_sql_array, [
|
@@ -326,7 +288,7 @@ module ActiveRecord::ConnectionHandling
|
|
326
288
|
end
|
327
289
|
|
328
290
|
sql = ActiveRecord::Base.send(:sanitize_sql_array, [
|
329
|
-
"SELECT kcu1.
|
291
|
+
"SELECT kcu1.TABLE_NAME, kcu1.COLUMN_NAME, kcu2.TABLE_NAME, kcu1.CONSTRAINT_NAME
|
330
292
|
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS rc
|
331
293
|
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu1
|
332
294
|
ON kcu1.CONSTRAINT_CATALOG = rc.CONSTRAINT_CATALOG
|
@@ -340,31 +302,8 @@ module ActiveRecord::ConnectionHandling
|
|
340
302
|
WHERE kcu1.CONSTRAINT_SCHEMA = ? -- COALESCE(current_setting('SEARCH_PATH'), 'public')", schema
|
341
303
|
# AND kcu2.TABLE_NAME = ?;", Apartment::Tenant.current, table_name
|
342
304
|
])
|
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
|
305
|
+
ActiveRecord::Base.connection.execute(sql).values.each { |fk| ::Brick._add_bt_and_hm(fk, relations) }
|
354
306
|
|
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
307
|
# Find associative tables that can be set up for has_many :through
|
369
308
|
relations.each do |_key, tbl|
|
370
309
|
tbl_cols = tbl[:cols].keys
|
@@ -406,12 +345,62 @@ module Brick
|
|
406
345
|
|
407
346
|
private
|
408
347
|
|
409
|
-
def _create_class()
|
410
|
-
end
|
411
348
|
end
|
412
349
|
end # module Extensions
|
413
350
|
# rubocop:enable Style/CommentedKeyword
|
414
351
|
|
352
|
+
def self._add_bt_and_hm(fk, relations = nil)
|
353
|
+
relations ||= ::Brick.relations
|
354
|
+
bt_assoc_name = fk[1].underscore
|
355
|
+
bt_assoc_name = bt_assoc_name[0..-4] if bt_assoc_name.end_with?('_id')
|
356
|
+
|
357
|
+
bts = (relation = relations.fetch(fk[0], nil))&.fetch(:fks) { relation[:fks] = {} }
|
358
|
+
hms = (relation = relations.fetch(fk[2], nil))&.fetch(:fks) { relation[:fks] = {} }
|
359
|
+
|
360
|
+
unless (cnstr_name = fk[3])
|
361
|
+
# For any appended references (those that come from config), arrive upon a definitely unique constraint name
|
362
|
+
cnstr_base = cnstr_name = "(brick) #{fk[0]}_#{fk[2]}"
|
363
|
+
cnstr_added_num = 1
|
364
|
+
cnstr_name = "#{cnstr_base}_#{cnstr_added_num += 1}" while bts&.key?(cnstr_name) || hms&.key?(cnstr_name)
|
365
|
+
missing = []
|
366
|
+
missing << fk[0] unless relations.key?(fk[0])
|
367
|
+
missing << fk[2] unless relations.key?(fk[2])
|
368
|
+
unless missing.empty?
|
369
|
+
tables = relations.reject { |k, v| v.fetch(:isView, nil) }.keys.sort
|
370
|
+
puts "Brick: Additional reference #{fk.inspect} refers to non-existent #{'table'.pluralize(missing.length)} #{missing.join(' and ')}. (Available tables include #{tables.join(', ')}.)"
|
371
|
+
return
|
372
|
+
end
|
373
|
+
unless (cols = relations[fk[0]][:cols]).key?(fk[1])
|
374
|
+
columns = cols.map { |k, v| "#{k} (#{v.first.split(' ').first})" }
|
375
|
+
puts "Brick: Additional reference #{fk.inspect} refers to non-existent column #{fk[1]}. (Columns present in #{fk[0]} are #{columns.join(', ')}.)"
|
376
|
+
return
|
377
|
+
end
|
378
|
+
if (redundant = bts.find{|k, v| v[:inverse][:inverse_table] == fk[0] && v[:fk] == fk[1] && v[:inverse_table] == fk[2] })
|
379
|
+
puts "Brick: Additional reference #{fk.inspect} is redundant and can be removed. (Already established by #{redundant.first}.)"
|
380
|
+
return
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
if (assoc_bt = bts[cnstr_name])
|
385
|
+
assoc_bt[:fk] = assoc_bt[:fk].is_a?(String) ? [assoc_bt[:fk], fk[1]] : assoc_bt[:fk].concat(fk[1])
|
386
|
+
assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[1]}"
|
387
|
+
else
|
388
|
+
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[1], assoc_name: bt_assoc_name, inverse_table: fk[2] }
|
389
|
+
end
|
390
|
+
|
391
|
+
if (assoc_hm = hms[cnstr_name])
|
392
|
+
assoc_hm[:fk] = assoc_hm[:fk].is_a?(String) ? [assoc_hm[:fk], fk[1]] : assoc_hm[:fk].concat(fk[1])
|
393
|
+
assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
|
394
|
+
assoc_hm[:inverse] = assoc_bt
|
395
|
+
else
|
396
|
+
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 }
|
397
|
+
hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
|
398
|
+
hm_counts[fk[0]] = hm_counts.fetch(fk[0]) { 0 } + 1
|
399
|
+
end
|
400
|
+
assoc_bt[:inverse] = assoc_hm
|
401
|
+
# hms[cnstr_name] << { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0] }
|
402
|
+
end
|
403
|
+
|
415
404
|
# Rails < 4.0 doesn't have ActiveRecord::RecordNotUnique, so use the more generic ActiveRecord::ActiveRecordError instead
|
416
405
|
ar_not_unique_error = ActiveRecord.const_defined?('RecordNotUnique') ? ActiveRecord::RecordNotUnique : ActiveRecord::ActiveRecordError
|
417
406
|
class NoUniqueColumnError < ar_not_unique_error
|
@@ -7,13 +7,67 @@ module Brick
|
|
7
7
|
# paths['app/models'] << 'lib/brick/frameworks/active_record/models'
|
8
8
|
config.brick = ActiveSupport::OrderedOptions.new
|
9
9
|
initializer 'brick.initialisation' do |app|
|
10
|
+
Brick.enable_models = app.config.brick.fetch(:enable_models, true)
|
11
|
+
Brick.enable_controllers = app.config.brick.fetch(:enable_controllers, true)
|
12
|
+
|
13
|
+
# ====================================
|
14
|
+
# Dynamically create generic templates
|
15
|
+
# ====================================
|
16
|
+
if (Brick.enable_views = app.config.brick.fetch(:enable_views, true))
|
17
|
+
ActionView::LookupContext.class_exec do
|
18
|
+
alias :_brick_template_exists? :template_exists?
|
19
|
+
def template_exists?(*args, **options)
|
20
|
+
unless (is_template_exists = _brick_template_exists?(*args, **options))
|
21
|
+
# Need to return true if we can fill in the blanks for a missing one
|
22
|
+
# args will be something like: ["index", ["categories"]]
|
23
|
+
model = args[1].map(&:camelize).join('::').singularize.constantize
|
24
|
+
if (
|
25
|
+
is_template_exists = model && (
|
26
|
+
['index', 'show'].include?(args.first) || # Everything has index and show
|
27
|
+
# Only CRU stuff has create / update / destroy
|
28
|
+
(!model.is_view? && ['new', 'create', 'edit', 'update', 'destroy'].include?(args.first))
|
29
|
+
)
|
30
|
+
)
|
31
|
+
instance_variable_set(:@_brick_model, model)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
is_template_exists
|
35
|
+
end
|
36
|
+
|
37
|
+
alias :_brick_find_template :find_template
|
38
|
+
def find_template(*args, **options)
|
39
|
+
if @_brick_model
|
40
|
+
inline = case args.first
|
41
|
+
when 'index'
|
42
|
+
# Something like: <%= @categories.inspect %>
|
43
|
+
"<%= @#{@_brick_model.name.underscore.pluralize}.inspect %>"
|
44
|
+
when 'show'
|
45
|
+
"<%= @#{@_brick_model.name.underscore}.inspect %>"
|
46
|
+
end
|
47
|
+
# As if it were an inline template (see #determine_template in actionview-5.2.6.2/lib/action_view/renderer/template_renderer.rb)
|
48
|
+
keys = options.has_key?(:locals) ? options[:locals].keys : []
|
49
|
+
handler = ActionView::Template.handler_for_extension(options[:type] || 'erb')
|
50
|
+
ActionView::Template.new(inline, "auto-generated #{args.first} template", handler, locals: keys)
|
51
|
+
else
|
52
|
+
_brick_find_template(*args, **options)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
10
58
|
# Auto-routing behaviour
|
11
59
|
if (::Brick.enable_routes = app.config.brick.fetch(:enable_routes, true))
|
12
60
|
::Brick.append_routes
|
13
61
|
end
|
14
|
-
#
|
15
|
-
|
16
|
-
|
62
|
+
# Additional references (virtual foreign keys)
|
63
|
+
if (ars = (::Brick.additional_references = app.config.brick.fetch(:additional_references, nil)))
|
64
|
+
ars = ars.call if ars.is_a?(Proc)
|
65
|
+
ars = ars.to_a unless ars.is_a?(Array)
|
66
|
+
ars = [ars] unless ars.empty? || ars.first.is_a?(Array)
|
67
|
+
ars.each do |fk|
|
68
|
+
::Brick._add_bt_and_hm(fk[0..2])
|
69
|
+
end
|
70
|
+
end
|
17
71
|
end
|
18
72
|
end
|
19
73
|
end
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -90,19 +90,73 @@ module Brick
|
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
|
+
# All tables and views (what Postgres calls "relations" including column and foreign key info)
|
94
|
+
def relations
|
95
|
+
connections = Brick.instance_variable_get(:@relations) ||
|
96
|
+
Brick.instance_variable_set(:@relations, (connections = {}))
|
97
|
+
# Key our list of relations for this connection off of the connection pool's object_id
|
98
|
+
(connections[ActiveRecord::Base.connection_pool.object_id] ||= Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = {} } })
|
99
|
+
end
|
100
|
+
|
101
|
+
# Switches Brick auto-models on or off, for all threads
|
102
|
+
# @api public
|
103
|
+
def enable_models=(value)
|
104
|
+
Brick.config.enable_models = value
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns `true` if Brick models are on, `false` otherwise. This affects all
|
108
|
+
# threads. Enabled by default.
|
109
|
+
# @api public
|
110
|
+
def enable_models?
|
111
|
+
!!Brick.config.enable_models
|
112
|
+
end
|
113
|
+
|
114
|
+
# Switches Brick auto-controllers on or off, for all threads
|
115
|
+
# @api public
|
116
|
+
def enable_controllers=(value)
|
117
|
+
Brick.config.enable_controllers = value
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns `true` if Brick controllers are on, `false` otherwise. This affects all
|
121
|
+
# threads. Enabled by default.
|
122
|
+
# @api public
|
123
|
+
def enable_controllers?
|
124
|
+
!!Brick.config.enable_controllers
|
125
|
+
end
|
126
|
+
|
127
|
+
# Switches Brick auto-views on or off, for all threads
|
128
|
+
# @api public
|
129
|
+
def enable_views=(value)
|
130
|
+
Brick.config.enable_views = value
|
131
|
+
end
|
132
|
+
|
133
|
+
# Returns `true` if Brick views are on, `false` otherwise. This affects all
|
134
|
+
# threads. Enabled by default.
|
135
|
+
# @api public
|
136
|
+
def enable_views?
|
137
|
+
!!Brick.config.enable_views
|
138
|
+
end
|
139
|
+
|
93
140
|
# Switches Brick auto-routes on or off, for all threads
|
94
141
|
# @api public
|
95
142
|
def enable_routes=(value)
|
96
143
|
Brick.config.enable_routes = value
|
97
144
|
end
|
98
145
|
|
99
|
-
# Returns `true` if Brick routes are on, `false` otherwise. This
|
100
|
-
#
|
146
|
+
# Returns `true` if Brick routes are on, `false` otherwise. This affects all
|
147
|
+
# threads. Enabled by default.
|
101
148
|
# @api public
|
102
149
|
def enable_routes?
|
103
150
|
!!Brick.config.enable_routes
|
104
151
|
end
|
105
152
|
|
153
|
+
# Additional table associations to use (Think of these as virtual foreign keys perhaps)
|
154
|
+
# @api public
|
155
|
+
def additional_references=(value)
|
156
|
+
Brick.config.additional_references = value
|
157
|
+
end
|
158
|
+
|
159
|
+
|
106
160
|
# Returns Brick's `::Gem::Version`, convenient for comparisons. This is
|
107
161
|
# recommended over `::Brick::VERSION::STRING`.
|
108
162
|
#
|
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.1
|
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-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|