actionmailer 4.2.11.2 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionmailer might be problematic. Click here for more details.

@@ -1,6 +1,8 @@
1
- require 'abstract_controller/collector'
2
- require 'active_support/core_ext/hash/reverse_merge'
3
- require 'active_support/core_ext/array/extract_options'
1
+ # frozen_string_literal: true
2
+
3
+ require "abstract_controller/collector"
4
+ require "active_support/core_ext/hash/reverse_merge"
5
+ require "active_support/core_ext/array/extract_options"
4
6
 
5
7
  module ActionMailer
6
8
  class Collector
@@ -1,13 +1,44 @@
1
- require 'active_job'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_job"
2
4
 
3
5
  module ActionMailer
4
6
  # The <tt>ActionMailer::DeliveryJob</tt> class is used when you
5
7
  # want to send emails outside of the request-response cycle.
8
+ #
9
+ # Exceptions are rescued and handled by the mailer class.
6
10
  class DeliveryJob < ActiveJob::Base # :nodoc:
7
- queue_as :mailers
11
+ queue_as { ActionMailer::Base.deliver_later_queue_name }
12
+
13
+ rescue_from StandardError, with: :handle_exception_with_mailer_class
14
+
15
+ before_perform do
16
+ ActiveSupport::Deprecation.warn <<~MSG.squish
17
+ Sending mail with DeliveryJob and Parameterized::DeliveryJob
18
+ is deprecated and will be removed in Rails 6.1.
19
+ Please use MailDeliveryJob instead.
20
+ MSG
21
+ end
8
22
 
9
- def perform(mailer, mail_method, delivery_method, *args) # :nodoc:
23
+ def perform(mailer, mail_method, delivery_method, *args) #:nodoc:
10
24
  mailer.constantize.public_send(mail_method, *args).send(delivery_method)
11
25
  end
26
+
27
+ private
28
+ # "Deserialize" the mailer class name by hand in case another argument
29
+ # (like a Global ID reference) raised DeserializationError.
30
+ def mailer_class
31
+ if mailer = Array(@serialized_arguments).first || Array(arguments).first
32
+ mailer.constantize
33
+ end
34
+ end
35
+
36
+ def handle_exception_with_mailer_class(exception)
37
+ if klass = mailer_class
38
+ klass.handle_exception exception
39
+ else
40
+ raise exception
41
+ end
42
+ end
12
43
  end
13
44
  end
@@ -1,4 +1,6 @@
1
- require 'tmpdir'
1
+ # frozen_string_literal: true
2
+
3
+ require "tmpdir"
2
4
 
3
5
  module ActionMailer
4
6
  # This module handles everything related to mail delivery, from registering
@@ -7,22 +9,18 @@ module ActionMailer
7
9
  extend ActiveSupport::Concern
8
10
 
9
11
  included do
10
- class_attribute :delivery_methods, :delivery_method
11
-
12
12
  # Do not make this inheritable, because we always want it to propagate
13
- cattr_accessor :raise_delivery_errors
14
- self.raise_delivery_errors = true
15
-
16
- cattr_accessor :perform_deliveries
17
- self.perform_deliveries = true
13
+ cattr_accessor :raise_delivery_errors, default: true
14
+ cattr_accessor :perform_deliveries, default: true
15
+ cattr_accessor :deliver_later_queue_name, default: :mailers
18
16
 
19
- self.delivery_methods = {}.freeze
20
- self.delivery_method = :smtp
17
+ class_attribute :delivery_methods, default: {}.freeze
18
+ class_attribute :delivery_method, default: :smtp
21
19
 
22
20
  add_delivery_method :smtp, Mail::SMTP,
23
21
  address: "localhost",
24
22
  port: 25,
25
- domain: 'localhost.localdomain',
23
+ domain: "localhost.localdomain",
26
24
  user_name: nil,
27
25
  password: nil,
28
26
  authentication: nil,
@@ -32,8 +30,8 @@ module ActionMailer
32
30
  location: defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
33
31
 
34
32
  add_delivery_method :sendmail, Mail::Sendmail,
35
- location: '/usr/sbin/sendmail',
36
- arguments: '-i'
33
+ location: "/usr/sbin/sendmail",
34
+ arguments: "-i"
37
35
 
