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.
@@ -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