duty_free 1.0.7 → 1.0.9

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.
@@ -25,17 +25,18 @@ module DutyFree
25
25
  def self._recurse_arel(piece, prefix = '')
26
26
  names = []
27
27
  # Our JOINs mashup of nested arrays and hashes
28
- if piece.is_a?(Array)
28
+ case piece
29
+ when Array
29
30
  names += piece.inject([]) { |s, v| s + _recurse_arel(v, prefix) }
30
- elsif piece.is_a?(Hash)
31
+ when Hash
31
32
  names += piece.inject([]) do |s, v|
32
33
  new_prefix = "#{prefix}#{v.first}_"
33
- s << new_prefix
34
+ s << [v.last.shift, new_prefix]
34
35
  s + _recurse_arel(v.last, new_prefix)
35
36
  end
36
37
 
37
38
  # ActiveRecord AREL objects
38
- elsif piece.is_a?(Arel::Nodes::Join) # INNER or OUTER JOIN
39
+ when Arel::Nodes::Join # INNER or OUTER JOIN
39
40
  # rubocop:disable Style/IdenticalConditionalBranches
40
41
  if piece.right.is_a?(Arel::Table) # Came in from AR < 3.2?
41
42
  # Arel 2.x and older is a little curious because these JOINs work "back to front".
@@ -45,28 +46,38 @@ module DutyFree
45
46
  # The right side here at the top is the very last table, and anywhere else down the tree it is
46
47
  # the later "JOIN" table of this pair. (The table that comes after all the rest of the JOINs
47
48
  # from the left side.)
48
- names << (piece.right.table_alias || piece.right.name)
49
+ names << [_arel_table_type(piece.right), (piece.right.table_alias || piece.right.name)]
49
50
  else # "Normal" setup, fed from a JoinSource which has an array of JOINs
50
51
  # The left side is the "JOIN" table
51
52
  names += _recurse_arel(piece.left)
52
53
  # (The right side of these is the "ON" clause)
53
54
  end
54
55
  # rubocop:enable Style/IdenticalConditionalBranches
55
- elsif piece.is_a?(Arel::Table) # Table
56
- names << (piece.table_alias || piece.name)
57
- elsif piece.is_a?(Arel::Nodes::TableAlias) # Alias
56
+ when Arel::Table # Table
57
+ names << [_arel_table_type(piece), (piece.table_alias || piece.name)]
58
+ when Arel::Nodes::TableAlias # Alias
58
59
  # Can get the real table name from: self._recurse_arel(piece.left)
59
- names << piece.right.to_s # This is simply a string; the alias name itself
60
- elsif piece.is_a?(Arel::Nodes::JoinSource) # Leaving this until the end because AR < 3.2 doesn't know at all about JoinSource!
60
+ names << [_arel_table_type(piece.left), piece.right.to_s] # This is simply a string; the alias name itself
61
+ when Arel::Nodes::JoinSource # Leaving this until the end because AR < 3.2 doesn't know at all about JoinSource!
61
62
  # The left side is the "FROM" table
62
63
  # names += _recurse_arel(piece.left)
63
- names << (piece.left.table_alias || piece.left.name)
64
+ names << [_arel_table_type(piece.left), (piece.left.table_alias || piece.left.name)]
64
65
  # The right side is an array of all JOINs
65
66
  names += piece.right.inject([]) { |s, v| s + _recurse_arel(v) }
66
67
  end
67
68
  names
68
69
  end
69
70
 
71
+ def self._arel_table_type(tbl)
72
+ # AR < 4.2 doesn't have type_caster at all, so rely on an instance variable getting set
73
+ # AR 4.2 - 5.1 have buggy type_caster entries for the root node
74
+ tbl.instance_variable_get(:@_arel_table_type) ||
75
+ # 5.2-6.1 does type_caster just fine, no bugs there, but the property with the type differs:
76
+ # 5.2 has "types" as public, 6.0 "types" as private, and 6.1 "klass" as private.
77
+ ((tc = tbl.send(:type_caster)) && tc.instance_variable_get(:@types)) ||
78
+ tc.send(:klass)
79
+ end
80
+
70
81
  def self._prefix_join(prefixes, separator = nil)
71
82
  prefixes.reject(&:blank?).join(separator || '.')
72
83
  end
@@ -83,5 +94,112 @@ module DutyFree
83
94
  end
84
95
  name
85
96
  end
97
+
98
+ # ===================================
99
+ # Epic require patch
100
+ def self._patch_require(module_filename, folder_matcher, search_text, replacement_text, autoload_symbol = nil)
101
+ mod_name_parts = module_filename.split('.')
102
+ extension = case mod_name_parts.last
103
+ when 'rb', 'so', 'o'
104
+ module_filename = mod_name_parts[0..-2].join('.')
105
+ ".#{mod_name_parts.last}"
106
+ else
107
+ '.rb'
108
+ end
109
+
110
+ if autoload_symbol
111
+ unless Object.const_defined?('ActiveSupport::Dependencies')
112
+ require 'active_support'
113
+ require 'active_support/dependencies'
114
+ end
115
+ alp = ActiveSupport::Dependencies.autoload_paths
116
+ custom_require_dir = ::DutyFree::Util._custom_require_dir
117
+ # Create any missing folder structure leading up to this file
118
+ module_filename.split('/')[0..-2].inject(custom_require_dir) do |s, part|
119
+ new_part = File.join(s, part)
120
+ Dir.mkdir(new_part) unless Dir.exist?(new_part)
121
+ new_part
122
+ end
123
+ if ::DutyFree::Util._write_patched(folder_matcher, module_filename, extension, custom_require_dir, nil, search_text, replacement_text) &&
124
+ !alp.include?(custom_require_dir)
125
+ alp.unshift(custom_require_dir)
126
+ end
127
+ else
128
+ unless (require_overrides = ::DutyFree::Util.instance_variable_get(:@_require_overrides))
129
+ ::DutyFree::Util.instance_variable_set(:@_require_overrides, (require_overrides = {}))
130
+
131
+ # Patch "require" itself so that when it specifically sees "active_support/values/time_zone" then
132
+ # a copy is taken of the original, an attempt is made to find the line with a circular error, that
133
+ # single line is patched, and then an updated version is written to a temporary folder which is
134
+ # then required in place of the original.
135
+
136
+ Kernel.module_exec do
137
+ # class << self
138
+ alias_method :orig_require, :require
139
+ # end
140
+ # To be most faithful to Ruby's normal behaviour, this should look like a public singleton
141
+ define_method(:require) do |name|
142
+ if (require_override = ::DutyFree::Util.instance_variable_get(:@_require_overrides)[name])
143
+ extension, folder_matcher, search_text, replacement_text, autoload_symbol = require_override
144
+ patched_filename = "/patched_#{name.tr('/', '_')}#{extension}"
145
+ if $LOADED_FEATURES.find { |f| f.end_with?(patched_filename) }
146
+ false
147
+ else
148
+ is_replaced = false
149
+ if (replacement_path = ::DutyFree::Util._write_patched(folder_matcher, name, extension, ::DutyFree::Util._custom_require_dir, patched_filename, search_text, replacement_text))
150
+ is_replaced = Kernel.send(:orig_require, replacement_path)
151
+ elsif replacement_path.nil?
152
+ puts "Couldn't find #{name} to require it!"
153
+ end
154
+ is_replaced
155
+ end
156
+ else
157
+ Kernel.send(:orig_require, name)
158
+ end
159
+ end
160
+ end
161
+ end
162
+ require_overrides[module_filename] = [extension, folder_matcher, search_text, replacement_text, autoload_symbol]
163
+ end
164
+ end
165
+
166
+ def self._custom_require_dir
167
+ unless (custom_require_dir = ::DutyFree::Util.instance_variable_get(:@_custom_require_dir))
168
+ ::DutyFree::Util.instance_variable_set(:@_custom_require_dir, (custom_require_dir = Dir.mktmpdir))
169
+ # So normal Ruby require will now pick this one up
170
+ $LOAD_PATH.unshift(custom_require_dir)
171
+ # When Ruby is exiting, remove this temporary directory
172
+ at_exit do
173
+ FileUtils.rm_rf(::DutyFree::Util.instance_variable_get(:@_custom_require_dir))
174
+ end
175
+ end
176
+ custom_require_dir
177
+ end
178
+
179
+ # Returns the full path to the replaced filename, or
180
+ # false if the file already exists, and nil if it was unable to write anything.
181
+ def self._write_patched(folder_matcher, name, extension, dir, patched_filename, search_text, replacement_text)
182
+ # See if our replacement file might already exist for some reason
183
+ name = +"/#{name}" unless name.start_with?('/')
184
+ name << extension unless name.end_with?(extension)
185
+ return false if File.exist?(replacement_path = "#{dir}#{patched_filename || name}")
186
+
187
+ # Dredge up the original .rb file, doctor it, and then require it instead
188
+ num_written = nil
189
+ orig_path = nil
190
+ orig_as = nil
191
+ # Using Ruby's approach to find files to require
192
+ $LOAD_PATH.each do |path|
193
+ orig_path = "#{path}#{name}"
194
+ break if path.include?(folder_matcher) && (orig_as = File.open(orig_path))
195
+ end
196
+ if (orig_text = orig_as&.read)
197
+ File.open(replacement_path, 'w') do |replacement|
198
+ num_written = replacement.write(orig_text.gsub(search_text, replacement_text))
199
+ end
200
+ orig_as.close
201
+ end
202
+ (num_written&.> 0) ? replacement_path : nil
203
+ end
86
204
  end
87
205
  end
@@ -5,7 +5,7 @@ module DutyFree
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 7
8
+ TINY = 9
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
data/lib/duty_free.rb CHANGED
@@ -21,8 +21,8 @@ if ActiveRecord.version < ::Gem::Version.new('5.0') &&
21
21
  end
22
22
  end
23
23
 
24
- # Allow Rails 4.0 and 4.1 to work with newer Ruby (>= 2.4) by avoiding a "stack level too deep" error
25
- # when ActiveSupport tries to smarten up Numeric by messing with Fixnum and Bignum at the end of:
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
26
  # activesupport-4.0.13/lib/active_support/core_ext/numeric/conversions.rb
27
27
  if ActiveRecord.version < ::Gem::Version.new('4.2') &&
28
28
  ActiveRecord.version > ::Gem::Version.new('3.2') &&
@@ -33,7 +33,7 @@ if ActiveRecord.version < ::Gem::Version.new('4.2') &&
33
33
  Numeric.const_set('Bignum', OurBignum)
34
34
  end
35
35
 
36
- # Allow Rails < 3.2 to run with newer versions of Psych gem
36
+ # Allow ActiveRecord < 3.2 to run with newer versions of Psych gem
37
37
  if BigDecimal.respond_to?(:yaml_tag) && !BigDecimal.respond_to?(:yaml_as)
38
38
  class BigDecimal
39
39
  class <<self
@@ -42,6 +42,28 @@ if BigDecimal.respond_to?(:yaml_tag) && !BigDecimal.respond_to?(:yaml_as)
42
42
  end
43
43
  end
44
44
 
45
+ require 'duty_free/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
+ ::Gem::Version.new(RUBY_VERSION) >= ::Gem::Version.new('2.7')
50
+ # Remove circular reference for "now"
51
+ ::DutyFree::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
+ ::DutyFree::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
+
45
67
  require 'active_record'
46
68
 
47
69
  require 'duty_free/config'
@@ -107,14 +129,31 @@ end
107
129
  # Major compatibility fixes for ActiveRecord < 4.2
108
130
  # ================================================
109
131
  ActiveSupport.on_load(:active_record) do
110
- # Rails < 4.0 cannot do #find_by, or do #pluck on multiple columns, so here are the patches:
111
- if ActiveRecord.version < ::Gem::Version.new('4.0')
112
- module ActiveRecord
113
- module Calculations # Normally find_by is in FinderMethods, which older AR doesn't have
132
+ # rubocop:disable Lint/ConstantDefinitionInBlock
133
+ module ActiveRecord
134
+ class Base
135
+ unless respond_to?(:execute_sql)
136
+ class << self
137
+ def execute_sql(sql, *param_array)
138
+ param_array = param_array.first if param_array.length == 1 && param_array.first.is_a?(Array)
139
+ connection.execute(send(:sanitize_sql_array, [sql] + param_array))
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ # Rails < 4.0 cannot do #find_by, #find_or_create_by, or do #pluck on multiple columns, so here are the patches:
146
+ if version < ::Gem::Version.new('4.0')
147
+ # Normally find_by is in FinderMethods, which older AR doesn't have
148
+ module Calculations
114
149
  def find_by(*args)
115
150
  where(*args).limit(1).to_a.first
116
151
  end
117
152
 
153
+ def find_or_create_by(attributes, &block)
154
+ find_by(attributes) || create(attributes, &block)
155
+ end
156
+
118
157
  def pluck(*column_names)
119
158
  column_names.map! do |column_name|
120
159
  if column_name.is_a?(Symbol) && self.column_names.include?(column_name.to_s)
@@ -174,7 +213,7 @@ ActiveSupport.on_load(:active_record) do
174
213
  unless Base.is_a?(Calculations)
175
214
  class Base
176
215
  class << self
177
- delegate :pluck, :find_by, to: :scoped
216
+ delegate :pluck, :find_by, :find_or_create_by, to: :scoped
178
217
  end
179
218
  end
180
219
  end
@@ -205,72 +244,208 @@ ActiveSupport.on_load(:active_record) do
205
244
  end
206
245
  end
207
246
  end
208
- end
209
- end
210
247
 
211
- # Rails < 4.2 is not innately compatible with Ruby 2.4 and later, and comes up with:
212
- # "TypeError: Cannot visit Integer" unless we patch like this:
213
- unless ::Gem::Version.new(RUBY_VERSION) < ::Gem::Version.new('2.4')
214
- unless Arel::Visitors::DepthFirst.private_instance_methods.include?(:visit_Integer)
215
- module Arel
216
- module Visitors
217
- class DepthFirst < Visitor
218
- alias visit_Integer terminal
219
- end
248
+ # ActiveRecord 3.1 and 3.2 didn't try to bring in &block for the .extending() convenience thing
249
+ # that smartens up scopes, and Ruby 2.7 complained loudly about just doing the magical "Proc.new"
250
+ # that historically would just capture the incoming block.
251
+ module QueryMethods
252
+ unless instance_method(:extending).parameters.include?([:block, :block])
253
+ # These first two lines used to be:
254
+ # def extending(*modules)
255
+ # modules << Module.new(&Proc.new) if block_given?
256
+
257
+ def extending(*modules, &block)
258
+ modules << Module.new(&block) if block_given?
259
+
260
+ return self if modules.empty?
220
261
 
221
- class Dot < Visitor
222
- alias visit_Integer visit_String
262
+ relation = clone
263
+ relation.send(:apply_modules, modules.flatten)
264
+ relation
223
265
  end
266
+ end
267
+ end
224
268
 
225
- class ToSql < Visitor
226
- private
269
+ # Same kind of thing for ActiveRecord::Scoping::Default#default_scope
270
+ module Scoping
271
+ module Default
272
+ module ClassMethods
273
+ if instance_methods.include?(:default_scope) &&
274
+ !instance_method(:default_scope).parameters.include?([:block, :block])
275
+ # Fix for AR 3.2-5.1
276
+ def default_scope(scope = nil, &block)
277
+ scope = block if block_given?
278
+
279
+ if scope.is_a?(Relation) || !scope.respond_to?(:call)
280
+ raise ArgumentError,
281
+ 'Support for calling #default_scope without a block is removed. For example instead ' \
282
+ "of `default_scope where(color: 'red')`, please use " \
283
+ "`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \
284
+ 'self.default_scope.)'
285
+ end
227
286
 
