activerecord 4.1.0.beta2 → 4.1.0.rc1
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 +622 -9
- data/MIT-LICENSE +1 -1
- data/lib/active_record.rb +1 -1
- data/lib/active_record/associations.rb +10 -7
- data/lib/active_record/associations/alias_tracker.rb +39 -29
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +56 -31
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -0
- data/lib/active_record/associations/builder/association.rb +6 -0
- data/lib/active_record/associations/builder/belongs_to.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +33 -9
- data/lib/active_record/associations/collection_proxy.rb +53 -5
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +8 -8
- data/lib/active_record/associations/preloader.rb +1 -1
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/attribute_methods.rb +28 -5
- data/lib/active_record/attribute_methods/dirty.rb +27 -4
- data/lib/active_record/attribute_methods/read.rb +1 -1
- data/lib/active_record/attribute_methods/serialization.rb +18 -0
- data/lib/active_record/autosave_association.rb +1 -1
- data/lib/active_record/base.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +16 -9
- data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -8
- data/lib/active_record/connection_adapters/abstract/transaction.rb +4 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/connection_specification.rb +200 -43
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +7 -1
- data/lib/active_record/connection_adapters/mysql_adapter.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/cast.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -17
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +25 -3
- data/lib/active_record/connection_handling.rb +64 -3
- data/lib/active_record/core.rb +28 -24
- data/lib/active_record/dynamic_matchers.rb +6 -2
- data/lib/active_record/enum.rb +111 -17
- data/lib/active_record/errors.rb +12 -0
- data/lib/active_record/fixtures.rb +13 -15
- data/lib/active_record/inheritance.rb +29 -9
- data/lib/active_record/integration.rb +4 -2
- data/lib/active_record/migration.rb +20 -7
- data/lib/active_record/migration/command_recorder.rb +18 -6
- data/lib/active_record/persistence.rb +10 -5
- data/lib/active_record/querying.rb +1 -0
- data/lib/active_record/railtie.rb +11 -8
- data/lib/active_record/railties/databases.rake +24 -38
- data/lib/active_record/relation.rb +3 -2
- data/lib/active_record/relation/batches.rb +24 -9
- data/lib/active_record/relation/finder_methods.rb +100 -11
- data/lib/active_record/relation/query_methods.rb +39 -27
- data/lib/active_record/result.rb +1 -1
- data/lib/active_record/sanitization.rb +7 -5
- data/lib/active_record/scoping.rb +5 -0
- data/lib/active_record/scoping/named.rb +6 -0
- data/lib/active_record/store.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +45 -23
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/transactions.rb +7 -7
- data/lib/active_record/validations/presence.rb +1 -1
- data/lib/active_record/version.rb +1 -1
- metadata +5 -6
- data/lib/active_record/associations/join_helper.rb +0 -36
@@ -181,6 +181,7 @@ module ActiveRecord
|
|
181
181
|
became = klass.new
|
182
182
|
became.instance_variable_set("@attributes", @attributes)
|
183
183
|
became.instance_variable_set("@attributes_cache", @attributes_cache)
|
184
|
+
became.instance_variable_set("@changed_attributes", @changed_attributes) if defined?(@changed_attributes)
|
184
185
|
became.instance_variable_set("@new_record", new_record?)
|
185
186
|
became.instance_variable_set("@destroyed", destroyed?)
|
186
187
|
became.instance_variable_set("@errors", errors)
|
@@ -195,7 +196,11 @@ module ActiveRecord
|
|
195
196
|
# share the same set of attributes.
|
196
197
|
def becomes!(klass)
|
197
198
|
became = becomes(klass)
|
198
|
-
|
199
|
+
sti_type = nil
|
200
|
+
if !klass.descends_from_active_record?
|
201
|
+
sti_type = klass.sti_name
|
202
|
+
end
|
203
|
+
became.public_send("#{klass.inheritance_column}=", sti_type)
|
199
204
|
became
|
200
205
|
end
|
201
206
|
|
@@ -264,7 +269,7 @@ module ActiveRecord
|
|
264
269
|
# This method raises an +ActiveRecord::ActiveRecordError+ when called on new
|
265
270
|
# objects, or when at least one of the attributes is marked as readonly.
|
266
271
|
def update_columns(attributes)
|
267
|
-
raise ActiveRecordError, "
|
272
|
+
raise ActiveRecordError, "cannot update on a new record object" unless persisted?
|
268
273
|
|
269
274
|
attributes.each_key do |key|
|
270
275
|
verify_readonly_attribute(key.to_s)
|
@@ -361,7 +366,7 @@ module ActiveRecord
|
|
361
366
|
# assert_equal 25, account.credit # check it is updated in memory
|
362
367
|
# assert_equal 25, account.reload.credit # check it is also persisted
|
363
368
|
#
|
364
|
-
# Another
|
369
|
+
# Another common use case is optimistic locking handling:
|
365
370
|
#
|
366
371
|
# def with_optimistic_retry
|
367
372
|
# begin
|
@@ -384,7 +389,7 @@ module ActiveRecord
|
|
384
389
|
|
385
390
|
fresh_object =
|
386
391
|
if options && options[:lock]
|
387
|
-
self.class.unscoped { self.class.lock.find(id) }
|
392
|
+
self.class.unscoped { self.class.lock(options[:lock]).find(id) }
|
388
393
|
else
|
389
394
|
self.class.unscoped { self.class.find(id) }
|
390
395
|
end
|
@@ -426,7 +431,7 @@ module ActiveRecord
|
|
426
431
|
# ball.touch(:updated_at) # => raises ActiveRecordError
|
427
432
|
#
|
428
433
|
def touch(name = nil)
|
429
|
-
raise ActiveRecordError, "
|
434
|
+
raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
|
430
435
|
|
431
436
|
attributes = timestamp_attributes_for_update_in_model
|
432
437
|
attributes << name if name
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Querying
|
3
3
|
delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, to: :all
|
4
|
+
delegate :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!, :forty_two, :forty_two!, to: :all
|
4
5
|
delegate :first_or_create, :first_or_create!, :first_or_initialize, to: :all
|
5
6
|
delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, to: :all
|
6
7
|
delegate :find_by, :find_by!, to: :all
|
@@ -31,22 +31,16 @@ module ActiveRecord
|
|
31
31
|
|
32
32
|
|
33
33
|
config.active_record.use_schema_cache_dump = true
|
34
|
+
config.active_record.maintain_test_schema = true
|
34
35
|
|
35
36
|
config.eager_load_namespaces << ActiveRecord
|
36
37
|
|
37
38
|
rake_tasks do
|
38
39
|
require "active_record/base"
|
39
40
|
|
40
|
-
ActiveRecord::Tasks::DatabaseTasks.seed_loader = Rails.application
|
41
|
-
ActiveRecord::Tasks::DatabaseTasks.env = Rails.env
|
42
|
-
|
43
41
|
namespace :db do
|
44
42
|
task :load_config do
|
45
|
-
ActiveRecord::Tasks::DatabaseTasks.db_dir = Rails.application.config.paths["db"].first
|
46
43
|
ActiveRecord::Tasks::DatabaseTasks.database_configuration = Rails.application.config.database_configuration
|
47
|
-
ActiveRecord::Tasks::DatabaseTasks.migrations_paths = Rails.application.paths['db/migrate'].to_a
|
48
|
-
ActiveRecord::Tasks::DatabaseTasks.fixtures_path = File.join Rails.root, 'test', 'fixtures'
|
49
|
-
ActiveRecord::Tasks::DatabaseTasks.root = Rails.root
|
50
44
|
|
51
45
|
if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH)
|
52
46
|
if engine.paths['db/migrate'].existent
|
@@ -122,7 +116,16 @@ module ActiveRecord
|
|
122
116
|
# and then establishes the connection.
|
123
117
|
initializer "active_record.initialize_database" do |app|
|
124
118
|
ActiveSupport.on_load(:active_record) do
|
125
|
-
|
119
|
+
|
120
|
+
class ActiveRecord::NoDatabaseError
|
121
|
+
remove_possible_method :extend_message
|
122
|
+
def extend_message(message)
|
123
|
+
message << "Run `$ bin/rake db:create db:migrate` to create your database"
|
124
|
+
message
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
self.configurations = Rails.application.config.database_configuration
|
126
129
|
establish_connection
|
127
130
|
end
|
128
131
|
end
|
@@ -2,7 +2,7 @@ require 'active_record'
|
|
2
2
|
|
3
3
|
db_namespace = namespace :db do
|
4
4
|
task :load_config do
|
5
|
-
ActiveRecord::Base.configurations
|
5
|
+
ActiveRecord::Base.configurations = ActiveRecord::Tasks::DatabaseTasks.database_configuration || {}
|
6
6
|
ActiveRecord::Migrator.migrations_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
|
7
7
|
end
|
8
8
|
|
@@ -12,13 +12,9 @@ db_namespace = namespace :db do
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
desc '
|
15
|
+
desc 'Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV it defaults to creating the development and test databases.'
|
16
16
|
task :create => [:load_config] do
|
17
|
-
|
18
|
-
ActiveRecord::Tasks::DatabaseTasks.create_database_url
|
19
|
-
else
|
20
|
-
ActiveRecord::Tasks::DatabaseTasks.create_current
|
21
|
-
end
|
17
|
+
ActiveRecord::Tasks::DatabaseTasks.create_current
|
22
18
|
end
|
23
19
|
|
24
20
|
namespace :drop do
|
@@ -27,13 +23,9 @@ db_namespace = namespace :db do
|
|
27
23
|
end
|
28
24
|
end
|
29
25
|
|
30
|
-
desc 'Drops the database
|
26
|
+
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.'
|
31
27
|
task :drop => [:load_config] do
|
32
|
-
|
33
|
-
ActiveRecord::Tasks::DatabaseTasks.drop_database_url
|
34
|
-
else
|
35
|
-
ActiveRecord::Tasks::DatabaseTasks.drop_current
|
36
|
-
end
|
28
|
+
ActiveRecord::Tasks::DatabaseTasks.drop_current
|
37
29
|
end
|
38
30
|
|
39
31
|
desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
|
@@ -42,7 +34,7 @@ db_namespace = namespace :db do
|
|
42
34
|
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
|
43
35
|
ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
|
44
36
|
end
|
45
|
-
db_namespace['_dump'].invoke
|
37
|
+
db_namespace['_dump'].invoke if ActiveRecord::Base.dump_schema_after_migration
|
46
38
|
end
|
47
39
|
|
48
40
|
task :_dump do
|
@@ -83,7 +75,7 @@ db_namespace = namespace :db do
|
|
83
75
|
# desc 'Runs the "down" for a given migration VERSION.'
|
84
76
|
task :down => [:environment, :load_config] do
|
85
77
|
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
86
|
-
raise 'VERSION is required' unless version
|
78
|
+
raise 'VERSION is required - To go down one migration, run db:rollback' unless version
|
87
79
|
ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
|
88
80
|
db_namespace['_dump'].invoke
|
89
81
|
end
|
@@ -187,9 +179,6 @@ db_namespace = namespace :db do
|
|
187
179
|
require 'active_record/fixtures'
|
188
180
|
|
189
181
|
base_dir = if ENV['FIXTURES_PATH']
|
190
|
-
STDERR.puts "Using FIXTURES_PATH env variable is deprecated, please use " +
|
191
|
-
"ActiveRecord::Tasks::DatabaseTasks.fixtures_path = '/path/to/fixtures' " +
|
192
|
-
"instead."
|
193
182
|
File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten
|
194
183
|
else
|
195
184
|
ActiveRecord::Tasks::DatabaseTasks.fixtures_path
|
@@ -212,9 +201,6 @@ db_namespace = namespace :db do
|
|
212
201
|
puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::FixtureSet.identify(label)}.) if label
|
213
202
|
|
214
203
|
base_dir = if ENV['FIXTURES_PATH']
|
215
|
-
STDERR.puts "Using FIXTURES_PATH env variable is deprecated, please use " +
|
216
|
-
"ActiveRecord::Tasks::DatabaseTasks.fixtures_path = '/path/to/fixtures' " +
|
217
|
-
"instead."
|
218
204
|
File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten
|
219
205
|
else
|
220
206
|
ActiveRecord::Tasks::DatabaseTasks.fixtures_path
|
@@ -248,9 +234,7 @@ db_namespace = namespace :db do
|
|
248
234
|
|
249
235
|
desc 'Load a schema.rb file into the database'
|
250
236
|
task :load => [:environment, :load_config] do
|
251
|
-
|
252
|
-
ActiveRecord::Tasks::DatabaseTasks.check_schema_file(file)
|
253
|
-
load(file)
|
237
|
+
ActiveRecord::Tasks::DatabaseTasks.load_schema(:ruby, ENV['SCHEMA'])
|
254
238
|
end
|
255
239
|
|
256
240
|
task :load_if_ruby => ['db:create', :environment] do
|
@@ -295,10 +279,7 @@ db_namespace = namespace :db do
|
|
295
279
|
|
296
280
|
# desc "Recreate the databases from the structure.sql file"
|
297
281
|
task :load => [:environment, :load_config] do
|
298
|
-
|
299
|
-
ActiveRecord::Tasks::DatabaseTasks.check_schema_file(filename)
|
300
|
-
current_config = ActiveRecord::Tasks::DatabaseTasks.current_config
|
301
|
-
ActiveRecord::Tasks::DatabaseTasks.structure_load(current_config, filename)
|
282
|
+
ActiveRecord::Tasks::DatabaseTasks.load_schema(:sql, ENV['DB_STRUCTURE'])
|
302
283
|
end
|
303
284
|
|
304
285
|
task :load_if_sql => ['db:create', :environment] do
|
@@ -308,8 +289,15 @@ db_namespace = namespace :db do
|
|
308
289
|
|
309
290
|
namespace :test do
|
310
291
|
|
292
|
+
task :deprecated do
|
293
|
+
Rake.application.top_level_tasks.grep(/^db:test:/).each do |task|
|
294
|
+
$stderr.puts "WARNING: #{task} is deprecated. The Rails test helper now maintains " \
|
295
|
+
"your test schema automatically, see the release notes for details."
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
311
299
|
# desc "Recreate the test database from the current schema"
|
312
|
-
task :load =>
|
300
|
+
task :load => %w(db:test:deprecated db:test:purge) do
|
313
301
|
case ActiveRecord::Base.schema_format
|
314
302
|
when :ruby
|
315
303
|
db_namespace["test:load_schema"].invoke
|
@@ -319,7 +307,7 @@ db_namespace = namespace :db do
|
|
319
307
|
end
|
320
308
|
|
321
309
|
# desc "Recreate the test database from an existent schema.rb file"
|
322
|
-
task :load_schema =>
|
310
|
+
task :load_schema => %w(db:test:deprecated db:test:purge) do
|
323
311
|
begin
|
324
312
|
should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
|
325
313
|
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
|
@@ -333,7 +321,7 @@ db_namespace = namespace :db do
|
|
333
321
|
end
|
334
322
|
|
335
323
|
# desc "Recreate the test database from an existent structure.sql file"
|
336
|
-
task :load_structure =>
|
324
|
+
task :load_structure => %w(db:test:deprecated db:test:purge) do
|
337
325
|
begin
|
338
326
|
ActiveRecord::Tasks::DatabaseTasks.current_config(:config => ActiveRecord::Base.configurations['test'])
|
339
327
|
db_namespace["structure:load"].invoke
|
@@ -343,7 +331,7 @@ db_namespace = namespace :db do
|
|
343
331
|
end
|
344
332
|
|
345
333
|
# desc "Recreate the test database from a fresh schema"
|
346
|
-
task :clone => :environment do
|
334
|
+
task :clone => %w(db:test:deprecated environment) do
|
347
335
|
case ActiveRecord::Base.schema_format
|
348
336
|
when :ruby
|
349
337
|
db_namespace["test:clone_schema"].invoke
|
@@ -353,18 +341,18 @@ db_namespace = namespace :db do
|
|
353
341
|
end
|
354
342
|
|
355
343
|
# desc "Recreate the test database from a fresh schema.rb file"
|
356
|
-
task :clone_schema =>
|
344
|
+
task :clone_schema => %w(db:test:deprecated db:schema:dump db:test:load_schema)
|
357
345
|
|
358
346
|
# desc "Recreate the test database from a fresh structure.sql file"
|
359
|
-
task :clone_structure =>
|
347
|
+
task :clone_structure => %w(db:test:deprecated db:structure:dump db:test:load_structure)
|
360
348
|
|
361
349
|
# desc "Empty the test database"
|
362
|
-
task :purge =>
|
350
|
+
task :purge => %w(db:test:deprecated environment load_config) do
|
363
351
|
ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations['test']
|
364
352
|
end
|
365
353
|
|
366
354
|
# desc 'Check for pending migrations and load the test schema'
|
367
|
-
task :prepare =>
|
355
|
+
task :prepare => %w(db:test:deprecated environment load_config) do
|
368
356
|
unless ActiveRecord::Base.configurations.blank?
|
369
357
|
db_namespace['test:load'].invoke
|
370
358
|
end
|
@@ -399,5 +387,3 @@ namespace :railties do
|
|
399
387
|
end
|
400
388
|
end
|
401
389
|
end
|
402
|
-
|
403
|
-
task 'test:prepare' => ['db:test:prepare', 'db:test:load', 'db:abort_if_pending_migrations']
|
@@ -24,6 +24,7 @@ module ActiveRecord
|
|
24
24
|
@klass = klass
|
25
25
|
@table = table
|
26
26
|
@values = values
|
27
|
+
@offsets = {}
|
27
28
|
@loaded = false
|
28
29
|
end
|
29
30
|
|
@@ -495,9 +496,10 @@ module ActiveRecord
|
|
495
496
|
end
|
496
497
|
|
497
498
|
def reset
|
498
|
-
@
|
499
|
+
@last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
|
499
500
|
@should_eager_load = @join_dependency = nil
|
500
501
|
@records = []
|
502
|
+
@offsets = {}
|
501
503
|
self
|
502
504
|
end
|
503
505
|
|
@@ -533,7 +535,6 @@ module ActiveRecord
|
|
533
535
|
}
|
534
536
|
|
535
537
|
binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
|
536
|
-
binds.merge!(Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }])
|
537
538
|
|
538
539
|
Hash[equalities.map { |where|
|
539
540
|
name = where.left.name
|
@@ -52,7 +52,9 @@ module ActiveRecord
|
|
52
52
|
records.each { |record| yield record }
|
53
53
|
end
|
54
54
|
else
|
55
|
-
enum_for :find_each, options
|
55
|
+
enum_for :find_each, options do
|
56
|
+
options[:start] ? where(table[primary_key].gteq(options[:start])).size : size
|
57
|
+
end
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
@@ -64,6 +66,16 @@ module ActiveRecord
|
|
64
66
|
# group.each { |person| person.party_all_night! }
|
65
67
|
# end
|
66
68
|
#
|
69
|
+
# If you do not provide a block to #find_in_batches, it will return an Enumerator
|
70
|
+
# for chaining with other methods:
|
71
|
+
#
|
72
|
+
# Person.find_in_batches.with_index do |group, batch|
|
73
|
+
# puts "Processing group ##{batch}"
|
74
|
+
# group.each(&:recover_from_last_night!)
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# To be yielded each record one by one, use #find_each instead.
|
78
|
+
#
|
67
79
|
# ==== Options
|
68
80
|
# * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
|
69
81
|
# * <tt>:start</tt> - Specifies the starting point for the batch processing.
|
@@ -88,30 +100,33 @@ module ActiveRecord
|
|
88
100
|
options.assert_valid_keys(:start, :batch_size)
|
89
101
|
|
90
102
|
relation = self
|
103
|
+
start = options[:start]
|
104
|
+
batch_size = options[:batch_size] || 1000
|
105
|
+
|
106
|
+
unless block_given?
|
107
|
+
return to_enum(:find_in_batches, options) do
|
108
|
+
total = start ? where(table[primary_key].gteq(start)).size : size
|
109
|
+
(total - 1).div(batch_size) + 1
|
110
|
+
end
|
111
|
+
end
|
91
112
|
|
92
113
|
if logger && (arel.orders.present? || arel.taken.present?)
|
93
114
|
logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
|
94
115
|
end
|
95
116
|
|
96
|
-
start = options.delete(:start)
|
97
|
-
batch_size = options.delete(:batch_size) || 1000
|
98
|
-
|
99
117
|
relation = relation.reorder(batch_order).limit(batch_size)
|
100
118
|
records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
|
101
119
|
|
102
120
|
while records.any?
|
103
121
|
records_size = records.size
|
104
122
|
primary_key_offset = records.last.id
|
123
|
+
raise "Primary key not included in the custom select clause" unless primary_key_offset
|
105
124
|
|
106
125
|
yield records
|
107
126
|
|
108
127
|
break if records_size < batch_size
|
109
128
|
|
110
|
-
|
111
|
-
records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
|
112
|
-
else
|
113
|
-
raise "Primary key not included in the custom select clause"
|
114
|
-
end
|
129
|
+
records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
|
115
130
|
end
|
116
131
|
end
|
117
132
|
|
@@ -127,9 +127,9 @@ module ActiveRecord
|
|
127
127
|
#
|
128
128
|
def first(limit = nil)
|
129
129
|
if limit
|
130
|
-
|
130
|
+
find_nth_with_limit(offset_value, limit)
|
131
131
|
else
|
132
|
-
|
132
|
+
find_nth(:first, offset_value)
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
@@ -172,6 +172,86 @@ module ActiveRecord
|
|
172
172
|
last or raise RecordNotFound
|
173
173
|
end
|
174
174
|
|
175
|
+
# Find the second record.
|
176
|
+
# If no order is defined it will order by primary key.
|
177
|
+
#
|
178
|
+
# Person.second # returns the second object fetched by SELECT * FROM people
|
179
|
+
# Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
|
180
|
+
# Person.where(["user_name = :u", { u: user_name }]).second
|
181
|
+
def second
|
182
|
+
find_nth(:second, offset_value ? offset_value + 1 : 1)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
186
|
+
# is found.
|
187
|
+
def second!
|
188
|
+
second or raise RecordNotFound
|
189
|
+
end
|
190
|
+
|
191
|
+
# Find the third record.
|
192
|
+
# If no order is defined it will order by primary key.
|
193
|
+
#
|
194
|
+
# Person.third # returns the third object fetched by SELECT * FROM people
|
195
|
+
# Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
|
196
|
+
# Person.where(["user_name = :u", { u: user_name }]).third
|
197
|
+
def third
|
198
|
+
find_nth(:third, offset_value ? offset_value + 2 : 2)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
202
|
+
# is found.
|
203
|
+
def third!
|
204
|
+
third or raise RecordNotFound
|
205
|
+
end
|
206
|
+
|
207
|
+
# Find the fourth record.
|
208
|
+
# If no order is defined it will order by primary key.
|
209
|
+
#
|
210
|
+
# Person.fourth # returns the fourth object fetched by SELECT * FROM people
|
211
|
+
# Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
|
212
|
+
# Person.where(["user_name = :u", { u: user_name }]).fourth
|
213
|
+
def fourth
|
214
|
+
find_nth(:fourth, offset_value ? offset_value + 3 : 3)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
218
|
+
# is found.
|
219
|
+
def fourth!
|
220
|
+
fourth or raise RecordNotFound
|
221
|
+
end
|
222
|
+
|
223
|
+
# Find the fifth record.
|
224
|
+
# If no order is defined it will order by primary key.
|
225
|
+
#
|
226
|
+
# Person.fifth # returns the fifth object fetched by SELECT * FROM people
|
227
|
+
# Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
|
228
|
+
# Person.where(["user_name = :u", { u: user_name }]).fifth
|
229
|
+
def fifth
|
230
|
+
find_nth(:fifth, offset_value ? offset_value + 4 : 4)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
234
|
+
# is found.
|
235
|
+
def fifth!
|
236
|
+
fifth or raise RecordNotFound
|
237
|
+
end
|
238
|
+
|
239
|
+
# Find the forty-second record. Also known as accessing "the reddit".
|
240
|
+
# If no order is defined it will order by primary key.
|
241
|
+
#
|
242
|
+
# Person.forty_two # returns the forty-second object fetched by SELECT * FROM people
|
243
|
+
# Person.offset(3).forty_two # returns the fifth object from OFFSET 3 (which is OFFSET 44)
|
244
|
+
# Person.where(["user_name = :u", { u: user_name }]).forty_two
|
245
|
+
def forty_two
|
246
|
+
find_nth(:forty_two, offset_value ? offset_value + 41 : 41)
|
247
|
+
end
|
248
|
+
|
249
|
+
# Same as +forty_two+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
250
|
+
# is found.
|
251
|
+
def forty_two!
|
252
|
+
forty_two or raise RecordNotFound
|
253
|
+
end
|
254
|
+
|
175
255
|
# Returns +true+ if a record exists in the table that matches the +id+ or
|
176
256
|
# conditions given, or +false+ otherwise. The argument can take six forms:
|
177
257
|
#
|
@@ -195,6 +275,7 @@ module ActiveRecord
|
|
195
275
|
# Person.exists?(5)
|
196
276
|
# Person.exists?('5')
|
197
277
|
# Person.exists?(['name LIKE ?', "%#{query}%"])
|
278
|
+
# Person.exists?(id: [1, 4, 8])
|
198
279
|
# Person.exists?(name: 'David')
|
199
280
|
# Person.exists?(false)
|
200
281
|
# Person.exists?
|
@@ -230,9 +311,9 @@ module ActiveRecord
|
|
230
311
|
conditions = " [#{conditions}]" if conditions
|
231
312
|
|
232
313
|
if Array(ids).size == 1
|
233
|
-
error = "Couldn't find #{@klass.name} with #{primary_key}=#{ids}#{conditions}"
|
314
|
+
error = "Couldn't find #{@klass.name} with '#{primary_key}'=#{ids}#{conditions}"
|
234
315
|
else
|
235
|
-
error = "Couldn't find all #{@klass.name.pluralize} with
|
316
|
+
error = "Couldn't find all #{@klass.name.pluralize} with '#{primary_key}': "
|
236
317
|
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
|
237
318
|
end
|
238
319
|
|
@@ -266,7 +347,15 @@ module ActiveRecord
|
|
266
347
|
end
|
267
348
|
|
268
349
|
def construct_relation_for_association_calculations
|
269
|
-
|
350
|
+
from = arel.froms.first
|
351
|
+
if Arel::Table === from
|
352
|
+
apply_join_dependency(self, construct_join_dependency)
|
353
|
+
else
|
354
|
+
# FIXME: as far as I can tell, `from` will always be an Arel::Table.
|
355
|
+
# There are no tests that test this branch, but presumably it's
|
356
|
+
# possible for `from` to be a list?
|
357
|
+
apply_join_dependency(self, construct_join_dependency(from))
|
358
|
+
end
|
270
359
|
end
|
271
360
|
|
272
361
|
def apply_join_dependency(relation, join_dependency)
|
@@ -363,19 +452,19 @@ module ActiveRecord
|
|
363
452
|
end
|
364
453
|
end
|
365
454
|
|
366
|
-
def
|
455
|
+
def find_nth(ordinal, offset)
|
367
456
|
if loaded?
|
368
|
-
@records.
|
457
|
+
@records.send(ordinal)
|
369
458
|
else
|
370
|
-
@
|
459
|
+
@offsets[offset] ||= find_nth_with_limit(offset, 1).first
|
371
460
|
end
|
372
461
|
end
|
373
462
|
|
374
|
-
def
|
463
|
+
def find_nth_with_limit(offset, limit)
|
375
464
|
if order_values.empty? && primary_key
|
376
|
-
order(arel_table[primary_key].asc).limit(limit).to_a
|
465
|
+
order(arel_table[primary_key].asc).limit(limit).offset(offset).to_a
|
377
466
|
else
|
378
|
-
limit(limit).to_a
|
467
|
+
limit(limit).offset(offset).to_a
|
379
468
|
end
|
380
469
|
end
|
381
470
|
|