activerecord 3.1.12 → 3.2.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.

Files changed (99) hide show
  1. data/CHANGELOG.md +6263 -103
  2. data/README.rdoc +2 -2
  3. data/examples/performance.rb +55 -31
  4. data/lib/active_record.rb +28 -2
  5. data/lib/active_record/aggregations.rb +2 -2
  6. data/lib/active_record/associations.rb +82 -69
  7. data/lib/active_record/associations/association.rb +2 -37
  8. data/lib/active_record/associations/association_scope.rb +3 -30
  9. data/lib/active_record/associations/builder/association.rb +6 -4
  10. data/lib/active_record/associations/builder/belongs_to.rb +3 -3
  11. data/lib/active_record/associations/builder/collection_association.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +4 -4
  13. data/lib/active_record/associations/builder/has_one.rb +5 -6
  14. data/lib/active_record/associations/builder/singular_association.rb +3 -16
  15. data/lib/active_record/associations/collection_association.rb +55 -28
  16. data/lib/active_record/associations/collection_proxy.rb +1 -35
  17. data/lib/active_record/associations/has_many_association.rb +5 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +11 -8
  19. data/lib/active_record/associations/join_dependency.rb +1 -1
  20. data/lib/active_record/associations/preloader/association.rb +3 -1
  21. data/lib/active_record/attribute_assignment.rb +221 -0
  22. data/lib/active_record/attribute_methods.rb +212 -32
  23. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  24. data/lib/active_record/attribute_methods/dirty.rb +3 -3
  25. data/lib/active_record/attribute_methods/primary_key.rb +62 -25
  26. data/lib/active_record/attribute_methods/read.rb +69 -80
  27. data/lib/active_record/attribute_methods/serialization.rb +89 -0
  28. data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -14
  29. data/lib/active_record/attribute_methods/write.rb +27 -5
  30. data/lib/active_record/autosave_association.rb +23 -8
  31. data/lib/active_record/base.rb +223 -1712
  32. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +98 -132
  33. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +82 -29
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +13 -42
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -4
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +36 -25
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -13
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +78 -43
  40. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
  41. data/lib/active_record/connection_adapters/mysql2_adapter.rb +138 -578
  42. data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -658
  43. data/lib/active_record/connection_adapters/postgresql_adapter.rb +144 -94
  44. data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
  45. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
  46. data/lib/active_record/connection_adapters/sqlite_adapter.rb +43 -22
  47. data/lib/active_record/counter_cache.rb +1 -1
  48. data/lib/active_record/dynamic_matchers.rb +79 -0
  49. data/lib/active_record/errors.rb +11 -1
  50. data/lib/active_record/explain.rb +83 -0
  51. data/lib/active_record/explain_subscriber.rb +21 -0
  52. data/lib/active_record/fixtures.rb +31 -76
  53. data/lib/active_record/fixtures/file.rb +65 -0
  54. data/lib/active_record/identity_map.rb +1 -7
  55. data/lib/active_record/inheritance.rb +167 -0
  56. data/lib/active_record/integration.rb +49 -0
  57. data/lib/active_record/locking/optimistic.rb +19 -11
  58. data/lib/active_record/locking/pessimistic.rb +1 -1
  59. data/lib/active_record/log_subscriber.rb +3 -3
  60. data/lib/active_record/migration.rb +38 -29
  61. data/lib/active_record/migration/command_recorder.rb +7 -7
  62. data/lib/active_record/model_schema.rb +362 -0
  63. data/lib/active_record/nested_attributes.rb +3 -2
  64. data/lib/active_record/persistence.rb +51 -1
  65. data/lib/active_record/querying.rb +58 -0
  66. data/lib/active_record/railtie.rb +24 -28
  67. data/lib/active_record/railties/controller_runtime.rb +3 -1
  68. data/lib/active_record/railties/databases.rake +133 -77
  69. data/lib/active_record/readonly_attributes.rb +26 -0
  70. data/lib/active_record/reflection.rb +7 -15
  71. data/lib/active_record/relation.rb +78 -35
  72. data/lib/active_record/relation/batches.rb +5 -2
  73. data/lib/active_record/relation/calculations.rb +27 -6
  74. data/lib/active_record/relation/delegation.rb +49 -0
  75. data/lib/active_record/relation/finder_methods.rb +5 -4
  76. data/lib/active_record/relation/predicate_builder.rb +13 -16
  77. data/lib/active_record/relation/query_methods.rb +59 -4
  78. data/lib/active_record/result.rb +1 -1
  79. data/lib/active_record/sanitization.rb +194 -0
  80. data/lib/active_record/schema_dumper.rb +5 -2
  81. data/lib/active_record/scoping.rb +152 -0
  82. data/lib/active_record/scoping/default.rb +140 -0
  83. data/lib/active_record/scoping/named.rb +202 -0
  84. data/lib/active_record/serialization.rb +1 -43
  85. data/lib/active_record/serializers/xml_serializer.rb +2 -44
  86. data/lib/active_record/session_store.rb +11 -11
  87. data/lib/active_record/store.rb +50 -0
  88. data/lib/active_record/test_case.rb +11 -7
  89. data/lib/active_record/timestamp.rb +16 -3
  90. data/lib/active_record/transactions.rb +5 -5
  91. data/lib/active_record/translation.rb +22 -0
  92. data/lib/active_record/validations.rb +1 -1
  93. data/lib/active_record/validations/associated.rb +5 -4
  94. data/lib/active_record/validations/uniqueness.rb +4 -4
  95. data/lib/active_record/version.rb +3 -3
  96. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
  97. metadata +48 -38
  98. checksums.yaml +0 -7
  99. data/lib/active_record/named_scope.rb +0 -200
