brick 0.1.0 → 1.0.2
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 +7 -0
- data/lib/brick/config.rb +92 -0
- data/lib/brick/extensions.rb +432 -0
- data/lib/brick/frameworks/cucumber.rb +39 -0
- data/lib/brick/frameworks/rails/controller.rb +41 -0
- data/lib/brick/frameworks/rails/engine.rb +211 -0
- data/lib/brick/frameworks/rails.rb +4 -0
- data/lib/brick/frameworks/rspec.rb +18 -0
- data/lib/brick/serializers/json.rb +36 -0
- data/lib/brick/serializers/yaml.rb +26 -0
- data/lib/brick/util.rb +123 -0
- data/lib/brick/version_number.rb +19 -0
- data/lib/brick.rb +528 -0
- data/lib/generators/brick/USAGE +2 -0
- data/lib/generators/brick/install_generator.rb +142 -0
- data/lib/generators/brick/model_generator.rb +117 -0
- data/lib/generators/brick/templates/add_object_changes_to_versions.rb.erb +12 -0
- data/lib/generators/brick/templates/create_versions.rb.erb +36 -0
- metadata +195 -48
- data/bin/brick +0 -15
data/lib/brick.rb
ADDED
@@ -0,0 +1,528 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record/version'
|
4
|
+
|
5
|
+
# ActiveRecord before 4.0 didn't have #version
|
6
|
+
unless ActiveRecord.respond_to?(:version)
|
7
|
+
module ActiveRecord
|
8
|
+
def self.version
|
9
|
+
::Gem::Version.new(ActiveRecord::VERSION::STRING)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# In ActiveSupport older than 5.0, the duplicable? test tries to new up a BigDecimal,
|
15
|
+
# and Ruby 2.6 and later deprecates #new. This removes the warning from BigDecimal.
|
16
|
+
require 'bigdecimal'
|
17
|
+
if (ruby_version = ::Gem::Version.new(RUBY_VERSION)) >= ::Gem::Version.new('2.6') &&
|
18
|
+
ActiveRecord.version < ::Gem::Version.new('5.0')
|
19
|
+
def BigDecimal.new(*args, **kwargs)
|
20
|
+
BigDecimal(*args, **kwargs)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Allow ActiveRecord 4.0 and 4.1 to work with newer Ruby (>= 2.4) by avoiding a "stack level too deep"
|
25
|
+
# error when ActiveSupport tries to smarten up Numeric by messing with Fixnum and Bignum at the end of:
|
26
|
+
# activesupport-4.0.13/lib/active_support/core_ext/numeric/conversions.rb
|
27
|
+
if ActiveRecord.version < ::Gem::Version.new('4.2') &&
|
28
|
+
ActiveRecord.version > ::Gem::Version.new('3.2') &&
|
29
|
+
Object.const_defined?('Integer') && Integer.superclass.name == 'Numeric'
|
30
|
+
class OurFixnum < Integer; end
|
31
|
+
Numeric.const_set('Fixnum', OurFixnum)
|
32
|
+
class OurBignum < Integer; end
|
33
|
+
Numeric.const_set('Bignum', OurBignum)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Allow ActiveRecord < 3.2 to run with newer versions of Psych gem
|
37
|
+
if BigDecimal.respond_to?(:yaml_tag) && !BigDecimal.respond_to?(:yaml_as)
|
38
|
+
class BigDecimal
|
39
|
+
class <<self
|
40
|
+
alias yaml_as yaml_tag
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
require 'brick/util'
|
46
|
+
|
47
|
+
# Allow ActiveRecord < 3.2 to work with Ruby 2.7 and later
|
48
|
+
if ActiveRecord.version < ::Gem::Version.new('3.2') &&
|
49
|
+
ruby_version >= ::Gem::Version.new('2.7')
|
50
|
+
# Remove circular reference for "now"
|
51
|
+
::Brick::Util._patch_require(
|
52
|
+
'active_support/values/time_zone.rb', '/activesupport',
|
53
|
+
' def parse(str, now=now)',
|
54
|
+
' def parse(str, now=now())'
|
55
|
+
)
|
56
|
+
# Remove circular reference for "reflection" for ActiveRecord 3.1
|
57
|
+
if ActiveRecord.version >= ::Gem::Version.new('3.1')
|
58
|
+
::Brick::Util._patch_require(
|
59
|
+
'active_record/associations/has_many_association.rb', '/activerecord',
|
60
|
+
'reflection = reflection)',
|
61
|
+
'reflection = reflection())',
|
62
|
+
:HasManyAssociation # Make sure the path for this guy is available to be autoloaded
|
63
|
+
)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# puts ::Brick::Util._patch_require(
|
68
|
+
# 'cucumber/cli/options.rb', '/cucumber/cli/options', # /cli/options
|
69
|
+
# ' def extract_environment_variables',
|
70
|
+
# " def extract_environment_variables\n
|
71
|
+
# puts 'Patch test!'"
|
72
|
+
# ).inspect
|
73
|
+
|
74
|
+
# An ActiveRecord extension that uses INFORMATION_SCHEMA views to reflect on all
|
75
|
+
# tables and views in the database (just once at the time the database connection
|
76
|
+
# is first established), and then automatically creates models, controllers, views,
|
77
|
+
# and routes based on those available relations.
|
78
|
+
require 'brick/config'
|
79
|
+
require 'brick/frameworks/rails'
|
80
|
+
module Brick
|
81
|
+
class << self
|
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
|
127
|
+
end
|
128
|
+
|
129
|
+
# Switches Brick auto-routes on or off, for all threads
|
130
|
+
# @api public
|
131
|
+
def enable_routes=(value)
|
132
|
+
Brick.config.enable_routes = value
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns `true` if Brick routes are on, `false` otherwise. This affects all
|
136
|
+
# threads. Enabled by default.
|
137
|
+
# @api public
|
138
|
+
def enable_routes?
|
139
|
+
!!Brick.config.enable_routes
|
140
|
+
end
|
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
|
+
|
164
|
+
# Returns Brick's `::Gem::Version`, convenient for comparisons. This is
|
165
|
+
# recommended over `::Brick::VERSION::STRING`.
|
166
|
+
#
|
167
|
+
# @api public
|
168
|
+
def gem_version
|
169
|
+
::Gem::Version.new(VERSION::STRING)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Set the Brick serializer. This setting affects all threads.
|
173
|
+
# @api public
|
174
|
+
def serializer=(value)
|
175
|
+
Brick.config.serializer = value
|
176
|
+
end
|
177
|
+
|
178
|
+
# Get the Brick serializer used by all threads.
|
179
|
+
# @api public
|
180
|
+
def serializer
|
181
|
+
Brick.config.serializer
|
182
|
+
end
|
183
|
+
|
184
|
+
# Returns Brick's global configuration object, a singleton. These
|
185
|
+
# settings affect all threads.
|
186
|
+
# @api private
|
187
|
+
def config
|
188
|
+
@config ||= Brick::Config.instance
|
189
|
+
yield @config if block_given?
|
190
|
+
@config
|
191
|
+
end
|
192
|
+
alias configure config
|
193
|
+
|
194
|
+
def version
|
195
|
+
VERSION::STRING
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
require 'brick/extensions'
|
201
|
+
require 'brick/version_number'
|
202
|
+
# require 'brick/serializers/json'
|
203
|
+
# require 'brick/serializers/yaml'
|
204
|
+
|
205
|
+
require 'active_record'
|
206
|
+
# Major compatibility fixes for ActiveRecord < 4.2
|
207
|
+
# ================================================
|
208
|
+
ActiveSupport.on_load(:active_record) do
|
209
|
+
# rubocop:disable Lint/ConstantDefinitionInBlock
|
210
|
+
module ActiveRecord
|
211
|
+
class Base
|
212
|
+
unless respond_to?(:execute_sql)
|
213
|
+
class << self
|
214
|
+
def execute_sql(sql, *param_array)
|
215
|
+
param_array = param_array.first if param_array.length == 1 && param_array.first.is_a?(Array)
|
216
|
+
connection.execute(send(:sanitize_sql_array, [sql] + param_array))
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# Rails < 4.0 cannot do #find_by, #find_or_create_by, or do #pluck on multiple columns, so here are the patches:
|
223
|
+
if version < ::Gem::Version.new('4.0')
|
224
|
+
# Normally find_by is in FinderMethods, which older AR doesn't have
|
225
|
+
module Calculations
|
226
|
+
def find_by(*args)
|
227
|
+
where(*args).limit(1).to_a.first
|
228
|
+
end
|
229
|
+
|
230
|
+
def find_or_create_by(attributes, &block)
|
231
|
+
find_by(attributes) || create(attributes, &block)
|
232
|
+
end
|
233
|
+
|
234
|
+
def pluck(*column_names)
|
235
|
+
column_names.map! do |column_name|
|
236
|
+
if column_name.is_a?(Symbol) && self.column_names.include?(column_name.to_s)
|
237
|
+
"#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
|
238
|
+
else
|
239
|
+
column_name
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# Same as: if has_include?(column_names.first)
|
244
|
+
if eager_loading? || (includes_values.present? && (column_names.first || references_eager_loaded_tables?))
|
245
|
+
construct_relation_for_association_calculations.pluck(*column_names)
|
246
|
+
else
|
247
|
+
relation = clone # spawn
|
248
|
+
relation.select_values = column_names
|
249
|
+
result = if klass.connection.class.name.end_with?('::PostgreSQLAdapter')
|
250
|
+
rslt = klass.connection.execute(relation.arel.to_sql)
|
251
|
+
rslt.type_map =
|
252
|
+
@type_map ||= proc do
|
253
|
+
# This aliasing avoids the warning:
|
254
|
+
# "no type cast defined for type "numeric" with oid 1700. Please cast this type
|
255
|
+
# explicitly to TEXT to be safe for future changes."
|
256
|
+
PG::BasicTypeRegistry.alias_type(0, 'numeric', 'text') # oid 1700
|
257
|
+
PG::BasicTypeRegistry.alias_type(0, 'time', 'text') # oid 1083
|
258
|
+
PG::BasicTypeMapForResults.new(klass.connection.raw_connection)
|
259
|
+
end.call
|
260
|
+
rslt.to_a
|
261
|
+
elsif respond_to?(:bind_values)
|
262
|
+
klass.connection.select_all(relation.arel, nil, bind_values)
|
263
|
+
else
|
264
|
+
klass.connection.select_all(relation.arel.to_sql, nil)
|
265
|
+
end
|
266
|
+
if result.empty?
|
267
|
+
[]
|
268
|
+
else
|
269
|
+
columns = result.first.keys.map do |key|
|
270
|
+
# rubocop:disable Style/SingleLineMethods Naming/MethodParameterName
|
271
|
+
klass.columns_hash.fetch(key) do
|
272
|
+
Class.new { def type_cast(v); v; end }.new
|
273
|
+
end
|
274
|
+
# rubocop:enable Style/SingleLineMethods Naming/MethodParameterName
|
275
|
+
end
|
276
|
+
|
277
|
+
result = result.map do |attributes|
|
278
|
+
values = klass.initialize_attributes(attributes).values
|
279
|
+
|
280
|
+
columns.zip(values).map do |column, value|
|
281
|
+
column.type_cast(value)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
columns.one? ? result.map!(&:first) : result
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
unless Base.is_a?(Calculations)
|
291
|
+
class Base
|
292
|
+
class << self
|
293
|
+
delegate :pluck, :find_by, :find_or_create_by, to: :scoped
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# ActiveRecord < 3.2 doesn't have initialize_attributes, used by .pluck()
|
299
|
+
unless AttributeMethods.const_defined?('Serialization')
|
300
|
+
class Base
|
301
|
+
class << self
|
302
|
+
def initialize_attributes(attributes, options = {}) #:nodoc:
|
303
|
+
serialized = (options.delete(:serialized) { true }) ? :serialized : :unserialized
|
304
|
+
# super(attributes, options)
|
305
|
+
|
306
|
+
serialized_attributes.each do |key, coder|
|
307
|
+
attributes[key] = Attribute.new(coder, attributes[key], serialized) if attributes.key?(key)
|
308
|
+
end
|
309
|
+
|
310
|
+
attributes
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# This only gets added for ActiveRecord < 3.2
|
317
|
+
module Reflection
|
318
|
+
unless AssociationReflection.instance_methods.include?(:foreign_key)
|
319
|
+
class AssociationReflection < MacroReflection
|
320
|
+
alias foreign_key association_foreign_key
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# ActiveRecord 3.1 and 3.2 didn't try to bring in &block for the .extending() convenience thing
|
326
|
+
# that smartens up scopes, and Ruby 2.7 complained loudly about just doing the magical "Proc.new"
|
327
|
+
# that historically would just capture the incoming block.
|
328
|
+
module QueryMethods
|
329
|
+
unless instance_method(:extending).parameters.include?([:block, :block])
|
330
|
+
# These first two lines used to be:
|
331
|
+
# def extending(*modules)
|
332
|
+
# modules << Module.new(&Proc.new) if block_given?
|
333
|
+
|
334
|
+
def extending(*modules, &block)
|
335
|
+
modules << Module.new(&block) if block_given?
|
336
|
+
|
337
|
+
return self if modules.empty?
|
338
|
+
|
339
|
+
relation = clone
|
340
|
+
relation.send(:apply_modules, modules.flatten)
|
341
|
+
relation
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# Same kind of thing for ActiveRecord::Scoping::Default#default_scope
|
347
|
+
module Scoping
|
348
|
+
module Default
|
349
|
+
module ClassMethods
|
350
|
+
if instance_methods.include?(:default_scope) &&
|
351
|
+
!instance_method(:default_scope).parameters.include?([:block, :block])
|
352
|
+
# Fix for AR 3.2-5.1
|
353
|
+
def default_scope(scope = nil, &block)
|
354
|
+
scope = block if block_given?
|
355
|
+
|
356
|
+
if scope.is_a?(Relation) || !scope.respond_to?(:call)
|
357
|
+
raise ArgumentError,
|
358
|
+
'Support for calling #default_scope without a block is removed. For example instead ' \
|
359
|
+
"of `default_scope where(color: 'red')`, please use " \
|
360
|
+
"`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \
|
361
|
+
'self.default_scope.)'
|
362
|
+
end
|
363
|
+
|
364
|
+
self.default_scopes += [scope]
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
# rubocop:enable Lint/ConstantDefinitionInBlock
|
373
|
+
|
374
|
+
# Rails < 4.2 is not innately compatible with Ruby 2.4 and later, and comes up with:
|
375
|
+
# "TypeError: Cannot visit Integer" unless we patch like this:
|
376
|
+
if ruby_version >= ::Gem::Version.new('2.4') &&
|
377
|
+
Arel::Visitors.const_defined?('DepthFirst') &&
|
378
|
+
!Arel::Visitors::DepthFirst.private_instance_methods.include?(:visit_Integer)
|
379
|
+
module Arel
|
380
|
+
module Visitors
|
381
|
+
class DepthFirst < Visitor
|
382
|
+
alias visit_Integer terminal
|
383
|
+
end
|
384
|
+
|
385
|
+
class Dot < Visitor
|
386
|
+
alias visit_Integer visit_String
|
387
|
+
end
|
388
|
+
|
389
|
+
class ToSql < Visitor
|
390
|
+
private
|
391
|
+
|
392
|
+
# ActiveRecord before v3.2 uses Arel < 3.x, which does not have Arel#literal.
|
393
|
+
unless private_instance_methods.include?(:literal)
|
394
|
+
def literal(obj)
|
395
|
+
obj
|
396
|
+
end
|
397
|
+
end
|
398
|
+
alias visit_Integer literal
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
unless DateTime.instance_methods.include?(:nsec)
|
405
|
+
class DateTime < Date
|
406
|
+
def nsec
|
407
|
+
(sec_fraction * 1_000_000_000).to_i
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
# First part of arel_table_type stuff:
|
413
|
+
# ------------------------------------
|
414
|
+
# (more found below)
|
415
|
+
# was: ActiveRecord.version >= ::Gem::Version.new('3.2') &&
|
416
|
+
if ActiveRecord.version < ::Gem::Version.new('5.0')
|
417
|
+
# Used by Util#_arel_table_type
|
418
|
+
module ActiveRecord
|
419
|
+
class Base
|
420
|
+
def self.arel_table
|
421
|
+
@arel_table ||= Arel::Table.new(table_name, arel_engine).tap do |x|
|
422
|
+
x.instance_variable_set(:@_arel_table_type, self)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
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
|
441
|
+
end
|
442
|
+
|
443
|
+
# Do this earlier because stuff here gets mixed into JoinDependency::JoinAssociation and AssociationScope
|
444
|
+
if ActiveRecord.version < ::Gem::Version.new('5.0') && Object.const_defined?('PG::Connection')
|
445
|
+
# Avoid pg gem deprecation warning: "You should use PG::Connection, PG::Result, and PG::Error instead"
|
446
|
+
PGconn = PG::Connection
|
447
|
+
PGresult = PG::Result
|
448
|
+
PGError = PG::Error
|
449
|
+
end
|
450
|
+
|
451
|
+
# More arel_table_type stuff:
|
452
|
+
# ---------------------------
|
453
|
+
if ActiveRecord.version < ::Gem::Version.new('5.2')
|
454
|
+
# Specifically for AR 3.1 and 3.2 to avoid: "undefined method `delegate' for ActiveRecord::Reflection::ThroughReflection:Class"
|
455
|
+
require 'active_support/core_ext/module/delegation' if ActiveRecord.version < ::Gem::Version.new('4.0')
|
456
|
+
# Used by Util#_arel_table_type
|
457
|
+
# rubocop:disable Style/CommentedKeyword
|
458
|
+
module ActiveRecord
|
459
|
+
module Reflection
|
460
|
+
# AR < 4.0 doesn't know about join_table and derive_join_table
|
461
|
+
unless AssociationReflection.instance_methods.include?(:join_table)
|
462
|
+
class AssociationReflection < MacroReflection
|
463
|
+
def join_table
|
464
|
+
@join_table ||= options[:join_table] || derive_join_table
|
465
|
+
end
|
466
|
+
|
467
|
+
private
|
468
|
+
|
469
|
+
def derive_join_table
|
470
|
+
[active_record.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", '_')
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
module Associations
|
477
|
+
# Specific to AR 4.2 - 5.1:
|
478
|
+
if Associations.const_defined?('JoinDependency') && JoinDependency.private_instance_methods.include?(:table_aliases_for)
|
479
|
+
class JoinDependency
|
480
|
+
private
|
481
|
+
|
482
|
+
if ActiveRecord.version < ::Gem::Version.new('5.1') # 4.2 or 5.0
|
483
|
+
def table_aliases_for(parent, node)
|
484
|
+
node.reflection.chain.map do |reflection|
|
485
|
+
alias_tracker.aliased_table_for(
|
486
|
+
reflection.table_name,
|
487
|
+
table_alias_for(reflection, parent, reflection != node.reflection)
|
488
|
+
).tap do |x|
|
489
|
+
# %%% Specific only to Rails 4.2 (and maybe 4.1?)
|
490
|
+
x = x.left if x.is_a?(Arel::Nodes::TableAlias)
|
491
|
+
y = reflection.chain.find { |c| c.table_name == x.name }
|
492
|
+
x.instance_variable_set(:@_arel_table_type, y.klass)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end
|
498
|
+
elsif Associations.const_defined?('JoinHelper') && JoinHelper.private_instance_methods.include?(:construct_tables)
|
499
|
+
module JoinHelper
|
500
|
+
private
|
501
|
+
|
502
|
+
# AR > 3.0 and < 4.2 (%%% maybe only < 4.1?) uses construct_tables like this:
|
503
|
+
def construct_tables
|
504
|
+
tables = []
|
505
|
+
chain.each do |reflection|
|
506
|
+
tables << alias_tracker.aliased_table_for(
|
507
|
+
table_name_for(reflection),
|
508
|
+
table_alias_for(reflection, reflection != self.reflection)
|
509
|
+
).tap do |x|
|
510
|
+
x = x.left if x.is_a?(Arel::Nodes::TableAlias)
|
511
|
+
x.instance_variable_set(:@_arel_table_type, reflection.chain.find { |c| c.table_name == x.name }.klass)
|
512
|
+
end
|
513
|
+
|
514
|
+
next unless reflection.source_macro == :has_and_belongs_to_many
|
515
|
+
|
516
|
+
tables << alias_tracker.aliased_table_for(
|
517
|
+
(reflection.source_reflection || reflection).join_table,
|
518
|
+
table_alias_for(reflection, true)
|
519
|
+
)
|
520
|
+
end
|
521
|
+
tables
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end # module ActiveRecord
|
527
|
+
# rubocop:enable Style/CommentedKeyword
|
528
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
# require 'rails/generators/active_record'
|
5
|
+
|
6
|
+
module Brick
|
7
|
+
class InstallGenerator < ::Rails::Generators::Base
|
8
|
+
# include ::Rails::Generators::Migration
|
9
|
+
|
10
|
+
source_root File.expand_path('templates', __dir__)
|
11
|
+
class_option(
|
12
|
+
:with_changes,
|
13
|
+
type: :boolean,
|
14
|
+
default: false,
|
15
|
+
desc: 'Store changeset (diff) with each version'
|
16
|
+
)
|
17
|
+
|
18
|
+
desc 'Generates an initializer file for configuring Brick'
|
19
|
+
|
20
|
+
def create_initializer_file
|
21
|
+
unless File.exists?(filename = 'config/initializers/brick.rb')
|
22
|
+
create_file filename, "# frozen_string_literal: true
|
23
|
+
|
24
|
+
# # Settings for the Brick gem
|
25
|
+
# # (By default this auto-creates models, controllers, views, and routes on-the-fly.)
|
26
|
+
|
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']
|
38
|
+
|
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
|
+
"
|
65
|
+
end
|
66
|
+
end
|
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
|
+
|
94
|
+
private
|
95
|
+
|
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
|
102
|
+
|
103
|
+
# def migration_version
|
104
|
+
# return unless (major = ActiveRecord::VERSION::MAJOR) >= 5
|
105
|
+
|
106
|
+
# "[#{major}.#{ActiveRecord::VERSION::MINOR}]"
|
107
|
+
# end
|
108
|
+
|
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
|
118
|
+
|
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
|
141
|
+
end
|
142
|
+
end
|