actionmailer 4.2.11.2 → 6.0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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