@@ -0,0 +1,58 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+
3
+ module ActiveRecord
4
+ module Querying
5
+ delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
6
+ delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :scoped
7
+ delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
8
+ delegate :find_each, :find_in_batches, :to => :scoped
9
+ delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
10
+ :where, :preload, :eager_load, :includes, :from, :lock, :readonly,
11
+ :having, :create_with, :uniq, :to => :scoped
12
+ delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :to => :scoped
13
+
14
+ # Executes a custom SQL query against your database and returns all the results. The results will
15
+ # be returned as an array with columns requested encapsulated as attributes of the model you call
16
+ # this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
17
+ # a Product object with the attributes you specified in the SQL query.
18
+ #
19
+ # If you call a complicated SQL query which spans multiple tables the columns specified by the
20
+ # SELECT will be attributes of the model, whether or not they are columns of the corresponding
21
+ # table.
22
+ #
23
+ # The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be
24
+ # no database agnostic conversions performed. This should be a last resort because using, for example,
25
+ # MySQL specific terms will lock you to using that particular database engine or require you to
26
+ # change your call if you switch engines.
27
+ #
28
+ # ==== Examples
29
+ # # A simple SQL query spanning multiple tables
30
+ # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
31
+ # > [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
32
+ #
33
+ # # You can use the same string replacement techniques as you can with ActiveRecord#find
34
+ # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
35
+ # > [#<Post:0x36bff9c @attributes={"title"=>"The Cheap Man Buys Twice"}>, ...]
36
+ def find_by_sql(sql, binds = [])
37
+ logging_query_plan do
38
+ connection.select_all(sanitize_sql(sql), "#{name} Load", binds).collect! { |record| instantiate(record) }
39
+ end
40
+ end
41
+
42
+ # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
43
+ # The use of this method should be restricted to complicated SQL queries that can't be executed
44
+ # using the ActiveRecord::Calculations class methods. Look into those before using this.
45
+ #
46
+ # ==== Parameters
47
+ #
48
+ # * +sql+ - An SQL statement which should return a count query from the database, see the example below.
49
+ #
50
+ # ==== Examples
51
+ #
52
+ # Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
53
+ def count_by_sql(sql)
54
+ sql = sanitize_conditions(sql)
55
+ connection.select_value(sql, "#{name} Count").to_i
56
+ end
57
+ end
58
+ end
@@ -22,6 +22,13 @@ module ActiveRecord
22
22
  config.app_middleware.insert_after "::ActionDispatch::Callbacks",
