Skip to content

Commit 2080ff2

Browse files
committed
Merge pull request rails#24457 from jeremy/mailer/dont-deliver-later-after-message-is-loaded
Disallow calling `#deliver_later` after local message modifications.
2 parents 55c1436 + 95e06e6 commit 2080ff2

File tree

3 files changed

+40
-2
lines changed

3 files changed

+40
-2
lines changed

actionmailer/CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
* Disallow calling `#deliver_later` after making local modifications to
2+
the message which would be lost when the delivery job is enqueued.
3+
4+
Prevents a common, hard-to-find bug like
5+
6+
message = Notifier.welcome(user, foo)
7+
message.message_id = my_generated_message_id
8+
message.deliver_later
9+
10+
The message_id is silently lost! *Only the mailer arguments are passed
11+
to the delivery job.*
12+
13+
This raises an exception now. Make modifications to the message within
14+
the mailer method instead, or use a custom Active Job to manage delivery
15+
instead of using #deliver_later.
16+
17+
*Jeremy Daer*
18+
119
* Removes `-t` from default Sendmail arguments to match the underlying
220
`Mail::Sendmail` setting.
321

actionmailer/lib/action_mailer/message_delivery.rb

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def initialize(mailer, mail_method, *args) #:nodoc:
1818
@mailer = mailer
1919
@mail_method = mail_method
2020
@args = args
21+
@obj = nil
2122
end
2223

2324
def __getobj__ #:nodoc:
@@ -91,8 +92,19 @@ def deliver_now
9192
private
9293

9394
def enqueue_delivery(delivery_method, options={})
94-
args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args
95-
ActionMailer::DeliveryJob.set(options).perform_later(*args)
95+
if @obj
96+
raise "You've accessed the message before asking to deliver it " \
97+
"later, so you may have made local changes that would be " \
98+
"silently lost if we enqueued a job to deliver it. Why? Only " \
99+
"the mailer method *arguments* are passed with the delivery job! " \
100+
"Do not access the message in any way if you mean to deliver it " \
101+
"later. Workarounds: 1. don't touch the message before calling " \
102+
"#deliver_later, 2. only touch the message *within your mailer " \
103+
"method*, or 3. use a custom Active Job instead of #deliver_later."
104+
else
105+
args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args
106+
ActionMailer::DeliveryJob.set(options).perform_later(*args)
107+
end
96108
end
97109
end
98110
end

actionmailer/test/message_delivery_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,12 @@ def test_should_enqueue_and_run_correctly_in_activejob
9393
@mail.deliver_later(queue: :another_queue)
9494
end
9595
end
96+
97+
test 'deliver_later after accessing the message is disallowed' do
98+
@mail.message # Load the message, which calls the mailer method.
99+
100+
assert_raise RuntimeError do
101+
@mail.deliver_later
102+
end
103+
end
96104
end

0 commit comments

Comments
 (0)