brick 1.0.1 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/brick/config.rb +26 -2
- data/lib/brick/extensions.rb +152 -72
- data/lib/brick/frameworks/rails/engine.rb +201 -49
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +33 -27
- data/lib/generators/brick/install_generator.rb +112 -66
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe6bf3e38aef34261d9398b25f804504e7152721673b0534253c4284c9018013
|
4
|
+
data.tar.gz: 766af0df2edc3dc8c8c73a5d1ecd99c8d738ed7cd00fc21c79c38ac3db991819
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40f2eacaf7fada3056f5bfc701b01441ad02e34e76e87dc8e53b36a546c87216e8a319c3f0763b8fecff49344224d5660ff5b875937a3e30e2e7c80c662f684c
|
7
|
+
data.tar.gz: 7901020af2ec1759c27c16a0c3a3ffbcddcd09691c652c14c02781823964e36409533afc562fb0d9beb508cc3329b601d6fe8c266c101f6db6d93aad5e055a3e
|
data/lib/brick/config.rb
CHANGED
@@ -57,12 +57,36 @@ module Brick
|
|
57
57
|
end
|
58
58
|
|
59
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
|
+
|
60
64
|
def additional_references=(references)
|
61
65
|
@mutex.synchronize { @additional_references = references }
|
62
66
|
end
|
63
67
|
|
64
|
-
def
|
65
|
-
@mutex.synchronize { @
|
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 }
|
66
90
|
end
|
67
91
|
end
|
68
92
|
end
|
data/lib/brick/extensions.rb
CHANGED
@@ -26,16 +26,58 @@
|
|
26
26
|
|
27
27
|
# colour coded origins
|
28
28
|
|
29
|
-
# Drag TmfModel#name onto the rows and have it automatically add five columns -- where type=zone / where type =
|
29
|
+
# Drag something like TmfModel#name onto the rows and have it automatically add five columns -- where type=zone / where type = section / etc
|
30
|
+
|
31
|
+
# Support for Postgres / MySQL enums (add enum to model, use model enums to make a drop-down in the UI)
|
32
|
+
|
33
|
+
# Currently quadrupling up routes
|
30
34
|
|
31
35
|
# ==========================================================
|
32
36
|
# Dynamically create model or controller classes when needed
|
33
37
|
# ==========================================================
|
34
38
|
|
35
39
|
# By default all models indicate that they are not views
|
36
|
-
|
37
|
-
|
38
|
-
|
40
|
+
module ActiveRecord
|
41
|
+
class Base
|
42
|
+
def self.is_view?
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
# Used to show a little prettier name for an object
|
47
|
+
def brick_descrip
|
48
|
+
klass = self.class
|
49
|
+
klass.primary_key ? "#{klass.name} ##{send(klass.primary_key)}" : to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def self._brick_get_fks
|
55
|
+
@_brick_get_fks ||= reflect_on_all_associations.select { |a2| a2.macro == :belongs_to }.map(&:foreign_key)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Relation
|
60
|
+
def brick_where(params)
|
61
|
+
wheres = {}
|
62
|
+
rel_joins = []
|
63
|
+
params.each do |k, v|
|
64
|
+
if (ks = k.split('.')).length > 1
|
65
|
+
assoc_name = ks.first.to_sym
|
66
|
+
# Make sure it's a good association name and that the model has that column name
|
67
|
+
next unless klass.reflect_on_association(assoc_name)&.klass&.columns&.map(&:name)&.include?(ks.last)
|
68
|
+
|
69
|
+
rel_joins << assoc_name unless rel_joins.include?(assoc_name)
|
70
|
+
else
|
71
|
+
next unless klass._brick_get_fks.include?(k)
|
72
|
+
end
|
73
|
+
wheres[k] = v.split(',')
|
74
|
+
end
|
75
|
+
unless wheres.empty?
|
76
|
+
where!(wheres)
|
77
|
+
joins!(rel_joins) unless rel_joins.empty?
|
78
|
+
wheres # Return the specific parameters that we did use
|
79
|
+
end
|
80
|
+
end
|
39
81
|
end
|
40
82
|
end
|
41
83
|
|
@@ -88,6 +130,9 @@ class Object
|
|
88
130
|
private
|
89
131
|
|
90
132
|
def build_model(model_name, singular_table_name, table_name, relations, matching)
|
133
|
+
return if ((is_view = (relation = relations[matching]).key?(:isView)) && ::Brick.config.skip_database_views) ||
|
134
|
+
::Brick.config.exclude_tables.include?(matching)
|
135
|
+
|
91
136
|
# Are they trying to use a pluralised class name such as "Employees" instead of "Employee"?
|
92
137
|
if table_name == singular_table_name && !ActiveSupport::Inflector.inflections.uncountable.include?(table_name)
|
93
138
|
raise NameError.new("Class name for a model that references table \"#{matching}\" should be \"#{ActiveSupport::Inflector.singularize(model_name)}\".")
|
@@ -100,7 +145,7 @@ class Object
|
|
100
145
|
|
101
146
|
# Override models backed by a view so they return true for #is_view?
|
102
147
|
# (Dynamically-created controllers and view templates for such models will then act in a read-only way)
|
103
|
-
if
|
148
|
+
if is_view
|
104
149
|
new_model_class.define_singleton_method :'is_view?' do
|
105
150
|
true
|
106
151
|
end
|
@@ -129,15 +174,6 @@ class Object
|
|
129
174
|
code << " # Could not identify any column(s) to use as a primary key\n" unless is_view
|
130
175
|
end
|
131
176
|
|
132
|
-
# if relation[:cols].key?('last_update')
|
133
|
-
# define_method :updated_at do
|
134
|
-
# last_update
|
135
|
-
# end
|
136
|
-
# define_method :'updated_at=' do |val|
|
137
|
-
# last_update=(val)
|
138
|
-
# end
|
139
|
-
# end
|
140
|
-
|
141
177
|
fks = relation[:fks] || {}
|
142
178
|
fks.each do |_constraint_name, assoc|
|
143
179
|
assoc_name = assoc[:assoc_name]
|
@@ -153,7 +189,7 @@ class Object
|
|
153
189
|
# need_class_name = ActiveSupport::Inflector.singularize(assoc_name) == ActiveSupport::Inflector.singularize(table_name.underscore)
|
154
190
|
# Are there multiple foreign keys out to the same table?
|
155
191
|
assoc_name, need_class_name = _brick_get_hm_assoc_name(relation, assoc)
|
156
|
-
need_fk = "#{
|
192
|
+
need_fk = "#{ActiveSupport::Inflector.singularize(assoc[:inverse][:inverse_table])}_id" != assoc[:fk]
|
157
193
|
# fks[table_name].find { |other_assoc| other_assoc.object_id != assoc.object_id && other_assoc[:assoc_name] == assoc[assoc_name] }
|
158
194
|
:has_many
|
159
195
|
end
|
@@ -183,7 +219,6 @@ class Object
|
|
183
219
|
[built_model, code]
|
184
220
|
end
|
185
221
|
|
186
|
-
|
187
222
|
def build_controller(class_name, plural_class_name, model, relations)
|
188
223
|
table_name = ActiveSupport::Inflector.underscore(plural_class_name)
|
189
224
|
singular_table_name = ActiveSupport::Inflector.singularize(table_name)
|
@@ -193,10 +228,12 @@ class Object
|
|
193
228
|
Object.const_set(class_name.to_sym, new_controller_class)
|
194
229
|
|
195
230
|
code << " def index\n"
|
196
|
-
code << " @#{table_name} = #{model.name}#{model.primary_key ? ".order(#{model.primary_key.inspect}" : '.all'}
|
231
|
+
code << " @#{table_name} = #{model.name}#{model.primary_key ? ".order(#{model.primary_key.inspect})" : '.all'}\n"
|
232
|
+
code << " @#{table_name}.brick_where(params)\n"
|
197
233
|
code << " end\n"
|
198
234
|
self.define_method :index do
|
199
235
|
ar_relation = model.primary_key ? model.order(model.primary_key) : model.all
|
236
|
+
instance_variable_set(:@_brick_params, ar_relation.brick_where(params))
|
200
237
|
instance_variable_set("@#{table_name}".to_sym, ar_relation)
|
201
238
|
end
|
202
239
|
|
@@ -234,19 +271,36 @@ end
|
|
234
271
|
# ==========================================================
|
235
272
|
|
236
273
|
module ActiveRecord::ConnectionHandling
|
237
|
-
alias
|
274
|
+
alias _brick_establish_connection establish_connection
|
238
275
|
def establish_connection(*args)
|
239
|
-
|
240
|
-
x = old_establish_connection(*args)
|
276
|
+
x = _brick_establish_connection(*args)
|
241
277
|
|
242
278
|
if (relations = ::Brick.relations).empty?
|
243
|
-
|
244
|
-
puts ActiveRecord::Base.connection.execute("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
245
|
-
|
279
|
+
# Only for Postgres? (Doesn't work in sqlite3)
|
280
|
+
# puts ActiveRecord::Base.connection.execute("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
281
|
+
|
282
|
+
case ActiveRecord::Base.connection.adapter_name
|
283
|
+
when 'PostgreSQL'
|
284
|
+
schema = 'public'
|
285
|
+
when 'Mysql2'
|
286
|
+
schema = ActiveRecord::Base.connection.current_database
|
287
|
+
when 'SQLite'
|
288
|
+
sql = "SELECT m.name AS relation_name, UPPER(m.type) AS table_type,
|
289
|
+
p.name AS column_name, p.type AS data_type,
|
290
|
+
CASE p.pk WHEN 1 THEN 'PRIMARY KEY' END AS const
|
291
|
+
FROM sqlite_master AS m
|
292
|
+
INNER JOIN pragma_table_info(m.name) AS p
|
293
|
+
WHERE m.name NOT IN ('ar_internal_metadata', 'schema_migrations')
|
294
|
+
ORDER BY m.name, p.cid"
|
295
|
+
else
|
296
|
+
puts "Unfamiliar with connection adapter #{ActiveRecord::Base.connection.adapter_name}"
|
297
|
+
end
|
298
|
+
|
299
|
+
sql ||= ActiveRecord::Base.send(:sanitize_sql_array, [
|
246
300
|
"SELECT t.table_name AS relation_name, t.table_type,
|
247
301
|
c.column_name, c.data_type,
|
248
302
|
COALESCE(c.character_maximum_length, c.numeric_precision) AS max_length,
|
249
|
-
tc.constraint_type AS const, kcu.constraint_name AS key
|
303
|
+
tc.constraint_type AS const, kcu.constraint_name AS \"key\"
|
250
304
|
FROM INFORMATION_SCHEMA.tables AS t
|
251
305
|
LEFT OUTER JOIN INFORMATION_SCHEMA.columns AS c ON t.table_schema = c.table_schema
|
252
306
|
AND t.table_name = c.table_name
|
@@ -258,66 +312,92 @@ module ActiveRecord::ConnectionHandling
|
|
258
312
|
AND kcu.ordinal_position = c.ordinal_position
|
259
313
|
LEFT OUTER JOIN INFORMATION_SCHEMA.table_constraints AS tc
|
260
314
|
ON kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
|
315
|
+
AND kcu.TABLE_NAME = tc.TABLE_NAME
|
261
316
|
AND kcu.CONSTRAINT_NAME = tc.constraint_name
|
262
317
|
WHERE t.table_schema = ? -- COALESCE(current_setting('SEARCH_PATH'), 'public')
|
263
318
|
-- AND t.table_type IN ('VIEW') -- 'BASE TABLE', 'FOREIGN TABLE'
|
264
319
|
AND t.table_name NOT IN ('pg_stat_statements', 'ar_internal_metadata', 'schema_migrations')
|
265
320
|
ORDER BY 1, t.table_type DESC, c.ordinal_position", schema
|
266
321
|
])
|
267
|
-
ActiveRecord::Base.connection.execute(sql).each do |r|
|
268
|
-
# next if internal_views.include?(r['relation_name']) # Skip internal views such as v_all_assessments
|
269
|
-
|
270
|
-
relation = relations[r['relation_name']]
|
271
|
-
relation[:index] = r['relation_name'].underscore
|
272
|
-
relation[:show] = relation[:index].singularize
|
273
|
-
relation[:index] = relation[:index].pluralize
|
274
|
-
relation[:isView] = true if r['table_type'] == 'VIEW'
|
275
|
-
col_name = r['column_name']
|
276
|
-
cols = relation[:cols] # relation.fetch(:cols) { relation[:cols] = [] }
|
277
|
-
key = case r['const']
|
278
|
-
when 'PRIMARY KEY'
|
279
|
-
relation[:pkey][r['key']] ||= []
|
280
|
-
when 'UNIQUE'
|
281
|
-
relation[:ukeys][r['key']] ||= []
|
282
|
-
# key = (relation[:ukeys] = Hash.new { |h, k| h[k] = [] }) if key.is_a?(Array)
|
283
|
-
# key[r['key']]
|
284
|
-
end
|
285
|
-
key << col_name if key
|
286
|
-
cols[col_name] = [r['data_type'], r['max_length'], r['measures']&.include?(col_name)]
|
287
|
-
# puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
|
288
|
-
end
|
289
322
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
fks = tbl[:fks].each_with_object({}) { |fk, s| s[fk.last[:fk]] = fk.last[:inverse_table] if fk.last[:is_bt]; s }
|
311
|
-
# Aside from the primary key and created_at, updated_at,This table has only foreign keys?
|
312
|
-
if fks.length > 1 && (tbl_cols - fks.keys - ['created_at', 'updated_at', 'deleted_at', 'last_update'] - tbl[:pkey].values.first).length.zero?
|
313
|
-
fks.each { |fk| tbl[:hmt_fks][fk.first] = fk.last }
|
323
|
+
measures = []
|
324
|
+
case ActiveRecord::Base.connection.adapter_name
|
325
|
+
when 'PostgreSQL', 'SQLite' # These bring back a hash for each row because the query uses column aliases
|
326
|
+
ActiveRecord::Base.connection.execute(sql).each do |r|
|
327
|
+
# next if internal_views.include?(r['relation_name']) # Skip internal views such as v_all_assessments
|
328
|
+
relation = relations[(relation_name = r['relation_name'])]
|
329
|
+
relation[:isView] = true if r['table_type'] == 'VIEW'
|
330
|
+
col_name = r['column_name']
|
331
|
+
key = case r['const']
|
332
|
+
when 'PRIMARY KEY'
|
333
|
+
relation[:pkey][r['key'] || relation_name] ||= []
|
334
|
+
when 'UNIQUE'
|
335
|
+
relation[:ukeys][r['key'] || "#{relation_name}.#{col_name}"] ||= []
|
336
|
+
# key = (relation[:ukeys] = Hash.new { |h, k| h[k] = [] }) if key.is_a?(Array)
|
337
|
+
# key[r['key']]
|
338
|
+
end
|
339
|
+
key << col_name if key
|
340
|
+
cols = relation[:cols] # relation.fetch(:cols) { relation[:cols] = [] }
|
341
|
+
cols[col_name] = [r['data_type'], r['max_length'], measures&.include?(col_name)]
|
342
|
+
# puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
|
314
343
|
end
|
344
|
+
else # MySQL2 acts a little differently, bringing back an array for each row
|
345
|
+
ActiveRecord::Base.connection.execute(sql).each do |r|
|
346
|
+
# next if internal_views.include?(r['relation_name']) # Skip internal views such as v_all_assessments
|
347
|
+
relation = relations[(relation_name = r[0])] # here relation represents a table or view from the database
|
348
|
+
relation[:isView] = true if r[1] == 'VIEW' # table_type
|
349
|
+
col_name = r[2]
|
350
|
+
key = case r[5] # constraint type
|
351
|
+
when 'PRIMARY KEY'
|
352
|
+
# key
|
353
|
+
relation[:pkey][r[6] || relation_name] ||= []
|
354
|
+
when 'UNIQUE'
|
355
|
+
relation[:ukeys][r[6] || "#{relation_name}.#{col_name}"] ||= []
|
356
|
+
# key = (relation[:ukeys] = Hash.new { |h, k| h[k] = [] }) if key.is_a?(Array)
|
357
|
+
# key[r['key']]
|
358
|
+
end
|
359
|
+
key << col_name if key
|
360
|
+
cols = relation[:cols] # relation.fetch(:cols) { relation[:cols] = [] }
|
361
|
+
# 'data_type', 'max_length'
|
362
|
+
cols[col_name] = [r[3], r[4], measures&.include?(col_name)]
|
363
|
+
# puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
case ActiveRecord::Base.connection.adapter_name
|
368
|
+
when 'PostgreSQL', 'Mysql2'
|
369
|
+
sql = ActiveRecord::Base.send(:sanitize_sql_array, [
|
370
|
+
"SELECT kcu1.TABLE_NAME, kcu1.COLUMN_NAME, kcu2.TABLE_NAME, kcu1.CONSTRAINT_NAME
|
371
|
+
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS rc
|
372
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu1
|
373
|
+
ON kcu1.CONSTRAINT_CATALOG = rc.CONSTRAINT_CATALOG
|
374
|
+
AND kcu1.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA
|
375
|
+
AND kcu1.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
|
376
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu2
|
377
|
+
ON kcu2.CONSTRAINT_CATALOG = rc.UNIQUE_CONSTRAINT_CATALOG
|
378
|
+
AND kcu2.CONSTRAINT_SCHEMA = rc.UNIQUE_CONSTRAINT_SCHEMA
|
379
|
+
AND kcu2.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
|
380
|
+
AND kcu2.ORDINAL_POSITION = kcu1.ORDINAL_POSITION
|
381
|
+
WHERE kcu1.CONSTRAINT_SCHEMA = ? -- COALESCE(current_setting('SEARCH_PATH'), 'public')", schema
|
382
|
+
# AND kcu2.TABLE_NAME = ?;", Apartment::Tenant.current, table_name
|
383
|
+
])
|
384
|
+
when 'SQLite'
|
385
|
+
sql = "SELECT m.name, fkl.\"from\", fkl.\"table\", m.name || '_' || fkl.\"from\" AS constraint_name
|
386
|
+
FROM sqlite_master m
|
387
|
+
INNER JOIN pragma_foreign_key_list(m.name) fkl ON m.type = 'table'
|
388
|
+
ORDER BY m.name, fkl.seq"
|
389
|
+
else
|
390
|
+
end
|
391
|
+
if sql
|
392
|
+
result = ActiveRecord::Base.connection.execute(sql)
|
393
|
+
result = result.values unless result.is_a?(Array)
|
394
|
+
result.each { |fk| ::Brick._add_bt_and_hm(fk, relations) }
|
315
395
|
end
|
316
396
|
end
|
317
397
|
|
318
|
-
puts "Classes built from tables:"
|
398
|
+
puts "Classes that can be built from tables:"
|
319
399
|
relations.select { |_k, v| !v.key?(:isView) }.keys.each { |k| puts ActiveSupport::Inflector.singularize(k).camelize }
|
320
|
-
puts "Classes built from views:"
|
400
|
+
puts "Classes that can be built from views:"
|
321
401
|
relations.select { |_k, v| v.key?(:isView) }.keys.each { |k| puts ActiveSupport::Inflector.singularize(k).camelize }
|
322
402
|
# pp relations; nil
|
323
403
|
|
@@ -5,67 +5,219 @@ module Brick
|
|
5
5
|
# See http://guides.rubyonrails.org/engines.html
|
6
6
|
class Engine < ::Rails::Engine
|
7
7
|
# paths['app/models'] << 'lib/brick/frameworks/active_record/models'
|
8
|
+
puts "BEFORE - engine set config"
|
8
9
|
config.brick = ActiveSupport::OrderedOptions.new
|
9
|
-
initializer 'brick.initialisation' do |app|
|
10
|
-
|
11
|
-
|
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)
|
12
18
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
+
)
|
29
48
|
)
|
30
|
-
|
31
|
-
|
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.first\}-\" unless bt[1].name.underscore == bt.first.to_s %><%= 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
|
+
#{"<hr><%= link_to \"New #{obj_name}\", new_#{obj_name}_path %>" unless @_brick_model.is_view?}
|
163
|
+
"
|
164
|
+
when 'show'
|
165
|
+
"<%= @#{@_brick_model.name.underscore}.inspect %>"
|
166
|
+
end
|
167
|
+
# As if it were an inline template (see #determine_template in actionview-5.2.6.2/lib/action_view/renderer/template_renderer.rb)
|
168
|
+
keys = options.has_key?(:locals) ? options[:locals].keys : []
|
169
|
+
handler = ActionView::Template.handler_for_extension(options[:type] || 'erb')
|
170
|
+
ActionView::Template.new(inline, "auto-generated #{args.first} template", handler, locals: keys)
|
171
|
+
else
|
172
|
+
_brick_find_template(*args, **options)
|
32
173
|
end
|
33
174
|
end
|
34
|
-
is_template_exists
|
35
175
|
end
|
176
|
+
end
|
36
177
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
178
|
+
if ::Brick.enable_routes?
|
179
|
+
ActionDispatch::Routing::RouteSet.class_exec do
|
180
|
+
alias _brick_finalize_routeset! finalize!
|
181
|
+
def finalize!(*args, **options)
|
182
|
+
unless @finalized
|
183
|
+
existing_controllers = routes.each_with_object({}) { |r, s| c = r.defaults[:controller]; s[c] = nil if c }
|
184
|
+
::Rails.application.routes.append do
|
185
|
+
# %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
|
186
|
+
# If auto-controllers and auto-models are both enabled then this makes sense:
|
187
|
+
relations = (::Brick.instance_variable_get(:@relations) || {})[ActiveRecord::Base.connection_pool.object_id] || {}
|
188
|
+
relations.each do |k, v|
|
189
|
+
unless existing_controllers.key?(controller_name = k.underscore.pluralize)
|
190
|
+
options = {}
|
191
|
+
options[:only] = [:index, :show] if v.key?(:isView)
|
192
|
+
send(:resources, controller_name.to_sym, **options)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
46
196
|
end
|
47
|
-
|
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)
|
197
|
+
_brick_finalize_routeset!(*args, **options)
|
53
198
|
end
|
54
199
|
end
|
55
200
|
end
|
56
|
-
end
|
57
201
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
202
|
+
# Additional references (virtual foreign keys)
|
203
|
+
if (ars = ::Brick.config.additional_references)
|
204
|
+
ars = ars.call if ars.is_a?(Proc)
|
205
|
+
ars = ars.to_a unless ars.is_a?(Array)
|
206
|
+
ars = [ars] unless ars.empty? || ars.first.is_a?(Array)
|
207
|
+
ars.each do |fk|
|
208
|
+
::Brick._add_bt_and_hm(fk[0..2])
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# Find associative tables that can be set up for has_many :through
|
213
|
+
::Brick.relations.each do |_key, tbl|
|
214
|
+
tbl_cols = tbl[:cols].keys
|
215
|
+
fks = tbl[:fks].each_with_object({}) { |fk, s| s[fk.last[:fk]] = fk.last[:inverse_table] if fk.last[:is_bt]; s }
|
216
|
+
# Aside from the primary key and the metadata columns created_at, updated_at, and deleted_at, if this table only has
|
217
|
+
# foreign keys then it can act as an associative table and thus be used with has_many :through.
|
218
|
+
if fks.length > 1 && (tbl_cols - fks.keys - (::Brick.config.metadata_columns || []) - tbl[:pkey].values.first).length.zero?
|
219
|
+
fks.each { |fk| tbl[:hmt_fks][fk.first] = fk.last }
|
220
|
+
end
|
69
221
|
end
|
70
222
|
end
|
71
223
|
end
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -76,20 +76,12 @@ end
|
|
76
76
|
# is first established), and then automatically creates models, controllers, views,
|
77
77
|
# and routes based on those available relations.
|
78
78
|
require 'brick/config'
|
79
|
-
|
79
|
+
if Gem::Specification.all_names.any? { |g| g.start_with?('rails-') }
|
80
|
+
require 'rails'
|
81
|
+
require 'brick/frameworks/rails'
|
82
|
+
end
|
80
83
|
module Brick
|
81
84
|
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
|
91
|
-
end
|
92
|
-
|
93
85
|
# All tables and views (what Postgres calls "relations" including column and foreign key info)
|
94
86
|
def relations
|
95
87
|
connections = Brick.instance_variable_get(:@relations) ||
|
@@ -150,6 +142,21 @@ module Brick
|
|
150
142
|
!!Brick.config.enable_routes
|
151
143
|
end
|
152
144
|
|
145
|
+
# @api public
|
146
|
+
def skip_database_views=(value)
|
147
|
+
Brick.config.skip_database_views = value
|
148
|
+
end
|
149
|
+
|
150
|
+
# @api public
|
151
|
+
def exclude_tables=(value)
|
152
|
+
Brick.config.exclude_tables = value
|
153
|
+
end
|
154
|
+
|
155
|
+
# @api public
|
156
|
+
def metadata_columns=(value)
|
157
|
+
Brick.config.metadata_columns = value
|
158
|
+
end
|
159
|
+
|
153
160
|
# Additional table associations to use (Think of these as virtual foreign keys perhaps)
|
154
161
|
# @api public
|
155
162
|
def additional_references=(value)
|
@@ -193,10 +200,7 @@ module Brick
|
|
193
200
|
end
|
194
201
|
end
|
195
202
|
|
196
|
-
require 'brick/extensions'
|
197
203
|
require 'brick/version_number'
|
198
|
-
# require 'brick/serializers/json'
|
199
|
-
# require 'brick/serializers/yaml'
|
200
204
|
|
201
205
|
require 'active_record'
|
202
206
|
# Major compatibility fixes for ActiveRecord < 4.2
|
@@ -422,18 +426,18 @@ ActiveSupport.on_load(:active_record) do
|
|
422
426
|
end
|
423
427
|
end
|
424
428
|
|
425
|
-
include ::Brick::Extensions
|
426
|
-
|
427
|
-
unless ::Brick::Extensions::IS_AMOEBA
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
end
|
429
|
+
# include ::Brick::Extensions
|
430
|
+
|
431
|
+
# unless ::Brick::Extensions::IS_AMOEBA
|
432
|
+
# # Add amoeba-compatible support
|
433
|
+
# module ActiveRecord
|
434
|
+
# class Base
|
435
|
+
# def self.amoeba(*args)
|
436
|
+
# puts "Amoeba called from #{name} with #{args.inspect}"
|
437
|
+
# end
|
438
|
+
# end
|
439
|
+
# end
|
440
|
+
# end
|
437
441
|
end
|
438
442
|
|
439
443
|
# Do this earlier because stuff here gets mixed into JoinDependency::JoinAssociation and AssociationScope
|
@@ -522,3 +526,5 @@ if ActiveRecord.version < ::Gem::Version.new('5.2')
|
|
522
526
|
end # module ActiveRecord
|
523
527
|
# rubocop:enable Style/CommentedKeyword
|
524
528
|
end
|
529
|
+
|
530
|
+
require 'brick/extensions'
|
@@ -1,12 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'rails/generators'
|
4
|
-
require 'rails/generators/active_record'
|
4
|
+
# require 'rails/generators/active_record'
|
5
5
|
|
6
6
|
module Brick
|
7
|
-
# Auto-generates an IMPORT_TEMPLATE entry for one or more models
|
8
7
|
class InstallGenerator < ::Rails::Generators::Base
|
9
|
-
include ::Rails::Generators::Migration
|
8
|
+
# include ::Rails::Generators::Migration
|
10
9
|
|
11
10
|
source_root File.expand_path('templates', __dir__)
|
12
11
|
class_option(
|
@@ -16,81 +15,128 @@ module Brick
|
|
16
15
|
desc: 'Store changeset (diff) with each version'
|
17
16
|
)
|
18
17
|
|
19
|
-
desc 'Generates
|
20
|
-
' Also generates an initializer file for configuring Brick'
|
18
|
+
desc 'Generates an initializer file for configuring Brick'
|
21
19
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
end
|
20
|
+
def create_initializer_file
|
21
|
+
unless File.exists?(filename = 'config/initializers/brick.rb')
|
22
|
+
create_file filename, "# frozen_string_literal: true
|
26
23
|
|
27
|
-
|
28
|
-
|
29
|
-
end
|
24
|
+
# # Settings for the Brick gem
|
25
|
+
# # (By default this auto-creates models, controllers, views, and routes on-the-fly.)
|
30
26
|
|
31
|
-
|
27
|
+
# # Normally these all start out as being enabled, but can be selectively disabled:
|
28
|
+
# Brick.enable_routes = false
|
29
|
+
# Brick.enable_models = false
|
30
|
+
# Brick.enable_controllers = false
|
31
|
+
# Brick.enable_views = false
|
32
|
+
|
33
|
+
# # By default models are auto-created from database views, and set to be read-only. This can be skipped.
|
34
|
+
# Brick.skip_database_views = true
|
35
|
+
|
36
|
+
# # Any tables or views you'd like to skip when auto-creating models
|
37
|
+
# Brick.exclude_tables = ['custom_metadata', 'version_info']
|
32
38
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
# # Additional table references which are used to create has_many / belongs_to associations inside auto-created
|
40
|
+
# # models. (You can consider these to be \"virtual foreign keys\" if you wish)... You only have to add these
|
41
|
+
# # in cases where your database for some reason does not have foreign key constraints defined. Sometimes for
|
42
|
+
# # performance reasons or just out of sheer laziness these might be missing.
|
43
|
+
# # Each of these virtual foreign keys is defined as an array having three values:
|
44
|
+
# # foreign table name / foreign key column / primary table name.
|
45
|
+
# # (We boldly expect that the primary key identified by ActiveRecord on the primary table will be accurate,
|
46
|
+
# # usually this is \"id\" but there are some good smarts that are used in case some other column has been set
|
47
|
+
# # to be the primary key.
|
48
|
+
# Brick.additional_references = [['orders', 'customer_id', 'customer'],
|
49
|
+
# ['customer', 'region_id', 'regions']]
|
50
|
+
|
51
|
+
# # We normally don't consider the timestamp columns \"created_at\", \"updated_at\", and \"deleted_at\" to count when
|
52
|
+
# # finding tables which can serve as associative tables in an N:M association. That is, ones that can be a
|
53
|
+
# # part of a has_many :through association. If you want to use different exclusion columns than our defaults
|
54
|
+
# # then this setting resets that list. For instance, here is the override for the Sakila sample database:
|
55
|
+
# Brick.metadata_columns = ['last_updated']
|
56
|
+
|
57
|
+
# # If a default route is not supplied, Brick attempts to find the most \"central\" table and wires up the default
|
58
|
+
# # route to go to the :index action for what would be a controller for that table. You can specify any controller
|
59
|
+
# # name and action you wish in order to override this and have that be the default route when none other has been
|
60
|
+
# # specified in routes.rb or elsewhere. (Or just use an empty string in order to disable this behaviour.)
|
61
|
+
# Brick.default_route_fallback = 'customers' # This defaults to \"customers/index\"
|
62
|
+
# Brick.default_route_fallback = 'orders/outstanding' # Example of a non-RESTful route
|
63
|
+
# Brick.default_route_fallback = '' # Omits setting a default route in the absence of any other
|
64
|
+
"
|
45
65
|
end
|
46
66
|
end
|
47
67
|
|
68
|
+
# def create_migration_file
|
69
|
+
# add_brick_migration('create_versions')
|
70
|
+
# add_brick_migration('add_object_changes_to_versions') if options.with_changes?
|
71
|
+
# end
|
72
|
+
|
73
|
+
# def self.next_migration_number(dirname)
|
74
|
+
# ::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
75
|
+
# end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
# def add_brick_migration(template)
|
80
|
+
# migration_dir = File.expand_path('db/migrate')
|
81
|
+
# if self.class.migration_exists?(migration_dir, template)
|
82
|
+
# ::Kernel.warn "Migration already exists: #{template}"
|
83
|
+
# else
|
84
|
+
# migration_template(
|
85
|
+
# "#{template}.rb.erb",
|
86
|
+
# "db/migrate/#{template}.rb",
|
87
|
+
# item_type_options: item_type_options,
|
88
|
+
# migration_version: migration_version,
|
89
|
+
# versions_table_options: versions_table_options
|
90
|
+
# )
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
|
48
94
|
private
|
49
95
|
|
50
|
-
# MySQL 5.6 utf8mb4 limit is 191 chars for keys used in indexes.
|
51
|
-
def item_type_options
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
96
|
+
# # MySQL 5.6 utf8mb4 limit is 191 chars for keys used in indexes.
|
97
|
+
# def item_type_options
|
98
|
+
# opt = { null: false }
|
99
|
+
# opt[:limit] = 191 if mysql?
|
100
|
+
# ", #{opt}"
|
101
|
+
# end
|
56
102
|
|
57
|
-
def migration_version
|
58
|
-
|
103
|
+
# def migration_version
|
104
|
+
# return unless (major = ActiveRecord::VERSION::MAJOR) >= 5
|
59
105
|
|
60
|
-
|
61
|
-
end
|
106
|
+
# "[#{major}.#{ActiveRecord::VERSION::MINOR}]"
|
107
|
+
# end
|
62
108
|
|
63
|
-
# Class names of MySQL adapters.
|
64
|
-
# - `MysqlAdapter` - Used by gems: `mysql`, `activerecord-jdbcmysql-adapter`.
|
65
|
-
# - `Mysql2Adapter` - Used by `mysql2` gem.
|
66
|
-
def mysql?
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
109
|
+
# # Class names of MySQL adapters.
|
110
|
+
# # - `MysqlAdapter` - Used by gems: `mysql`, `activerecord-jdbcmysql-adapter`.
|
111
|
+
# # - `Mysql2Adapter` - Used by `mysql2` gem.
|
112
|
+
# def mysql?
|
113
|
+
# [
|
114
|
+
# 'ActiveRecord::ConnectionAdapters::MysqlAdapter',
|
115
|
+
# 'ActiveRecord::ConnectionAdapters::Mysql2Adapter'
|
116
|
+
# ].freeze.include?(ActiveRecord::Base.connection.class.name)
|
117
|
+
# end
|
72
118
|
|
73
|
-
# Even modern versions of MySQL still use `latin1` as the default character
|
74
|
-
# encoding. Many users are not aware of this, and run into trouble when they
|
75
|
-
# try to use Brick in apps that otherwise tend to use UTF-8. Postgres, by
|
76
|
-
# comparison, uses UTF-8 except in the unusual case where the OS is configured
|
77
|
-
# with a custom locale.
|
78
|
-
#
|
79
|
-
# - https://dev.mysql.com/doc/refman/5.7/en/charset-applications.html
|
80
|
-
# - http://www.postgresql.org/docs/9.4/static/multibyte.html
|
81
|
-
#
|
82
|
-
# Furthermore, MySQL's original implementation of UTF-8 was flawed, and had
|
83
|
-
# to be fixed later by introducing a new charset, `utf8mb4`.
|
84
|
-
#
|
85
|
-
# - https://mathiasbynens.be/notes/mysql-utf8mb4
|
86
|
-
# - https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
|
87
|
-
#
|
88
|
-
def versions_table_options
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
119
|
+
# # Even modern versions of MySQL still use `latin1` as the default character
|
120
|
+
# # encoding. Many users are not aware of this, and run into trouble when they
|
121
|
+
# # try to use Brick in apps that otherwise tend to use UTF-8. Postgres, by
|
122
|
+
# # comparison, uses UTF-8 except in the unusual case where the OS is configured
|
123
|
+
# # with a custom locale.
|
124
|
+
# #
|
125
|
+
# # - https://dev.mysql.com/doc/refman/5.7/en/charset-applications.html
|
126
|
+
# # - http://www.postgresql.org/docs/9.4/static/multibyte.html
|
127
|
+
# #
|
128
|
+
# # Furthermore, MySQL's original implementation of UTF-8 was flawed, and had
|
129
|
+
# # to be fixed later by introducing a new charset, `utf8mb4`.
|
130
|
+
# #
|
131
|
+
# # - https://mathiasbynens.be/notes/mysql-utf8mb4
|
132
|
+
# # - https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
|
133
|
+
# #
|
134
|
+
# def versions_table_options
|
135
|
+
# if mysql?
|
136
|
+
# ', { options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }'
|
137
|
+
# else
|
138
|
+
# ''
|
139
|
+
# end
|
140
|
+
# end
|
95
141
|
end
|
96
142
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
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-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -156,6 +156,20 @@ dependencies:
|
|
156
156
|
- - "~>"
|
157
157
|
- !ruby/object:Gem::Version
|
158
158
|
version: 1.42.0
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
name: mysql2
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - "~>"
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0.5'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - "~>"
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '0.5'
|
159
173
|
- !ruby/object:Gem::Dependency
|
160
174
|
name: pg
|
161
175
|
requirement: !ruby/object:Gem::Requirement
|