Preview emails for any user with Rails 4.1

Last night I was preparing a weekly email to remind my dear TimeSampler users about the work they have done in their previous week.

I started by adding a couple of previews one for the user that has actual stuff done (see an example below):

Weekly email for user with registered bursts

Setup a mailer (you can skip this)

The first thing you need to preview an email is to have a Mailer (yuk!), here’s a simple one:

# app/mailers/user_notifier

class UserNotifier < ActionMailer::Base
  default from: 'weekly-report@timesampler.com', reply_to: 'elia+timesampler-weekly-report@schito.me'
  helper DayHelper

  def weekly_report user
    @user         = user
    @project_days = project_days
    @days         = project_days.group_by(&:day).to_a.sort
    @end_time     = end_time
    @start_time   = start_time

    mail to: user.email, subject: "TimeSampler: Weekly activity report · week #{start_time.cweek}"
  end
end

And its view:

/ app/views/user_notifier
= render 'style'

%h1 TimeSampler: Weekly activity report · week #{@start_time.cweek}

%p
  Hi #{@user.some_name}!

- if @project_days.any?
  %p
    Here's what you've done during the past week (#{link_to "from #{@start_time} to #{@end_time}", journal_path}):
  .days
    - @days.sort.reverse.each do |(day, project_days)|
      = render 'day', day: day, project_days: project_days
- else
  = render 'blank_slate'

= render 'footer'

Setup a previewer

Then we need to add our previewer. Problem is that the default location for the previewers is test/mailers/previews
and as you may have guessed already we have no test dir in this app, instead we’ll put our
ActionMailer::Preview classes inside app/mailer_previews.

To do that let’s add the following line to config/environments/development:

Rails.application.configure do
  # …
  config.action_mailer.preview_path = "#{Rails.root}/app/mailer_previews"
end

Your previewer could be something like this:

class UserNotifierPreview < ActionMailer::Preview
  def weekly_report_empty
    UserNotifier.weekly_report(andrea)
  end

  def weekly_report_full
    UserNotifier.weekly_report(elia)
  end

  # …s
end

And have beautyful previews for your emails. For example, this is how the blankslate TimeSampler mail looks like:

blank slate mail preview

POWER PREVIEWING !!1!

Now what if you want to preview emails for arbitrary users without having to manually update the code?

No problem, luckly it’s Ruby! After a bit of inspection inside actionmailer and railties I found the right hook…

WARNING: rails hackery ahead

Rails asks the previewer if an email_exists? before trying to display it, so we’ll hook into that method and
auto-define a previewer instance method for each user in out system (please don’t try this with more than a hundred users).

Here teh codez:

class UserNotifierPreview < ActionMailer::Preview
  def self.email_exists?(*)
    User.all.each do |user|
      next if user.email.blank?
      email = user.email.gsub(/\W/, '_')
      method_name = "weekly_report_#{email}"
      user_id = user.id

      define_method method_name do
        UserNotifier.weekly_report(User.find(user_id))
      end
    end

    super
  end

  # …
end

Of course it’s advisable to be careful and probably reduce the set of users you’re gonna prepare a preview method for.
In my case the development database is quite manageable and hence User.all is fine.

Final Pro-tip

You find cumbersome to dig the sources of the gems in your bundle even
if you setup the $EDITOR var in your bash and are a regular client of bundle open <my-gem>?

Then you can give a try to the Bundler bundle for TextMate2 which will give you an
incredible speed boost while perusing the gems in your current Gemfile.lock.

To get some more info on my TextMate2 setup you can read:

Read next: Pimp my TextMate2 — Ruby edition

opening gems from the current bundle

Notes

  1. olistik reblogged this from mikamayhem
  2. mikamayhem posted this