fixed support for DATABASE_URL for rake db tasks
- added tests to confirm establish_connection uses DATABASE_URL and Rails.env correctly even when no arguments are passed in. - updated rake db tasks to support DATABASE_URL, and added tests to confirm correct behavior for these rake tasks. (Removed establish_connection call from some tasks since in those cases the :environment task already made sure the function would be called) - updated Resolver so that when it resolves the database url, it removes hash values with empty strings from the config spec (e.g. to support connection to postgresql when no username is specified).
This commit is contained in:
parent
34b23e7110
commit
148c50b49a
@ -72,7 +72,7 @@ def connection_url_to_hash(url) # :nodoc:
|
||||
:port => config.port,
|
||||
:database => config.path.sub(%r{^/},""),
|
||||
:host => config.host }
|
||||
spec.reject!{ |_,value| !value }
|
||||
spec.reject!{ |_,value| value.blank? }
|
||||
if config.query
|
||||
options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
|
||||
spec.merge!(options)
|
||||
|
@ -18,9 +18,13 @@ 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] do
|
||||
ActiveRecord::Tasks::DatabaseTasks.create_current
|
||||
if ENV['DATABASE_URL']
|
||||
ActiveRecord::Tasks::DatabaseTasks.create_database_url
|
||||
else
|
||||
ActiveRecord::Tasks::DatabaseTasks.create_current
|
||||
end
|
||||
end
|
||||
|
||||
namespace :drop do
|
||||
@ -29,9 +33,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] do
|
||||
ActiveRecord::Tasks::DatabaseTasks.drop_current
|
||||
if ENV['DATABASE_URL']
|
||||
ActiveRecord::Tasks::DatabaseTasks.drop_database_url
|
||||
else
|
||||
ActiveRecord::Tasks::DatabaseTasks.drop_current
|
||||
end
|
||||
end
|
||||
|
||||
desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
|
||||
@ -88,8 +96,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
|
||||
@ -110,7 +116,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|
|
||||
@ -186,7 +192,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
|
||||
|
||||
@ -225,7 +230,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
|
||||
@ -277,22 +281,22 @@ db_namespace = namespace :db 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
|
||||
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
|
||||
case abcs[Rails.env]['adapter']
|
||||
current_config = ActiveRecord::Tasks::DatabaseTasks.current_config
|
||||
case current_config['adapter']
|
||||
when /mysql/, /postgresql/, /sqlite/
|
||||
ActiveRecord::Tasks::DatabaseTasks.structure_dump(abcs[Rails.env], filename)
|
||||
ActiveRecord::Tasks::DatabaseTasks.structure_dump(current_config, filename)
|
||||
when 'oci', 'oracle'
|
||||
ActiveRecord::Base.establish_connection(abcs[Rails.env])
|
||||
ActiveRecord::Base.establish_connection(current_config)
|
||||
File.open(filename, "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump }
|
||||
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 #{current_config['host']} -d #{current_config['database']} -u #{current_config['username']} -p #{current_config['password']} -f #{filename} -A -U`
|
||||
when "firebird"
|
||||
set_firebird_env(abcs[Rails.env])
|
||||
db_string = firebird_db_string(abcs[Rails.env])
|
||||
set_firebird_env(current_config)
|
||||
db_string = firebird_db_string(current_config)
|
||||
sh "isql -a #{db_string} > #{filename}"
|
||||
else
|
||||
raise "Task not supported by '#{abcs[Rails.env]["adapter"]}'"
|
||||
raise "Task not supported by '#{current_config["adapter"]}'"
|
||||
end
|
||||
|
||||
if ActiveRecord::Base.connection.supports_migrations?
|
||||
@ -303,26 +307,24 @@ db_namespace = namespace :db do
|
||||
|
||||
# desc "Recreate the databases from the structure.sql file"
|
||||
task :load => [:environment, :load_config] do
|
||||
env = ENV['RAILS_ENV'] || 'test'
|
||||
|
||||
abcs = ActiveRecord::Base.configurations
|
||||
current_config = ActiveRecord::Tasks::DatabaseTasks.current_config(:env => (ENV['RAILS_ENV'] || 'test'))
|
||||
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
|
||||
case abcs[env]['adapter']
|
||||
case current_config['adapter']
|
||||
when /mysql/, /postgresql/, /sqlite/
|
||||
ActiveRecord::Tasks::DatabaseTasks.structure_load(abcs[env], filename)
|
||||
ActiveRecord::Tasks::DatabaseTasks.structure_load(current_config, filename)
|
||||
when 'sqlserver'
|
||||
`sqlcmd -S #{abcs[env]['host']} -d #{abcs[env]['database']} -U #{abcs[env]['username']} -P #{abcs[env]['password']} -i #{filename}`
|
||||
`sqlcmd -S #{current_config['host']} -d #{current_config['database']} -U #{current_config['username']} -P #{current_config['password']} -i #{filename}`
|
||||
when 'oci', 'oracle'
|
||||
ActiveRecord::Base.establish_connection(abcs[env])
|
||||
ActiveRecord::Base.establish_connection(current_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(current_config)
|
||||
db_string = firebird_db_string(current_config)
|
||||
sh "isql -i #{filename} #{db_string}"
|
||||
else
|
||||
raise "Task not supported by '#{abcs[env]['adapter']}'"
|
||||
raise "Task not supported by '#{current_config['adapter']}'"
|
||||
end
|
||||
end
|
||||
|
||||
@ -353,10 +355,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'
|
||||
ActiveRecord::Tasks::DatabaseTasks.current_config(:config => ActiveRecord::Base.configurations['test'])
|
||||
db_namespace["structure:load"].invoke
|
||||
ensure
|
||||
ENV['RAILS_ENV'] = old_env
|
||||
ActiveRecord::Tasks::DatabaseTasks.current_config(:config => nil)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -3,6 +3,8 @@ module Tasks # :nodoc:
|
||||
module DatabaseTasks # :nodoc:
|
||||
extend self
|
||||
|
||||
attr_writer :current_config
|
||||
|
||||
LOCAL_HOSTS = ['127.0.0.1', 'localhost']
|
||||
|
||||
def register_task(pattern, task)
|
||||
@ -14,6 +16,19 @@ def register_task(pattern, task)
|
||||
register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks)
|
||||
register_task(/sqlite/, ActiveRecord::Tasks::SQLiteDatabaseTasks)
|
||||
|
||||
def current_config(options = {})
|
||||
options.reverse_merge! :env => Rails.env
|
||||
if options.has_key?(:config)
|
||||
@current_config = options[:config]
|
||||
else
|
||||
@current_config ||= if ENV['DATABASE_URL']
|
||||
database_url_config
|
||||
else
|
||||
ActiveRecord::Base.configurations[options[:env]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create(*arguments)
|
||||
configuration = arguments.first
|
||||
class_for_adapter(configuration['adapter']).new(*arguments).create
|
||||
@ -33,6 +48,10 @@ def create_current(environment = Rails.env)
|
||||
ActiveRecord::Base.establish_connection environment
|
||||
end
|
||||
|
||||
def create_database_url
|
||||
create database_url_config
|
||||
end
|
||||
|
||||
def drop(*arguments)
|
||||
configuration = arguments.first
|
||||
class_for_adapter(configuration['adapter']).new(*arguments).drop
|
||||
@ -51,6 +70,10 @@ def drop_current(environment = Rails.env)
|
||||
}
|
||||
end
|
||||
|
||||
def drop_database_url
|
||||
drop database_url_config
|
||||
end
|
||||
|
||||
def charset_current(environment = Rails.env)
|
||||
charset ActiveRecord::Base.configurations[environment]
|
||||
end
|
||||
@ -87,6 +110,11 @@ def structure_load(*arguments)
|
||||
|
||||
private
|
||||
|
||||
def database_url_config
|
||||
@database_url_config ||=
|
||||
ConnectionAdapters::ConnectionSpecification::Resolver.new(ENV["DATABASE_URL"], {}).spec.config.stringify_keys
|
||||
end
|
||||
|
||||
def class_for_adapter(adapter)
|
||||
key = @tasks.keys.detect { |pattern| adapter[pattern] }
|
||||
@tasks[key]
|
||||
|
@ -13,7 +13,6 @@ def test_url_host_no_db
|
||||
spec = resolve 'mysql://foo?encoding=utf8'
|
||||
assert_equal({
|
||||
:adapter => "mysql",
|
||||
:database => "",
|
||||
:host => "foo",
|
||||
:encoding => "utf8" }, spec)
|
||||
end
|
||||
@ -33,7 +32,6 @@ def test_url_port
|
||||
spec = resolve 'mysql://foo:123?encoding=utf8'
|
||||
assert_equal({
|
||||
:adapter => "mysql",
|
||||
:database => "",
|
||||
:port => 123,
|
||||
:host => "foo",
|
||||
:encoding => "utf8" }, spec)
|
||||
|
@ -1,5 +1,7 @@
|
||||
## Rails 4.0.0 (unreleased) ##
|
||||
|
||||
* Fixed support for DATABASE_URL environment variable for rake db tasks. *Grace Liu*
|
||||
|
||||
* rails dbconsole now can use SSL for MySQL. The database.yml options sslca, sslcert, sslcapath, sslcipher,
|
||||
and sslkey now affect rails dbconsole. *Jim Kingdon and Lars Petrus*
|
||||
|
||||
|
@ -195,5 +195,37 @@ def from_bar_helper
|
||||
assert !ActiveRecord::Base.connection.schema_cache.tables["posts"]
|
||||
}
|
||||
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
|
||||
|
181
railties/test/application/rake/dbs_test.rb
Normal file
181
railties/test/application/rake/dbs_test.rb
Normal file
@ -0,0 +1,181 @@
|
||||
require "isolation/abstract_unit"
|
||||
|
||||
module ApplicationTests
|
||||
module RakeTests
|
||||
class RakeDbsTest < ActiveSupport::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`
|
||||
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:migrate`
|
||||
`bundle exec rake db:structure:dump`
|
||||
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 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 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
|
Loading…
Reference in New Issue
Block a user