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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 580f34a900e392172361e334170169b30c63f83e96bd7b3119e9d957d440d6b1
4
- data.tar.gz: b02a7265b8010fe06d9599d353f7cdaf2435f872411fe8d6871fbd79e275ce19
3
+ metadata.gz: 1b4733a804fae7a4759eae48690541f1873cb81fd1b2a9986f849202c1c76c5d
4
+ data.tar.gz: d721f215cfed0473804f82969fd3c89dd7a94671d24b0f44b0e03cc73d787754
5
5
  SHA512:
6
- metadata.gz: '038902e9a8bc77505c28cf0038ff6aa77d0440f389bbc43b29ffaccae4e344e37d1d8fa7a683aae44c5c36d6b51f282fcd22174539c47c8f30e162b3e9dc71b9'
7
- data.tar.gz: f8e00496a7af527e47d75ef8a94cb02994224fa7fe536056b4399aac7758fdd3d3f3eddc364cbf6aec30efd4a56b852fa9982b7e06a48261a6bc9a178bc161fb
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
@@ -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
- if class_name.end_with?('Controller') && (plural_class_name = class_name[0..-11]).length.positive?
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
- is_controller = true
102
- table_name = ActiveSupport::Inflector.underscore(plural_class_name)
103
- model_name = ActiveSupport::Inflector.singularize(plural_class_name)
104
- singular_table_name = ActiveSupport::Inflector.singularize(table_name)
105
- else # Model
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
- relations = Brick.instance_variable_get(:@relations)[ActiveRecord::Base.connection_pool.object_id] || {}
113
- # Maybe, just maybe there's a database table that will satisfy this need
114
- matches = [table_name, singular_table_name, plural_class_name, model_name]
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(class_name)}\".")
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
- # By default, views get marked as read-only
143
- if (relation = relations[matching]).key?(:isView)
144
- self.define_method :'readonly?' do
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, model_name, singular_table_name, table_name, relations, matching)
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} = #{model_name}#{model.primary_key ? ".order(#{model.primary_key.inspect}" : '.all'})\n"
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
- relation = model.primary_key ? model.order(model.primary_key) : model.all
236
- instance_variable_set("@#{table_name}".to_sym, relation)
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} = #{model_name}.find(params[:id].split(','))\n"
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[matching]).key?(:isView)
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.CONSTRAINT_NAME, kcu1.TABLE_NAME, kcu1.COLUMN_NAME, kcu2.TABLE_NAME
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 do |fk|
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
- # 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)
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
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 0
8
+ TINY = 1
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
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 is the
100
- # on/off switch that affects all threads. Enabled by default.
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.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-12 00:00:00.000000000 Z
11
+ date: 2022-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord