actionmailer 4.2.11.3 → 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.

@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMailer
4
+ # Provides the option to parameterize mailers in order to share instance variable
5
+ # setup, processing, and common headers.
6
+ #
7
+ # Consider this example that does not use parameterization:
8
+ #
9
+ # class InvitationsMailer < ApplicationMailer
10
+ # def account_invitation(inviter, invitee)
11
+ # @account = inviter.account
12
+ # @inviter = inviter
13
+ # @invitee = invitee
14
+ #
15
+ # subject = "#{@inviter.name} invited you to their Basecamp (#{@account.name})"
16
+ #
17
+ # mail \
18
+ # subject: subject,
19
+ # to: invitee.email_address,
20
+ # from: common_address(inviter),
21
+ # reply_to: inviter.email_address_with_name
22
+ # end
23
+ #
24
+ # def project_invitation(project, inviter, invitee)
25
+ # @account = inviter.account
26
+ # @project = project
27
+ # @inviter = inviter
28
+ # @invitee = invitee
29
+ # @summarizer = ProjectInvitationSummarizer.new(@project.bucket)
30
+ #
31
+ # subject = "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})"
32
+ #
33
+ # mail \
34
+ # subject: subject,
35
+ # to: invitee.email_address,
36
+ # from: common_address(inviter),
37
+ # reply_to: inviter.email_address_with_name
38
+ # end
39
+ #
40
+ # def bulk_project_invitation(projects, inviter, invitee)
41
+ # @account = inviter.account
42
+ # @projects = projects.sort_by(&:name)
43
+ # @inviter = inviter
44
+ # @invitee = invitee
45
+ #
46
+ # subject = "#{@inviter.name.familiar} added you to some new stuff in Basecamp (#{@account.name})"
47
+ #
48
+ # mail \
49
+ # subject: subject,
50
+ # to: invitee.email_address,
51
+ # from: common_address(inviter),
52
+ # reply_to: inviter.email_address_with_name
53
+ # end
54
+ # end
55
+ #
56
+ # InvitationsMailer.account_invitation(person_a, person_b).deliver_later
57
+ #
58
+ # Using parameterized mailers, this can be rewritten as:
59
+ #
60
+ # class InvitationsMailer < ApplicationMailer
61
+ # before_action { @inviter, @invitee = params[:inviter], params[:invitee] }
62
+ # before_action { @account = params[:inviter].account }
63
+ #
64
+ # default to: -> { @invitee.email_address },
65
+ # from: -> { common_address(@inviter) },
66
+ # reply_to: -> { @inviter.email_address_with_name }
67
+ #
68
+ # def account_invitation
69
+ # mail subject: "#{@inviter.name} invited you to their Basecamp (#{@account.name})"
70
+ # end
71
+ #
72
+ # def project_invitation
73
+ # @project = params[:project]
74
+ # @summarizer = ProjectInvitationSummarizer.new(@project.bucket)
75
+ #
76
+ # mail subject: "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})"
77
+ # end
78
+ #
79
+ # def bulk_project_invitation
80
+ # @projects = params[:projects].sort_by(&:name)
81
+ #
82
+ # mail subject: "#{@inviter.name.familiar} added you to some new stuff in Basecamp (#{@account.name})"
83
+ # end
84
+ # end
85
+ #
86
+ # InvitationsMailer.with(inviter: person_a, invitee: person_b).account_invitation.deliver_later
87
+ module Parameterized
88
+ extend ActiveSupport::Concern
89
+
90
+ included do
91
+ attr_accessor :params
92
+ end
93
+
94
+ module ClassMethods
95
+ # Provide the parameters to the mailer in order to use them in the instance methods and callbacks.
96
+ #
97
+ # InvitationsMailer.with(inviter: person_a, invitee: person_b).account_invitation.deliver_later
98
+ #
99
+ # See Parameterized documentation for full example.
100
+ def with(params)
101
+ ActionMailer::Parameterized::Mailer.new(self, params)
102
+ end
103
+ end
104
+
105
+ class Mailer # :nodoc:
106
+ def initialize(mailer, params)
107
+ @mailer, @params = mailer, params
108
+ end
109
+
110
+ private
111
+ def method_missing(method_name, *args)
112
+ if @mailer.action_methods.include?(method_name.to_s)
113
+ ActionMailer::Parameterized::MessageDelivery.new(@mailer, method_name, @params, *args)
114
+ else
115
+ super
116
+ end
117
+ end
118
+
119
+ def respond_to_missing?(method, include_all = false)
120
+ @mailer.respond_to?(method, include_all)
121
+ end
122
+ end
123
+
124
+ class DeliveryJob < ActionMailer::DeliveryJob # :nodoc:
125
+ def perform(mailer, mail_method, delivery_method, params, *args)
126
+ mailer.constantize.with(params).public_send(mail_method, *args).send(delivery_method)
127
+ end
128
+ end
129
+
130
+ class MessageDelivery < ActionMailer::MessageDelivery # :nodoc:
131
+ def initialize(mailer_class, action, params, *args)
132
+ super(mailer_class, action, *args)
133
+ @params = params
134
+ end
135
+
136
+ private
137
+ def processed_mailer
138
+ @processed_mailer ||= @mailer_class.new.tap do |mailer|
139
+ mailer.params = @params
140
+ mailer.process @action, *@args
141
+ end
142
+ end
143
+
144
+ def enqueue_delivery(delivery_method, options = {})
145
+ if processed?
146
+ super
147
+ else
148
+ job = delivery_job_class
149
+ args = arguments_for(job, delivery_method)
150
+ job.set(options).perform_later(*args)
151
+ end
152
+ end
153
+
154
+ def delivery_job_class
155
+ if @mailer_class.delivery_job <= MailDeliveryJob
156
+ @mailer_class.delivery_job
157
+ else
158
+ Parameterized::DeliveryJob
159
+ end
160
+ end
161
+
162
+ def arguments_for(delivery_job, delivery_method)
163
+ if delivery_job <= MailDeliveryJob
164
+ [@mailer_class.name, @action.to_s, delivery_method.to_s, params: @params, args: @args]
165
+ else
166
+ [@mailer_class.name, @action.to_s, delivery_method.to_s, @params, *@args]
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
@@ -1,4 +1,6 @@
1
- require 'active_support/descendants_tracker'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/descendants_tracker"
2
4
 
3
5
  module ActionMailer
4
6
  module Previews #:nodoc:
@@ -15,13 +17,12 @@ module ActionMailer
15
17
  #
16
18
  # config.action_mailer.show_previews = true
17
19
  #
18
- # Defaults to true for development environment
20
+ # Defaults to +true+ for development environment
19
21
  #
20
22
  mattr_accessor :show_previews, instance_writer: false
21
23
 
22
24
  # :nodoc:
23
- mattr_accessor :preview_interceptors, instance_writer: false
24
- self.preview_interceptors = []
25
+ mattr_accessor :preview_interceptors, instance_writer: false, default: [ActionMailer::InlinePreviewInterceptor]
25
26
  end
26
27
 
27
28
  module ClassMethods
@@ -30,29 +31,53 @@ module ActionMailer
30
31
  interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) }
31
32
  end
32
33
 
34
+ # Unregister one or more previously registered Interceptors.
35
+ def unregister_preview_interceptors(*interceptors)
36
+ interceptors.flatten.compact.each { |interceptor| unregister_preview_interceptor(interceptor) }
37
+ end
38
+
33
39
  # Register an Interceptor which will be called before mail is previewed.
34
40
  # Either a class or a string can be passed in as the Interceptor. If a
35
- # string is passed in it will be <tt>constantize</tt>d.
41
+ # string is passed in it will be constantized.
36
42
  def register_preview_interceptor(interceptor)
37
- preview_interceptor = case interceptor
43
+ preview_interceptor = interceptor_class_for(interceptor)
44
+
45
+ unless preview_interceptors.include?(preview_interceptor)
46
+ preview_interceptors << preview_interceptor
47
+ end
48
+ end
49
+
50
+ # Unregister a previously registered Interceptor.
51
+ # Either a class or a string can be passed in as the Interceptor. If a
52
+ # string is passed in it will be constantized.
53
+ def unregister_preview_interceptor(interceptor)
54
+ preview_interceptors.delete(interceptor_class_for(interceptor))
55
+ end
56
+
57
+ private
58
+
59
+ def interceptor_class_for(interceptor)
60
+ case interceptor
38
61
  when String, Symbol
39
62
  interceptor.to_s.camelize.constantize
40
63
  else
41
64
  interceptor
42
65
  end
43
-
44
- unless preview_interceptors.include?(preview_interceptor)
45
- preview_interceptors << preview_interceptor
46
66
  end
47
- end
48
67
  end
49
68
  end
50
69
 
51
70
  class Preview
52
71
  extend ActiveSupport::DescendantsTracker
53
72
 
73
+ attr_reader :params
74
+
75
+ def initialize(params = {})
76
+ @params = params
77
+ end
78
+
54
79
  class << self
55
- # Returns all mailer preview classes
80
+ # Returns all mailer preview classes.
56
81
  def all
57
82
  load_previews if descendants.empty?
58
83
  descendants
@@ -61,54 +86,54 @@ module ActionMailer
61
86
  # Returns the mail object for the given email name. The registered preview
62
87
  # interceptors will be informed so that they can transform the message
63
88
  # as they would if the mail was actually being delivered.
64
- def call(email)
65
- preview = self.new
89
+ def call(email, params = {})
90
+ preview = new(params)
66
91
  message = preview.public_send(email)
67
92
  inform_preview_interceptors(message)
68
93
  message
69
94
  end
70
95
 
71
- # Returns all of the available email previews
96
+ # Returns all of the available email previews.
72
97
  def emails
73
98
  public_instance_methods(false).map(&:to_s).sort
74
99
  end
75
100
 
76
- # Returns true if the email exists
101
+ # Returns +true+ if the email exists.
77
102
  def email_exists?(email)
78
103
  emails.include?(email)
79
104
  end
80
105
 
81
- # Returns true if the preview exists
106
+ # Returns +true+ if the preview exists.
82
107
  def exists?(preview)
83
- all.any?{ |p| p.preview_name == preview }
108
+ all.any? { |p| p.preview_name == preview }
84
109
  end
85
110
 
86
- # Find a mailer preview by its underscored class name
111
+ # Find a mailer preview by its underscored class name.
87
112
  def find(preview)
88
- all.find{ |p| p.preview_name == preview }
113
+ all.find { |p| p.preview_name == preview }
89
114
  end
90
115
 
91
- # Returns the underscored name of the mailer preview without the suffix
116
+ # Returns the underscored name of the mailer preview without the suffix.
92
117
  def preview_name
93
- name.sub(/Preview$/, '').underscore
118
+ name.sub(/Preview$/, "").underscore
94
119
  end
95
120
 
96
- protected
97
- def load_previews #:nodoc:
121
+ private
122
+ def load_previews
98
123
  if preview_path
99
- Dir["#{preview_path}/**/*_preview.rb"].each{ |file| require_dependency file }
124
+ Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require_dependency file }
100
125
  end
101
126
  end
102
127
 
103
- def preview_path #:nodoc:
128
+ def preview_path
104
129
  Base.preview_path
105
130
  end
106
131
 
107
- def show_previews #:nodoc:
132
+ def show_previews
108
133
  Base.show_previews
109
134
  end
110
135
 
111
- def inform_preview_interceptors(message) #:nodoc:
136
+ def inform_preview_interceptors(message)
112
137
  Base.preview_interceptors.each do |interceptor|
113
138
  interceptor.previewing_email(message)
114
139
  end
@@ -1,4 +1,6 @@
1
- require 'active_job/railtie'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_job/railtie"
2
4
  require "action_mailer"
3
5
  require "rails"
4
6
  require "abstract_controller/railties/routes_helpers"
@@ -16,13 +18,19 @@ module ActionMailer
16
18
  paths = app.config.paths
17
19
  options = app.config.action_mailer
18
20
 
21
+ if app.config.force_ssl
22
+ options.default_url_options ||= {}
23
+ options.default_url_options[:protocol] ||= "https"
24
+ end
25
+
19
26
  options.assets_dir ||= paths["public"].first
20
27
  options.javascripts_dir ||= paths["public/javascripts"].first
21
28
  options.stylesheets_dir ||= paths["public/stylesheets"].first
22
29
  options.show_previews = Rails.env.development? if options.show_previews.nil?
30
+ options.cache_store ||= Rails.cache
23
31
 
24
32
  if options.show_previews
25
- options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil
33
+ options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil
26
34
  end
27
35
 
28
36
  # make sure readers methods get compiled
@@ -38,14 +46,24 @@ module ActionMailer
38
46
  register_preview_interceptors(options.delete(:preview_interceptors))
39
47
  register_observers(options.delete(:observers))
40
48
 
41
- options.each { |k,v| send("#{k}=", v) }
42
-
43
- if options.show_previews
44
- app.routes.append do
45
- get '/rails/mailers' => "rails/mailers#index"
46
- get '/rails/mailers/*path' => "rails/mailers#preview"
47
- end
49
+ if delivery_job = options.delete(:delivery_job)
50
+ self.delivery_job = delivery_job.constantize
48
51
  end
52
+
53
+ options.each { |k, v| send("#{k}=", v) }
54
+ end
55
+
56
+ ActiveSupport.on_load(:action_dispatch_integration_test) do
57
+ include ActionMailer::TestHelper
58
+ include ActionMailer::TestCase::ClearTestDeliveries
59
+ end
60
+ end
61
+
62
+ initializer "action_mailer.set_autoload_paths" do |app|
63
+ options = app.config.action_mailer
64
+
65
+ if options.show_previews && options.preview_path
66
+ ActiveSupport::Dependencies.autoload_paths << options.preview_path
49
67
  end
50
68
  end
51
69
 
@@ -55,9 +73,20 @@ module ActionMailer
55
73
  end
56
74
  end
57
75
 
58
- config.after_initialize do
59
- if ActionMailer::Base.preview_path
60
- ActiveSupport::Dependencies.autoload_paths << ActionMailer::Base.preview_path
76
+ initializer "action_mailer.eager_load_actions" do
77
+ ActiveSupport.on_load(:after_initialize) do
78
+ ActionMailer::Base.descendants.each(&:action_methods) if config.eager_load
79
+ end
80
+ end
81
+
82
+ config.after_initialize do |app|
83
+ options = app.config.action_mailer
84
+
85
+ if options.show_previews
86
+ app.routes.prepend do
87
+ get "/rails/mailers" => "rails/mailers#index", internal: true
88
+ get "/rails/mailers/*path" => "rails/mailers#preview", internal: true
89
+ end
61
90
  end
62
91
  end
63
92
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMailer #:nodoc:
4
+ # Provides +rescue_from+ for mailers. Wraps mailer action processing,
5
+ # mail job processing, and mail delivery.
6
+ module Rescuable
7
+ extend ActiveSupport::Concern
8
+ include ActiveSupport::Rescuable
9
+
10
+ class_methods do
11
+ def handle_exception(exception) #:nodoc:
12
+ rescue_with_handler(exception) || raise(exception)
13
+ end
14
+ end
15
+
16
+ def handle_exceptions #:nodoc:
17
+ yield
18
+ rescue => exception
19
+ rescue_with_handler(exception) || raise
20
+ end
21
+
22
+ private
23
+ def process(*)
24
+ handle_exceptions do
25
+ super
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,16 +1,35 @@
1
- require 'active_support/test_case'
2
- require 'rails-dom-testing'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/test_case"
4
+ require "rails-dom-testing"
3
5
 
4
6
  module ActionMailer
5
7
  class NonInferrableMailerError < ::StandardError
6
8
  def initialize(name)
7
- super "Unable to determine the mailer to test from #{name}. " +
8
- "You'll need to specify it using tests YourMailer in your " +
9
+ super "Unable to determine the mailer to test from #{name}. " \
10
+ "You'll need to specify it using tests YourMailer in your " \
9
11
  "test case definition"
10
12
  end
11
13
  end
12
14
 
13
15
  class TestCase < ActiveSupport::TestCase
16
+ module ClearTestDeliveries
17
+ extend ActiveSupport::Concern
18
+
19
+ included do
20
+ setup :clear_test_deliveries
21
+ teardown :clear_test_deliveries
22
+ end
23
+
24
+ private
25
+
26
+ def clear_test_deliveries
27
+ if ActionMailer::Base.delivery_method == :test
28
+ ActionMailer::Base.deliveries.clear
29
+ end
30
+ end
31
+ end
32
+
14
33
  module Behavior
15
34
  extend ActiveSupport::Concern
16
35
 
@@ -24,6 +43,7 @@ module ActionMailer
24
43
  setup :initialize_test_deliveries
25
44
  setup :set_expected_mail
26
45
  teardown :restore_test_deliveries
46
+ ActiveSupport.run_load_hooks(:action_mailer_test_case, self)
27
47
  end
28
48
 
29
49
  module ClassMethods
@@ -39,7 +59,7 @@ module ActionMailer
39
59
  end
40
60
 
41
61
  def mailer_class
42
- if mailer = self._mailer_class
62
+ if mailer = _mailer_class
43
63
  mailer
44
64
  else
45
65
  tests determine_default_mailer(name)
@@ -55,7 +75,7 @@ module ActionMailer
55
75
  end
56
76
  end
57
77
 
58
- protected
78
+ private
59
79
 
60
80
  def initialize_test_deliveries
61
81
  set_delivery_method :test
@@ -82,11 +102,9 @@ module ActionMailer
82
102
  def set_expected_mail
83
103
  @expected = Mail.new
84
104
  @expected.content_type ["text", "plain", { "charset" => charset }]
85
- @expected.mime_version = '1.0'
105
+ @expected.mime_version = "1.0"
86
106
  end
87
107
 
88
- private
89
-
90
108
  def charset
91
109
  "UTF-8"
92
110
  end
@@ -96,7 +114,7 @@ module ActionMailer
96
114
  end
97
115
 
98
116
  def read_fixture(action)
99
- IO.readlines(File.join(Rails.root, 'test', 'fixtures', self.class.mailer_class.name.underscore, action))
117
+ IO.readlines(File.join(Rails.root, "test", "fixtures", self.class.mailer_class.name.underscore, action))
100
118
  end
101
119
  end
102
120