38
36
  add_delivery_method :test, Mail::TestMailer
39
37
  end
@@ -48,15 +46,15 @@ module ActionMailer
48
46
  #
49
47
  # add_delivery_method :sendmail, Mail::Sendmail,
50
48
  # location: '/usr/sbin/sendmail',
51
- # arguments: '-i -t'
52
- def add_delivery_method(symbol, klass, default_options={})
49
+ # arguments: '-i'
50
+ def add_delivery_method(symbol, klass, default_options = {})
53
51
  class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
54
52
  send(:"#{symbol}_settings=", default_options)
55
53
  self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
56
54
  end
57
55
 
58
- def wrap_delivery_behavior(mail, method=nil, options=nil) # :nodoc:
59
- method ||= self.delivery_method
56
+ def wrap_delivery_behavior(mail, method = nil, options = nil) # :nodoc:
57
+ method ||= delivery_method
60
58
  mail.delivery_handler = self
61
59
 
62
60
  case method
@@ -1,14 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionMailer
2
- # Returns the version of the currently loaded Action Mailer as a <tt>Gem::Version</tt>
4
+ # Returns the version of the currently loaded Action Mailer as a <tt>Gem::Version</tt>.
3
5
  def self.gem_version
4
6
  Gem::Version.new VERSION::STRING
5
7
  end
6
8
 
7
9
  module VERSION
8
- MAJOR = 4
9
- MINOR = 2
10
- TINY = 11
11
- PRE = "2"
10
+ MAJOR = 6
11
+ MINOR = 0
12
+ TINY = 0
13
+ PRE = nil
12
14
 
13
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
16
  end
@@ -1,17 +1,19 @@
1
- require 'base64'
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
2
4
 
3
5
  module ActionMailer
4
6
  # Implements a mailer preview interceptor that converts image tag src attributes
5
- # that use inline cid: style urls to data: style urls so that they are visible
6
- # when previewing a HTML email in a web browser.
7
+ # that use inline cid: style URLs to data: style URLs so that they are visible
8
+ # when previewing an HTML email in a web browser.
7
9
  #
8
- # This interceptor is not enabled by default, to use it just register it like any
9
- # other mailer preview interceptor:
10
+ # This interceptor is enabled by default. To disable it, delete it from the
11
+ # <tt>ActionMailer::Base.preview_interceptors</tt> array:
10
12
  #
11
- # ActionMailer::Base.register_preview_interceptor(ActionMailer::InlinePreviewInterceptor)
13
+ # ActionMailer::Base.preview_interceptors.delete(ActionMailer::InlinePreviewInterceptor)
12
14
  #
13
15
  class InlinePreviewInterceptor
14
- PATTERN = /src=(?:"cid:[^"]+"|'cid:[^']+')/i
16
+ PATTERN = /src=(?:"cid:[^"]+"|'cid:[^']+')/i
15
17
 
16
18
  include Base64
17
19
 
@@ -26,7 +28,7 @@ module ActionMailer
26
28
  def transform! #:nodoc:
27
29
  return message if html_part.blank?
28
30
 
29
- html_source.gsub!(PATTERN) do |match|
31
+ html_part.body = html_part.decoded.gsub(PATTERN) do |match|
30
32
  if part = find_part(match[9..-2])
31
33
  %[src="#{data_url(part)}"]
32
34
  else
@@ -38,24 +40,18 @@ module ActionMailer
38
40
  end
39
41
 
40
42
  private
41
- def message
42
- @message
43
- end
43
+ attr_reader :message
44
44
 
45
45
  def html_part
46
46
  @html_part ||= message.html_part
47
47
  end
48
48
 
49
- def html_source
50
- html_part.body.raw_source
51
- end
52
-
53
49
  def data_url(part)
54
50
  "data:#{part.mime_type};base64,#{strict_encode64(part.body.raw_source)}"
55
51
  end
56
52
 
57
53
  def find_part(cid)
58
- message.all_parts.find{ |p| p.attachment? && p.cid == cid }
54
+ message.all_parts.find { |p| p.attachment? && p.cid == cid }
59
55
  end
60
56
  end
61
57
  end
@@ -1,14 +1,20 @@
1
- require 'active_support/log_subscriber'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/log_subscriber"
2
4
 
