activerecord 5.0.0.beta1.1 → 5.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +123 -15
- data/MIT-LICENSE +2 -2
- data/README.rdoc +1 -1
- data/lib/active_record.rb +2 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/associations.rb +3 -0
- data/lib/active_record/associations/builder/belongs_to.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +5 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -2
- data/lib/active_record/associations/preloader/through_association.rb +7 -2
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -7
- data/lib/active_record/autosave_association.rb +18 -3
- data/lib/active_record/base.rb +0 -3
- data/lib/active_record/collection_cache_key.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +21 -34
- data/lib/active_record/connection_adapters/abstract/quoting.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +7 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +36 -20
- data/lib/active_record/connection_adapters/abstract/transaction.rb +8 -2
- data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -15
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +57 -198
- data/lib/active_record/connection_adapters/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +24 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +15 -34
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +7 -69
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +8 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -7
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +15 -23
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -31
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +1 -1
- data/lib/active_record/counter_cache.rb +4 -4
- data/lib/active_record/enum.rb +8 -5
- data/lib/active_record/errors.rb +6 -1
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +6 -1
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/migration.rb +85 -20
- data/lib/active_record/migration/compatibility.rb +28 -2
- data/lib/active_record/model_schema.rb +25 -1
- data/lib/active_record/persistence.rb +11 -10
- data/lib/active_record/railtie.rb +6 -3
- data/lib/active_record/railties/databases.rake +20 -6
- data/lib/active_record/reflection.rb +39 -31
- data/lib/active_record/relation.rb +4 -4
- data/lib/active_record/relation/batches.rb +26 -41
- data/lib/active_record/relation/batches/batch_enumerator.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +35 -13
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +3 -0
- data/lib/active_record/relation/predicate_builder.rb +19 -1
- data/lib/active_record/relation/predicate_builder/range_handler.rb +17 -1
- data/lib/active_record/relation/query_methods.rb +37 -19
- data/lib/active_record/relation/record_fetch_warning.rb +4 -6
- data/lib/active_record/relation/where_clause.rb +1 -1
- data/lib/active_record/relation/where_clause_factory.rb +1 -0
- data/lib/active_record/sanitization.rb +1 -1
- data/lib/active_record/schema.rb +3 -0
- data/lib/active_record/schema_dumper.rb +1 -1
- data/lib/active_record/schema_migration.rb +5 -14
- data/lib/active_record/scoping.rb +17 -11
- data/lib/active_record/scoping/default.rb +2 -2
- data/lib/active_record/tasks/database_tasks.rb +18 -0
- data/lib/active_record/timestamp.rb +5 -1
- data/lib/active_record/transactions.rb +3 -3
- data/lib/active_record/validations/uniqueness.rb +6 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +7 -1
- metadata +14 -7
@@ -5,6 +5,12 @@ module ActiveRecord
|
|
5
5
|
|
6
6
|
module FourTwoShared
|
7
7
|
module TableDefinition
|
8
|
+
def references(*, **options)
|
9
|
+
options[:index] ||= false
|
10
|
+
super
|
11
|
+
end
|
12
|
+
alias :belongs_to :references
|
13
|
+
|
8
14
|
def timestamps(*, **options)
|
9
15
|
options[:null] = true if options[:null].nil?
|
10
16
|
super
|
@@ -24,6 +30,25 @@ module ActiveRecord
|
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
33
|
+
def change_table(table_name, options = {})
|
34
|
+
if block_given?
|
35
|
+
super(table_name, options) do |t|
|
36
|
+
class << t
|
37
|
+
prepend TableDefinition
|
38
|
+
end
|
39
|
+
yield t
|
40
|
+
end
|
41
|
+
else
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_reference(*, **options)
|
47
|
+
options[:index] ||= false
|
48
|
+
super
|
49
|
+
end
|
50
|
+
alias :add_belongs_to :add_reference
|
51
|
+
|
27
52
|
def add_timestamps(*, **options)
|
28
53
|
options[:null] = true if options[:null].nil?
|
29
54
|
super
|
@@ -41,8 +66,9 @@ module ActiveRecord
|
|
41
66
|
end
|
42
67
|
|
43
68
|
def remove_index(table_name, options = {})
|
44
|
-
|
45
|
-
|
69
|
+
options = { column: options } unless options.is_a?(Hash)
|
70
|
+
options[:name] = index_name_for_remove(table_name, options)
|
71
|
+
super(table_name, options)
|
46
72
|
end
|
47
73
|
|
48
74
|
private
|
@@ -42,6 +42,19 @@ module ActiveRecord
|
|
42
42
|
class_attribute :schema_migrations_table_name, instance_accessor: false
|
43
43
|
self.schema_migrations_table_name = "schema_migrations"
|
44
44
|
|
45
|
+
##
|
46
|
+
# :singleton-method:
|
47
|
+
# Accessor for the name of the internal metadata table. By default, the value is "ar_internal_metadata"
|
48
|
+
class_attribute :internal_metadata_table_name, instance_accessor: false
|
49
|
+
self.internal_metadata_table_name = "ar_internal_metadata"
|
50
|
+
|
51
|
+
##
|
52
|
+
# :singleton-method:
|
53
|
+
# Accessor for an array of names of environments where destructive actions should be prohibited. By default,
|
54
|
+
# the value is ["production"]
|
55
|
+
class_attribute :protected_environments, instance_accessor: false
|
56
|
+
self.protected_environments = ["production"]
|
57
|
+
|
45
58
|
##
|
46
59
|
# :singleton-method:
|
47
60
|
# Indicates whether table names should be the pluralized versions of the corresponding class names.
|
@@ -242,7 +255,18 @@ module ActiveRecord
|
|
242
255
|
@attribute_types ||= Hash.new(Type::Value.new)
|
243
256
|
end
|
244
257
|
|
245
|
-
|
258
|
+
# Returns the type of the attribute with the given name, after applying
|
259
|
+
# all modifiers. This method is the only valid source of information for
|
260
|
+
# anything related to the types of a model's attributes. This method will
|
261
|
+
# access the database and load the model's schema if it is required.
|
262
|
+
#
|
263
|
+
# The return value of this method will implement the interface described
|
264
|
+
# by ActiveModel::Type::Value (though the object itself may not subclass
|
265
|
+
# it).
|
266
|
+
#
|
267
|
+
# +attr_name+ The name of the attribute to retrieve the type for. Must be
|
268
|
+
# a string
|
269
|
+
def type_for_attribute(attr_name)
|
246
270
|
attribute_types[attr_name]
|
247
271
|
end
|
248
272
|
|
@@ -102,11 +102,11 @@ module ActiveRecord
|
|
102
102
|
|
103
103
|
# Saves the model.
|
104
104
|
#
|
105
|
-
# If the model is new a record gets created in the database, otherwise
|
105
|
+
# If the model is new, a record gets created in the database, otherwise
|
106
106
|
# the existing record gets updated.
|
107
107
|
#
|
108
|
-
# By default, save always
|
109
|
-
# is cancelled and #save returns +false
|
108
|
+
# By default, save always runs validations. If any of them fail the action
|
109
|
+
# is cancelled and #save returns +false+, and the record won't be saved. However, if you supply
|
110
110
|
# validate: false, validations are bypassed altogether. See
|
111
111
|
# ActiveRecord::Validations for more information.
|
112
112
|
#
|
@@ -132,9 +132,10 @@ module ActiveRecord
|
|
132
132
|
# If the model is new, a record gets created in the database, otherwise
|
133
133
|
# the existing record gets updated.
|
134
134
|
#
|
135
|
-
#
|
136
|
-
# ActiveRecord::RecordInvalid gets raised.
|
137
|
-
#
|
135
|
+
# By default, #save! always runs validations. If any of them fail
|
136
|
+
# ActiveRecord::RecordInvalid gets raised, and the record won't be saved. However, if you supply
|
137
|
+
# validate: false, validations are bypassed altogether. See
|
138
|
+
# ActiveRecord::Validations for more information.
|
138
139
|
#
|
139
140
|
# By default, #save! also sets the +updated_at+/+updated_on+ attributes to
|
140
141
|
# the current time. However, if you supply <tt>touch: false</tt>, these
|
@@ -246,7 +247,7 @@ module ActiveRecord
|
|
246
247
|
# This method raises an ActiveRecord::ActiveRecordError if the
|
247
248
|
# attribute is marked as readonly.
|
248
249
|
#
|
249
|
-
#
|
250
|
+
# Also see #update_column.
|
250
251
|
def update_attribute(name, value)
|
251
252
|
name = name.to_s
|
252
253
|
verify_readonly_attribute(name)
|
@@ -269,7 +270,7 @@ module ActiveRecord
|
|
269
270
|
alias update_attributes update
|
270
271
|
|
271
272
|
# Updates its receiver just like #update but calls #save! instead
|
272
|
-
# of +save+, so an exception is raised if the record is invalid.
|
273
|
+
# of +save+, so an exception is raised if the record is invalid and saving will fail.
|
273
274
|
def update!(attributes)
|
274
275
|
# The following transaction covers any possible database side-effects of the
|
275
276
|
# attributes assignment. For example, setting the IDs of a child collection.
|
@@ -348,7 +349,7 @@ module ActiveRecord
|
|
348
349
|
end
|
349
350
|
|
350
351
|
# Wrapper around #decrement that saves the record. This method differs from
|
351
|
-
# its non-bang version in that it passes through the attribute setter.
|
352
|
+
# its non-bang version in the sense that it passes through the attribute setter.
|
352
353
|
# Saving is not subjected to validation checks. Returns +true+ if the
|
353
354
|
# record could be saved.
|
354
355
|
def decrement!(attribute, by = 1)
|
@@ -373,7 +374,7 @@ module ActiveRecord
|
|
373
374
|
end
|
374
375
|
|
375
376
|
# Wrapper around #toggle that saves the record. This method differs from
|
376
|
-
# its non-bang version in that it passes through the attribute setter.
|
377
|
+
# its non-bang version in the sense that it passes through the attribute setter.
|
377
378
|
# Saving is not subjected to validation checks. Returns +true+ if the
|
378
379
|
# record could be saved.
|
379
380
|
def toggle!(attribute)
|
@@ -40,7 +40,7 @@ module ActiveRecord
|
|
40
40
|
task :load_config do
|
41
41
|
ActiveRecord::Tasks::DatabaseTasks.database_configuration = Rails.application.config.database_configuration
|
42
42
|
|
43
|
-
if defined?(
|
43
|
+
if defined?(ENGINE_ROOT) && engine = Rails::Engine.find(ENGINE_ROOT)
|
44
44
|
if engine.paths['db/migrate'].existent
|
45
45
|
ActiveRecord::Tasks::DatabaseTasks.migrations_paths += engine.paths['db/migrate'].to_a
|
46
46
|
end
|
@@ -57,8 +57,10 @@ module ActiveRecord
|
|
57
57
|
console do |app|
|
58
58
|
require "active_record/railties/console_sandbox" if app.sandbox?
|
59
59
|
require "active_record/base"
|
60
|
-
|
61
|
-
|
60
|
+
unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDERR, STDOUT)
|
61
|
+
console = ActiveSupport::Logger.new(STDERR)
|
62
|
+
Rails.logger.extend ActiveSupport::Logger.broadcast console
|
63
|
+
end
|
62
64
|
end
|
63
65
|
|
64
66
|
runner do
|
@@ -69,6 +71,7 @@ module ActiveRecord
|
|
69
71
|
ActiveSupport.on_load(:active_record) do
|
70
72
|
self.time_zone_aware_attributes = true
|
71
73
|
self.default_timezone = :utc
|
74
|
+
self.time_zone_aware_types = ActiveRecord::Base.time_zone_aware_types
|
72
75
|
end
|
73
76
|
end
|
74
77
|
|
@@ -1,6 +1,16 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
|
3
3
|
db_namespace = namespace :db do
|
4
|
+
desc "Set the environment value for the database"
|
5
|
+
task "environment:set" => [:environment, :load_config] do
|
6
|
+
ActiveRecord::InternalMetadata.create_table
|
7
|
+
ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
|
8
|
+
end
|
9
|
+
|
10
|
+
task :check_protected_environments => [:environment, :load_config] do
|
11
|
+
ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
|
12
|
+
end
|
13
|
+
|
4
14
|
task :load_config do
|
5
15
|
ActiveRecord::Base.configurations = ActiveRecord::Tasks::DatabaseTasks.database_configuration || {}
|
6
16
|
ActiveRecord::Migrator.migrations_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
|
@@ -18,24 +28,28 @@ db_namespace = namespace :db do
|
|
18
28
|
end
|
19
29
|
|
20
30
|
namespace :drop do
|
21
|
-
task :all => :load_config do
|
31
|
+
task :all => [:load_config, :check_protected_environments] do
|
22
32
|
ActiveRecord::Tasks::DatabaseTasks.drop_all
|
23
33
|
end
|
24
34
|
end
|
25
35
|
|
26
36
|
desc 'Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV, it defaults to dropping the development and test databases.'
|
27
|
-
task :drop => [:load_config] do
|
37
|
+
task :drop => [:load_config, :check_protected_environments] do
|
38
|
+
db_namespace["drop:_unsafe"].invoke
|
39
|
+
end
|
40
|
+
|
41
|
+
task "drop:_unsafe" => [:load_config] do
|
28
42
|
ActiveRecord::Tasks::DatabaseTasks.drop_current
|
29
43
|
end
|
30
44
|
|
31
45
|
namespace :purge do
|
32
|
-
task :all => :load_config do
|
46
|
+
task :all => [:load_config, :check_protected_environments] do
|
33
47
|
ActiveRecord::Tasks::DatabaseTasks.purge_all
|
34
48
|
end
|
35
49
|
end
|
36
50
|
|
37
51
|
# desc "Empty the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:purge:all to purge all databases in the config). Without RAILS_ENV it defaults to purging the development and test databases."
|
38
|
-
task :purge => [:load_config] do
|
52
|
+
task :purge => [:load_config, :check_protected_environments] do
|
39
53
|
ActiveRecord::Tasks::DatabaseTasks.purge_current
|
40
54
|
end
|
41
55
|
|
@@ -288,7 +302,7 @@ db_namespace = namespace :db do
|
|
288
302
|
end
|
289
303
|
|
290
304
|
desc "Recreates the databases from the structure.sql file"
|
291
|
-
task :load => [:load_config] do
|
305
|
+
task :load => [:environment, :load_config] do
|
292
306
|
ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:sql, ENV['SCHEMA'])
|
293
307
|
end
|
294
308
|
|
@@ -351,7 +365,7 @@ db_namespace = namespace :db do
|
|
351
365
|
task :clone_structure => %w(db:test:deprecated db:structure:dump db:test:load_structure)
|
352
366
|
|
353
367
|
# desc "Empty the test database"
|
354
|
-
task :purge => %w(environment load_config) do
|
368
|
+
task :purge => %w(environment load_config check_protected_environments) do
|
355
369
|
ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations['test']
|
356
370
|
end
|
357
371
|
|
@@ -483,28 +483,7 @@ module ActiveRecord
|
|
483
483
|
# Returns +true+ if +self+ is a +has_one+ reflection.
|
484
484
|
def has_one?; false; end
|
485
485
|
|
486
|
-
def association_class
|
487
|
-
case macro
|
488
|
-
when :belongs_to
|
489
|
-
if polymorphic?
|
490
|
-
Associations::BelongsToPolymorphicAssociation
|
491
|
-
else
|
492
|
-
Associations::BelongsToAssociation
|
493
|
-
end
|
494
|
-
when :has_many
|
495
|
-
if options[:through]
|
496
|
-
Associations::HasManyThroughAssociation
|
497
|
-
else
|
498
|
-
Associations::HasManyAssociation
|
499
|
-
end
|
500
|
-
when :has_one
|
501
|
-
if options[:through]
|
502
|
-
Associations::HasOneThroughAssociation
|
503
|
-
else
|
504
|
-
Associations::HasOneAssociation
|
505
|
-
end
|
506
|
-
end
|
507
|
-
end
|
486
|
+
def association_class; raise NotImplementedError; end
|
508
487
|
|
509
488
|
def polymorphic?
|
510
489
|
options[:polymorphic]
|
@@ -522,14 +501,7 @@ module ActiveRecord
|
|
522
501
|
private
|
523
502
|
|
524
503
|
def calculate_constructable(macro, options)
|
525
|
-
|
526
|
-
when :belongs_to
|
527
|
-
!polymorphic?
|
528
|
-
when :has_one
|
529
|
-
!options[:through]
|
530
|
-
else
|
531
|
-
true
|
532
|
-
end
|
504
|
+
true
|
533
505
|
end
|
534
506
|
|
535
507
|
# Attempts to find the inverse association name automatically.
|
@@ -545,7 +517,7 @@ module ActiveRecord
|
|
545
517
|
end
|
546
518
|
end
|
547
519
|
|
548
|
-
# returns either
|
520
|
+
# returns either false or the inverse association name that it finds.
|
549
521
|
def automatic_inverse_of
|
550
522
|
if can_find_inverse_of_automatically?(self)
|
551
523
|
inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
|
@@ -629,6 +601,14 @@ module ActiveRecord
|
|
629
601
|
def macro; :has_many; end
|
630
602
|
|
631
603
|
def collection?; true; end
|
604
|
+
|
605
|
+
def association_class
|
606
|
+
if options[:through]
|
607
|
+
Associations::HasManyThroughAssociation
|
608
|
+
else
|
609
|
+
Associations::HasManyAssociation
|
610
|
+
end
|
611
|
+
end
|
632
612
|
end
|
633
613
|
|
634
614
|
class HasOneReflection < AssociationReflection # :nodoc:
|
@@ -639,6 +619,20 @@ module ActiveRecord
|
|
639
619
|
def macro; :has_one; end
|
640
620
|
|
641
621
|
def has_one?; true; end
|
622
|
+
|
623
|
+
def association_class
|
624
|
+
if options[:through]
|
625
|
+
Associations::HasOneThroughAssociation
|
626
|
+
else
|
627
|
+
Associations::HasOneAssociation
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
private
|
632
|
+
|
633
|
+
def calculate_constructable(macro, options)
|
634
|
+
!options[:through]
|
635
|
+
end
|
642
636
|
end
|
643
637
|
|
644
638
|
class BelongsToReflection < AssociationReflection # :nodoc:
|
@@ -650,6 +644,14 @@ module ActiveRecord
|
|
650
644
|
|
651
645
|
def belongs_to?; true; end
|
652
646
|
|
647
|
+
def association_class
|
648
|
+
if polymorphic?
|
649
|
+
Associations::BelongsToPolymorphicAssociation
|
650
|
+
else
|
651
|
+
Associations::BelongsToAssociation
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
653
655
|
def join_keys(association_klass)
|
654
656
|
key = polymorphic? ? association_primary_key(association_klass) : association_primary_key
|
655
657
|
JoinKeys.new(key, foreign_key)
|
@@ -658,6 +660,12 @@ module ActiveRecord
|
|
658
660
|
def join_id_for(owner) # :nodoc:
|
659
661
|
owner[foreign_key]
|
660
662
|
end
|
663
|
+
|
664
|
+
private
|
665
|
+
|
666
|
+
def calculate_constructable(macro, options)
|
667
|
+
!polymorphic?
|
668
|
+
end
|
661
669
|
end
|
662
670
|
|
663
671
|
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
|
@@ -99,7 +99,7 @@ module ActiveRecord
|
|
99
99
|
end
|
100
100
|
|
101
101
|
substitutes = values.map do |(arel_attr, _)|
|
102
|
-
[arel_attr,
|
102
|
+
[arel_attr, Arel::Nodes::BindParam.new]
|
103
103
|
end
|
104
104
|
|
105
105
|
[substitutes, binds]
|
@@ -132,7 +132,7 @@ module ActiveRecord
|
|
132
132
|
# ==== Examples
|
133
133
|
#
|
134
134
|
# users = User.where(name: 'Oscar')
|
135
|
-
# users.create # => #<User id: 3, name: "
|
135
|
+
# users.create # => #<User id: 3, name: "Oscar", ...>
|
136
136
|
#
|
137
137
|
# users.create(name: 'fxn')
|
138
138
|
# users.create # => #<User id: 4, name: "fxn", ...>
|
@@ -371,11 +371,11 @@ module ActiveRecord
|
|
371
371
|
|
372
372
|
stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
|
373
373
|
stmt.table(table)
|
374
|
-
stmt.key = table[primary_key]
|
375
374
|
|
376
375
|
if joins_values.any?
|
377
|
-
@klass.connection.join_to_update(stmt, arel)
|
376
|
+
@klass.connection.join_to_update(stmt, arel, table[primary_key])
|
378
377
|
else
|
378
|
+
stmt.key = table[primary_key]
|
379
379
|
stmt.take(arel.limit)
|
380
380
|
stmt.order(*arel.orders)
|
381
381
|
stmt.wheres = arel.constraints
|
@@ -29,15 +29,15 @@ module ActiveRecord
|
|
29
29
|
#
|
30
30
|
# ==== Options
|
31
31
|
# * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
|
32
|
-
# * <tt>:
|
33
|
-
# * <tt>:
|
32
|
+
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
|
33
|
+
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
34
34
|
# This is especially useful if you want multiple workers dealing with
|
35
35
|
# the same processing queue. You can make worker 1 handle all the records
|
36
36
|
# between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
|
37
|
-
# (by setting the +:
|
37
|
+
# (by setting the +:start+ and +:finish+ option on each worker).
|
38
38
|
#
|
39
39
|
# # Let's process for a batch of 2000 records, skipping the first 2000 rows
|
40
|
-
# Person.find_each(
|
40
|
+
# Person.find_each(start: 2000, batch_size: 2000) do |person|
|
41
41
|
# person.party_all_night!
|
42
42
|
# end
|
43
43
|
#
|
@@ -48,22 +48,15 @@ module ActiveRecord
|
|
48
48
|
#
|
49
49
|
# NOTE: You can't set the limit either, that's used to control
|
50
50
|
# the batch sizes.
|
51
|
-
def find_each(
|
52
|
-
if start
|
53
|
-
begin_at = start
|
54
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
55
|
-
Passing `start` value to find_each is deprecated, and will be removed in Rails 5.1.
|
56
|
-
Please pass `begin_at` instead.
|
57
|
-
MSG
|
58
|
-
end
|
51
|
+
def find_each(start: nil, finish: nil, batch_size: 1000)
|
59
52
|
if block_given?
|
60
|
-
find_in_batches(
|
53
|
+
find_in_batches(start: start, finish: finish, batch_size: batch_size) do |records|
|
61
54
|
records.each { |record| yield record }
|
62
55
|
end
|
63
56
|
else
|
64
|
-
enum_for(:find_each,
|
57
|
+
enum_for(:find_each, start: start, finish: finish, batch_size: batch_size) do
|
65
58
|
relation = self
|
66
|
-
apply_limits(relation,
|
59
|
+
apply_limits(relation, start, finish).size
|
67
60
|
end
|
68
61
|
end
|
69
62
|
end
|
@@ -88,15 +81,15 @@ module ActiveRecord
|
|
88
81
|
#
|
89
82
|
# ==== Options
|
90
83
|
# * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
|
91
|
-
# * <tt>:
|
92
|
-
# * <tt>:
|
84
|
+
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
|
85
|
+
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
93
86
|
# This is especially useful if you want multiple workers dealing with
|
94
87
|
# the same processing queue. You can make worker 1 handle all the records
|
95
88
|
# between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
|
96
|
-
# (by setting the +:
|
89
|
+
# (by setting the +:start+ and +:finish+ option on each worker).
|
97
90
|
#
|
98
91
|
# # Let's process the next 2000 records
|
99
|
-
# Person.find_in_batches(
|
92
|
+
# Person.find_in_batches(start: 2000, batch_size: 2000) do |group|
|
100
93
|
# group.each { |person| person.party_all_night! }
|
101
94
|
# end
|
102
95
|
#
|
@@ -107,24 +100,16 @@ module ActiveRecord
|
|
107
100
|
#
|
108
101
|
# NOTE: You can't set the limit either, that's used to control
|
109
102
|
# the batch sizes.
|
110
|
-
def find_in_batches(
|
111
|
-
if start
|
112
|
-
begin_at = start
|
113
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
114
|
-
Passing `start` value to find_in_batches is deprecated, and will be removed in Rails 5.1.
|
115
|
-
Please pass `begin_at` instead.
|
116
|
-
MSG
|
117
|
-
end
|
118
|
-
|
103
|
+
def find_in_batches(start: nil, finish: nil, batch_size: 1000)
|
119
104
|
relation = self
|
120
105
|
unless block_given?
|
121
|
-
return to_enum(:find_in_batches,
|
122
|
-
total = apply_limits(relation,
|
106
|
+
return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size) do
|
107
|
+
total = apply_limits(relation, start, finish).size
|
123
108
|
(total - 1).div(batch_size) + 1
|
124
109
|
end
|
125
110
|
end
|
126
111
|
|
127
|
-
in_batches(of: batch_size,
|
112
|
+
in_batches(of: batch_size, start: start, finish: finish, load: true) do |batch|
|
128
113
|
yield batch.to_a
|
129
114
|
end
|
130
115
|
end
|
@@ -153,18 +138,18 @@ module ActiveRecord
|
|
153
138
|
# ==== Options
|
154
139
|
# * <tt>:of</tt> - Specifies the size of the batch. Default to 1000.
|
155
140
|
# * <tt>:load</tt> - Specifies if the relation should be loaded. Default to false.
|
156
|
-
# * <tt>:
|
157
|
-
# * <tt>:
|
141
|
+
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
|
142
|
+
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
158
143
|
#
|
159
144
|
# This is especially useful if you want to work with the
|
160
145
|
# ActiveRecord::Relation object instead of the array of records, or if
|
161
146
|
# you want multiple workers dealing with the same processing queue. You can
|
162
147
|
# make worker 1 handle all the records between id 0 and 10,000 and worker 2
|
163
|
-
# handle from 10,000 and beyond (by setting the +:
|
148
|
+
# handle from 10,000 and beyond (by setting the +:start+ and +:finish+
|
164
149
|
# option on each worker).
|
165
150
|
#
|
166
151
|
# # Let's process the next 2000 records
|
167
|
-
# Person.in_batches(of: 2000,
|
152
|
+
# Person.in_batches(of: 2000, start: 2000).update_all(awesome: true)
|
168
153
|
#
|
169
154
|
# An example of calling where query method on the relation:
|
170
155
|
#
|
@@ -186,10 +171,10 @@ module ActiveRecord
|
|
186
171
|
#
|
187
172
|
# NOTE: You can't set the limit either, that's used to control the batch
|
188
173
|
# sizes.
|
189
|
-
def in_batches(of: 1000,
|
174
|
+
def in_batches(of: 1000, start: nil, finish: nil, load: false)
|
190
175
|
relation = self
|
191
176
|
unless block_given?
|
192
|
-
return BatchEnumerator.new(of: of,
|
177
|
+
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
|
193
178
|
end
|
194
179
|
|
195
180
|
if logger && (arel.orders.present? || arel.taken.present?)
|
@@ -197,7 +182,7 @@ module ActiveRecord
|
|
197
182
|
end
|
198
183
|
|
199
184
|
relation = relation.reorder(batch_order).limit(of)
|
200
|
-
relation = apply_limits(relation,
|
185
|
+
relation = apply_limits(relation, start, finish)
|
201
186
|
batch_relation = relation
|
202
187
|
|
203
188
|
loop do
|
@@ -225,9 +210,9 @@ module ActiveRecord
|
|
225
210
|
|
226
211
|
private
|
227
212
|
|
228
|
-
def apply_limits(relation,
|
229
|
-
relation = relation.where(table[primary_key].gteq(
|
230
|
-
relation = relation.where(table[primary_key].lteq(
|
213
|
+
def apply_limits(relation, start, finish)
|
214
|
+
relation = relation.where(table[primary_key].gteq(start)) if start
|
215
|
+
relation = relation.where(table[primary_key].lteq(finish)) if finish
|
231
216
|
relation
|
232
217
|
end
|
233
218
|
|