Skip to content

Commit

Permalink
Updated rdoc / guides / release notes related to ActiveJob / ActionMa…
Browse files Browse the repository at this point in the history
…iler
  • Loading branch information
cristianbica committed Aug 20, 2014
1 parent f4ee114 commit 9e7f4a9
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 54 deletions.
10 changes: 6 additions & 4 deletions actionmailer/CHANGELOG.md
@@ -1,7 +1,9 @@
* 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
Expand Down
6 changes: 3 additions & 3 deletions actionmailer/README.rdoc
Expand Up @@ -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

Expand Down
21 changes: 16 additions & 5 deletions actionmailer/lib/action_mailer/base.rb
Expand Up @@ -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.
#
Expand Down Expand Up @@ -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"
Expand Down
4 changes: 2 additions & 2 deletions actionmailer/lib/action_mailer/delivery_job.rb
@@ -1,10 +1,10 @@
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
Expand Down
89 changes: 68 additions & 21 deletions actionmailer/lib/action_mailer/message_delivery.rb
@@ -1,65 +1,112 @@
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
12 changes: 6 additions & 6 deletions actionmailer/lib/action_mailer/test_helper.rb
Expand Up @@ -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
#
Expand All @@ -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)
Expand All @@ -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
#
Expand Down
3 changes: 3 additions & 0 deletions guides/source/4_2_release_notes.md
Expand Up @@ -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
Expand Down
32 changes: 28 additions & 4 deletions guides/source/action_mailer_basics.md
Expand Up @@ -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
Expand All @@ -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 }
Expand All @@ -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

Expand Down
6 changes: 3 additions & 3 deletions guides/source/active_job_basics.md
Expand Up @@ -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
```

Expand Down
10 changes: 5 additions & 5 deletions guides/source/active_record_querying.md
Expand Up @@ -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
```

Expand All @@ -292,15 +292,15 @@ 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
```

To add conditions to a `find_each` operation you can chain other Active Record methods such as `where`:

```ruby
User.where(weekly_subscriber: true).find_each do |user|
NewsMailer.weekly(user).deliver
NewsMailer.weekly(user).deliver_now
end
```

Expand All @@ -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
```

Expand All @@ -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
```

Expand Down
2 changes: 1 addition & 1 deletion guides/source/testing.md
Expand Up @@ -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
Expand Down

0 comments on commit 9e7f4a9

Please sign in to comment.