228
- # ActiveRecord before v3.2 uses Arel < 3.x, which does not have Arel#literal.
229
- unless private_instance_methods.include?(:literal)
230
- def literal(obj)
231
- obj
287
+ self.default_scopes += [scope]
232
288
  end
233
289
  end
234
- alias visit_Integer literal
235
290
  end
236
291
  end
237
292
  end
238
293
  end
239
294
  end
295
+ # rubocop:enable Lint/ConstantDefinitionInBlock
240
296
 
241
- if ActiveRecord.version < ::Gem::Version.new('5.0')
242
- # Avoid pg gem deprecation warning: "You should use PG::Connection, PG::Result, and PG::Error instead"
243
- PGconn = PG::Connection
244
- PGresult = PG::Result
245
- PGError = PG::Error
297
+ # Rails < 4.2 is not innately compatible with Ruby 2.4 and later, and comes up with:
298
+ # "TypeError: Cannot visit Integer" unless we patch like this:
299
+ if ::Gem::Version.new(RUBY_VERSION) >= ::Gem::Version.new('2.4') &&
300
+ Arel::Visitors.const_defined?('DepthFirst') &&
301
+ !Arel::Visitors::DepthFirst.private_instance_methods.include?(:visit_Integer)
302
+ module Arel
303
+ module Visitors
304
+ class DepthFirst < Visitor
305
+ alias visit_Integer terminal
306
+ end
307
+
308
+ class Dot < Visitor
309
+ alias visit_Integer visit_String
310
+ end
311
+
312
+ class ToSql < Visitor
313
+ private
314
+
315
+ # ActiveRecord before v3.2 uses Arel < 3.x, which does not have Arel#literal.
316
+ unless private_instance_methods.include?(:literal)
317
+ def literal(obj)
318
+ obj
319
+ end
320
+ end
321
+ alias visit_Integer literal
322
+ end
323
+ end
324
+ end
246
325
  end
247
326
 
248
327
  unless DateTime.instance_methods.include?(:nsec)
249
328
  class DateTime < Date
250
329
  def nsec
251
- (sec_fraction * 1000000000).to_i
330
+ (sec_fraction * 1_000_000_000).to_i
331
+ end
332
+ end
333
+ end
334
+
335
+ # First part of arel_table_type stuff:
336
+ # ------------------------------------
337
+ # (more found below)
338
+ # was: ActiveRecord.version >= ::Gem::Version.new('3.2') &&
339
+ if ActiveRecord.version < ::Gem::Version.new('5.0')
340
+ # Used by Util#_arel_table_type
341
+ module ActiveRecord
342
+ class Base
343
+ def self.arel_table
344
+ @arel_table ||= Arel::Table.new(table_name, arel_engine).tap do |x|
345
+ x.instance_variable_set(:@_arel_table_type, self)
346
+ end
347
+ end
252
348
  end
253
349
  end
254
350
  end
255
351
 
256
352
  include ::DutyFree::Extensions
353
+
354
+ unless ::DutyFree::Extensions::IS_AMOEBA
355
+ # Add amoeba-compatible support
356
+ module ActiveRecord
357
+ class Base
358
+ def self.amoeba(*args)
359
+ puts "Amoeba called from #{name} with #{args.inspect}"
360
+ end
361
+ end
362
+ end
363
+ end
257
364
  end
258
365
 