3
5
  module ActionMailer
4
6
  # Implements the ActiveSupport::LogSubscriber for logging notifications when
5
- # email is delivered and received.
7
+ # email is delivered or received.
6
8
  class LogSubscriber < ActiveSupport::LogSubscriber
7
9
  # An email was delivered.
8
10
  def deliver(event)
9
11
  info do
10
- recipients = Array(event.payload[:to]).join(', ')
11
- "\nSent mail to #{recipients} (#{event.duration.round(1)}ms)"
12
+ perform_deliveries = event.payload[:perform_deliveries]
13
+ if perform_deliveries
14
+ "Delivered mail #{event.payload[:message_id]} (#{event.duration.round(1)}ms)"
15
+ else
16
+ "Skipped delivery of mail #{event.payload[:message_id]} as `perform_deliveries` is false"
17
+ end
12
18
  end
13
19
 
14
20
  debug { event.payload[:mail] }
@@ -16,7 +22,7 @@ module ActionMailer
16
22
 
17
23
  # An email was received.
18
24
  def receive(event)
19
- info { "\nReceived mail (#{event.duration.round(1)}ms)" }
25
+ info { "Received mail (#{event.duration.round(1)}ms)" }
20
26
  debug { event.payload[:mail] }
21
27
  end
22
28
 
@@ -25,11 +31,11 @@ module ActionMailer
25
31
  debug do
26
32
  mailer = event.payload[:mailer]
27
33
  action = event.payload[:action]
28
- "\n#{mailer}##{action}: processed outbound mail in #{event.duration.round(1)}ms"
34
+ "#{mailer}##{action}: processed outbound mail in #{event.duration.round(1)}ms"
29
35
  end
30
36
  end
31
37
 
32
- # Use the logger configured for ActionMailer::Base
38
+ # Use the logger configured for ActionMailer::Base.
33
39
  def logger
34
40
  ActionMailer::Base.logger
35
41
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_job"
4
+
5
+ module ActionMailer
6
+ # The <tt>ActionMailer::MailDeliveryJob</tt> class is used when you
7
+ # want to send emails outside of the request-response cycle. It supports
8
+ # sending either parameterized or normal mail.
9
+ #
10
+ # Exceptions are rescued and handled by the mailer class.
11
+ class MailDeliveryJob < ActiveJob::Base # :nodoc:
12
+ queue_as { ActionMailer::Base.deliver_later_queue_name }
13
+
14
+ rescue_from StandardError, with: :handle_exception_with_mailer_class
15
+
16
+ def perform(mailer, mail_method, delivery_method, args:, params: nil) #:nodoc:
17
+ mailer_class = params ? mailer.constantize.with(params) : mailer.constantize
18
+ mailer_class.public_send(mail_method, *args).send(delivery_method)
19
+ end
20
+
21
+ private
22
+ # "Deserialize" the mailer class name by hand in case another argument
23
+ # (like a Global ID reference) raised DeserializationError.
24
+ def mailer_class
25
+ if mailer = Array(@serialized_arguments).first || Array(arguments).first
26
+ mailer.constantize
27
+ end
28
+ end
29
+
30
+ def handle_exception_with_mailer_class(exception)
31
+ if klass = mailer_class
32
+ klass.handle_exception exception
33
+ else
34
+ raise exception
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,10 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionMailer
2
4
  # Provides helper methods for ActionMailer::Base that can be used for easily
3
5
  # formatting messages, accessing mailer or message instances, and the
4
6
  # attachments list.
5
7
  module MailHelper
6
8
  # Take the text and format it, indented two spaces for each line, and
7
- # wrapped at 72 columns.
9
+ # wrapped at 72 columns:
10
+ #
11
+ # text = <<-TEXT
12
+ # This is
13
+ # the paragraph.
14
+ #
15
+ # * item1 * item2
16
+ # TEXT
17
+ #
18
+ # block_format text
19
+ # # => " This is the paragraph.\n\n * item1\n * item2\n"
8
20
  def block_format(text)
9
21
  formatted = text.split(/\n\r?\n/).collect { |paragraph|
10
22
  format_paragraph(paragraph)
@@ -33,6 +45,8 @@ module ActionMailer
33
45
  end
34
46
 
35
47
  # Returns +text+ wrapped at +len+ columns and indented +indent+ spaces.
48
+ # By default column length +len+ equals 72 characters and indent
49
+ # +indent+ equal two spaces.
36
50
  #
37
51
  # my_text = 'Here is a sample text with more than 40 characters'
38
52
  #
@@ -42,7 +56,7 @@ module ActionMailer
42
56
  sentences = [[]]
43
57
 
44
58
  text.split.each do |word|
45
- if sentences.first.present? && (sentences.last + [word]).join(' ').length > len
59
+ if sentences.first.present? && (sentences.last + [word]).join(" ").length > len
46
60
  sentences << [word]
47
61
  else
48
62
  sentences.last << word
@@ -1,10 +1,10 @@
1
- require 'delegate'
2
- require 'active_support/core_ext/string/filters'
1
+ # frozen_string_literal: true
3
2
 
4
- module ActionMailer
3
+ require "delegate"
5
4
 
5
+ module ActionMailer
6
6
  # The <tt>ActionMailer::MessageDelivery</tt> class is used by
7
- # <tt>ActionMailer::Base</tt> when creating a new mailer.
7
+ # ActionMailer::Base when creating a new mailer.
8
8
  # <tt>MessageDelivery</tt> is a wrapper (+Delegator+ subclass) around a lazy
9
9
  # created <tt>Mail::Message</tt>. You can get direct access to the
10
10
  # <tt>Mail::Message</tt>, deliver the email or schedule the email to be sent
@@ -15,25 +15,35 @@ module ActionMailer
15
15
  # Notifier.welcome(User.first).deliver_later # enqueue email delivery as a job through Active Job
16
16
  # Notifier.welcome(User.first).message # a Mail::Message object
17
17
  class MessageDelivery < Delegator
18
- def initialize(mailer, mail_method, *args) #:nodoc:
19
- @mailer = mailer
20
- @mail_method = mail_method
21
- @args = args
18
+ def initialize(mailer_class, action, *args) #:nodoc:
19
+ @mailer_class, @action, @args = mailer_class, action, args
20
+
21
+ # The mail is only processed if we try to call any methods on it.
22
+ # Typical usage will leave it unloaded and call deliver_later.
23
+ @processed_mailer = nil
24
+ @mail_message = nil
22
25
  end
23
26
 
27
+ # Method calls are delegated to the Mail::Message that's ready to deliver.
24
28
  def __getobj__ #:nodoc:
25
- @obj ||= @mailer.send(:new, @mail_method, *@args).message
29
+ @mail_message ||= processed_mailer.message
26
30
  end
27
31
 
28
- def __setobj__(obj) #:nodoc:
29
- @obj = obj
32
+ # Unused except for delegator internals (dup, marshalling).
33
+ def __setobj__(mail_message) #:nodoc:
34
+ @mail_message = mail_message
30
35
  end
31
36
 
32
- # Returns the Mail::Message object
37
+ # Returns the resulting Mail::Message
33
38
  def message
34
39
  __getobj__
35
40
  end
36
41
 
42
+ # Was the delegate loaded, causing the mailer action to be processed?
43
+ def processed?
44
+ @processed_mailer || @mail_message
45
+ end
46
+
37
47
  # Enqueues the email to be delivered through Active Job. When the
38
48
  # job runs it will send the email using +deliver_now!+. That means
39
49
  # that the message will be sent bypassing checking +perform_deliveries+
@@ -48,7 +58,15 @@ module ActionMailer
48
58
  # * <tt>:wait</tt> - Enqueue the email to be delivered with a delay
49
59
  # * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time
50
60
  # * <tt>:queue</tt> - Enqueue the email on the specified queue
51
- def deliver_later!(options={})
61
+ #
62
+ # By default, the email will be enqueued using <tt>ActionMailer::DeliveryJob</tt>. Each
63
+ # <tt>ActionMailer::Base</tt> class can specify the job to use by setting the class variable
64
+ # +delivery_job+.
65
+ #
66
+ # class AccountRegistrationMailer < ApplicationMailer
67
+ # self.delivery_job = RegistrationDeliveryJob
68
+ # end
69
+ def deliver_later!(options = {})
52
70
  enqueue_delivery :deliver_now!, options
53
71
  end
54
72
 
@@ -61,10 +79,18 @@ module ActionMailer
61
79
  #
62
80
  # Options:
63
81
  #
64
- # * <tt>:wait</tt> - Enqueue the email to be delivered with a delay
65
- # * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time
66
- # * <tt>:queue</tt> - Enqueue the email on the specified queue
67
- def deliver_later(options={})
82
+ # * <tt>:wait</tt> - Enqueue the email to be delivered with a delay.
83
+ # * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time.
84
+ # * <tt>:queue</tt> - Enqueue the email on the specified queue.
85
+ #
86
+ # By default, the email will be enqueued using <tt>ActionMailer::DeliveryJob</tt>. Each
87
+ # <tt>ActionMailer::Base</tt> class can specify the job to use by setting the class variable
88
+ # +delivery_job+.
89
+ #
90
+ # class AccountRegistrationMailer < ApplicationMailer
91
+ # self.delivery_job = RegistrationDeliveryJob
92
+ # end
93
+ def deliver_later(options = {})
68
94
  enqueue_delivery :deliver_now, options
69
95
  end
70
96
 
@@ -74,7 +100,9 @@ module ActionMailer
74
100
  # Notifier.welcome(User.first).deliver_now!
75
101
  #
76
102
  def deliver_now!
77
- message.deliver!
103
+ processed_mailer.handle_exceptions do
104
+ message.deliver!
105
+ end
78
106
  end
79
107
 
80
108
  # Delivers an email:
@@ -82,34 +110,43 @@ module ActionMailer
82
110
  # Notifier.welcome(User.first).deliver_now
83
111
  #
84
112
  def deliver_now
85
- message.deliver
86
- end
87
-
88
- def deliver! #:nodoc:
89
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
90
- `#deliver!` is deprecated and will be removed in Rails 5. Use
91
- `#deliver_now!` to deliver immediately or `#deliver_later!` to
92
- deliver through Active Job.
93
- MSG
94
-
95
- deliver_now!
96
- end
97
-
98
- def deliver #:nodoc:
99
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
100
- `#deliver` is deprecated and will be removed in Rails 5. Use
101
- `#deliver_now` to deliver immediately or `#deliver_later` to
102
- deliver through Active Job.
103
- MSG
104
-
105
- deliver_now
113
+ processed_mailer.handle_exceptions do
114
+ message.deliver
115
+ end
106
116
  end
107
117
 
108
118
  private
119
+ # Returns the processed Mailer instance. We keep this instance
120
+ # on hand so we can delegate exception handling to it.
121
+ def processed_mailer
122
+ @processed_mailer ||= @mailer_class.new.tap do |mailer|
123
+ mailer.process @action, *@args
124
+ end
125
+ end
126
+
127
+ def enqueue_delivery(delivery_method, options = {})
128
+ if processed?
129
+ ::Kernel.raise "You've accessed the message before asking to " \
130
+ "deliver it later, so you may have made local changes that would " \
131
+ "be silently lost if we enqueued a job to deliver it. Why? Only " \
132
+ "the mailer method *arguments* are passed with the delivery job! " \
133
+ "Do not access the message in any way if you mean to deliver it " \
134
+ "later. Workarounds: 1. don't touch the message before calling " \
135
+ "#deliver_later, 2. only touch the message *within your mailer " \
136
+ "method*, or 3. use a custom Active Job instead of #deliver_later."
137
+ else
138
+ job = @mailer_class.delivery_job
139
+ args = arguments_for(job, delivery_method)
140
+ job.set(options).perform_later(*args)
141
+ end
142
+ end
109
143
 
110
- def enqueue_delivery(delivery_method, options={})
111
- args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args
112
- ActionMailer::DeliveryJob.set(options).perform_later(*args)
144
+ def arguments_for(delivery_job, delivery_method)
145
+ if delivery_job <= MailDeliveryJob
146
+ [@mailer_class.name, @action.to_s, delivery_method.to_s, args: @args]
147
+ else
148
+ [@mailer_class.name, @action.to_s, delivery_method.to_s, *@args]
149
+ end
113
150
  end
114
151
  end
115
152
  end