Search code examples
ruby-on-railsrubyruby-on-rails-4actionmailermustache

Rendering mailer templates with Mustache in Rails


I'm trying to port my Rails email templates over to mustache using the stache gem.

I have a standard mailer action which simply sends an email to a user. I've renamed my template path in order to prevent a naming clash when I create the stash view class for the template associated with this mailer action. This process is outlined in the Rails guides.

# app/mailers/registration_mailer.rb
class RegistrationMailer < ActionMailer::Base
  def bailed(user)
    @user = user
    mail to: user.email, template_path: 'er_registration_mailer'
  end
end

This is the Stache view associated with the action above. Notice that the module name matches the template path above.

# app/views/er_registration_mailer/bailed.rb
module ErRegistrationMailer
  class Bailed < ::Stache::Mustache::View
    def continue_registration_link
      link_to "Continue registration by connecting your Twitter account", 
        connect_registrations_url
    end

    def signature
      @view.render "shared/mailer/sig"
    end
  end
end

Finally I have a mustache template for my mailer.

# app/templates/er_registration_mailer/bailed.html.mustache
<p>Hi there!</p>

<p>I noticed you recently started the signup process for my app but didn't complete it.</p>

<p>{{{continue_registration_link}}}</p>

{{{signature}}}

When I try to send an email, I get an error when it tries to render the signature partial. This partial lives in app/templates/shared/mailer where I have both mustache and erb versions of it called _sig.html.mustache and _sig.html.erb respectively.

Here's the error:

Failure/Error: RegistrationMailer.bailed(user).deliver
  ActionView::Template::Error:
    Missing partial shared/mailer/sig with {:locale=>[:en], :formats=>[:html, :text, :js, :css, :ics, :csv, :png, :jpeg, :gif, :bmp, :tiff, :mpeg, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json, :pdf, :zip], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee, :slim, :arb, :rb, :mustache]}. Searched in:
      * "/Users/davidtuite/dev/shareshaper/app/app/views"
      * "/Users/davidtuite/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/bundler/gems/active_admin-6f04ed5cec24/app/views"
      * "/Users/davidtuite/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/devise-3.2.2/app/views"
      * "/Users/davidtuite/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/foundation-rails-5.0.3.1/app/views"
      * "/Users/davidtuite/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/kaminari-0.15.1/app/views"
  # ./app/views/er_registration_mailer/bailed.rb:17:in `signature'

Rails appears to be searching in app/views for the template (or view class, I'm not sure which).

How can I get the partial to render correctly?

I'm using Rails 4.0.3, Ruby 2.1.1 and stache 1.0.3.

Things I've tried

Using the wrapper class functionality of the stache gem instead of specifying the template_path to mailers and prefixing the name spacing of the view class.

I've tried both:

# app/views/registration_mailer/bailed.rb
module Wrapper
  module RegistrationMailer
    class Bailed < ::Stache::Mustache::View
    end
  end
end

and (note the directory structure):

# app/views/wrapper/registration_mailer/bailed.rb
module Wrapper
  module RegistrationMailer
    class Bailed < ::Stache::Mustache::View
    end
  end
end

But I just get an Uninitialized const: Wrapper error.

I've also tried using mustache to specify the partial in the mailer template:

# app/templates/er_registration_mailer/bailed.html.mustache
<!-- HTML as in above code sample -->

{{{>shared/mailer/sig}}}

That just gives me a different 'not found' error.


Solution

  • Here is an example of using Stache with a mailer:

    # config / initializers / stache.rb
    Stache.configure do |c|
      c.template_base_path = Rails.root.join('app', 'views')
      c.wrapper_module_name = "Wrapper"
    
      c.use :mustache
    end
    
    # app / mailers / registration_mailer.rb
    class RegistrationMailer < ActionMailer::Base
      default template_path: 'mailers/registration_mailer'
    
      def bailed(user)
       @user = user
       mail to: user.email
     end
    end
    
    # app / models / wrapper / mailers / regisration_mailer / bailed.rb
    module Wrapper
      module Mailers
        module RegistrationMailer
          class Bailed < ::Stache::Mustache::View
            def continue_registration_link
              link_to "Continue registration by connecting your Twitter account", 
                connect_registrations_url
            end
    
            def signature
              @view.render "shared/mailer/sig"
            end
          end
        end
      end
    end
    

    I think what you are missing is the need to configure both the wrapper module and the template path.