brick 1.0.0 → 1.0.1
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 +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
|