aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionmailer/lib/action_mailer/async.rb41
-rw-r--r--actionmailer/lib/action_mailer/base.rb28
-rw-r--r--actionmailer/lib/action_mailer/queued_message.rb27
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb2
-rw-r--r--actionmailer/test/abstract_unit.rb2
-rw-r--r--actionmailer/test/base_test.rb6
-rw-r--r--actionmailer/test/mailers/async_mailer.rb1
-rw-r--r--actionpack/test/controller/assert_select_test.rb10
-rw-r--r--actionpack/test/fixtures/ruby_template.rb1
-rw-r--r--activemodel/lib/active_model/validations/clusivity.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb29
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb2
-rw-r--r--activerecord/lib/active_record/model.rb12
-rw-r--r--activerecord/lib/active_record/railties/databases.rake60
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb28
-rw-r--r--activerecord/test/cases/connection_pool_test.rb2
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb2
-rw-r--r--guides/source/action_mailer_basics.textile21
-rw-r--r--guides/source/configuring.textile5
-rw-r--r--railties/CHANGELOG.md2
-rw-r--r--railties/lib/rails/application/configuration.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt4
-rw-r--r--railties/lib/rails/queueing.rb8
-rw-r--r--railties/test/application/initializers/frameworks_test.rb49
-rw-r--r--railties/test/application/queue_test.rb11
-rw-r--r--railties/test/application/rake/dbs_test.rb181
29 files changed, 402 insertions, 152 deletions
diff --git a/actionmailer/lib/action_mailer/async.rb b/actionmailer/lib/action_mailer/async.rb
deleted file mode 100644
index a364342745..0000000000
--- a/actionmailer/lib/action_mailer/async.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require 'delegate'
-
-module ActionMailer
- module Async
- def method_missing(method_name, *args)
- if action_methods.include?(method_name.to_s)
- QueuedMessage.new(queue, self, method_name, *args)
- else
- super
- end
- end
-
- def queue
- Rails.queue
- end
-
- class QueuedMessage < ::Delegator
- attr_reader :queue
-
- def initialize(queue, mailer_class, method_name, *args)
- @queue = queue
- @mailer_class = mailer_class
- @method_name = method_name
- @args = args
- end
-
- def __getobj__
- @actual_message ||= @mailer_class.send(:new, @method_name, *@args).message
- end
-
- def run
- __getobj__.deliver
- end
-
- # Will push the message onto the Queue to be processed
- def deliver
- @queue << self
- end
- end
- end
-end \ No newline at end of file
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 4a099553c0..26787c9b5e 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -1,4 +1,5 @@
require 'mail'
+require 'action_mailer/queued_message'
require 'action_mailer/collector'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/hash/except'
@@ -362,6 +363,9 @@ module ActionMailer #:nodoc:
# * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with
# <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
#
+ # * <tt>queue</> - The queue that will be used to deliver the mail. The queue should expect a job that
+ # responds to <tt>run</tt>
+ #
class Base < AbstractController::Base
include DeliveryMethods
abstract!
@@ -388,6 +392,8 @@ module ActionMailer #:nodoc:
:parts_order => [ "text/plain", "text/enriched", "text/html" ]
}.freeze
+ class_attribute :queue
+
class << self
# Register one or more Observers which will be notified when mail is delivered.
def register_observers(*observers)
@@ -464,19 +470,6 @@ module ActionMailer #:nodoc:
super || action_methods.include?(method.to_s)
end
- # Will force ActionMailer to push new messages to the queue defined
- # in the ActionMailer class when set to true.
- #
- # class WelcomeMailer < ActionMailer::Base
- # self.async = true
- # end
- def async=(truth)
- if truth
- require 'action_mailer/async'
- extend ActionMailer::Async
- end
- end
-
protected
def set_payload_for_mail(payload, mail) #:nodoc:
@@ -491,9 +484,12 @@ module ActionMailer #:nodoc:
payload[:mail] = mail.encoded
end
- def method_missing(method, *args) #:nodoc:
- return super unless respond_to?(method)
- new(method, *args).message
+ def method_missing(method_name, *args)
+ if action_methods.include?(method_name.to_s)
+ QueuedMessage.new(queue, self, method_name, *args)
+ else
+ super
+ end
end
end
diff --git a/actionmailer/lib/action_mailer/queued_message.rb b/actionmailer/lib/action_mailer/queued_message.rb
new file mode 100644
index 0000000000..e5868ab43b
--- /dev/null
+++ b/actionmailer/lib/action_mailer/queued_message.rb
@@ -0,0 +1,27 @@
+require 'delegate'
+
+module ActionMailer
+ class QueuedMessage < ::Delegator
+ attr_reader :queue
+
+ def initialize(queue, mailer_class, method_name, *args)
+ @queue = queue
+ @mailer_class = mailer_class
+ @method_name = method_name
+ @args = args
+ end
+
+ def __getobj__
+ @actual_message ||= @mailer_class.send(:new, @method_name, *@args).message
+ end
+
+ def run
+ __getobj__.deliver
+ end
+
+ # Will push the message onto the Queue to be processed
+ def deliver
+ @queue << self
+ end
+ end
+end
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index 8679096735..1fa689de5b 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -19,6 +19,8 @@ module ActionMailer
options.javascripts_dir ||= paths["public/javascripts"].first
options.stylesheets_dir ||= paths["public/stylesheets"].first
+ options.queue ||= app.queue
+
# make sure readers methods get compiled
options.asset_path ||= app.config.asset_path
options.asset_host ||= app.config.asset_host
diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb
index 99c44179fd..1167387def 100644
--- a/actionmailer/test/abstract_unit.rb
+++ b/actionmailer/test/abstract_unit.rb
@@ -11,6 +11,7 @@ end
require 'minitest/autorun'
require 'action_mailer'
require 'action_mailer/test_case'
+require 'rails/queueing'
silence_warnings do
# These external dependencies have warnings :/
@@ -26,6 +27,7 @@ ActionView::Template.register_template_handler :bak, lambda { |template| "Lame b
FIXTURE_LOAD_PATH = File.expand_path('fixtures', File.dirname(__FILE__))
ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH
+ActionMailer::Base.queue = Rails::Queueing::SynchronousQueue.new
class MockSMTP
def self.deliveries
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index c7b8f47d89..862b954f9e 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -575,11 +575,11 @@ class BaseTest < ActiveSupport::TestCase
end
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
- mail1 = ProcMailer.welcome
+ mail1 = ProcMailer.welcome['X-Proc-Method']
yesterday = 1.day.ago
Time.stubs(:now).returns(yesterday)
- mail2 = ProcMailer.welcome
- assert(mail1['X-Proc-Method'].to_s.to_i > mail2['X-Proc-Method'].to_s.to_i)
+ mail2 = ProcMailer.welcome['X-Proc-Method']
+ assert(mail1.to_s.to_i > mail2.to_s.to_i)
end
test "we can call other defined methods on the class as needed" do
diff --git a/actionmailer/test/mailers/async_mailer.rb b/actionmailer/test/mailers/async_mailer.rb
index ce601e7343..8a87e2e1cf 100644
--- a/actionmailer/test/mailers/async_mailer.rb
+++ b/actionmailer/test/mailers/async_mailer.rb
@@ -1,3 +1,2 @@
class AsyncMailer < BaseMailer
- self.async = true
end
diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb
index 3d667f0a2f..38598a520c 100644
--- a/actionpack/test/controller/assert_select_test.rb
+++ b/actionpack/test/controller/assert_select_test.rb
@@ -10,6 +10,16 @@ require 'controller/fake_controllers'
require 'action_mailer'
ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH
+class SynchronousQueue < Queue
+ def push(job)
+ job.run
+ end
+ alias << push
+ alias enq push
+end
+
+ActionMailer::Base.queue = SynchronousQueue.new
+
class AssertSelectTest < ActionController::TestCase
Assertion = ActiveSupport::TestCase::Assertion
diff --git a/actionpack/test/fixtures/ruby_template.rb b/actionpack/test/fixtures/ruby_template.rb
index d99833a24d..5097bce47c 100644
--- a/actionpack/test/fixtures/ruby_template.rb
+++ b/actionpack/test/fixtures/ruby_template.rb
@@ -1,3 +1,2 @@
body = ""
body << ["Hello", "from", "Ruby", "code"].join(" ")
-body
diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb
index cf1415b6c2..3d7067fbcb 100644
--- a/activemodel/lib/active_model/validations/clusivity.rb
+++ b/activemodel/lib/active_model/validations/clusivity.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/range.rb'
+require 'active_support/core_ext/range'
module ActiveModel
module Validations
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]
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 8287b35aaf..0718d0886f 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -89,7 +89,7 @@ module ActiveRecord
end
def test_full_pool_exception
- assert_raises(PoolFullError) do
+ assert_raises(ConnectionTimeoutError) do
(@pool.size + 1).times do
@pool.checkout
end
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
index 673a2b2b88..434d2b7ba5 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/guides/source/action_mailer_basics.textile b/guides/source/action_mailer_basics.textile
index 5f09b8e410..d92d5c48fb 100644
--- a/guides/source/action_mailer_basics.textile
+++ b/guides/source/action_mailer_basics.textile
@@ -472,7 +472,6 @@ The following configuration options are best made in one of the environment file
|+delivery_method+|Defines a delivery method. Possible values are <tt>:smtp</tt> (default), <tt>:sendmail</tt>, <tt>:file</tt> and <tt>:test</tt>.|
|+perform_deliveries+|Determines whether deliveries are actually carried out when the +deliver+ method is invoked on the Mail message. By default they are, but this can be turned off to help functional testing.|
|+deliveries+|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful for unit and functional testing.|
-|+async+|Setting this flag will turn on asynchronous message sending, message rendering and delivery will be pushed to <tt>Rails.queue</tt> for processing.|
|+default_options+|Allows you to set default values for the <tt>mail</tt> method options (<tt>:from</tt>, <tt>:reply_to</tt>, etc.).|
h4. Example Action Mailer Configuration
@@ -535,30 +534,22 @@ In the test we send the email and store the returned object in the +email+ varia
h3. Asynchronous
-You can turn on application-wide asynchronous message sending by adding to your <tt>config/application.rb</tt> file:
+Rails provides a Synchronous Queue by default. If you want to use an Asynchronous one you will need to configure an async Queue provider like Resque. Queue providers are supposed to have a Railtie where they configure it's own async queue.
-<ruby>
-config.action_mailer.async = true
-</ruby>
+h4. Custom Queues
-Alternatively you can turn on async within specific mailers:
+If you need a different queue than <tt>Rails.queue</tt> for your mailer you can use <tt>ActionMailer::Base.queue=</tt>:
<ruby>
class WelcomeMailer < ActionMailer::Base
- self.async = true
+ self.queue = MyQueue.new
end
</ruby>
-h4. Custom Queues
-
-If you need a different queue than <tt>Rails.queue</tt> for your mailer you can override <tt>ActionMailer::Base#queue</tt>:
+or adding to your <tt>config/environments/$RAILS_ENV.rb</tt>:
<ruby>
-class WelcomeMailer < ActionMailer::Base
- def queue
- MyQueue.new
- end
-end
+config.action_mailer.queue = MyQueue.new
</ruby>
Your custom queue should expect a job that responds to <tt>#run</tt>.
diff --git a/guides/source/configuring.textile b/guides/source/configuring.textile
index dd84754b22..73d6102eac 100644
--- a/guides/source/configuring.textile
+++ b/guides/source/configuring.textile
@@ -434,6 +434,11 @@ config.action_mailer.observers = ["MailObserver"]
config.action_mailer.interceptors = ["MailInterceptor"]
</ruby>
+* +config.action_mailer.queue+ registers the queue that will be used to deliver the mail.
+<ruby>
+config.action_mailer.queue = SomeQueue.new
+</ruby>
+
h4. Configuring Active Support
There are a few configuration options available in Active Support:
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index e889f0c23c..f4f81f1128 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -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*
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 0202e86f32..7a0bb21043 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -41,7 +41,7 @@ module Rails
@exceptions_app = nil
@autoflush_log = true
@log_formatter = ActiveSupport::Logger::SimpleFormatter.new
- @queue = Rails::Queueing::Queue
+ @queue = Rails::Queueing::SynchronousQueue
@queue_consumer = Rails::Queueing::ThreadedConsumer
@eager_load = nil
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index 1ac0248bcf..09b08d5663 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -59,8 +59,5 @@ module <%= app_const_base %>
# Version of your assets, change this if you want to expire all your assets.
config.assets.version = '1.0'
<% end -%>
-
- # Enable app-wide asynchronous ActionMailer.
- # config.action_mailer.async = true
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index 122e7e2b34..bcd0e7c898 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -41,7 +41,4 @@
# Debug mode disables concatenation and preprocessing of assets.
config.assets.debug = true
<%- end -%>
-
- # In development, use an in-memory queue for queueing.
- config.queue = Rails::Queueing::Queue
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index a627636089..fdf011a510 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -80,7 +80,7 @@
# Use default logging formatter so that PID and timestamp are not suppressed.
config.log_formatter = ::Logger::Formatter.new
- # Default the production mode queue to an in-memory queue. You will probably
+ # Default the production mode queue to an synchronous queue. You will probably
# want to replace this with an out-of-process queueing solution.
- config.queue = Rails::Queueing::Queue
+ # config.queue = Rails::Queueing::SynchronousQueue
end
diff --git a/railties/lib/rails/queueing.rb b/railties/lib/rails/queueing.rb
index baf6811d3e..7cd755b0f7 100644
--- a/railties/lib/rails/queueing.rb
+++ b/railties/lib/rails/queueing.rb
@@ -35,6 +35,14 @@ module Rails
class Queue < ::Queue
end
+ class SynchronousQueue < ::Queue
+ def push(job)
+ job.run
+ end
+ alias << push
+ alias enq push
+ end
+
# In test mode, the Rails queue is backed by an Array so that assertions
# can be made about its contents. The test queue provides a +jobs+
# method to make assertions about the queue's contents and a +drain+
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index d3bbac811c..1eb5fce384 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -50,6 +50,23 @@ module ApplicationTests
assert_equal "test.rails", ActionMailer::Base.default_url_options[:host]
end
+ test "uses the default queue for ActionMailer" do
+ require "#{app_path}/config/environment"
+ assert_kind_of Rails::Queueing::Container, ActionMailer::Base.queue
+ end
+
+ test "allows me to configure queue for ActionMailer" do
+ app_file "config/environments/development.rb", <<-RUBY
+ AppTemplate::Application.configure do
+ Rails.queue[:mailer] = Rails::Queueing::TestQueue.new
+ config.action_mailer.queue = Rails.queue[:mailer]
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_kind_of Rails::Queueing::TestQueue, ActionMailer::Base.queue
+ end
+
test "does not include url helpers as action methods" do
app_file "config/routes.rb", <<-RUBY
AppTemplate::Application.routes.draw do
@@ -195,5 +212,37 @@ module ApplicationTests
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
diff --git a/railties/test/application/queue_test.rb b/railties/test/application/queue_test.rb
index c856089540..f4c11c5f4f 100644
--- a/railties/test/application/queue_test.rb
+++ b/railties/test/application/queue_test.rb
@@ -23,10 +23,10 @@ module ApplicationTests
assert_kind_of Rails::Queueing::TestQueue, Rails.queue[:default]
end
- test "the queue is a Queue in development mode" do
+ test "the queue is a SynchronousQueue in development mode" do
app("development")
- assert_kind_of Rails::Queueing::Queue, Rails.application.queue[:default]
- assert_kind_of Rails::Queueing::Queue, Rails.queue[:default]
+ assert_kind_of Rails::Queueing::SynchronousQueue, Rails.application.queue[:default]
+ assert_kind_of Rails::Queueing::SynchronousQueue, Rails.queue[:default]
end
class ThreadTrackingJob
@@ -47,7 +47,7 @@ module ApplicationTests
end
end
- test "in development mode, an enqueued job will be processed in a separate thread" do
+ test "in development mode, an enqueued job will be processed in the same thread" do
app("development")
job = ThreadTrackingJob.new
@@ -55,7 +55,7 @@ module ApplicationTests
sleep 0.1
assert job.ran?, "Expected job to be run"
- assert job.ran_in_different_thread?, "Expected job to run in a different thread"
+ refute job.ran_in_different_thread?, "Expected job to run in the same thread"
end
test "in test mode, explicitly draining the queue will process it in a separate thread" do
@@ -160,6 +160,7 @@ module ApplicationTests
test "a custom consumer implementation can be provided" do
add_to_env_config "production", <<-RUBY
require "my_queue_consumer"
+ config.queue = Rails::Queueing::Queue
config.queue_consumer = MyQueueConsumer
RUBY
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
new file mode 100644
index 0000000000..52c07cee9f
--- /dev/null
+++ b/railties/test/application/rake/dbs_test.rb
@@ -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 \ No newline at end of file