提交 9e7f4a94 编写于 作者: C Cristian Bica

Updated rdoc / guides / release notes related to ActiveJob / ActionMailer

上级 f4ee1147
* Added #deliver_later in addition to #deliver, which will enqueue a job to render and
deliver the mail instead of delivering it right at that moment. The job is enqueued
using the new Active Job framework in Rails, and will use whatever queue is configured for Rails.
* Added #deliver_later, #deliver_now and deprecate #deliver in favour of
#deliver_now. #deliver_later will enqueue a job to render and deliver
the mail instead of delivering it right at that moment. The job is enqueued
using the new Active Job framework in Rails, and will use whatever queue is
configured for Rails.
*DHH/Abdelkader Boudih/Cristian Bica*
* Make ActionMailer::Previews methods class methods. Previously they were
......
......@@ -65,12 +65,12 @@ In order to send mails, you simply call the method and then call +deliver+ on th
Calling the method returns a Mail Message object:
message = Notifier.welcome("david@loudthinking.com") # => Returns a Mail::Message object
message.deliver # => delivers the email
message = Notifier.welcome("david@loudthinking.com") # => Returns a Mail::Message object
message.deliver_now # => delivers the email
Or you can just chain the methods together like:
Notifier.welcome("david@loudthinking.com").deliver # Creates the email and sends it immediately
Notifier.welcome("david@loudthinking.com").deliver_now # Creates the email and sends it immediately
== Setting defaults
......
......@@ -138,9 +138,20 @@ module ActionMailer
# Once a mailer action and template are defined, you can deliver your message or create it and save it
# for delivery later:
#
# Notifier.welcome(david).deliver # sends the email
# mail = Notifier.welcome(david) # => a Mail::Message object
# mail.deliver # sends the email
# Notifier.welcome(david).deliver_now # sends the email
# mail = Notifier.welcome(david) # => an ActionMailer::MessageDeliver object
# mail.deliver_now # sends the email
#
# The <tt>ActionMailer::MessageDeliver</tt> class is a wrapper around a <tt>Mail::Message</tt> object. If
# you want direct access to the <tt>Mail::Message</tt> object you can call the <tt>message</tt> method on
# the <tt>ActionMailer::MessageDeliver</tt> object.
#
# Notifier.welcome(david).message # => a Mail::Message object
#
# ActionMailer is nicely integrated with ActiveJob so you can send emails in the background (example: outside
# of the request-response cycle, so the user doesn't have to wait on it):
#
# Notifier.welcome(david).deliver_later # enqueue the email sending to ActiveJob
#
# You never instantiate your mailer class. Rather, you just call the method you defined on the class itself.
#
......@@ -322,8 +333,8 @@ module ActionMailer
# end
#
# Methods must return a <tt>Mail::Message</tt> object which can be generated by calling the mailer
# method without the additional <tt>deliver</tt>. The location of the mailer previews
# directory can be configured using the <tt>preview_path</tt> option which has a default
# method without the additional <tt>deliver_now</tt> / <tt>deliver_later</tt>. The location of the
# mailer previews directory can be configured using the <tt>preview_path</tt> option which has a default
# of <tt>test/mailers/previews</tt>:
#
# config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
......
require 'active_job'
module ActionMailer
class DeliveryJob < ActiveJob::Base
class DeliveryJob < ActiveJob::Base #:nodoc:
queue_as :mailers
def perform(mailer, mail_method, delivery_method, *args)
def perform(mailer, mail_method, delivery_method, *args) #:nodoc#
mailer.constantize.public_send(mail_method, *args).send(delivery_method)
end
end
......
require 'delegate'
module ActionMailer
# The ActionMailer::MessageDeliver class is used by ActionMailer::Base when
# creating a new mailer. MessageDeliver is a wrapper (Delegator subclass)
# around a lazy created Mail::Message. You can get direct access to the
# Mail::Message, deliver the email or schedule the email to be sent through ActiveJob.
#
# Notifier.welcome(david) # an ActionMailer::MessageDeliver object
# Notifier.welcome(david).deliver_now # sends the email
# Notifier.welcome(david).deliver_later # enqueue the deliver email job to ActiveJob
# Notifier.welcome(david).message # a Mail::Message object
class MessageDelivery < Delegator
def initialize(mailer, mail_method, *args)
def initialize(mailer, mail_method, *args) #:nodoc:
@mailer = mailer
@mail_method = mail_method
@args = args
end
def __getobj__
def __getobj__ #:nodoc:
@obj ||= @mailer.send(:new, @mail_method, *@args).message
end
def __setobj__(obj)
def __setobj__(obj) #:nodoc:
@obj = obj
end
def message #:nodoc:
# Returns the Mail::Message object
def message
__getobj__
end
# Enqueues the message to be delivered through ActiveJob. When the
# ActiveJob job runs it will send the email using #deliver_now!. That
# means that the message will be sent bypassing checking perform_deliveries
# and raise_delivery_errors, so use with caution.
#
# ==== Examples
#
# Notifier.welcome(david).deliver_later
# Notifier.welcome(david).deliver_later(in: 1.hour)
# Notifier.welcome(david).deliver_later(at: 10.hours.from_now)
#
# ==== Options
# * <tt>in</tt> - Enqueue the message to be delivered with a delay
# * <tt>at</tt> - Enqueue the message to be delivered at (after) a specific date / time
def deliver_later!(options={})
enqueue_delivery :deliver_now!, options
end
# Enqueues the message to be delivered through ActiveJob. When the
# ActiveJob job runs it will send the email using #deliver_now.
#
# ==== Examples
#
# Notifier.welcome(david).deliver_later
# Notifier.welcome(david).deliver_later(in: 1.hour)
# Notifier.welcome(david).deliver_later(at: 10.hours.from_now)
#
# ==== Options
# * <tt>in</tt> - Enqueue the message to be delivered with a delay
# * <tt>at</tt> - Enqueue the message to be delivered at (after) a specific date / time
def deliver_later(options={})
enqueue_delivery :deliver_now, options
end
# Delivers a message. The message will be sent bypassing checking perform_deliveries
# and raise_delivery_errors, so use with caution.
#
# Notifier.welcome(david).deliver_now!
#
def deliver_now!
message.deliver!
end
# Delivers a message:
#
# Notifier.welcome(david).deliver_now
#
def deliver_now
message.deliver
end
def deliver!
ActiveSupport::Deprecation.warn "#deliver! is deprecated and will be removed on Rails 5. " \
"Use #deliver_now! to deliver immediately or #deliver_later! to deliver through ActiveJob"
def deliver! #:nodoc:
ActiveSupport::Deprecation.warn "#deliver! is deprecated and will be removed in Rails 5. " \
"Use #deliver_now! to deliver immediately or #deliver_later! to deliver through ActiveJob."
deliver_now!
end
def deliver
ActiveSupport::Deprecation.warn "#deliver is deprecated and will be removed on Rails 5. " \
"Use #deliver_now to deliver immediately or #deliver_later to deliver through ActiveJob"
def deliver #:nodoc:
ActiveSupport::Deprecation.warn "#deliver is deprecated and will be removed in Rails 5. " \
"Use #deliver_now to deliver immediately or #deliver_later to deliver through ActiveJob."
deliver_now
end
private
def enqueue_delivery(delivery_method, options={})
args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args
enqueue_method = :enqueue
if options[:at]
enqueue_method = :enqueue_at
args.unshift options[:at]
elsif options[:in]
enqueue_method = :enqueue_in
args.unshift options[:in]
def enqueue_delivery(delivery_method, options={})
args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args
enqueue_method = :enqueue
if options[:at]
enqueue_method = :enqueue_at
args.unshift options[:at]
elsif options[:in]
enqueue_method = :enqueue_in
args.unshift options[:in]
end
ActionMailer::DeliveryJob.send enqueue_method, *args
end
ActionMailer::DeliveryJob.send enqueue_method, *args
end
end
end
......@@ -6,9 +6,9 @@ module TestHelper
#
# def test_emails
# assert_emails 0
# ContactMailer.welcome.deliver
# ContactMailer.welcome.deliver_now
# assert_emails 1
# ContactMailer.welcome.deliver
# ContactMailer.welcome.deliver_now
# assert_emails 2
# end
#
......@@ -17,12 +17,12 @@ module TestHelper
#
# def test_emails_again
# assert_emails 1 do
# ContactMailer.welcome.deliver
# ContactMailer.welcome.deliver_now
# end
#
# assert_emails 2 do
# ContactMailer.welcome.deliver
# ContactMailer.welcome.deliver
# ContactMailer.welcome.deliver_now
# ContactMailer.welcome.deliver_now
# end
# end
def assert_emails(number)
......@@ -40,7 +40,7 @@ def assert_emails(number)
#
# def test_emails
# assert_no_emails
# ContactMailer.welcome.deliver
# ContactMailer.welcome.deliver_now
# assert_emails 1
# end
#
......
......@@ -311,6 +311,9 @@ Please refer to the [Changelog][action-mailer] for detailed changes.
* Deprecated `*_path` helpers in mailers. Always use `*_url` helpers instead.
([Pull Request](https://github.com/rails/rails/pull/15840))
* Deprecated `deliver` / `deliver!` in favour of `deliver_now` / `deliver_now!`.
([Pull Request](https://github.com/rails/rails/pull/16582))
### Notable changes
* Introduced `deliver_later` which enqueues a job on the application's queue
......
......@@ -159,7 +159,10 @@ $ bin/rake db:migrate
Now that we have a user model to play with, we will just edit the
`app/controllers/users_controller.rb` make it instruct the `UserMailer` to deliver
an email to the newly created user by editing the create action and inserting a
call to `UserMailer.welcome_email` right after the user is successfully saved:
call to `UserMailer.welcome_email` right after the user is successfully saved.
Action Mailer is nicely integrated with Active Job so you can send emails outside
of the request-response cycle, so the user doesn't have to wait on it:
```ruby
class UsersController < ApplicationController
......@@ -171,7 +174,7 @@ class UsersController < ApplicationController
respond_to do |format|
if @user.save
# Tell the UserMailer to send a welcome email after save
UserMailer.welcome_email(@user).deliver
UserMailer.welcome_email(@user).deliver_later
format.html { redirect_to(@user, notice: 'User was successfully created.') }
format.json { render json: @user, status: :created, location: @user }
......@@ -184,8 +187,29 @@ class UsersController < ApplicationController
end
```
The method `welcome_email` returns a `Mail::Message` object which can then just
be told `deliver` to send itself out.
NOTE: By default Active Job is configured to execute the job `:inline`. So you can
use `deliver_later` now to send the emails and when you decide to start sending the
email from a background job you'll just have to setup Active Job to use a queueing
backend (Sidekiq, Resque, etc).
If you want to send the emails right away (from a cronjob for example) just
call `deliver_now`:
```ruby
class SendWeeklySummary
def run
User.find_each do |user|
UserMailer.weekly_summary(user).deliver_now
end
end
end
```
The method `welcome_email` returns a `ActionMailer::MessageDelivery` object which
can then just be told `deliver_now` or `deliver_later` to send itself out. The
`ActionMailer::MessageDelivery` object is just a wrapper around a `Mail::Message`. If
you want to inspect, alter or do anything else with the `Mail::Message` object you can
access it with the `message` method on the `ActionMailer::MessageDelivery` object.
### Auto encoding header values
......
......@@ -196,10 +196,10 @@ of the request-response cycle, so the user doesn't have to wait on it. Active Jo
is integrated with Action Mailer so you can easily send emails async:
```ruby
# Instead of the classic
UserMailer.welcome(@user).deliver
# If you want to send the email now use #deliver_now
UserMailer.welcome(@user).deliver_now
# use #deliver later to send the email async
# If you want to send the email through ActiveJob use #deliver_later
UserMailer.welcome(@user).deliver_later
```
......
......@@ -276,7 +276,7 @@ This may appear straightforward:
```ruby
# This is very inefficient when the users table has thousands of rows.
User.all.each do |user|
NewsMailer.weekly(user).deliver
NewsMailer.weekly(user).deliver_now
end
```
......@@ -292,7 +292,7 @@ The `find_each` method retrieves a batch of records and then yields _each_ recor
```ruby
User.find_each do |user|
NewsMailer.weekly(user).deliver
NewsMailer.weekly(user).deliver_now
end
```
......@@ -300,7 +300,7 @@ To add conditions to a `find_each` operation you can chain other Active Record m
```ruby
User.where(weekly_subscriber: true).find_each do |user|
NewsMailer.weekly(user).deliver
NewsMailer.weekly(user).deliver_now
end
```
......@@ -316,7 +316,7 @@ The `:batch_size` option allows you to specify the number of records to be retri
```ruby
User.find_each(batch_size: 5000) do |user|
NewsMailer.weekly(user).deliver
NewsMailer.weekly(user).deliver_now
end
```
......@@ -328,7 +328,7 @@ For example, to send newsletters only to users with the primary key starting fro
```ruby
User.find_each(start: 2000, batch_size: 5000) do |user|
NewsMailer.weekly(user).deliver
NewsMailer.weekly(user).deliver_now
end
```
......
......@@ -949,7 +949,7 @@ class UserMailerTest < ActionMailer::TestCase
test "invite" do
# Send the email, then test that it got queued
email = UserMailer.create_invite('me@example.com',
'friend@example.com', Time.now).deliver
'friend@example.com', Time.now).deliver_now
assert_not ActionMailer::Base.deliveries.empty?
# Test the body of the sent email contains what we expect it to
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册