How can you use a setup method in ActionMailer Previews?

Viewed 693

I would like to avoid duplicating the setup for multiple mailer previews. What is the best way to clean this up?

class MyMailerPreview < ActionMailer::Preview
  def email1
    setup
    mailer.email1
  end

  def email2
    setup
    mailer.email2
  end

  def email3
    setup
    mailer.email3
  end
end
3 Answers

Here are two possible solutions I found:

There is something called preview_interceptors that are used when generating mailer previews, you could add your own like this:

config/environments/development.rb

config.action_mailer.preview_interceptors = :my_setup

test/mailers/previews/my_setup.rb

class MySetup                                                                                         
  def self.previewing_email(message)                                                                   
    message.subject = "New subject"                                                                                             
  end                                                                                                  
end  

test/mailers/previews/user_mailer_preview.rb

class UserMailerPreview < ActionMailer::Preview 
  include ActionMailer::Previews
  register_preview_interceptor :my_setup

  def welcome_email                                                                                    
    UserMailer.with(user: User.first).welcome_email                                                    
  end                                                                                                                                                                                                                                                                                                         
end          

The message parameter is an instance of ActionMailer::Parameterized::MessageDelivery, I am not sure everything you can do with it, but you can set some attributes on the email itself.

I couldn't find much documentation on preview interceptors, but here is a link to how they are used in Rails.

  # 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.preview_interceptors :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.

I tried to include Rails before_action in the class, but it wouldn't hook the methods in the previewer, so the second option I found is to build your own before_action like this:

module MySetup                                                                                      
  def before_action(*names)                                                                         
    UserMailer.instance_methods(false).each do |method|                                             
      alias_method "old_#{method}", method                                                          

      define_method method do                                                                       
        names.each do |name|                                                                        
          send(name)                                                                                
        end                                                                                         

        send("old_#{method}")                                                                       
      end                                                                                           
    end                                                                                             
  end                                                                                               
end    

class UserMailerPreview < ActionMailer::Preview 
  extend MySetup

  def welcome_email                                                                                 
    UserMailer.with(user: User.first).welcome_email                                                 
  end 

  before_action :setup

  private

  def setup
    puts "Setting up"
  end
end

Use an initialize method.

Just override the parent initialize method, call super and then run your setup:

class MyMailerPreview < ActionMailer::Preview

  def initialize( params = {} )
    super( params )

    @email_address = "jules@verne.com"
  end

  def email1
    mailer.email1( @email_address )
  end

end

You can view the ActionMailer::Preview.new method here as a reference.

Based on my understanding of what you're asking maybe you could add it into one single method that takes the mailer method as a param

class MyMailerPreview < ActionMailer::Preview
  def email_for(emailx) # (Pass the method(email1, etc) as an argument where you're calling it
    setup
    mailer.send(emailx.to_sym) # Call the method param as a method on the mailer
  end
end

Would that work for you?

Related