activerecord 3.1.11 → 3.2.0
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.
- data/CHANGELOG.md +6294 -97
- data/README.rdoc +2 -2
- data/examples/performance.rb +55 -31
- data/lib/active_record/aggregations.rb +2 -2
- data/lib/active_record/associations/association.rb +2 -42
- data/lib/active_record/associations/association_scope.rb +3 -30
- data/lib/active_record/associations/builder/association.rb +6 -4
- data/lib/active_record/associations/builder/belongs_to.rb +3 -3
- data/lib/active_record/associations/builder/collection_association.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +5 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -16
- data/lib/active_record/associations/collection_association.rb +55 -28
- data/lib/active_record/associations/collection_proxy.rb +1 -35
- data/lib/active_record/associations/has_many_association.rb +5 -1
- data/lib/active_record/associations/has_many_through_association.rb +11 -8
- data/lib/active_record/associations/join_dependency.rb +1 -1
- data/lib/active_record/associations/preloader/association.rb +3 -1
- data/lib/active_record/associations.rb +82 -69
- data/lib/active_record/attribute_assignment.rb +221 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
- data/lib/active_record/attribute_methods/dirty.rb +3 -3
- data/lib/active_record/attribute_methods/primary_key.rb +62 -25
- data/lib/active_record/attribute_methods/read.rb +72 -83
- data/lib/active_record/attribute_methods/serialization.rb +93 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -14
- data/lib/active_record/attribute_methods/write.rb +27 -5
- data/lib/active_record/attribute_methods.rb +209 -30
- data/lib/active_record/autosave_association.rb +23 -8
- data/lib/active_record/base.rb +217 -1709
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +98 -132
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +82 -29
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +13 -42
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +9 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +36 -25
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +78 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
- data/lib/active_record/connection_adapters/column.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +138 -578
- data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -658
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +144 -94
- data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +43 -22
- data/lib/active_record/counter_cache.rb +4 -3
- data/lib/active_record/dynamic_matchers.rb +79 -0
- data/lib/active_record/errors.rb +11 -1
- data/lib/active_record/explain.rb +83 -0
- data/lib/active_record/explain_subscriber.rb +21 -0
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/fixtures.rb +31 -76
- data/lib/active_record/identity_map.rb +4 -11
- data/lib/active_record/inheritance.rb +167 -0
- data/lib/active_record/integration.rb +49 -0
- data/lib/active_record/locking/optimistic.rb +30 -25
- data/lib/active_record/locking/pessimistic.rb +23 -1
- data/lib/active_record/log_subscriber.rb +3 -3
- data/lib/active_record/migration/command_recorder.rb +8 -8
- data/lib/active_record/migration.rb +47 -30
- data/lib/active_record/model_schema.rb +366 -0
- data/lib/active_record/nested_attributes.rb +3 -2
- data/lib/active_record/persistence.rb +51 -9
- data/lib/active_record/querying.rb +58 -0
- data/lib/active_record/railtie.rb +24 -28
- data/lib/active_record/railties/controller_runtime.rb +3 -1
- data/lib/active_record/railties/databases.rake +134 -77
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +26 -0
- data/lib/active_record/reflection.rb +7 -15
- data/lib/active_record/relation/batches.rb +5 -2
- data/lib/active_record/relation/calculations.rb +27 -6
- data/lib/active_record/relation/delegation.rb +49 -0
- data/lib/active_record/relation/finder_methods.rb +6 -5
- data/lib/active_record/relation/predicate_builder.rb +12 -19
- data/lib/active_record/relation/query_methods.rb +76 -10
- data/lib/active_record/relation/spawn_methods.rb +11 -2
- data/lib/active_record/relation.rb +77 -34
- data/lib/active_record/result.rb +1 -1
- data/lib/active_record/sanitization.rb +194 -0
- data/lib/active_record/schema_dumper.rb +5 -2
- data/lib/active_record/scoping/default.rb +142 -0
- data/lib/active_record/scoping/named.rb +202 -0
- data/lib/active_record/scoping.rb +152 -0
- data/lib/active_record/serialization.rb +1 -43
- data/lib/active_record/serializers/xml_serializer.rb +2 -44
- data/lib/active_record/session_store.rb +15 -15
- data/lib/active_record/store.rb +50 -0
- data/lib/active_record/test_case.rb +11 -7
- data/lib/active_record/timestamp.rb +16 -3
- data/lib/active_record/transactions.rb +5 -5
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +5 -4
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/version.rb +2 -2
- data/lib/active_record.rb +28 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +9 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -1
- data/lib/rails/generators/active_record/model/templates/migration.rb +3 -5
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
- metadata +50 -40
- checksums.yaml +0 -7
- data/lib/active_record/named_scope.rb +0 -200
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require 'active_support/core_ext/object/inclusion'
|
|
2
|
+
require 'active_record'
|
|
2
3
|
|
|
3
4
|
db_namespace = namespace :db do
|
|
4
5
|
task :load_config => :rails_env do
|
|
5
|
-
require 'active_record'
|
|
6
6
|
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
|
|
7
7
|
ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a
|
|
8
8
|
|
|
@@ -27,7 +27,7 @@ db_namespace = namespace :db do
|
|
|
27
27
|
#
|
|
28
28
|
# development:
|
|
29
29
|
# database: blog_development
|
|
30
|
-
#
|
|
30
|
+
# *defaults
|
|
31
31
|
next unless config['database']
|
|
32
32
|
# Only connect to local databases
|
|
33
33
|
local_database?(config) { create_database(config) }
|
|
@@ -37,11 +37,8 @@ db_namespace = namespace :db do
|
|
|
37
37
|
|
|
38
38
|
desc 'Create the database from config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)'
|
|
39
39
|
task :create => :load_config do
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
create_database(ActiveRecord::Base.configurations['test'])
|
|
43
|
-
end
|
|
44
|
-
create_database(ActiveRecord::Base.configurations[Rails.env])
|
|
40
|
+
configs_for_environment.each { |config| create_database(config) }
|
|
41
|
+
ActiveRecord::Base.establish_connection(configs_for_environment.first)
|
|
45
42
|
end
|
|
46
43
|
|
|
47
44
|
def mysql_creation_options(config)
|
|
@@ -138,12 +135,7 @@ db_namespace = namespace :db do
|
|
|
138
135
|
|
|
139
136
|
desc 'Drops the database for the current Rails.env (use db:drop:all to drop all databases)'
|
|
140
137
|
task :drop => :load_config do
|
|
141
|
-
|
|
142
|
-
begin
|
|
143
|
-
drop_database(config)
|
|
144
|
-
rescue Exception => e
|
|
145
|
-
$stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}"
|
|
146
|
-
end
|
|
138
|
+
configs_for_environment.each { |config| drop_database_and_rescue(config) }
|
|
147
139
|
end
|
|
148
140
|
|
|
149
141
|
def local_database?(config, &block)
|
|
@@ -158,8 +150,19 @@ db_namespace = namespace :db do
|
|
|
158
150
|
desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
|
|
159
151
|
task :migrate => [:environment, :load_config] do
|
|
160
152
|
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
|
161
|
-
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
|
|
162
|
-
|
|
153
|
+
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
|
|
154
|
+
ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
|
|
155
|
+
end
|
|
156
|
+
db_namespace['_dump'].invoke
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
task :_dump do
|
|
160
|
+
case ActiveRecord::Base.schema_format
|
|
161
|
+
when :ruby then db_namespace["schema:dump"].invoke
|
|
162
|
+
when :sql then db_namespace["structure:dump"].invoke
|
|
163
|
+
else
|
|
164
|
+
raise "unknown schema format #{ActiveRecord::Base.schema_format}"
|
|
165
|
+
end
|
|
163
166
|
end
|
|
164
167
|
|
|
165
168
|
namespace :migrate do
|
|
@@ -182,7 +185,7 @@ db_namespace = namespace :db do
|
|
|
182
185
|
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
|
183
186
|
raise 'VERSION is required' unless version
|
|
184
187
|
ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
|
|
185
|
-
db_namespace['
|
|
188
|
+
db_namespace['_dump'].invoke
|
|
186
189
|
end
|
|
187
190
|
|
|
188
191
|
# desc 'Runs the "down" for a given migration VERSION.'
|
|
@@ -190,7 +193,7 @@ db_namespace = namespace :db do
|
|
|
190
193
|
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
|
191
194
|
raise 'VERSION is required' unless version
|
|
192
195
|
ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
|
|
193
|
-
db_namespace['
|
|
196
|
+
db_namespace['_dump'].invoke
|
|
194
197
|
end
|
|
195
198
|
|
|
196
199
|
desc 'Display status of migrations'
|
|
@@ -230,18 +233,21 @@ db_namespace = namespace :db do
|
|
|
230
233
|
task :rollback => [:environment, :load_config] do
|
|
231
234
|
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
|
232
235
|
ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
|
|
233
|
-
db_namespace['
|
|
236
|
+
db_namespace['_dump'].invoke
|
|
234
237
|
end
|
|
235
238
|
|
|
236
239
|
# desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
|
|
237
240
|
task :forward => [:environment, :load_config] do
|
|
238
241
|
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
|
239
242
|
ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step)
|
|
240
|
-
db_namespace['
|
|
243
|
+
db_namespace['_dump'].invoke
|
|
241
244
|
end
|
|
242
245
|
|
|
243
246
|
# desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
|
|
244
|
-
task :reset =>
|
|
247
|
+
task :reset => :environment do
|
|
248
|
+
db_namespace["drop"].invoke
|
|
249
|
+
db_namespace["setup"].invoke
|
|
250
|
+
end
|
|
245
251
|
|
|
246
252
|
# desc "Retrieves the charset for the current environment's database"
|
|
247
253
|
task :charset => :environment do
|
|
@@ -280,29 +286,28 @@ db_namespace = namespace :db do
|
|
|
280
286
|
|
|
281
287
|
# desc "Raises an error if there are pending migrations"
|
|
282
288
|
task :abort_if_pending_migrations => :environment do
|
|
283
|
-
|
|
284
|
-
pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations
|
|
289
|
+
pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations
|
|
285
290
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
end
|
|
291
|
-
abort %{Run "rake db:migrate" to update your database then try again.}
|
|
291
|
+
if pending_migrations.any?
|
|
292
|
+
puts "You have #{pending_migrations.size} pending migrations:"
|
|
293
|
+
pending_migrations.each do |pending_migration|
|
|
294
|
+
puts ' %4d %s' % [pending_migration.version, pending_migration.name]
|
|
292
295
|
end
|
|
296
|
+
abort %{Run `rake db:migrate` to update your database then try again.}
|
|
293
297
|
end
|
|
294
298
|
end
|
|
295
299
|
|
|
296
300
|
desc 'Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)'
|
|
297
|
-
task :setup => [
|
|
301
|
+
task :setup => ['db:schema:load_if_ruby', 'db:structure:load_if_sql', :seed]
|
|
298
302
|
|
|
299
303
|
desc 'Load the seed data from db/seeds.rb'
|
|
300
|
-
task :seed
|
|
304
|
+
task :seed do
|
|
305
|
+
db_namespace['abort_if_pending_migrations'].invoke
|
|
301
306
|
Rails.application.load_seed
|
|
302
307
|
end
|
|
303
308
|
|
|
304
309
|
namespace :fixtures do
|
|
305
|
-
desc "Load fixtures into the current environment's database.
|
|
310
|
+
desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
|
|
306
311
|
task :load => :environment do
|
|
307
312
|
require 'active_record/fixtures'
|
|
308
313
|
|
|
@@ -357,93 +362,125 @@ db_namespace = namespace :db do
|
|
|
357
362
|
if File.exists?(file)
|
|
358
363
|
load(file)
|
|
359
364
|
else
|
|
360
|
-
abort %{#{file} doesn't exist yet. Run
|
|
365
|
+
abort %{#{file} doesn't exist yet. Run `rake db:migrate` to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded}
|
|
361
366
|
end
|
|
362
367
|
end
|
|
368
|
+
|
|
369
|
+
task :load_if_ruby => 'db:create' do
|
|
370
|
+
db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby
|
|
371
|
+
end
|
|
363
372
|
end
|
|
364
373
|
|
|
365
374
|
namespace :structure do
|
|
366
|
-
desc 'Dump the database structure to
|
|
375
|
+
desc 'Dump the database structure to db/structure.sql. Specify another file with DB_STRUCTURE=db/my_structure.sql'
|
|
367
376
|
task :dump => :environment do
|
|
368
377
|
abcs = ActiveRecord::Base.configurations
|
|
378
|
+
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
|
|
369
379
|
case abcs[Rails.env]['adapter']
|
|
370
380
|
when /mysql/, 'oci', 'oracle'
|
|
371
381
|
ActiveRecord::Base.establish_connection(abcs[Rails.env])
|
|
372
|
-
File.open(
|
|
382
|
+
File.open(filename, "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump }
|
|
373
383
|
when /postgresql/
|
|
374
|
-
|
|
375
|
-
ENV['PGPORT'] = abcs[Rails.env]["port"].to_s if abcs[Rails.env]['port']
|
|
376
|
-
ENV['PGPASSWORD'] = abcs[Rails.env]['password'].to_s if abcs[Rails.env]['password']
|
|
384
|
+
set_psql_env(abcs[Rails.env])
|
|
377
385
|
search_path = abcs[Rails.env]['schema_search_path']
|
|
378
386
|
unless search_path.blank?
|
|
379
|
-
search_path = search_path.split(",").map{|
|
|
387
|
+
search_path = search_path.split(",").map{|search_path_part| "--schema=#{search_path_part.strip}" }.join(" ")
|
|
380
388
|
end
|
|
381
|
-
`pg_dump -i -
|
|
389
|
+
`pg_dump -i -s -x -O -f #{filename} #{search_path} #{abcs[Rails.env]['database']}`
|
|
382
390
|
raise 'Error dumping database' if $?.exitstatus == 1
|
|
383
391
|
when /sqlite/
|
|
384
|
-
dbfile = abcs[Rails.env]['database']
|
|
385
|
-
`sqlite3 #{dbfile} .schema >
|
|
392
|
+
dbfile = abcs[Rails.env]['database']
|
|
393
|
+
`sqlite3 #{dbfile} .schema > #{filename}`
|
|
386
394
|
when 'sqlserver'
|
|
387
|
-
`smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f
|
|
395
|
+
`smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f #{filename} -A -U`
|
|
388
396
|
when "firebird"
|
|
389
397
|
set_firebird_env(abcs[Rails.env])
|
|
390
398
|
db_string = firebird_db_string(abcs[Rails.env])
|
|
391
|
-
sh "isql -a #{db_string} > #{
|
|
399
|
+
sh "isql -a #{db_string} > #{filename}"
|
|
392
400
|
else
|
|
393
401
|
raise "Task not supported by '#{abcs[Rails.env]["adapter"]}'"
|
|
394
402
|
end
|
|
395
403
|
|
|
396
404
|
if ActiveRecord::Base.connection.supports_migrations?
|
|
397
|
-
File.open(
|
|
405
|
+
File.open(filename, "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
|
|
398
406
|
end
|
|
399
407
|
end
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
namespace :test do
|
|
403
|
-
# desc "Recreate the test database from the current schema.rb"
|
|
404
|
-
task :load => 'db:test:purge' do
|
|
405
|
-
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
|
|
406
|
-
ActiveRecord::Schema.verbose = false
|
|
407
|
-
db_namespace['schema:load'].invoke
|
|
408
|
-
end
|
|
409
408
|
|
|
410
|
-
# desc "Recreate the
|
|
411
|
-
task :
|
|
409
|
+
# desc "Recreate the databases from the structure.sql file"
|
|
410
|
+
task :load => [:environment, :load_config] do
|
|
411
|
+
env = ENV['RAILS_ENV'] || 'test'
|
|
412
412
|
|
|
413
|
-
# desc "Recreate the test databases from the development structure"
|
|
414
|
-
task :clone_structure => [ 'db:structure:dump', 'db:test:purge' ] do
|
|
415
413
|
abcs = ActiveRecord::Base.configurations
|
|
416
|
-
|
|
414
|
+
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
|
|
415
|
+
case abcs[env]['adapter']
|
|
417
416
|
when /mysql/
|
|
418
|
-
ActiveRecord::Base.establish_connection(
|
|
417
|
+
ActiveRecord::Base.establish_connection(abcs[env])
|
|
419
418
|
ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0')
|
|
420
|
-
IO.
|
|
419
|
+
IO.read(filename).split("\n\n").each do |table|
|
|
421
420
|
ActiveRecord::Base.connection.execute(table)
|
|
422
421
|
end
|
|
423
422
|
when /postgresql/
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
ENV['PGPASSWORD'] = abcs['test']['password'].to_s if abcs['test']['password']
|
|
427
|
-
`psql -U "#{abcs['test']['username']}" -f "#{Rails.root}/db/#{Rails.env}_structure.sql" #{abcs['test']['database']} #{abcs['test']['template']}`
|
|
423
|
+
set_psql_env(abcs[env])
|
|
424
|
+
`psql -f "#{filename}" #{abcs[env]['database']} #{abcs[env]['template']}`
|
|
428
425
|
when /sqlite/
|
|
429
|
-
dbfile = abcs[
|
|
430
|
-
`sqlite3 #{dbfile} < "#{
|
|
426
|
+
dbfile = abcs[env]['database']
|
|
427
|
+
`sqlite3 #{dbfile} < "#{filename}"`
|
|
431
428
|
when 'sqlserver'
|
|
432
|
-
`sqlcmd -S #{abcs[
|
|
429
|
+
`sqlcmd -S #{abcs[env]['host']} -d #{abcs[env]['database']} -U #{abcs[env]['username']} -P #{abcs[env]['password']} -i #{filename}`
|
|
433
430
|
when 'oci', 'oracle'
|
|
434
|
-
ActiveRecord::Base.establish_connection(
|
|
435
|
-
IO.
|
|
431
|
+
ActiveRecord::Base.establish_connection(abcs[env])
|
|
432
|
+
IO.read(filename).split(";\n\n").each do |ddl|
|
|
436
433
|
ActiveRecord::Base.connection.execute(ddl)
|
|
437
434
|
end
|
|
438
435
|
when 'firebird'
|
|
439
|
-
set_firebird_env(abcs[
|
|
440
|
-
db_string = firebird_db_string(abcs[
|
|
441
|
-
sh "isql -i #{
|
|
436
|
+
set_firebird_env(abcs[env])
|
|
437
|
+
db_string = firebird_db_string(abcs[env])
|
|
438
|
+
sh "isql -i #{filename} #{db_string}"
|
|
442
439
|
else
|
|
443
|
-
raise "Task not supported by '#{abcs[
|
|
440
|
+
raise "Task not supported by '#{abcs[env]['adapter']}'"
|
|
444
441
|
end
|
|
445
442
|
end
|
|
446
443
|
|
|
444
|
+
task :load_if_sql => 'db:create' do
|
|
445
|
+
db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
namespace :test do
|
|
450
|
+
|
|
451
|
+
# desc "Recreate the test database from the current schema"
|
|
452
|
+
task :load => 'db:test:purge' do
|
|
453
|
+
case ActiveRecord::Base.schema_format
|
|
454
|
+
when :ruby
|
|
455
|
+
db_namespace["test:load_schema"].invoke
|
|
456
|
+
when :sql
|
|
457
|
+
db_namespace["test:load_structure"].invoke
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
# desc "Recreate the test database from an existent structure.sql file"
|
|
462
|
+
task :load_structure => 'db:test:purge' do
|
|
463
|
+
begin
|
|
464
|
+
old_env, ENV['RAILS_ENV'] = ENV['RAILS_ENV'], 'test'
|
|
465
|
+
db_namespace["structure:load"].invoke
|
|
466
|
+
ensure
|
|
467
|
+
ENV['RAILS_ENV'] = old_env
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# desc "Recreate the test database from an existent schema.rb file"
|
|
472
|
+
task :load_schema => 'db:test:purge' do
|
|
473
|
+
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
|
|
474
|
+
ActiveRecord::Schema.verbose = false
|
|
475
|
+
db_namespace["schema:load"].invoke
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# desc "Recreate the test database from a fresh schema.rb file"
|
|
479
|
+
task :clone => %w(db:schema:dump db:test:load_schema)
|
|
480
|
+
|
|
481
|
+
# desc "Recreate the test database from a fresh structure.sql file"
|
|
482
|
+
task :clone_structure => [ "db:structure:dump", "db:test:load_structure" ]
|
|
483
|
+
|
|
447
484
|
# desc "Empty the test database"
|
|
448
485
|
task :purge => :environment do
|
|
449
486
|
abcs = ActiveRecord::Base.configurations
|
|
@@ -456,7 +493,7 @@ db_namespace = namespace :db do
|
|
|
456
493
|
drop_database(abcs['test'])
|
|
457
494
|
create_database(abcs['test'])
|
|
458
495
|
when /sqlite/
|
|
459
|
-
dbfile = abcs['test']['database']
|
|
496
|
+
dbfile = abcs['test']['database']
|
|
460
497
|
File.delete(dbfile) if File.exist?(dbfile)
|
|
461
498
|
when 'sqlserver'
|
|
462
499
|
test = abcs.deep_dup['test']
|
|
@@ -479,7 +516,7 @@ db_namespace = namespace :db do
|
|
|
479
516
|
|
|
480
517
|
# desc 'Check for pending migrations and load the test schema'
|
|
481
518
|
task :prepare => 'db:abort_if_pending_migrations' do
|
|
482
|
-
|
|
519
|
+
unless ActiveRecord::Base.configurations.blank?
|
|
483
520
|
db_namespace[{ :sql => 'test:clone_structure', :ruby => 'test:load' }[ActiveRecord::Base.schema_format]].invoke
|
|
484
521
|
end
|
|
485
522
|
end
|
|
@@ -489,8 +526,7 @@ db_namespace = namespace :db do
|
|
|
489
526
|
# desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
|
|
490
527
|
task :create => :environment do
|
|
491
528
|
raise 'Task unavailable to this database (no migration support)' unless ActiveRecord::Base.connection.supports_migrations?
|
|
492
|
-
|
|
493
|
-
Rails::Generators.configure!
|
|
529
|
+
Rails.application.load_generators
|
|
494
530
|
require 'rails/generators/rails/session_migration/session_migration_generator'
|
|
495
531
|
Rails::Generators::SessionMigrationGenerator.start [ ENV['MIGRATION'] || 'add_sessions_table' ]
|
|
496
532
|
end
|
|
@@ -549,6 +585,20 @@ def drop_database(config)
|
|
|
549
585
|
end
|
|
550
586
|
end
|
|
551
587
|
|
|
588
|
+
def drop_database_and_rescue(config)
|
|
589
|
+
begin
|
|
590
|
+
drop_database(config)
|
|
591
|
+
rescue Exception => e
|
|
592
|
+
$stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}"
|
|
593
|
+
end
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
def configs_for_environment
|
|
597
|
+
environments = [Rails.env]
|
|
598
|
+
environments << 'test' if Rails.env.development?
|
|
599
|
+
ActiveRecord::Base.configurations.values_at(*environments).compact.reject { |config| config['database'].blank? }
|
|
600
|
+
end
|
|
601
|
+
|
|
552
602
|
def session_table_name
|
|
553
603
|
ActiveRecord::SessionStore::Session.table_name
|
|
554
604
|
end
|
|
@@ -561,3 +611,10 @@ end
|
|
|
561
611
|
def firebird_db_string(config)
|
|
562
612
|
FireRuby::Database.db_string_for(config.symbolize_keys)
|
|
563
613
|
end
|
|
614
|
+
|
|
615
|
+
def set_psql_env(config)
|
|
616
|
+
ENV['PGHOST'] = config['host'] if config['host']
|
|
617
|
+
ENV['PGPORT'] = config['port'].to_s if config['port']
|
|
618
|
+
ENV['PGPASSWORD'] = config['password'].to_s if config['password']
|
|
619
|
+
ENV['PGUSER'] = config['username'].to_s if config['username']
|
|
620
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'active_support/concern'
|
|
2
|
+
require 'active_support/core_ext/class/attribute'
|
|
3
|
+
|
|
4
|
+
module ActiveRecord
|
|
5
|
+
module ReadonlyAttributes
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
class_attribute :_attr_readonly, :instance_writer => false
|
|
10
|
+
self._attr_readonly = []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module ClassMethods
|
|
14
|
+
# Attributes listed as readonly will be used to create a new record but update operations will
|
|
15
|
+
# ignore these fields.
|
|
16
|
+
def attr_readonly(*attributes)
|
|
17
|
+
self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Returns an array of all the attributes that have been specified as readonly.
|
|
21
|
+
def readonly_attributes
|
|
22
|
+
self._attr_readonly
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'active_support/core_ext/class/attribute'
|
|
2
|
-
require 'active_support/core_ext/module/deprecation'
|
|
3
2
|
require 'active_support/core_ext/object/inclusion'
|
|
4
3
|
|
|
5
4
|
module ActiveRecord
|
|
@@ -125,7 +124,7 @@ module ActiveRecord
|
|
|
125
124
|
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
|
|
126
125
|
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
|
127
126
|
def class_name
|
|
128
|
-
@class_name ||= options[:class_name] || derive_class_name
|
|
127
|
+
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
|
129
128
|
end
|
|
130
129
|
|
|
131
130
|
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
|
|
@@ -178,15 +177,9 @@ module ActiveRecord
|
|
|
178
177
|
@collection = macro.in?([:has_many, :has_and_belongs_to_many])
|
|
179
178
|
end
|
|
180
179
|
|
|
181
|
-
# This is a hack so that we can tell if build_association was overridden, in order to
|
|
182
|
-
# provide an appropriate deprecation if the overridden method ignored the &block. Please
|
|
183
|
-
# see Association#build_record for details.
|
|
184
|
-
attr_accessor :original_build_association_called # :nodoc
|
|
185
|
-
|
|
186
180
|
# Returns a new, unsaved instance of the associated class. +options+ will
|
|
187
181
|
# be passed to the class's constructor.
|
|
188
182
|
def build_association(*options, &block)
|
|
189
|
-
@original_build_association_called = true
|
|
190
183
|
klass.new(*options, &block)
|
|
191
184
|
end
|
|
192
185
|
|
|
@@ -202,11 +195,6 @@ module ActiveRecord
|
|
|
202
195
|
@foreign_key ||= options[:foreign_key] || derive_foreign_key
|
|
203
196
|
end
|
|
204
197
|
|
|
205
|
-
def primary_key_name
|
|
206
|
-
foreign_key
|
|
207
|
-
end
|
|
208
|
-
deprecate :primary_key_name => :foreign_key
|
|
209
|
-
|
|
210
198
|
def foreign_type
|
|
211
199
|
@foreign_type ||= options[:foreign_type] || "#{name}_type"
|
|
212
200
|
end
|
|
@@ -274,6 +262,10 @@ module ActiveRecord
|
|
|
274
262
|
[self]
|
|
275
263
|
end
|
|
276
264
|
|
|
265
|
+
def nested?
|
|
266
|
+
false
|
|
267
|
+
end
|
|
268
|
+
|
|
277
269
|
# An array of arrays of conditions. Each item in the outside array corresponds to a reflection
|
|
278
270
|
# in the #chain. The inside arrays are simply conditions (and each condition may itself be
|
|
279
271
|
# a hash, array, arel predicate, etc...)
|
|
@@ -381,7 +373,7 @@ module ActiveRecord
|
|
|
381
373
|
delegate :foreign_key, :foreign_type, :association_foreign_key,
|
|
382
374
|
:active_record_primary_key, :type, :to => :source_reflection
|
|
383
375
|
|
|
384
|
-
# Gets the source of the through reflection.
|
|
376
|
+
# Gets the source of the through reflection. It checks both a singularized
|
|
385
377
|
# and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
|
|
386
378
|
#
|
|
387
379
|
# class Post < ActiveRecord::Base
|
|
@@ -469,7 +461,7 @@ module ActiveRecord
|
|
|
469
461
|
source_reflection.source_macro
|
|
470
462
|
end
|
|
471
463
|
|
|
472
|
-
# A through association is nested
|
|
464
|
+
# A through association is nested if there would be more than one join table
|
|
473
465
|
def nested?
|
|
474
466
|
chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many
|
|
475
467
|
end
|
|
@@ -66,11 +66,14 @@ module ActiveRecord
|
|
|
66
66
|
records = relation.where(table[primary_key].gteq(start)).all
|
|
67
67
|
|
|
68
68
|
while records.any?
|
|
69
|
+
records_size = records.size
|
|
70
|
+
primary_key_offset = records.last.id
|
|
71
|
+
|
|
69
72
|
yield records
|
|
70
73
|
|
|
71
|
-
break if
|
|
74
|
+
break if records_size < batch_size
|
|
72
75
|
|
|
73
|
-
if primary_key_offset
|
|
76
|
+
if primary_key_offset
|
|
74
77
|
records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
|
|
75
78
|
else
|
|
76
79
|
raise "Primary key not included in the custom select clause"
|
|
@@ -66,7 +66,7 @@ module ActiveRecord
|
|
|
66
66
|
calculate(:average, column_name, options)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
# Calculates the minimum value on a given column.
|
|
69
|
+
# Calculates the minimum value on a given column. The value is returned
|
|
70
70
|
# with the same data type of the column, or +nil+ if there's no row. See
|
|
71
71
|
# +calculate+ for examples with options.
|
|
72
72
|
#
|
|
@@ -89,11 +89,15 @@ module ActiveRecord
|
|
|
89
89
|
# +calculate+ for examples with options.
|
|
90
90
|
#
|
|
91
91
|
# Person.sum('age') # => 4562
|
|
92
|
-
def sum(
|
|
93
|
-
|
|
92
|
+
def sum(*args)
|
|
93
|
+
if block_given?
|
|
94
|
+
self.to_a.sum(*args) {|*block_args| yield(*block_args)}
|
|
95
|
+
else
|
|
96
|
+
calculate(:sum, *args)
|
|
97
|
+
end
|
|
94
98
|
end
|
|
95
99
|
|
|
96
|
-
# This calculates aggregate values in the given column.
|
|
100
|
+
# This calculates aggregate values in the given column. Methods for count, sum, average,
|
|
97
101
|
# minimum, and maximum have been added as shortcuts. Options such as <tt>:conditions</tt>,
|
|
98
102
|
# <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
|
|
99
103
|
#
|
|
@@ -101,7 +105,7 @@ module ActiveRecord
|
|
|
101
105
|
# * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
|
|
102
106
|
# for AVG, and the given column's type for everything else.
|
|
103
107
|
# * Grouped values: This returns an ordered hash of the values and groups them by the
|
|
104
|
-
# <tt>:group</tt> option.
|
|
108
|
+
# <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
|
|
105
109
|
#
|
|
106
110
|
# values = Person.maximum(:age, :group => 'last_name')
|
|
107
111
|
# puts values["Drake"]
|
|
@@ -119,7 +123,7 @@ module ActiveRecord
|
|
|
119
123
|
# Options:
|
|
120
124
|
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
|
|
121
125
|
# See conditions in the intro to ActiveRecord::Base.
|
|
122
|
-
# * <tt>:include</tt>: Eager loading, see Associations for details.
|
|
126
|
+
# * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
|
|
123
127
|
# the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
|
|
124
128
|
# * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id".
|
|
125
129
|
# (Rarely needed).
|
|
@@ -162,6 +166,23 @@ module ActiveRecord
|
|
|
162
166
|
0
|
|
163
167
|
end
|
|
164
168
|
|
|
169
|
+
# This method is designed to perform select by a single column as direct SQL query
|
|
170
|
+
# Returns <tt>Array</tt> with values of the specified column name
|
|
171
|
+
# The values has same data type as column.
|
|
172
|
+
#
|
|
173
|
+
# Examples:
|
|
174
|
+
#
|
|
175
|
+
# Person.pluck(:id) # SELECT people.id FROM people
|
|
176
|
+
# Person.uniq.pluck(:role) # SELECT DISTINCT role FROM people
|
|
177
|
+
# Person.where(:confirmed => true).limit(5).pluck(:id)
|
|
178
|
+
#
|
|
179
|
+
def pluck(column_name)
|
|
180
|
+
column_name = column_name.to_s
|
|
181
|
+
klass.connection.select_all(select(column_name).arel).map! do |attributes|
|
|
182
|
+
klass.type_cast_attribute(attributes.keys.first, klass.initialize_attributes(attributes))
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
165
186
|
private
|
|
166
187
|
|
|
167
188
|
def perform_calculation(operation, column_name, options = {})
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Delegation
|
|
5
|
+
# Set up common delegations for performance (avoids method_missing)
|
|
6
|
+
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
|
|
7
|
+
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
|
|
8
|
+
:connection, :columns_hash, :auto_explain_threshold_in_seconds, :to => :klass
|
|
9
|
+
|
|
10
|
+
def self.delegate_to_scoped_klass(method)
|
|
11
|
+
if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
|
|
12
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
13
|
+
def #{method}(*args, &block)
|
|
14
|
+
scoping { @klass.#{method}(*args, &block) }
|
|
15
|
+
end
|
|
16
|
+
RUBY
|
|
17
|
+
else
|
|
18
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
19
|
+
def #{method}(*args, &block)
|
|
20
|
+
scoping { @klass.send(#{method.inspect}, *args, &block) }
|
|
21
|
+
end
|
|
22
|
+
RUBY
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def respond_to?(method, include_private = false)
|
|
27
|
+
super || Array.method_defined?(method) ||
|
|
28
|
+
@klass.respond_to?(method, include_private) ||
|
|
29
|
+
arel.respond_to?(method, include_private)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
protected
|
|
33
|
+
|
|
34
|
+
def method_missing(method, *args, &block)
|
|
35
|
+
if Array.method_defined?(method)
|
|
36
|
+
::ActiveRecord::Delegation.delegate method, :to => :to_a
|
|
37
|
+
to_a.send(method, *args, &block)
|
|
38
|
+
elsif @klass.respond_to?(method)
|
|
39
|
+
::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
|
|
40
|
+
scoping { @klass.send(method, *args, &block) }
|
|
41
|
+
elsif arel.respond_to?(method)
|
|
42
|
+
::ActiveRecord::Delegation.delegate method, :to => :arel
|
|
43
|
+
arel.send(method, *args, &block)
|
|
44
|
+
else
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|