23
23
  "ActiveRecord::ConnectionAdapters::ConnectionManagement"
24
24
 
25
+ config.action_dispatch.rescue_responses.merge!(
26
+ 'ActiveRecord::RecordNotFound' => :not_found,
27
+ 'ActiveRecord::StaleObjectError' => :conflict,
28
+ 'ActiveRecord::RecordInvalid' => :unprocessable_entity,
29
+ 'ActiveRecord::RecordNotSaved' => :unprocessable_entity
30
+ )
31
+
25
32
  rake_tasks do
26
33
  load "active_record/railties/databases.rake"
27
34
  end
@@ -78,15 +85,27 @@ module ActiveRecord
78
85
  end
79
86
  end
80
87
 
81
- initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
82
- ActiveSupport.on_load(:active_record) do
83
- ActionDispatch::Reloader.to_cleanup do
84
- ActiveRecord::Base.clear_reloadable_connections!
85
- ActiveRecord::Base.clear_cache!
88
+ initializer "active_record.set_reloader_hooks" do |app|
89
+ hook = lambda do
90
+ ActiveRecord::Base.clear_reloadable_connections!
91
+ ActiveRecord::Base.clear_cache!
92
+ end
93
+
94
+ if app.config.reload_classes_only_on_change
95
+ ActiveSupport.on_load(:active_record) do
96
+ ActionDispatch::Reloader.to_prepare(&hook)
97
+ end
98
+ else
99
+ ActiveSupport.on_load(:active_record) do
100
+ ActionDispatch::Reloader.to_cleanup(&hook)
86
101
  end
87
102
  end
88
103
  end
89
104
 
105
+ initializer "active_record.add_watchable_files" do |app|
106
+ config.watchable_files.concat ["#{app.root}/db/schema.rb", "#{app.root}/db/structure.sql"]
107
+ end
108
+
90
109
  config.after_initialize do
91
110
  ActiveSupport.on_load(:active_record) do
92
111
  instantiate_observers
@@ -96,28 +115,5 @@ module ActiveRecord
96
115
  end
97
116
  end
98
117
  end
99
-
100
- config.after_initialize do
101
- container = :"activerecord.attributes"
102
- lookup = I18n.t(container, :default => {})
103
- if lookup.is_a?(Hash)
104
- lookup.each do |key, value|
105
- if value.is_a?(Hash) && value.any? { |k,v| v.is_a?(Hash) }
106
- $stderr.puts "[DEPRECATION WARNING] Nested I18n namespace lookup under \"#{container}.#{key}\" is no longer supported"
107
- end
108
- end
109
- end
110
-
111
- container = :"activerecord.models"
112
- lookup = I18n.t(container, :default => {})
113
- if lookup.is_a?(Hash)
114
- lookup.each do |key, value|
115
- if value.is_a?(Hash) && !value.key?(:one)
116
- $stderr.puts "[DEPRECATION WARNING] Nested I18n namespace lookup under \"#{container}.#{key}\" is no longer supported"
117
- end
118
- end
119
- end
120
- end
121
-
122
118
  end
123
119
  end
@@ -32,7 +32,9 @@ module ActiveRecord
32
32
 
33
33
  def append_info_to_payload(payload)
34
34
  super
35
- payload[:db_runtime] = db_runtime
35
+ if ActiveRecord::Base.connected?
36
+ payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::LogSubscriber.reset_runtime
37
+ end
36
38
  end
37
39
 
