Skip to content

Commit

Permalink
Add the ability to intercept emails before previewing
Browse files Browse the repository at this point in the history
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 #13622.
  • Loading branch information
pixeltrix committed Jan 26, 2014
1 parent 14d59d2 commit 35fd816
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 5 deletions.
15 changes: 15 additions & 0 deletions actionmailer/CHANGELOG.md
@@ -1,3 +1,18 @@
* Add the ability to intercept emails before previewing in a similar fashion
to how emails can be intercepted before delivery, e.g:

class CSSInlineStyler
def self.previewing_email(message)
# inline CSS styles
end
end

ActionMailer::Base.register_preview_interceptor CSSInlineStyler

Fixes #13622.

*Andrew White*

* Add mailer previews feature based on 37 Signals mail_view gem * Add mailer previews feature based on 37 Signals mail_view gem


*Andrew White* *Andrew White*
Expand Down
15 changes: 15 additions & 0 deletions actionmailer/lib/action_mailer/base.rb
Expand Up @@ -330,6 +330,21 @@ module ActionMailer
# An overview of all previews is accessible at <tt>http://localhost:3000/rails/mailers</tt> # An overview of all previews is accessible at <tt>http://localhost:3000/rails/mailers</tt>
# on a running development server instance. # on a running development server instance.
# #
# Previews can also be intercepted in a similar manner as deliveries can be by registering
# a preview interceptor that has a <tt>previewing_email</tt> method:
#
# class CssInlineStyler
# def self.previewing_email(message)
# # inline CSS styles
# end
# end
#
# config.action_mailer.register_preview_interceptor :css_inline_styler
#
# Note that interceptors need to be registered both with <tt>register_interceptor</tt>
# and <tt>register_preview_interceptor</tt> if they should operate on both sending and
# previewing emails.
#
# = Configuration options # = Configuration options
# #
# These options are specified on the class level, like # These options are specified on the class level, like
Expand Down
39 changes: 37 additions & 2 deletions actionmailer/lib/action_mailer/preview.rb
Expand Up @@ -10,6 +10,31 @@ module Previews #:nodoc:
# config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews" # config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
# #
class_attribute :preview_path, instance_writer: false class_attribute :preview_path, instance_writer: false

# :nodoc:
mattr_accessor :preview_interceptors, instance_writer: false
self.preview_interceptors = []

# Register one or more Interceptors which will be called before mail is previewed.
def register_preview_interceptors(*interceptors)
interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) }
end

# Register am Interceptor which will be called before mail is previewed.
# Either a class or a string can be passed in as the Interceptor. If a
# string is passed in it will be <tt>constantize</tt>d.
def register_preview_interceptor(interceptor)
preview_interceptor = case interceptor
when String, Symbol
interceptor.to_s.camelize.constantize
else
interceptor
end

unless preview_interceptors.include?(preview_interceptor)
preview_interceptors << preview_interceptor
end
end
end end
end end


Expand All @@ -23,10 +48,14 @@ def all
descendants descendants
end end


# Returns the mail object for the given email name # Returns the mail object for the given email name. The registered preview
# interceptors will be informed so that they can transform the message
# as they would if the mail was actually being delivered.
def call(email) def call(email)
preview = self.new preview = self.new
preview.public_send(email) message = preview.public_send(email)
inform_preview_interceptors(message)
message
end end


# Returns all of the available email previews # Returns all of the available email previews
Expand Down Expand Up @@ -68,6 +97,12 @@ def preview_path #:nodoc:
def preview_path? #:nodoc: def preview_path? #:nodoc:
Base.preview_path? Base.preview_path?
end end

def inform_preview_interceptors(message) #:nodoc:
Base.preview_interceptors.each do |interceptor|
interceptor.previewing_email(message)
end
end
end end
end end
end end
45 changes: 42 additions & 3 deletions actionmailer/test/base_test.rb
Expand Up @@ -539,12 +539,18 @@ def self.delivered_email(mail)
end end


class MyInterceptor class MyInterceptor
def self.delivering_email(mail) def self.delivering_email(mail); end
end def self.previewing_email(mail); end
end end


class MySecondInterceptor class MySecondInterceptor
def self.delivering_email(mail) def self.delivering_email(mail); end
def self.previewing_email(mail); end
end

class BaseMailerPreview < ActionMailer::Preview
def welcome
BaseMailer.welcome
end end
end end


Expand All @@ -570,6 +576,39 @@ def self.delivering_email(mail)
mail.deliver mail.deliver
end end


test "you can register a preview interceptor to the mail object that gets passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptor(MyInterceptor)
mail = BaseMailer.welcome
BaseMailerPreview.stubs(:welcome).returns(mail)
MyInterceptor.expects(:previewing_email).with(mail)
BaseMailerPreview.call(:welcome)
end

test "you can register a preview interceptor using its stringified name to the mail object that gets passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptor("BaseTest::MyInterceptor")
mail = BaseMailer.welcome
BaseMailerPreview.stubs(:welcome).returns(mail)
MyInterceptor.expects(:previewing_email).with(mail)
BaseMailerPreview.call(:welcome)
end

test "you can register an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptor(:"base_test/my_interceptor")
mail = BaseMailer.welcome
BaseMailerPreview.stubs(:welcome).returns(mail)
MyInterceptor.expects(:previewing_email).with(mail)
BaseMailerPreview.call(:welcome)
end

test "you can register multiple preview interceptors to the mail object that both get passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
mail = BaseMailer.welcome
BaseMailerPreview.stubs(:welcome).returns(mail)
MyInterceptor.expects(:previewing_email).with(mail)
MySecondInterceptor.expects(:previewing_email).with(mail)
BaseMailerPreview.call(:welcome)
end

test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
mail1 = ProcMailer.welcome['X-Proc-Method'] mail1 = ProcMailer.welcome['X-Proc-Method']
yesterday = 1.day.ago yesterday = 1.day.ago
Expand Down

0 comments on commit 35fd816

Please sign in to comment.