activerecord 3.0.0.beta → 3.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.
- data/CHANGELOG +8 -1
- data/lib/active_record.rb +9 -6
- data/lib/active_record/aggregations.rb +5 -0
- data/lib/active_record/association_preload.rb +7 -2
- data/lib/active_record/associations.rb +74 -54
- data/lib/active_record/associations/association_collection.rb +1 -0
- data/lib/active_record/associations/association_proxy.rb +2 -1
- data/lib/active_record/associations/has_many_association.rb +4 -0
- data/lib/active_record/associations/has_many_through_association.rb +1 -0
- data/lib/active_record/attribute_methods/dirty.rb +11 -9
- data/lib/active_record/attribute_methods/primary_key.rb +6 -0
- data/lib/active_record/attribute_methods/query.rb +2 -0
- data/lib/active_record/base.rb +57 -212
- data/lib/active_record/callbacks.rb +10 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +24 -1
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +10 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +22 -5
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +34 -8
- data/lib/active_record/dynamic_finder_match.rb +3 -0
- data/lib/active_record/errors.rb +165 -0
- data/lib/active_record/fixtures.rb +1 -0
- data/lib/active_record/migration.rb +8 -6
- data/lib/active_record/named_scope.rb +14 -5
- data/lib/active_record/nested_attributes.rb +6 -2
- data/lib/active_record/query_cache.rb +2 -0
- data/lib/active_record/railtie.rb +30 -19
- data/lib/active_record/railties/databases.rake +13 -7
- data/lib/active_record/railties/{subscriber.rb → log_subscriber.rb} +7 -2
- data/lib/active_record/reflection.rb +5 -3
- data/lib/active_record/relation.rb +13 -2
- data/lib/active_record/relation/batches.rb +84 -0
- data/lib/active_record/relation/calculations.rb +2 -0
- data/lib/active_record/relation/finder_methods.rb +13 -2
- data/lib/active_record/relation/predicate_builder.rb +2 -7
- data/lib/active_record/relation/query_methods.rb +20 -27
- data/lib/active_record/relation/spawn_methods.rb +18 -28
- data/lib/active_record/schema.rb +2 -0
- data/lib/active_record/validations/uniqueness.rb +2 -4
- data/lib/active_record/version.rb +3 -2
- data/lib/{generators → rails/generators}/active_record.rb +0 -0
- data/lib/{generators → rails/generators}/active_record/migration/migration_generator.rb +1 -1
- data/lib/{generators → rails/generators}/active_record/migration/templates/migration.rb +0 -0
- data/lib/{generators → rails/generators}/active_record/model/model_generator.rb +1 -1
- data/lib/{generators → rails/generators}/active_record/model/templates/migration.rb +0 -0
- data/lib/{generators → rails/generators}/active_record/model/templates/model.rb +0 -0
- data/lib/{generators → rails/generators}/active_record/observer/observer_generator.rb +1 -1
- data/lib/{generators → rails/generators}/active_record/observer/templates/observer.rb +0 -0
- data/lib/{generators → rails/generators}/active_record/session_migration/session_migration_generator.rb +1 -1
- data/lib/{generators → rails/generators}/active_record/session_migration/templates/migration.rb +0 -0
- metadata +61 -34
- data/lib/active_record/batches.rb +0 -79
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'active_support/core_ext/object/
|
1
|
+
require 'active_support/core_ext/object/singleton_class'
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
# Exception that can be raised to stop migrations from going backwards.
|
@@ -107,7 +107,7 @@ module ActiveRecord
|
|
107
107
|
# The Rails package has several tools to help create and apply migrations.
|
108
108
|
#
|
109
109
|
# To generate a new migration, you can use
|
110
|
-
#
|
110
|
+
# rails generate migration MyNewMigration
|
111
111
|
#
|
112
112
|
# where MyNewMigration is the name of your migration. The generator will
|
113
113
|
# create an empty migration file <tt>timestamp_my_new_migration.rb</tt> in the <tt>db/migrate/</tt>
|
@@ -117,7 +117,7 @@ module ActiveRecord
|
|
117
117
|
# MyNewMigration.
|
118
118
|
#
|
119
119
|
# There is a special syntactic shortcut to generate migrations that add fields to a table.
|
120
|
-
#
|
120
|
+
# rails generate migration add_fieldname_to_tablename fieldname:string
|
121
121
|
#
|
122
122
|
# This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
|
123
123
|
# class AddFieldnameToTablename < ActiveRecord::Migration
|
@@ -303,7 +303,7 @@ module ActiveRecord
|
|
303
303
|
|
304
304
|
case sym
|
305
305
|
when :up, :down
|
306
|
-
|
306
|
+
singleton_class.send(:alias_method_chain, sym, "benchmarks")
|
307
307
|
end
|
308
308
|
ensure
|
309
309
|
@ignore_new_methods = false
|
@@ -315,7 +315,9 @@ module ActiveRecord
|
|
315
315
|
end
|
316
316
|
|
317
317
|
def announce(message)
|
318
|
-
|
318
|
+
version = defined?(@version) ? @version : nil
|
319
|
+
|
320
|
+
text = "#{version} #{name}: #{message}"
|
319
321
|
length = [0, 75 - text.length].max
|
320
322
|
write "== %s %s" % [text, "=" * length]
|
321
323
|
end
|
@@ -372,7 +374,7 @@ module ActiveRecord
|
|
372
374
|
end
|
373
375
|
|
374
376
|
def load_migration
|
375
|
-
|
377
|
+
require(File.expand_path(filename))
|
376
378
|
name.constantize
|
377
379
|
end
|
378
380
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'active_support/core_ext/array'
|
2
2
|
require 'active_support/core_ext/hash/except'
|
3
|
-
require 'active_support/core_ext/object/
|
3
|
+
require 'active_support/core_ext/object/singleton_class'
|
4
|
+
require 'active_support/core_ext/object/blank'
|
4
5
|
|
5
6
|
module ActiveRecord
|
6
7
|
module NamedScope
|
@@ -26,7 +27,7 @@ module ActiveRecord
|
|
26
27
|
if options.present?
|
27
28
|
Scope.init(self, options, &block)
|
28
29
|
else
|
29
|
-
current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.
|
30
|
+
current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.clone
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
@@ -101,7 +102,8 @@ module ActiveRecord
|
|
101
102
|
name = name.to_sym
|
102
103
|
|
103
104
|
if !scopes[name] && respond_to?(name, true)
|
104
|
-
|
105
|
+
logger.warn "Creating scope :#{name}. " \
|
106
|
+
"Overwriting existing method #{self.name}.#{name}."
|
105
107
|
end
|
106
108
|
|
107
109
|
scopes[name] = lambda do |parent_scope, *args|
|
@@ -112,7 +114,7 @@ module ActiveRecord
|
|
112
114
|
options.call(*args)
|
113
115
|
end, &block)
|
114
116
|
end
|
115
|
-
|
117
|
+
singleton_class.instance_eval do
|
116
118
|
define_method name do |*args|
|
117
119
|
scopes[name].call(self, *args)
|
118
120
|
end
|
@@ -165,7 +167,14 @@ module ActiveRecord
|
|
165
167
|
end
|
166
168
|
|
167
169
|
def ==(other)
|
168
|
-
|
170
|
+
case other
|
171
|
+
when Scope
|
172
|
+
to_sql == other.to_sql
|
173
|
+
when Relation
|
174
|
+
other == self
|
175
|
+
when Array
|
176
|
+
to_a == other.to_a
|
177
|
+
end
|
169
178
|
end
|
170
179
|
|
171
180
|
private
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_support/core_ext/hash/except'
|
2
2
|
require 'active_support/core_ext/object/try'
|
3
|
+
require 'active_support/core_ext/object/blank'
|
3
4
|
|
4
5
|
module ActiveRecord
|
5
6
|
module NestedAttributes #:nodoc:
|
@@ -243,11 +244,14 @@ module ActiveRecord
|
|
243
244
|
# def pirate_attributes=(attributes)
|
244
245
|
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
|
245
246
|
# end
|
246
|
-
class_eval
|
247
|
+
class_eval <<-eoruby, __FILE__, __LINE__ + 1
|
248
|
+
if method_defined?(:#{association_name}_attributes=)
|
249
|
+
remove_method(:#{association_name}_attributes=)
|
250
|
+
end
|
247
251
|
def #{association_name}_attributes=(attributes)
|
248
252
|
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
|
249
253
|
end
|
250
|
-
|
254
|
+
eoruby
|
251
255
|
else
|
252
256
|
raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
|
253
257
|
end
|
@@ -10,7 +10,7 @@ require "action_controller/railtie"
|
|
10
10
|
|
11
11
|
module ActiveRecord
|
12
12
|
class Railtie < Rails::Railtie
|
13
|
-
|
13
|
+
config.active_record = ActiveSupport::OrderedOptions.new
|
14
14
|
|
15
15
|
config.generators.orm :active_record, :migration => true,
|
16
16
|
:timestamps => true
|
@@ -19,36 +19,43 @@ module ActiveRecord
|
|
19
19
|
load "active_record/railties/databases.rake"
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
subscriber ActiveRecord::Railties::Subscriber.new
|
22
|
+
require "active_record/railties/log_subscriber"
|
23
|
+
log_subscriber :active_record, ActiveRecord::Railties::LogSubscriber.new
|
25
24
|
|
26
25
|
initializer "active_record.initialize_timezone" do
|
27
|
-
|
28
|
-
|
26
|
+
ActiveSupport.on_load(:active_record) do
|
27
|
+
self.time_zone_aware_attributes = true
|
28
|
+
self.default_timezone = :utc
|
29
|
+
end
|
29
30
|
end
|
30
31
|
|
31
32
|
initializer "active_record.logger" do
|
32
|
-
|
33
|
+
ActiveSupport.on_load(:active_record) { self.logger ||= ::Rails.logger }
|
33
34
|
end
|
34
35
|
|
35
36
|
initializer "active_record.set_configs" do |app|
|
36
|
-
|
37
|
-
|
37
|
+
ActiveSupport.on_load(:active_record) do
|
38
|
+
app.config.active_record.each do |k,v|
|
39
|
+
send "#{k}=", v
|
40
|
+
end
|
38
41
|
end
|
39
42
|
end
|
40
43
|
|
41
44
|
# This sets the database configuration from Configuration#database_configuration
|
42
45
|
# and then establishes the connection.
|
43
46
|
initializer "active_record.initialize_database" do |app|
|
44
|
-
|
45
|
-
|
47
|
+
ActiveSupport.on_load(:active_record) do
|
48
|
+
self.configurations = app.config.database_configuration
|
49
|
+
establish_connection
|
50
|
+
end
|
46
51
|
end
|
47
52
|
|
48
53
|
# Expose database runtime to controller for logging.
|
49
54
|
initializer "active_record.log_runtime" do |app|
|
50
55
|
require "active_record/railties/controller_runtime"
|
51
|
-
|
56
|
+
ActiveSupport.on_load(:action_controller) do
|
57
|
+
include ActiveRecord::Railties::ControllerRuntime
|
58
|
+
end
|
52
59
|
end
|
53
60
|
|
54
61
|
# Setup database middleware after initializers have run
|
@@ -64,18 +71,22 @@ module ActiveRecord
|
|
64
71
|
end
|
65
72
|
|
66
73
|
initializer "active_record.load_observers" do
|
67
|
-
|
74
|
+
ActiveSupport.on_load(:active_record) { instantiate_observers }
|
68
75
|
|
69
|
-
|
70
|
-
|
76
|
+
ActiveSupport.on_load(:active_record) do
|
77
|
+
ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do
|
78
|
+
ActiveRecord::Base.instantiate_observers
|
79
|
+
end
|
71
80
|
end
|
72
81
|
end
|
73
82
|
|
74
83
|
initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
84
|
+
ActiveSupport.on_load(:active_record) do
|
85
|
+
unless app.config.cache_classes
|
86
|
+
ActionDispatch::Callbacks.after do
|
87
|
+
ActiveRecord::Base.reset_subclasses
|
88
|
+
ActiveRecord::Base.clear_reloadable_connections!
|
89
|
+
end
|
79
90
|
end
|
80
91
|
end
|
81
92
|
end
|
@@ -84,9 +84,15 @@ namespace :db do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
when 'postgresql'
|
87
|
-
@encoding = config[
|
87
|
+
@encoding = config['encoding'] || ENV['CHARSET'] || 'utf8'
|
88
|
+
schema_search_path = config['schema_search_path'] || 'public'
|
89
|
+
first_in_schema_search_path = schema_search_path.split(',').first.strip
|
88
90
|
begin
|
89
91
|
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
|
92
|
+
unless ActiveRecord::Base.connection.all_schemas.include?(first_in_schema_search_path)
|
93
|
+
ActiveRecord::Base.connection.create_schema(first_in_schema_search_path, config['username'])
|
94
|
+
$stderr.puts "Schema #{first_in_schema_search_path} has been created."
|
95
|
+
end
|
90
96
|
ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding))
|
91
97
|
ActiveRecord::Base.establish_connection(config)
|
92
98
|
rescue
|
@@ -109,7 +115,7 @@ namespace :db do
|
|
109
115
|
# Only connect to local databases
|
110
116
|
local_database?(config) { drop_database(config) }
|
111
117
|
rescue Exception => e
|
112
|
-
puts "Couldn't drop #{config['database']} : #{e.inspect}"
|
118
|
+
$stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}"
|
113
119
|
end
|
114
120
|
end
|
115
121
|
end
|
@@ -121,7 +127,7 @@ namespace :db do
|
|
121
127
|
begin
|
122
128
|
drop_database(config)
|
123
129
|
rescue Exception => e
|
124
|
-
puts "Couldn't drop #{config['database']} : #{e.inspect}"
|
130
|
+
$stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}"
|
125
131
|
end
|
126
132
|
end
|
127
133
|
|
@@ -129,7 +135,7 @@ namespace :db do
|
|
129
135
|
if %w( 127.0.0.1 localhost ).include?(config['host']) || config['host'].blank?
|
130
136
|
yield
|
131
137
|
else
|
132
|
-
puts "This task only modifies local databases. #{config['database']} is on a remote host."
|
138
|
+
$stderr.puts "This task only modifies local databases. #{config['database']} is on a remote host."
|
133
139
|
end
|
134
140
|
end
|
135
141
|
|
@@ -204,7 +210,7 @@ namespace :db do
|
|
204
210
|
ActiveRecord::Base.establish_connection(config)
|
205
211
|
puts ActiveRecord::Base.connection.encoding
|
206
212
|
else
|
207
|
-
puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
|
213
|
+
$stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
|
208
214
|
end
|
209
215
|
end
|
210
216
|
|
@@ -216,7 +222,7 @@ namespace :db do
|
|
216
222
|
ActiveRecord::Base.establish_connection(config)
|
217
223
|
puts ActiveRecord::Base.connection.collation
|
218
224
|
else
|
219
|
-
puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
|
225
|
+
$stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
|
220
226
|
end
|
221
227
|
end
|
222
228
|
|
@@ -435,7 +441,7 @@ namespace :db do
|
|
435
441
|
task :create => :environment do
|
436
442
|
raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations?
|
437
443
|
require 'rails/generators'
|
438
|
-
require 'generators/rails/session_migration/session_migration_generator'
|
444
|
+
require 'rails/generators/rails/session_migration/session_migration_generator'
|
439
445
|
Rails::Generators::SessionMigrationGenerator.start [ ENV["MIGRATION"] || "add_sessions_table" ]
|
440
446
|
end
|
441
447
|
|
@@ -1,6 +1,11 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Railties
|
3
|
-
class
|
3
|
+
class LogSubscriber < Rails::LogSubscriber
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
@odd_or_even = false
|
7
|
+
end
|
8
|
+
|
4
9
|
def sql(event)
|
5
10
|
name = '%s (%.1fms)' % [event.payload[:name], event.duration]
|
6
11
|
sql = event.payload[:sql].squeeze(' ')
|
@@ -24,4 +29,4 @@ module ActiveRecord
|
|
24
29
|
end
|
25
30
|
end
|
26
31
|
end
|
27
|
-
end
|
32
|
+
end
|
@@ -154,6 +154,11 @@ module ActiveRecord
|
|
154
154
|
@klass ||= active_record.send(:compute_type, class_name)
|
155
155
|
end
|
156
156
|
|
157
|
+
def initialize(macro, name, options, active_record)
|
158
|
+
super
|
159
|
+
@collection = [:has_many, :has_and_belongs_to_many].include?(macro)
|
160
|
+
end
|
161
|
+
|
157
162
|
# Returns a new, unsaved instance of the associated class. +options+ will
|
158
163
|
# be passed to the class's constructor.
|
159
164
|
def build_association(*options)
|
@@ -256,9 +261,6 @@ module ActiveRecord
|
|
256
261
|
# association. Returns +true+ if the +macro+ is one of +has_many+ or
|
257
262
|
# +has_and_belongs_to_many+, +false+ otherwise.
|
258
263
|
def collection?
|
259
|
-
if @collection.nil?
|
260
|
-
@collection = [:has_many, :has_and_belongs_to_many].include?(macro)
|
261
|
-
end
|
262
264
|
@collection
|
263
265
|
end
|
264
266
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
class Relation
|
3
5
|
JoinOperation = Struct.new(:relation, :join_class, :on)
|
@@ -5,7 +7,7 @@ module ActiveRecord
|
|
5
7
|
MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having]
|
6
8
|
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :create_with, :from]
|
7
9
|
|
8
|
-
include FinderMethods, Calculations, SpawnMethods, QueryMethods
|
10
|
+
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches
|
9
11
|
|
10
12
|
delegate :length, :collect, :map, :each, :all?, :include?, :to => :to_a
|
11
13
|
delegate :insert, :to => :arel
|
@@ -14,6 +16,11 @@ module ActiveRecord
|
|
14
16
|
|
15
17
|
def initialize(klass, table)
|
16
18
|
@klass, @table = klass, table
|
19
|
+
|
20
|
+
@implicit_readonly = nil
|
21
|
+
@loaded = nil
|
22
|
+
|
23
|
+
SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)}
|
17
24
|
(ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])}
|
18
25
|
end
|
19
26
|
|
@@ -21,6 +28,10 @@ module ActiveRecord
|
|
21
28
|
with_create_scope { @klass.new(*args, &block) }
|
22
29
|
end
|
23
30
|
|
31
|
+
def initialize_copy(other)
|
32
|
+
reset
|
33
|
+
end
|
34
|
+
|
24
35
|
alias build new
|
25
36
|
|
26
37
|
def create(*args, &block)
|
@@ -50,7 +61,7 @@ module ActiveRecord
|
|
50
61
|
|
51
62
|
preload = @preload_values
|
52
63
|
preload += @includes_values unless eager_loading?
|
53
|
-
preload.each {|associations| @klass.send(:preload_associations, @records, associations) }
|
64
|
+
preload.each {|associations| @klass.send(:preload_associations, @records, associations) }
|
54
65
|
|
55
66
|
# @readonly_value is true only if set explicity. @implicit_readonly is true if there are JOINS and no explicit SELECT.
|
56
67
|
readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Batches # :nodoc:
|
5
|
+
# Yields each record that was found by the find +options+. The find is
|
6
|
+
# performed by find_in_batches with a batch size of 1000 (or as
|
7
|
+
# specified by the <tt>:batch_size</tt> option).
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# Person.where("age > 21").find_each do |person|
|
12
|
+
# person.party_all_night!
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# Note: This method is only intended to use for batch processing of
|
16
|
+
# large amounts of records that wouldn't fit in memory all at once. If
|
17
|
+
# you just need to loop over less than 1000 records, it's probably
|
18
|
+
# better just to use the regular find methods.
|
19
|
+
def find_each(options = {})
|
20
|
+
find_in_batches(options) do |records|
|
21
|
+
records.each { |record| yield record }
|
22
|
+
end
|
23
|
+
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
# Yields each batch of records that was found by the find +options+ as
|
28
|
+
# an array. The size of each batch is set by the <tt>:batch_size</tt>
|
29
|
+
# option; the default is 1000.
|
30
|
+
#
|
31
|
+
# You can control the starting point for the batch processing by
|
32
|
+
# supplying the <tt>:start</tt> option. This is especially useful if you
|
33
|
+
# want multiple workers dealing with the same processing queue. You can
|
34
|
+
# make worker 1 handle all the records between id 0 and 10,000 and
|
35
|
+
# worker 2 handle from 10,000 and beyond (by setting the <tt>:start</tt>
|
36
|
+
# option on that worker).
|
37
|
+
#
|
38
|
+
# It's not possible to set the order. That is automatically set to
|
39
|
+
# ascending on the primary key ("id ASC") to make the batch ordering
|
40
|
+
# work. This also mean that this method only works with integer-based
|
41
|
+
# primary keys. You can't set the limit either, that's used to control
|
42
|
+
# the the batch sizes.
|
43
|
+
#
|
44
|
+
# Example:
|
45
|
+
#
|
46
|
+
# Person.where("age > 21").find_in_batches do |group|
|
47
|
+
# sleep(50) # Make sure it doesn't get too crowded in there!
|
48
|
+
# group.each { |person| person.party_all_night! }
|
49
|
+
# end
|
50
|
+
def find_in_batches(options = {})
|
51
|
+
relation = self
|
52
|
+
|
53
|
+
if orders.present? || taken.present?
|
54
|
+
ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
|
55
|
+
end
|
56
|
+
|
57
|
+
if (finder_options = options.except(:start, :batch_size)).present?
|
58
|
+
raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present?
|
59
|
+
raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit].present?
|
60
|
+
|
61
|
+
relation = apply_finder_options(finder_options)
|
62
|
+
end
|
63
|
+
|
64
|
+
start = options.delete(:start).to_i
|
65
|
+
batch_size = options.delete(:batch_size) || 1000
|
66
|
+
|
67
|
+
relation = relation.except(:order).order(batch_order).limit(batch_size)
|
68
|
+
records = relation.where(primary_key.gteq(start)).all
|
69
|
+
|
70
|
+
while records.any?
|
71
|
+
yield records
|
72
|
+
|
73
|
+
break if records.size < batch_size
|
74
|
+
records = relation.where(primary_key.gt(records.last.id)).all
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def batch_order
|
81
|
+
"#{@klass.table_name}.#{@klass.primary_key} ASC"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|