38
40
  module ClassMethods
@@ -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
- # <<: *defaults
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,7 @@ 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
- # Make the test database at the same time as the development one, if it exists
41
- if Rails.env.development? && ActiveRecord::Base.configurations['test']
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) }
45
41
  end
46
42
 
47
43
  def mysql_creation_options(config)
@@ -138,12 +134,7 @@ db_namespace = namespace :db do
138
134
 
139
135
  desc 'Drops the database for the current Rails.env (use db:drop:all to drop all databases)'
140
136
  task :drop => :load_config do
141
- config = ActiveRecord::Base.configurations[Rails.env || 'development']
142
- begin
143
- drop_database(config)
144
- rescue Exception => e
145
- $stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}"
146
- end
137
+ configs_for_environment.each { |config| drop_database_and_rescue(config) }
147
138
  end
148
139
 
149
140
  def local_database?(config, &block)
@@ -158,8 +149,19 @@ db_namespace = namespace :db do
158
149
  desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
159
150
  task :migrate => [:environment, :load_config] do
160
151
  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
- db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
152
+ ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
153
+ ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
154
+ end
155
+ db_namespace['_dump'].invoke
156
+ end
157
+
158
+ task :_dump do
159
+ case ActiveRecord::Base.schema_format
160
+ when :ruby then db_namespace["schema:dump"].invoke
161
+ when :sql then db_namespace["structure:dump"].invoke
162
+ else
163
+ raise "unknown schema format #{ActiveRecord::Base.schema_format}"
164
+ end
163
165
  end
164
166
 
165
167
  namespace :migrate do
@@ -182,7 +184,7 @@ db_namespace = namespace :db do
182
184
  version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
183
185
  raise 'VERSION is required' unless version
184
186
  ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
185
- db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
187
+ db_namespace['_dump'].invoke
186
188
  end
187
189
 
188
190
  # desc 'Runs the "down" for a given migration VERSION.'
@@ -190,7 +192,7 @@ db_namespace = namespace :db do
190
192
  version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
191
193
  raise 'VERSION is required' unless version
192
194
  ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
193
- db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
195
+ db_namespace['_dump'].invoke
194
196
  end
195
197
 
196
198
  desc 'Display status of migrations'
@@ -230,18 +232,21 @@ db_namespace = namespace :db do
230
232
  task :rollback => [:environment, :load_config] do
231
233
  step = ENV['STEP'] ? ENV['STEP'].to_i : 1
232
234
  ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
233
- db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
235
+ db_namespace['_dump'].invoke
234
236
  end
235
237
 
236
238
  # desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
237
239
  task :forward => [:environment, :load_config] do
238
240
  step = ENV['STEP'] ? ENV['STEP'].to_i : 1
239
241
  ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step)
240
- db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
242
+ db_namespace['_dump'].invoke
241
243
  end
242
244
 
243
245
  # desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
244
- task :reset => [ 'db:drop', 'db:setup' ]
246
+ task :reset => :environment do
247
+ db_namespace["drop"].invoke
248
+ db_namespace["setup"].invoke
249
+ end
245
250
 
246
251
  # desc "Retrieves the charset for the current environment's database"
247
252
  task :charset => :environment do
@@ -280,29 +285,28 @@ db_namespace = namespace :db do
280
285
 
281
286
  # desc "Raises an error if there are pending migrations"
282
287
  task :abort_if_pending_migrations => :environment do
283
- if defined? ActiveRecord
284
- pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations
288
+ pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations
285
289
 
286
- if pending_migrations.any?
287
- puts "You have #{pending_migrations.size} pending migrations:"
288
- pending_migrations.each do |pending_migration|
289
- puts ' %4d %s' % [pending_migration.version, pending_migration.name]
290
- end
291
- abort %{Run "rake db:migrate" to update your database then try again.}
290
+ if pending_migrations.any?
291
+ puts "You have #{pending_migrations.size} pending migrations:"
292
+ pending_migrations.each do |pending_migration|
293
+ puts ' %4d %s' % [pending_migration.version, pending_migration.name]
292
294
  end
