diff options
author | Rafael Mendonça França <rafaelmfranca@gmail.com> | 2012-10-29 15:10:24 -0200 |
---|---|---|
committer | Rafael Mendonça França <rafaelmfranca@gmail.com> | 2012-10-29 15:10:24 -0200 |
commit | e6b41845efe2252fe7de6882e355c31f93c65cc3 (patch) | |
tree | f3be197dab12a76855dde2877e863ff3dddc7f66 | |
parent | fdfb3990cde29c48554551d1309b06c8ef584f32 (diff) | |
parent | e7a6b92959012ffde730b2daa38ecd006570779c (diff) | |
download | rails-e6b41845efe2252fe7de6882e355c31f93c65cc3.tar.gz rails-e6b41845efe2252fe7de6882e355c31f93c65cc3.tar.bz2 rails-e6b41845efe2252fe7de6882e355c31f93c65cc3.zip |
Merge pull request #8009 from graceliu/3-2-fix_database_url_support
fixed support for DATABASE_URL for rake db tasks
6 files changed, 287 insertions, 37 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index aa68934099..c762210941 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,7 @@ ## Rails 3.2.9 (unreleased) +* Fixed support for DATABASE_URL environment variable for rake db tasks. *Grace Liu* + * Fix bug where `update_columns` and `update_column` would not let you update the primary key column. *Henrik Nyh* diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index f366392c50..04ab7e0a43 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -67,7 +67,7 @@ module ActiveRecord :port => config.port, :database => config.path.sub(%r{^/},""), :host => config.host } - spec.reject!{ |_,value| !value } + spec.reject!{ |_,value| value.blank? } spec.map { |key,value| spec[key] = URI.unescape(value) if value.is_a?(String) } if config.query options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 987bb558e5..ae6d9c8653 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -2,6 +2,25 @@ require 'active_support/core_ext/object/inclusion' require 'active_record' db_namespace = namespace :db do + def database_url_config + @database_url_config ||= + ActiveRecord::Base::ConnectionSpecification::Resolver.new(ENV["DATABASE_URL"], {}).spec.config.stringify_keys + end + + def current_config(options = {}) + options = { :env => Rails.env }.merge! options + + if options[:config] + @current_config = options[:config] + else + @current_config ||= if ENV['DATABASE_URL'] + database_url_config + else + ActiveRecord::Base.configurations[options[:env]] + end + end + end + task :load_config do ActiveRecord::Base.configurations = Rails.application.config.database_configuration ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a @@ -35,10 +54,14 @@ db_namespace = namespace :db do end end - desc 'Create the database from config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)' + desc 'Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)' task :create => [:load_config, :rails_env] do - configs_for_environment.each { |config| create_database(config) } - ActiveRecord::Base.establish_connection(configs_for_environment.first) + if ENV['DATABASE_URL'] + create_database(database_url_config) + else + configs_for_environment.each { |config| create_database(config) } + ActiveRecord::Base.establish_connection(configs_for_environment.first) + end end def mysql_creation_options(config) @@ -133,9 +156,13 @@ db_namespace = namespace :db do end end - desc 'Drops the database for the current Rails.env (use db:drop:all to drop all databases)' + desc 'Drops the database using DATABASE_URL or the current Rails.env (use db:drop:all to drop all databases)' task :drop => [:load_config, :rails_env] do - configs_for_environment.each { |config| drop_database_and_rescue(config) } + if ENV['DATABASE_URL'] + drop_database_and_rescue(database_url_config) + else + configs_for_environment.each { |config| drop_database_and_rescue(config) } + end end def local_database?(config, &block) @@ -146,7 +173,6 @@ db_namespace = namespace :db do end end - desc "Migrate the database (options: VERSION=x, VERBOSE=false)." task :migrate => [:environment, :load_config] do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true @@ -201,8 +227,6 @@ db_namespace = namespace :db do desc 'Display status of migrations' task :status => [:environment, :load_config] do - config = ActiveRecord::Base.configurations[Rails.env] - ActiveRecord::Base.establish_connection(config) unless ActiveRecord::Base.connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) puts 'Schema migrations table does not exist yet.' next # means "return" for rake task @@ -222,7 +246,7 @@ db_namespace = namespace :db do ['up', version, '********** NO FILE **********'] end # output - puts "\ndatabase: #{config['database']}\n\n" + puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n" puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name" puts "-" * 50 (db_list + file_list).sort_by {|migration| migration[1]}.each do |migration| @@ -314,7 +338,6 @@ db_namespace = namespace :db do task :load => [:environment, :load_config] do require 'active_record/fixtures' - ActiveRecord::Base.establish_connection(Rails.env) base_dir = File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten fixtures_dir = File.join [base_dir, ENV['FIXTURES_DIR']].compact @@ -353,7 +376,6 @@ db_namespace = namespace :db do require 'active_record/schema_dumper' filename = ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb" File.open(filename, "w:utf-8") do |file| - ActiveRecord::Base.establish_connection(Rails.env) ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) end db_namespace['schema:dump'].reenable @@ -377,25 +399,25 @@ db_namespace = namespace :db do namespace :structure do desc 'Dump the database structure to db/structure.sql. Specify another file with DB_STRUCTURE=db/my_structure.sql' task :dump => [:environment, :load_config] do - abcs = ActiveRecord::Base.configurations + config = current_config filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql") - case abcs[Rails.env]['adapter'] + case config['adapter'] when /mysql/, 'oci', 'oracle' - ActiveRecord::Base.establish_connection(abcs[Rails.env]) + ActiveRecord::Base.establish_connection(config) File.open(filename, "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump } when /postgresql/ - set_psql_env(abcs[Rails.env]) - search_path = abcs[Rails.env]['schema_search_path'] + set_psql_env(config) + search_path = config['schema_search_path'] unless search_path.blank? search_path = search_path.split(",").map{|search_path_part| "--schema=#{Shellwords.escape(search_path_part.strip)}" }.join(" ") end - `pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(abcs[Rails.env]['database'])}` + `pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(config['database'])}` raise 'Error dumping database' if $?.exitstatus == 1 when /sqlite/ - dbfile = abcs[Rails.env]['database'] + dbfile = config['database'] `sqlite3 #{dbfile} .schema > #{filename}` when 'sqlserver' - `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` + `smoscript -s #{config['host']} -d #{config['database']} -u #{config['username']} -p #{config['password']} -f #{filename} -A -U` when "firebird" set_firebird_env(abcs[Rails.env]) db_string = firebird_db_string(abcs[Rails.env]) @@ -412,36 +434,34 @@ db_namespace = namespace :db do # desc "Recreate the databases from the structure.sql file" task :load => [:environment, :load_config] do - env = Rails.env - - abcs = ActiveRecord::Base.configurations + config = current_config filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql") - case abcs[env]['adapter'] + case config['adapter'] when /mysql/ - ActiveRecord::Base.establish_connection(abcs[env]) + ActiveRecord::Base.establish_connection(config) ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') IO.read(filename).split("\n\n").each do |table| ActiveRecord::Base.connection.execute(table) end when /postgresql/ - set_psql_env(abcs[env]) - `psql -f "#{filename}" #{abcs[env]['database']}` + set_psql_env(config) + `psql -f "#{filename}" #{config['database']}` when /sqlite/ - dbfile = abcs[env]['database'] + dbfile = config['database'] `sqlite3 #{dbfile} < "#{filename}"` when 'sqlserver' - `sqlcmd -S #{abcs[env]['host']} -d #{abcs[env]['database']} -U #{abcs[env]['username']} -P #{abcs[env]['password']} -i #{filename}` + `sqlcmd -S #{config['host']} -d #{config['database']} -U #{config['username']} -P #{config['password']} -i #{filename}` when 'oci', 'oracle' - ActiveRecord::Base.establish_connection(abcs[env]) + ActiveRecord::Base.establish_connection(config) IO.read(filename).split(";\n\n").each do |ddl| ActiveRecord::Base.connection.execute(ddl) end when 'firebird' - set_firebird_env(abcs[env]) - db_string = firebird_db_string(abcs[env]) + set_firebird_env(config) + db_string = firebird_db_string(config) sh "isql -i #{filename} #{db_string}" else - raise "Task not supported by '#{abcs[env]['adapter']}'" + raise "Task not supported by '#{config['adapter']}'" end end @@ -465,10 +485,10 @@ db_namespace = namespace :db do # desc "Recreate the test database from an existent structure.sql file" task :load_structure => 'db:test:purge' do begin - old_env, ENV['RAILS_ENV'] = ENV['RAILS_ENV'], 'test' + current_config(:config => ActiveRecord::Base.configurations['test']) db_namespace["structure:load"].invoke ensure - ENV['RAILS_ENV'] = old_env + current_config(:config => nil) end end diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb index 46fc1a252f..451652dfcb 100644 --- a/activerecord/test/cases/connection_specification/resolver_test.rb +++ b/activerecord/test/cases/connection_specification/resolver_test.rb @@ -13,7 +13,6 @@ module ActiveRecord spec = resolve 'mysql://foo?encoding=utf8' assert_equal({ :adapter => "mysql", - :database => "", :host => "foo", :encoding => "utf8" }, spec) end @@ -33,7 +32,6 @@ module ActiveRecord spec = resolve 'mysql://foo:123?encoding=utf8' assert_equal({ :adapter => "mysql", - :database => "", :port => 123, :host => "foo", :encoding => "utf8" }, spec) diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index cf6c4d8fc2..b4c9741b05 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -193,5 +193,41 @@ module ApplicationTests require "#{app_path}/config/environment" assert_nil defined?(ActiveRecord::Base) end + + test "active record establish_connection uses Rails.env if DATABASE_URL is not set" do + begin + require "#{app_path}/config/environment" + orig_database_url = ENV.delete("DATABASE_URL") + orig_rails_env, Rails.env = Rails.env, 'development' + + ActiveRecord::Base.establish_connection + + assert ActiveRecord::Base.connection + assert_match /#{ActiveRecord::Base.configurations[Rails.env]['database']}/, ActiveRecord::Base.connection_config[:database] + ensure + ActiveRecord::Base.remove_connection + ENV["DATABASE_URL"] = orig_database_url if orig_database_url + Rails.env = orig_rails_env if orig_rails_env + end + end + + test "active record establish_connection uses DATABASE_URL even if Rails.env is set" do + begin + require "#{app_path}/config/environment" + orig_database_url = ENV.delete("DATABASE_URL") + orig_rails_env, Rails.env = Rails.env, 'development' + database_url_db_name = "db/database_url_db.sqlite3" + ENV["DATABASE_URL"] = "sqlite3://:@localhost/#{database_url_db_name}" + + ActiveRecord::Base.establish_connection + + assert ActiveRecord::Base.connection + assert_match /#{database_url_db_name}/, ActiveRecord::Base.connection_config[:database] + ensure + ActiveRecord::Base.remove_connection + ENV["DATABASE_URL"] = orig_database_url if orig_database_url + Rails.env = orig_rails_env if orig_rails_env + end + end end end diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb new file mode 100644 index 0000000000..479ed2612b --- /dev/null +++ b/railties/test/application/rake/dbs_test.rb @@ -0,0 +1,194 @@ +require "isolation/abstract_unit" + +module ApplicationTests + module RakeTests + class RakeDbsTest < Test::Unit::TestCase + include ActiveSupport::Testing::Isolation + + def setup + build_app + boot_rails + FileUtils.rm_rf("#{app_path}/config/environments") + end + + def teardown + teardown_app + end + + def database_url_db_name + "db/database_url_db.sqlite3" + end + + def set_database_url + ENV['DATABASE_URL'] = "sqlite3://:@localhost/#{database_url_db_name}" + end + + def expected + @expected ||= {} + end + + def db_create_and_drop + Dir.chdir(app_path) do + output = `bundle exec rake db:create` + assert_equal output, "" + assert File.exists?(expected[:database]) + assert_equal expected[:database], + ActiveRecord::Base.connection_config[:database] + output = `bundle exec rake db:drop` + assert_equal output, "" + assert !File.exists?(expected[:database]) + end + end + + test 'db:create and db:drop without database url' do + require "#{app_path}/config/environment" + expected[:database] = ActiveRecord::Base.configurations[Rails.env]['database'] + db_create_and_drop + end + + test 'db:create and db:drop with database url' do + require "#{app_path}/config/environment" + set_database_url + expected[:database] = database_url_db_name + db_create_and_drop + end + + def db_migrate_and_status + Dir.chdir(app_path) do + `rails generate model book title:string` + `bundle exec rake db:migrate` + output = `bundle exec rake db:migrate:status` + assert_match(/database:\s+\S*#{expected[:database]}/, output) + assert_match(/up\s+\d{14}\s+Create books/, output) + end + end + + test 'db:migrate and db:migrate:status without database_url' do + require "#{app_path}/config/environment" + expected[:database] = ActiveRecord::Base.configurations[Rails.env]['database'] + db_migrate_and_status + end + + test 'db:migrate and db:migrate:status with database_url' do + require "#{app_path}/config/environment" + set_database_url + expected[:database] = database_url_db_name + db_migrate_and_status + end + + def db_schema_dump + Dir.chdir(app_path) do + `rails generate model book title:string` + `rake db:migrate` + `rake db:schema:dump` + + assert File.exists?("db/schema.rb"), "db/schema.rb doesn't exist" + + schema_dump = File.read("db/schema.rb") + + assert_match(/create_table \"books\"/, schema_dump) + end + end + + test 'db:schema:dump without database_url' do + db_schema_dump + end + + test 'db:schema:dump with database_url' do + set_database_url + db_schema_dump + end + + def db_fixtures_load + Dir.chdir(app_path) do + `rails generate model book title:string` + `bundle exec rake db:migrate` + `bundle exec rake db:fixtures:load` + assert_match /#{expected[:database]}/, + ActiveRecord::Base.connection_config[:database] + require "#{app_path}/app/models/book" + assert_equal 2, Book.count + end + end + + test 'db:fixtures:load without database_url' do + require "#{app_path}/config/environment" + expected[:database] = ActiveRecord::Base.configurations[Rails.env]['database'] + db_fixtures_load + end + + test 'db:fixtures:load with database_url' do + require "#{app_path}/config/environment" + set_database_url + expected[:database] = database_url_db_name + db_fixtures_load + end + + def db_structure_dump_and_load + Dir.chdir(app_path) do + `rails generate model book title:string` + `bundle exec rake db:create` + `bundle exec rake db:migrate` + `bundle exec rake db:structure:dump` + + assert File.exists?("db/structure.sql"), "db/structure.sql doesn't exist" + + structure_dump = File.read("db/structure.sql") + + assert_match /CREATE TABLE \"books\"/, structure_dump + + `bundle exec rake db:drop` + `bundle exec rake db:structure:load` + + assert_match /#{expected[:database]}/, + ActiveRecord::Base.connection_config[:database] + + require "#{app_path}/app/models/book" + #if structure is not loaded correctly, exception would be raised + assert_equal Book.count, 0 + end + end + + test 'db:structure:dump and db:structure:load without database_url' do + require "#{app_path}/config/environment" + expected[:database] = ActiveRecord::Base.configurations[Rails.env]['database'] + db_structure_dump_and_load + end + + test 'db:structure:dump and db:structure:load with database_url' do + require "#{app_path}/config/environment" + set_database_url + expected[:database] = database_url_db_name + db_structure_dump_and_load + end + + def db_test_load_structure + Dir.chdir(app_path) do + `rails generate model book title:string` + `bundle exec rake db:migrate` + `bundle exec rake db:structure:dump` + `bundle exec rake db:test:load_structure` + ActiveRecord::Base.configurations = Rails.application.config.database_configuration + ActiveRecord::Base.establish_connection 'test' + require "#{app_path}/app/models/book" + + #if structure is not loaded correctly, exception would be raised + assert_equal Book.count, 0 + assert_match /#{ActiveRecord::Base.configurations['test']['database']}/, + ActiveRecord::Base.connection_config[:database] + end + end + + test 'db:test:load_structure without database_url' do + require "#{app_path}/config/environment" + db_test_load_structure + end + + test 'db:test:load_structure with database_url' do + require "#{app_path}/config/environment" + set_database_url + db_test_load_structure + end + end + end +end |