/rails_email_preview

Preview and edit app mailer templates in Rails.

Primary LanguageRubyMIT LicenseMIT

Rails Email Preview Build Status Test Coverage Code Climate Gitter

Preview email in the browser with this Rails engine. Compatible with Rails 6.1+.

An email review:

screenshot

The list of all email previews:

screenshot

REP comes with two themes: a simple standalone theme, and a theme that uses Bootstrap 3.

Installation

Add Gem Version to Gemfile:

gem 'rails_email_preview', '~> 2.2.3'

This gem requires a Sass engine, please ensure you have one of these gems in your Gemfile:

Add an initializer and the routes:

$ rails g rails_email_preview:install

Generate preview classes and method stubs in app/mailer_previews/

$ rails g rails_email_preview:update_previews

Usage

The last generator above will add a stub for each of your emails, then you populate the stubs with mock data:

# app/mailer_previews/user_mailer_preview.rb:
class UserMailerPreview
  # preview methods should return Mail objects, e.g.:
  def invitation
    UserMailer.invitation mock_user('Alice'), mock_user('Bob')
  end

  def welcome
    UserMailer.welcome mock_user
  end

  private
  # You can put all your mock helpers in a module
  # or you can use your factories / fabricators, just make sure you are not creating anything
  def mock_user(name = 'Bill Gates')
    fake_id User.new(name: name, email: "user#{rand 100}@test.com")
  end

  def fake_id(obj)
    # overrides the method on just this object
    obj.define_singleton_method(:id) { 123 + rand(100) }
    obj
  end
end

Parameters as instance variables

All parameters in the search query will be available to the preview class as instance variables. For example, if URL to mailer preview looks like:

/emails/user_mailer_preview-welcome?user_id=1

The method welcome in UserMailerPreview have a @user_id instance variable defined:

class UserMailerPreview
  def welcome
    user = @user_id ? User.find(@user_id) : mock_user
    UserMailer.welcome(user)
  end
end

Now you can preview or send the welcome email to a specific user.

Routing

You can access REP urls like this:

# engine root:
rails_email_preview.rep_root_url
# list of emails (same as root):
rails_email_preview.rep_emails_url
# email show:
rails_email_preview.rep_email_url('user_mailer-welcome')

Sending Emails

You can send emails via REP. This is especially useful when testing with limited clients (Blackberry, Outlook, etc.). This will use the environment's mailer settings, but the handler will perform_deliveries. Uncomment this line in the initializer to disable sending test emails:

config.enable_send_email = false

Editing Emails

Emails can be stored in the database and edited in the browser. REP works with Comfortable Mexican Sofa CMS to achieve this -- see the CMS Guide to learn more.

screenshot

CSS inlining

For CSS inlining, REP supports Roadie and Premailer. Both of these automatically translate CSS rules into inline styles and turn relative URLs into absolute ones.

Roadie additionally extracts styles that cannot be inlined into a separate <style> tag that is supported by some email clients. For this reason I recommend Roadie over Premailer.

Unlike Premailer, Roadie does not automatically generate a plain text version for HTML emails, but you can use another gem for this, such as plain-david.

Roadie

To integrate Roadie with your Rails app, use roadie-rails. To integrate roadie-rails with REP, uncomment the relevant option in the initializer. initializer is generated during rails g rails_email_preview:install

Premailer

To integrate Premailer with your Rails app you can use either actionmailer_inline_css or premailer-rails. To integrate either with REP, uncomment the relevant options in the initializer. initializer is generated during rails g rails_email_preview:install

I18n

REP expects emails to use current I18n.locale:

# current locale
AccountMailer.some_notification.deliver
# different locale
I18n.with_locale('es') do
  InviteMailer.send_invites.deliver
end

If you are using Resque::Mailer or Devise::Async, you can automatically remember I18n.locale when the mail job is scheduled with this initializer.

When linking to REP pages you can pass email_locale to set the locale for rendering:

# will render email in Spanish:
rails_email_preview.root_url(email_locale: 'es')

REP displays too many locales? Make sure to set config.i18n.available_locales, since it defaults to all locales in Rails.

User interface is available in English, German (Danke, @baschtl), and Russian. You can set the language in config.to_prepare section of the initializer, default is English.

# config/initializers/rails_email_preview.rb
RailsEmailPreview.locale = :de

Views

By default REP views will render inside its own layout.

To render all REP views inside your app layout, first set the layout to use in the initializer:

Rails.application.config.to_prepare do
  # Use admin layout with REP (this will also make app routes accessible within REP):
  RailsEmailPreview.layout = 'admin'
end

Then, import REP styles into your application.css.scss:

@import "rails_email_preview/application";

Alternatively, if you are using Bootstrap 3, @import "rails_email_preview/bootstrap3", and add the following to the initializer:

config.style.merge!(
    btn_active_class_modifier: 'active',
    btn_danger_class:          'btn btn-danger',
    btn_default_class:         'btn btn-default',
    btn_group_class:           'btn-group btn-group-sm',
    btn_primary_class:         'btn btn-primary',
    form_control_class:        'form-control',
    list_group_class:          'list-group',
    list_group_item_class:     'list-group-item',
    row_class:                 'row',
)

You can also override any individual view by placing a file with the same path in your project's app/views, e.g. app/views/rails_email_preview/emails/index.html.slim.

Hooks

You can add content around or replacing REP UI elements by registering view hooks in the initializer:

# Pass position (before, after, or replace) and render arguments:
RailsEmailPreview.view_hooks.add_render :list, :before, partial: 'shared/hello'

# Pass hook id and position (before, after, or replace):
RailsEmailPreview.view_hooks.add :headers_content, :after do |mail:, preview:|
  raw "<dt>ID</dt><dd>#{h mail.header['X-APP-EMAIL-ID']}</dd>"
end

All of the available hooks can be found here.

Authentication & authorization

You can specify the parent controller for REP controller, and it will inherit all the before filters. Note that this must be placed before any other references to REP application controller in the initializer (and before layout= call):

RailsEmailPreview.parent_controller = 'Admin::ApplicationController' # default: '::ApplicationController'

Alternatively, to have custom rules just for REP you can:

Rails.application.config.to_prepare do
  RailsEmailPreview::ApplicationController.module_eval do
    before_action :check_rep_permissions

    private
    def check_rep_permissions
       render status: 403 unless current_user && can_manage_emails?(current_user)
    end
  end
end

Development

Run the tests:

$ rspec

Start a development web server on localhost:9292:

$ rake dev

This project rocks and uses MIT-LICENSE.