295
+ abort %{Run `rake db:migrate` to update your database then try again.}
293
296
  end
294
297
  end
295
298
 
296
299
  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 => [ 'db:create', 'db:schema:load', 'db:seed' ]
300
+ task :setup => ['db:schema:load_if_ruby', 'db:structure:load_if_sql', :seed]
298
301
 
299
302
  desc 'Load the seed data from db/seeds.rb'
300
- task :seed => 'db:abort_if_pending_migrations' do
303
+ task :seed do
304
+ db_namespace['abort_if_pending_migrations'].invoke
301
305
  Rails.application.load_seed
302
306
  end
303
307
 
304
308
  namespace :fixtures do
305
- 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."
309
+ 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
310
  task :load => :environment do
307
311
  require 'active_record/fixtures'
308
312
 
@@ -357,93 +361,125 @@ db_namespace = namespace :db do
357
361
  if File.exists?(file)
358
362
  load(file)
359
363
  else
360
- 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}
364
+ 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
365
  end
362
366
  end
367
+
368
+ task :load_if_ruby => 'db:create' do
369
+ db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby
370
+ end
363
371
  end
364
372
 
365
373
  namespace :structure do
366
- desc 'Dump the database structure to an SQL file'
374
+ desc 'Dump the database structure to db/structure.sql. Specify another file with DB_STRUCTURE=db/my_structure.sql'
367
375
  task :dump => :environment do
368
376
  abcs = ActiveRecord::Base.configurations
377
+ filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
369
378
  case abcs[Rails.env]['adapter']
370
379
  when /mysql/, 'oci', 'oracle'
371
380
  ActiveRecord::Base.establish_connection(abcs[Rails.env])
372
- File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
381
+ File.open(filename, "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump }
373
382
  when /postgresql/
374
- ENV['PGHOST'] = abcs[Rails.env]['host'] if abcs[Rails.env]['host']
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']
383
+ set_psql_env(abcs[Rails.env])
377
384
  search_path = abcs[Rails.env]['schema_search_path']
378
385
  unless search_path.blank?
379
- search_path = search_path.split(",").map{|search_path| "--schema=#{search_path.strip}" }.join(" ")
386
+ search_path = search_path.split(",").map{|search_path_part| "--schema=#{search_path_part.strip}" }.join(" ")
380
387
  end
381
- `pg_dump -i -U "#{abcs[Rails.env]['username']}" -s -x -O -f db/#{Rails.env}_structure.sql #{search_path} #{abcs[Rails.env]['database']}`
388
+ `pg_dump -i -s -x -O -f #{filename} #{search_path} #{abcs[Rails.env]['database']}`
382
389
  raise 'Error dumping database' if $?.exitstatus == 1
383
390
  when /sqlite/
384
- dbfile = abcs[Rails.env]['database'] || abcs[Rails.env]['dbfile']
385
- `sqlite3 #{dbfile} .schema > db/#{Rails.env}_structure.sql`
391
+ dbfile = abcs[Rails.env]['database']
392
+ `sqlite3 #{dbfile} .schema > #{filename}`
386
393
  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 db\\#{Rails.env}_structure.sql -A -U`
394
+ `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
395
  when "firebird"
389
396
  set_firebird_env(abcs[Rails.env])
390
397
  db_string = firebird_db_string(abcs[Rails.env])
391
- sh "isql -a #{db_string} > #{Rails.root}/db/#{Rails.env}_structure.sql"
398
+ sh "isql -a #{db_string} > #{filename}"
392
399
  else
393
400
  raise "Task not supported by '#{abcs[Rails.env]["adapter"]}'"
394
401
  end
395
402
 
396
403
  if ActiveRecord::Base.connection.supports_migrations?
397
- File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
404
+ File.open(filename, "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
398
405
  end
399
406
  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
407
 
410
- # desc "Recreate the test database from the current environment's database schema"
411
- task :clone => %w(db:schema:dump db:test:load)
408
+ # desc "Recreate the databases from the structure.sql file"
409
+ task :load => [:environment, :load_config] do
410
+ env = ENV['RAILS_ENV'] || 'test'
412
411
 
413
- # desc "Recreate the test databases from the development structure"
414
- task :clone_structure => [ 'db:structure:dump', 'db:test:purge' ] do
415
412
  abcs = ActiveRecord::Base.configurations
416
- case abcs['test']['adapter']
413
+ filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
414
+ case abcs[env]['adapter']
417
415
  when /mysql/
418
- ActiveRecord::Base.establish_connection(:test)
416
+ ActiveRecord::Base.establish_connection(abcs[env])
419
417
  ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0')
420
- IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split("\n\n").each do |table|
418
+ IO.read(filename).split("\n\n").each do |table|
421
419
  ActiveRecord::Base.connection.execute(table)
422
420
  end
423
421
  when /postgresql/
424
- ENV['PGHOST'] = abcs['test']['host'] if abcs['test']['host']
425
- ENV['PGPORT'] = abcs['test']['port'].to_s if abcs['test']['port']
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']}`
422
+ set_psql_env(abcs[env])
423
+ `psql -f "#{filename}" #{abcs[env]['database']} #{abcs[env]['template']}`
428
424
  when /sqlite/
429
- dbfile = abcs['test']['database'] || abcs['test']['dbfile']
430
- `sqlite3 #{dbfile} < "#{Rails.root}/db/#{Rails.env}_structure.sql"`
425
+ dbfile = abcs[env]['database']
426
+ `sqlite3 #{dbfile} < "#{filename}"`
431
427
  when 'sqlserver'
432
- `sqlcmd -S #{abcs['test']['host']} -d #{abcs['test']['database']} -U #{abcs['test']['username']} -P #{abcs['test']['password']} -i db\\#{Rails.env}_structure.sql`
428
+ `sqlcmd -S #{abcs[env]['host']} -d #{abcs[env]['database']} -U #{abcs[env]['username']} -P #{abcs[env]['password']} -i #{filename}`
433
429
  when 'oci', 'oracle'
434
- ActiveRecord::Base.establish_connection(:test)
435
- IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split(";\n\n").each do |ddl|
430
+ ActiveRecord::Base.establish_connection(abcs[env])
431
+ IO.read(filename).split(";\n\n").each do |ddl|
436
432
  ActiveRecord::Base.connection.execute(ddl)
437
433
  end
438
434
  when 'firebird'
439
- set_firebird_env(abcs['test'])
440
- db_string = firebird_db_string(abcs['test'])
441
- sh "isql -i #{Rails.root}/db/#{Rails.env}_structure.sql #{db_string}"
435
+ set_firebird_env(abcs[env])
436
+ db_string = firebird_db_string(abcs[env])
437
+ sh "isql -i #{filename} #{db_string}"
442
438
  else
443
- raise "Task not supported by '#{abcs['test']['adapter']}'"
439
+ raise "Task not supported by '#{abcs[env]['adapter']}'"
444
440
  end
445
441
  end
446
442
 
443
+ task :load_if_sql => 'db:create' do
444
+ db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql
445
+ end
446
+ end
447
+
448
+ namespace :test do
449
+
450
+ # desc "Recreate the test database from the current schema"
451
+ task :load => 'db:test:purge' do
452
+ case ActiveRecord::Base.schema_format
453
+ when :ruby
454
+ db_namespace["test:load_schema"].invoke
455
+ when :sql
456
+ db_namespace["test:load_structure"].invoke
457
+ end
458
+ end
459
+
460
+ # desc "Recreate the test database from an existent structure.sql file"
461
+ task :load_structure => 'db:test:purge' do
462
+ begin
463
+ old_env, ENV['RAILS_ENV'] = ENV['RAILS_ENV'], 'test'
464
+ db_namespace["structure:load"].invoke
465
+ ensure
466
+ ENV['RAILS_ENV'] = old_env
467
+ end
468
+ end
469
+
470
+ # desc "Recreate the test database from an existent schema.rb file"
471
+ task :load_schema => 'db:test:purge' do
472
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
473
+ ActiveRecord::Schema.verbose = false
474
+ db_namespace["schema:load"].invoke
475
+ end
476
+
477
+ # desc "Recreate the test database from a fresh schema.rb file"
478
+ task :clone => %w(db:schema:dump db:test:load_schema)
479
+
480
+ # desc "Recreate the test database from a fresh structure.sql file"
481
+ task :clone_structure => [ "db:structure:dump", "db:test:load_structure" ]
482
+
447
483
  # desc "Empty the test database"
448
484
  task :purge => :environment do
449
485
  abcs = ActiveRecord::Base.configurations
@@ -456,7 +492,7 @@ db_namespace = namespace :db do
456
492
  drop_database(abcs['test'])
457
493
  create_database(abcs['test'])
458
494
  when /sqlite/
459
- dbfile = abcs['test']['database'] || abcs['test']['dbfile']
495
+ dbfile = abcs['test']['database']
460
496
  File.delete(dbfile) if File.exist?(dbfile)
461
497
  when 'sqlserver'
462
498
  test = abcs.deep_dup['test']
@@ -479,7 +515,7 @@ db_namespace = namespace :db do
479
515
 
480
516
  # desc 'Check for pending migrations and load the test schema'
481
517
  task :prepare => 'db:abort_if_pending_migrations' do
482
- if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
518
+ unless ActiveRecord::Base.configurations.blank?
483
519
  db_namespace[{ :sql => 'test:clone_structure', :ruby => 'test:load' }[ActiveRecord::Base.schema_format]].invoke
484
520
  end
485
521
  end
@@ -489,8 +525,7 @@ db_namespace = namespace :db do
489
525
  # desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
490
526
  task :create => :environment do
491
527
  raise 'Task unavailable to this database (no migration support)' unless ActiveRecord::Base.connection.supports_migrations?
492
- require 'rails/generators'
493
- Rails::Generators.configure!
528
+ Rails.application.load_generators
494
529
  require 'rails/generators/rails/session_migration/session_migration_generator'
495
530
  Rails::Generators::SessionMigrationGenerator.start [ ENV['MIGRATION'] || 'add_sessions_table' ]
496
531
  end
@@ -549,6 +584,20 @@ def drop_database(config)
549
584
  end
550
585
  end
551
586
 
587
+ def drop_database_and_rescue(config)
588
+ begin
589
+ drop_database(config)
590
+ rescue Exception => e
591
+ $stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}"
592
+ end
593
+ end
594
+
595
+ def configs_for_environment
596
+ environments = [Rails.env]
597
+ environments << 'test' if Rails.env.development?
598
+ ActiveRecord::Base.configurations.values_at(*environments).compact.reject { |config| config['database'].blank? }
599
+ end
600
+
552
601
  def session_table_name
553
602
  ActiveRecord::SessionStore::Session.table_name
554
603
  end
@@ -561,3 +610,10 @@ end
561
610
  def firebird_db_string(config)
562
611
  FireRuby::Database.db_string_for(config.symbolize_keys)
563
612
  end
613
+
614
+ def set_psql_env(config)
615
+ ENV['PGHOST'] = config['host'] if config['host']
616
+ ENV['PGPORT'] = config['port'].to_s if config['port']
617
+ ENV['PGPASSWORD'] = config['password'].to_s if config['password']
618
+ ENV['PGUSER'] = config['username'].to_s if config['username']
619
+ end