diff options
Diffstat (limited to 'activerecord/lib/active_record')
6 files changed, 84 insertions, 57 deletions
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index ee8b816ef4..66132b7260 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -18,14 +18,8 @@ module ActiveRecord # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro. # - # This class has most of the basic instance methods removed, and delegates - # unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a - # corner case, it even removes the +class+ method and that's why you get - # - # blog.posts.class # => Array - # - # though the object behind <tt>blog.posts</tt> is not an Array, but an - # ActiveRecord::Associations::HasManyAssociation. + # This class delegates unknown methods to <tt>@target</tt> via + # <tt>method_missing</tt>. # # The <tt>@target</tt> object is not \loaded until needed. For example, # diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index bf08459b3b..f42a5df75f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -5,14 +5,11 @@ require 'active_support/core_ext/module/deprecation' module ActiveRecord # Raised when a connection could not be obtained within the connection - # acquisition timeout period. + # acquisition timeout period: because max connections in pool + # are in use. class ConnectionTimeoutError < ConnectionNotEstablished end - # Raised when a connection pool is full and another connection is requested - class PoolFullError < ConnectionNotEstablished - end - module ConnectionAdapters # Connection pool base class for managing Active Record database # connections. @@ -187,7 +184,11 @@ module ActiveRecord return remove if any? elapsed = Time.now - t0 - raise ConnectionTimeoutError if elapsed >= timeout + if elapsed >= timeout + msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' % + [timeout, elapsed] + raise ConnectionTimeoutError, msg + end end ensure @num_waiting -= 1 @@ -350,12 +351,12 @@ module ActiveRecord # # If all connections are leased and the pool is at capacity (meaning the # number of currently leased connections is greater than or equal to the - # size limit set), an ActiveRecord::PoolFullError exception will be raised. + # size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised. # # Returns: an AbstractAdapter object. # # Raises: - # - PoolFullError: no connection can be obtained from the pool. + # - ConnectionTimeoutError: no connection can be obtained from the pool. def checkout synchronize do conn = acquire_connection @@ -416,22 +417,14 @@ module ActiveRecord # queue for a connection to become available. # # Raises: - # - PoolFullError if a connection could not be acquired (FIXME: - # why not ConnectionTimeoutError? + # - ConnectionTimeoutError if a connection could not be acquired def acquire_connection if conn = @available.poll conn elsif @connections.size < @size checkout_new_connection else - t0 = Time.now - begin - @available.poll(@checkout_timeout) - rescue ConnectionTimeoutError - msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' % - [@checkout_timeout, Time.now - t0] - raise PoolFullError, msg - end + @available.poll(@checkout_timeout) end end diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index dd40351a38..b9a61f7d91 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -72,7 +72,7 @@ module ActiveRecord :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) diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb index 57553c29eb..44cde49bd5 100644 --- a/activerecord/lib/active_record/model.rb +++ b/activerecord/lib/active_record/model.rb @@ -101,9 +101,19 @@ module ActiveRecord def abstract_class? false end - + # Defines the name of the table column which will store the class name on single-table # inheritance situations. + # + # The default inheritance column name is +type+, which means it's a + # reserved word inside Active Record. To be able to use single-table + # inheritance with another column name, or to use the column +type+ in + # your own model for something else, you can override this method to + # return a different name: + # + # def self.inheritance_column + # 'zoink' + # end def inheritance_column 'type' end diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index ae24542521..bcb26f72c8 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -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 diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index b41cc68b6a..fda51b3d76 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -3,6 +3,8 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord } 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 @@ module ActiveRecord 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] |