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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 580f34a900e392172361e334170169b30c63f83e96bd7b3119e9d957d440d6b1
4
- data.tar.gz: b02a7265b8010fe06d9599d353f7cdaf2435f872411fe8d6871fbd79e275ce19
3
+ metadata.gz: ff255b32c913dbb0e02d3f197d035faf43dc44afc4041cda3f57e92fc9df893a
4
+ data.tar.gz: 763b5579402a7668abf3d266665879643b8d44ed04686a35f2df145b1ddfee72
5
5
  SHA512:
6
- metadata.gz: '038902e9a8bc77505c28cf0038ff6aa77d0440f389bbc43b29ffaccae4e344e37d1d8fa7a683aae44c5c36d6b51f282fcd22174539c47c8f30e162b3e9dc71b9'
7
- data.tar.gz: f8e00496a7af527e47d75ef8a94cb02994224fa7fe536056b4399aac7758fdd3d3f3eddc364cbf6aec30efd4a56b852fa9982b7e06a48261a6bc9a178bc161fb
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
@@ -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
- # 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
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
- 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 %>"
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
- # Dynamically create model or controller classes when needed
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
- if class_name.end_with?('Controller') && (plural_class_name = class_name[0..-11]).length.positive?
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
- 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
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
- 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"
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(class_name)}\".")
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
- # By default, views get marked as read-only
143
- if (relation = relations[matching]).key?(:isView)
144
- self.define_method :'readonly?' do
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} = #{model_name}#{model.primary_key ? ".order(#{model.primary_key.inspect}" : '.all'})\n"
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
- relation = model.primary_key ? model.order(model.primary_key) : model.all
236
- instance_variable_set("@#{table_name}".to_sym, relation)
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} = #{model_name}.find(params[:id].split(','))\n"
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[matching]).key?(:isView)
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 old_establish_connection establish_connection
289
+ alias _brick_establish_connection establish_connection
272
290
  def establish_connection(*args)
273
- connections = Brick.instance_variable_get(:@relations) ||
274
- Brick.instance_variable_set(:@relations, (connections = {}))
275
- # puts connections.inspect
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.CONSTRAINT_NAME, kcu1.TABLE_NAME, kcu1.COLUMN_NAME, kcu2.TABLE_NAME
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 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
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
- # Auto-routing behaviour
11
- if (::Brick.enable_routes = app.config.brick.fetch(:enable_routes, true))
12
- ::Brick.append_routes
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
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 0
8
+ TINY = 3
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
@@ -79,15 +79,51 @@ require 'brick/config'
79
79
  require 'brick/frameworks/rails'
80
80
  module Brick
81
81
  class << self
82
- def append_routes
83
- ::Rails.application.routes.append do
84
- relations = (::Brick.instance_variable_get(:@relations) || {})[ActiveRecord::Base.connection_pool.object_id] || {}
85
- relations.each do |k, v|
86
- options = {}
87
- options[:only] = [:index, :show] if v.key?(:isView)
88
- send(:resources, k.underscore.pluralize.to_sym, **options)
89
- end
90
- end
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 is the
100
- # on/off switch that affects all threads. Enabled by default.
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 (but does not run) a migration to add a versions table.' \
20
- ' Also generates an initializer file for configuring Brick'
18
+ desc 'Generates an initializer file for configuring Brick'
21
19
 
22
- def create_migration_file
23
- add_brick_migration('create_versions')
24
- add_brick_migration('add_object_changes_to_versions') if options.with_changes?
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
- def self.next_migration_number(dirname)
28
- ::ActiveRecord::Generators::Base.next_migration_number(dirname)
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
- protected
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
- def add_brick_migration(template)
34
- migration_dir = File.expand_path('db/migrate')
35
- if self.class.migration_exists?(migration_dir, template)
36
- ::Kernel.warn "Migration already exists: #{template}"
37
- else
38
- migration_template(
39
- "#{template}.rb.erb",
40
- "db/migrate/#{template}.rb",
41
- item_type_options: item_type_options,
42
- migration_version: migration_version,
43
- versions_table_options: versions_table_options
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
- opt = { null: false }
53
- opt[:limit] = 191 if mysql?
54
- ", #{opt}"
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
- return unless (major = ActiveRecord::VERSION::MAJOR) >= 5
103
+ # def migration_version
104
+ # return unless (major = ActiveRecord::VERSION::MAJOR) >= 5
59
105
 
60
- "[#{major}.#{ActiveRecord::VERSION::MINOR}]"
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
- 'ActiveRecord::ConnectionAdapters::MysqlAdapter',
69
- 'ActiveRecord::ConnectionAdapters::Mysql2Adapter'
70
- ].freeze.include?(ActiveRecord::Base.connection.class.name)
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
- if mysql?
90
- ', { options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }'
91
- else
92
- ''
93
- end
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.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-12 00:00:00.000000000 Z
11
+ date: 2022-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord