Skip to content

Commit 35fd816

Browse files
committed
Add the ability to intercept emails before previewing
To support the ability for tools like CSS style inliners to operate on emails being previewed this commit adds a hook in a similar fashion to the existing delivery interceptor hook, e.g: class CSSInlineStyler def self.previewing_email(message) # inline CSS styles end end ActionMailer::Base.register_preview_interceptor CSSInlineStyler Fixes rails#13622.
1 parent 14d59d2 commit 35fd816

File tree

4 files changed

+109
-5
lines changed

4 files changed

+109
-5
lines changed

actionmailer/CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
* Add the ability to intercept emails before previewing in a similar fashion
2+
to how emails can be intercepted before delivery, e.g:
3+
4+
class CSSInlineStyler
5+
def self.previewing_email(message)
6+
# inline CSS styles
7+
end
8+
end
9+
10+
ActionMailer::Base.register_preview_interceptor CSSInlineStyler
11+
12+
Fixes #13622.
13+
14+
*Andrew White*
15+
116
* Add mailer previews feature based on 37 Signals mail_view gem
217

318
*Andrew White*

actionmailer/lib/action_mailer/base.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,21 @@ module ActionMailer
330330
# An overview of all previews is accessible at <tt>http://localhost:3000/rails/mailers</tt>
331331
# on a running development server instance.
332332
#
333+
# Previews can also be intercepted in a similar manner as deliveries can be by registering
334+
# a preview interceptor that has a <tt>previewing_email</tt> method:
335+
#
336+
# class CssInlineStyler
337+
# def self.previewing_email(message)
338+
# # inline CSS styles
339+
# end
340+
# end
341+
#
342+
# config.action_mailer.register_preview_interceptor :css_inline_styler
343+
#
344+
# Note that interceptors need to be registered both with <tt>register_interceptor</tt>
345+
# and <tt>register_preview_interceptor</tt> if they should operate on both sending and
346+
# previewing emails.
347+
#
333348
# = Configuration options
334349
#
335350
# These options are specified on the class level, like

actionmailer/lib/action_mailer/preview.rb

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,31 @@ module Previews #:nodoc:
1010
# config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
1111
#
1212
class_attribute :preview_path, instance_writer: false
13+
14+
# :nodoc:
15+
mattr_accessor :preview_interceptors, instance_writer: false
16+
self.preview_interceptors = []
17+
18+
# Register one or more Interceptors which will be called before mail is previewed.
19+
def register_preview_interceptors(*interceptors)
20+
interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) }
21+
end
22+
23+
# Register am Interceptor which will be called before mail is previewed.
24+
# Either a class or a string can be passed in as the Interceptor. If a
25+
# string is passed in it will be <tt>constantize</tt>d.
26+
def register_preview_interceptor(interceptor)
27+
preview_interceptor = case interceptor
28+
when String, Symbol
29+
interceptor.to_s.camelize.constantize
30+
else
31+
interceptor
32+
end
33+
34+
unless preview_interceptors.include?(preview_interceptor)
35+
preview_interceptors << preview_interceptor
36+
end
37+
end
1338
end
1439
end
1540

@@ -23,10 +48,14 @@ def all
2348
descendants
2449
end
2550

26-
# Returns the mail object for the given email name
51+
# Returns the mail object for the given email name. The registered preview
52+
# interceptors will be informed so that they can transform the message
53+
# as they would if the mail was actually being delivered.
2754
def call(email)
2855
preview = self.new
29-
preview.public_send(email)
56+
message = preview.public_send(email)
57+
inform_preview_interceptors(message)
58+
message
3059
end
3160

3261
# Returns all of the available email previews
@@ -68,6 +97,12 @@ def preview_path #:nodoc:
6897
def preview_path? #:nodoc:
6998
Base.preview_path?
7099
end
100+
101+
def inform_preview_interceptors(message) #:nodoc:
102+
Base.preview_interceptors.each do |interceptor|
103+
interceptor.previewing_email(message)
104+
end
105+
end
71106
end
72107
end
73108
end

actionmailer/test/base_test.rb

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -539,12 +539,18 @@ def self.delivered_email(mail)
539539
end
540540

541541
class MyInterceptor
542-
def self.delivering_email(mail)
543-
end
542+
def self.delivering_email(mail); end
543+
def self.previewing_email(mail); end
544544
end
545545

546546
class MySecondInterceptor
547-
def self.delivering_email(mail)
547+
def self.delivering_email(mail); end
548+
def self.previewing_email(mail); end
549+
end
550+
551+
class BaseMailerPreview < ActionMailer::Preview
552+
def welcome
553+
BaseMailer.welcome
548554
end
549555
end
550556

@@ -570,6 +576,39 @@ def self.delivering_email(mail)
570576
mail.deliver
571577
end
572578

579+
test "you can register a preview interceptor to the mail object that gets passed the mail object before previewing" do
580+
ActionMailer::Base.register_preview_interceptor(MyInterceptor)
581+
mail = BaseMailer.welcome
582+
BaseMailerPreview.stubs(:welcome).returns(mail)
583+
MyInterceptor.expects(:previewing_email).with(mail)
584+
BaseMailerPreview.call(:welcome)
585+
end
586+
587+
test "you can register a preview interceptor using its stringified name to the mail object that gets passed the mail object before previewing" do
588+
ActionMailer::Base.register_preview_interceptor("BaseTest::MyInterceptor")
589+
mail = BaseMailer.welcome
590+
BaseMailerPreview.stubs(:welcome).returns(mail)
591+
MyInterceptor.expects(:previewing_email).with(mail)
592+
BaseMailerPreview.call(:welcome)
593+
end
594+
595+
test "you can register an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before previewing" do
596+
ActionMailer::Base.register_preview_interceptor(:"base_test/my_interceptor")
597+
mail = BaseMailer.welcome
598+
BaseMailerPreview.stubs(:welcome).returns(mail)
599+
MyInterceptor.expects(:previewing_email).with(mail)
600+
BaseMailerPreview.call(:welcome)
601+
end
602+
603+
test "you can register multiple preview interceptors to the mail object that both get passed the mail object before previewing" do
604+
ActionMailer::Base.register_preview_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
605+
mail = BaseMailer.welcome
606+
BaseMailerPreview.stubs(:welcome).returns(mail)
607+
MyInterceptor.expects(:previewing_email).with(mail)
608+
MySecondInterceptor.expects(:previewing_email).with(mail)
609+
BaseMailerPreview.call(:welcome)
610+
end
611+
573612
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
574613
mail1 = ProcMailer.welcome['X-Proc-Method']
575614
yesterday = 1.day.ago

0 commit comments

Comments
 (0)