1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Guarantee the connection resolver handles string values

This commit also cleans up the rake tasks that were checking
for DATABASE_URL in different places.

In fact, it would be nice to deprecate DATABASE_URL usage in the long
term, considering the direction we are moving of allowing those in .yml
files.
This commit is contained in:
José Valim 2013-12-23 20:15:52 +01:00
parent d22a359a18
commit c390e60811
6 changed files with 58 additions and 53 deletions

View file

@ -16,34 +16,43 @@ module ActiveRecord
## ##
# Builds a ConnectionSpecification from user input # Builds a ConnectionSpecification from user input
class Resolver # :nodoc: class Resolver # :nodoc:
attr_reader :config, :klass, :configurations attr_reader :configurations
def initialize(config, configurations) def initialize(configurations)
@config = config
@configurations = configurations @configurations = configurations
end end
def spec def resolve(config)
case config if config
when nil resolve_connection config
raise AdapterNotSpecified unless defined?(Rails.env) elsif defined?(Rails.env)
resolve_string_connection Rails.env resolve_env_connection Rails.env
when Symbol, String else
resolve_string_connection config.to_s raise AdapterNotSpecified
when Hash
resolve_hash_connection config
end end
end end
private private
def resolve_string_connection(spec) # :nodoc:
hash = configurations.fetch(spec) do |k| def resolve_connection(spec) #:nodoc:
connection_url_to_hash(k) case spec
when Symbol, String
resolve_env_connection spec.to_s
when Hash
resolve_hash_connection spec
end
end end
raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash def resolve_env_connection(spec) # :nodoc:
# Rails has historically accepted a string to mean either
resolve_hash_connection hash # an environment key or a url spec. So we support both for
# now but it would be nice to limit the environment key only
# for symbols.
spec = configurations.fetch(spec.to_s) do
resolve_string_connection(spec) if spec.is_a?(String)
end
raise(AdapterNotSpecified, "#{spec} database is not configured") unless spec
resolve_connection spec
end end
def resolve_hash_connection(spec) # :nodoc: def resolve_hash_connection(spec) # :nodoc:
@ -65,8 +74,8 @@ module ActiveRecord
ConnectionSpecification.new(spec, adapter_method) ConnectionSpecification.new(spec, adapter_method)
end end
def connection_url_to_hash(url) # :nodoc: def resolve_string_connection(spec) # :nodoc:
config = URI.parse url config = URI.parse spec
adapter = config.scheme adapter = config.scheme
adapter = "postgresql" if adapter == "postgres" adapter = "postgresql" if adapter == "postgres"

View file

@ -35,8 +35,8 @@ module ActiveRecord
# The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
# may be returned on an error. # may be returned on an error.
def establish_connection(spec = ENV["DATABASE_URL"]) def establish_connection(spec = ENV["DATABASE_URL"])
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new spec, configurations resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations
spec = resolver.spec spec = resolver.resolve(spec)
unless respond_to?(spec.adapter_method) unless respond_to?(spec.adapter_method)
raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter" raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"

View file

@ -43,11 +43,24 @@ module ActiveRecord
namespace :db do namespace :db do
task :load_config do task :load_config do
ActiveRecord::Tasks::DatabaseTasks.db_dir = Rails.application.config.paths["db"].first ActiveRecord::Tasks::DatabaseTasks.db_dir = Rails.application.config.paths["db"].first
ActiveRecord::Tasks::DatabaseTasks.database_configuration = Rails.application.config.database_configuration
ActiveRecord::Tasks::DatabaseTasks.migrations_paths = Rails.application.paths['db/migrate'].to_a ActiveRecord::Tasks::DatabaseTasks.migrations_paths = Rails.application.paths['db/migrate'].to_a
ActiveRecord::Tasks::DatabaseTasks.fixtures_path = File.join Rails.root, 'test', 'fixtures' ActiveRecord::Tasks::DatabaseTasks.fixtures_path = File.join Rails.root, 'test', 'fixtures'
ActiveRecord::Tasks::DatabaseTasks.root = Rails.root ActiveRecord::Tasks::DatabaseTasks.root = Rails.root
configuration = if ENV["DATABASE_URL"]
{ Rails.env => ENV["DATABASE_URL"] }
else
Rails.application.config.database_configuration || {}
end
resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(configuration)
configuration.each do |key, value|
configuration[key] = resolver.resolve(value) if value
end
ActiveRecord::Tasks::DatabaseTasks.database_configuration = configuration
if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH) if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH)
if engine.paths['db/migrate'].existent if engine.paths['db/migrate'].existent
ActiveRecord::Tasks::DatabaseTasks.migrations_paths += engine.paths['db/migrate'].to_a ActiveRecord::Tasks::DatabaseTasks.migrations_paths += engine.paths['db/migrate'].to_a

View file

@ -14,12 +14,8 @@ db_namespace = namespace :db do
desc 'Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all databases in the config)' desc 'Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all databases in the config)'
task :create => [:load_config] do task :create => [:load_config] do
if ENV['DATABASE_URL']
ActiveRecord::Tasks::DatabaseTasks.create_database_url
else
ActiveRecord::Tasks::DatabaseTasks.create_current ActiveRecord::Tasks::DatabaseTasks.create_current
end end
end
namespace :drop do namespace :drop do
task :all => :load_config do task :all => :load_config do
@ -29,12 +25,8 @@ db_namespace = namespace :db do
desc 'Drops the database using DATABASE_URL or 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 task :drop => [:load_config] do
if ENV['DATABASE_URL']
ActiveRecord::Tasks::DatabaseTasks.drop_database_url
else
ActiveRecord::Tasks::DatabaseTasks.drop_current ActiveRecord::Tasks::DatabaseTasks.drop_current
end end
end
desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)." desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
task :migrate => [:environment, :load_config] do task :migrate => [:environment, :load_config] do

View file

@ -56,11 +56,7 @@ module ActiveRecord
if options.has_key?(:config) if options.has_key?(:config)
@current_config = options[:config] @current_config = options[:config]
else else
@current_config ||= if ENV['DATABASE_URL'] @current_config ||= ActiveRecord::Base.configurations[options[:env]]
database_url_config
else
ActiveRecord::Base.configurations[options[:env]]
end
end end
end end
@ -85,10 +81,6 @@ module ActiveRecord
ActiveRecord::Base.establish_connection environment ActiveRecord::Base.establish_connection environment
end end
def create_database_url
create database_url_config
end
def drop(*arguments) def drop(*arguments)
configuration = arguments.first configuration = arguments.first
class_for_adapter(configuration['adapter']).new(*arguments).drop class_for_adapter(configuration['adapter']).new(*arguments).drop
@ -107,10 +99,6 @@ module ActiveRecord
} }
end end
def drop_database_url
drop database_url_config
end
def charset_current(environment = env) def charset_current(environment = env)
charset ActiveRecord::Base.configurations[environment] charset ActiveRecord::Base.configurations[environment]
end end
@ -165,11 +153,6 @@ module ActiveRecord
private 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) def class_for_adapter(adapter)
key = @tasks.keys.detect { |pattern| adapter[pattern] } key = @tasks.keys.detect { |pattern| adapter[pattern] }
unless key unless key

View file

@ -4,8 +4,8 @@ module ActiveRecord
module ConnectionAdapters module ConnectionAdapters
class ConnectionSpecification class ConnectionSpecification
class ResolverTest < ActiveRecord::TestCase class ResolverTest < ActiveRecord::TestCase
def resolve(spec) def resolve(spec, config={})
Resolver.new(spec, {}).spec.config Resolver.new(config).resolve(spec).config
end end
def test_url_invalid_adapter def test_url_invalid_adapter
@ -17,6 +17,14 @@ module ActiveRecord
# The abstract adapter is used simply to bypass the bit of code that # The abstract adapter is used simply to bypass the bit of code that
# checks that the adapter file can be required in. # checks that the adapter file can be required in.
def test_url_from_environment
spec = resolve :production, 'production' => 'abstract://foo?encoding=utf8'
assert_equal({
adapter: "abstract",
host: "foo",
encoding: "utf8" }, spec)
end
def test_url_host_no_db def test_url_host_no_db
spec = resolve 'abstract://foo?encoding=utf8' spec = resolve 'abstract://foo?encoding=utf8'
assert_equal({ assert_equal({