259
- # # Require frameworks
260
- # if defined?(::Rails)
261
- # # Rails module is sometimes defined by gems like rails-html-sanitizer
262
- # # so we check for presence of Rails.application.
263
- # if defined?(::Rails.application)
264
- # require "duty_free/frameworks/rails"
265
- # else
266
- # ::Kernel.warn(<<-EOS.freeze
267
- # DutyFree has been loaded too early, before rails is loaded. This can
268
- # happen when another gem defines the ::Rails namespace, then DF is loaded,
269
- # all before rails is loaded. You may want to reorder your Gemfile, or defer
270
- # the loading of DF by using `require: false` and a manual require elsewhere.
271
- # EOS
272
- # )
273
- # end
274
- # else
275
- # require "duty_free/frameworks/active_record"
276
- # end
366
+ # Do this earlier because stuff here gets mixed into JoinDependency::JoinAssociation and AssociationScope
367
+ if ActiveRecord.version < ::Gem::Version.new('5.0') && Object.const_defined?('PG::Connection')
368
+ # Avoid pg gem deprecation warning: "You should use PG::Connection, PG::Result, and PG::Error instead"
369
+ PGconn = PG::Connection
370
+ PGresult = PG::Result
371
+ PGError = PG::Error
372
+ end
373
+
374
+ # More arel_table_type stuff:
375
+ # ---------------------------
376
+ if ActiveRecord.version < ::Gem::Version.new('5.2')
377
+ # Specifically for AR 3.1 and 3.2 to avoid: "undefined method `delegate' for ActiveRecord::Reflection::ThroughReflection:Class"
378
+ require 'active_support/core_ext/module/delegation' if ActiveRecord.version < ::Gem::Version.new('4.0')
379
+ # Used by Util#_arel_table_type
380
+ # rubocop:disable Style/CommentedKeyword
381
+ module ActiveRecord
382
+ module Reflection
383
+ # AR < 4.0 doesn't know about join_table and derive_join_table
384
+ unless AssociationReflection.instance_methods.include?(:join_table)
385
+ class AssociationReflection < MacroReflection
386
+ def join_table
387
+ @join_table ||= options[:join_table] || derive_join_table
388
+ end
389
+
390
+ private
391
+
392
+ def derive_join_table
393
+ [active_record.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", '_')
394
+ end
395
+ end
396
+ end
397
+ end
398
+
399
+ module Associations
400
+ # Specific to AR 4.2 - 5.1:
401
+ if Associations.const_defined?('JoinDependency') && JoinDependency.private_instance_methods.include?(:table_aliases_for)
402
+ class JoinDependency
403
+ private
404
+
405
+ if ActiveRecord.version < ::Gem::Version.new('5.1') # 4.2 or 5.0
406
+ def table_aliases_for(parent, node)
407
+ node.reflection.chain.map do |reflection|
408
+ alias_tracker.aliased_table_for(
409
+ reflection.table_name,
410
+ table_alias_for(reflection, parent, reflection != node.reflection)
411
+ ).tap do |x|
412
+ # %%% Specific only to Rails 4.2 (and maybe 4.1?)
413
+ x = x.left if x.is_a?(Arel::Nodes::TableAlias)
414
+ y = reflection.chain.find { |c| c.table_name == x.name }
415
+ x.instance_variable_set(:@_arel_table_type, y.klass)
416
+ end
417
+ end
418
+ end
419
+ end
420
+ end
421
+ elsif Associations.const_defined?('JoinHelper') && JoinHelper.private_instance_methods.include?(:construct_tables)
422
+ module JoinHelper
423
+ private
424
+
425
+ # AR > 3.0 and < 4.2 (%%% maybe only < 4.1?) uses construct_tables like this:
426
+ def construct_tables
427
+ tables = []
428
+ chain.each do |reflection|
429
+ tables << alias_tracker.aliased_table_for(
430
+ table_name_for(reflection),
431
+ table_alias_for(reflection, reflection != self.reflection)
432
+ ).tap do |x|
433
+ x = x.left if x.is_a?(Arel::Nodes::TableAlias)
434
+ x.instance_variable_set(:@_arel_table_type, reflection.chain.find { |c| c.table_name == x.name }.klass)
435
+ end
436
+
437
+ next unless reflection.source_macro == :has_and_belongs_to_many
438
+
439
+ tables << alias_tracker.aliased_table_for(
440
+ (reflection.source_reflection || reflection).join_table,
441
+ table_alias_for(reflection, true)
442
+ )
443
+ end
444
+ tables
445
+ end
446
+ end
447
+ end
448
+ end
449
+ end # module ActiveRecord
450
+ # rubocop:enable Style/CommentedKeyword
451
+ end