ActionMailer: Hacking multiple template paths

I was up against a little wall this afternoon and I had to monkey patch my way out.

It struck me as weird that with ActionView you can have a whole array (really Array) of view/template paths that are iteratively searched for a matching template, however, with ActionMailer (even though it uses ActionView) you can only define or override the single template_root.

Bizarre, right? I bet rails core had some good reason for this, but in this specific project I'm relying heavily on using ActionView::Base.prepend_view_path() to have the app structured with default templates that are easily replaced by dropping templates in to a different view path. So if I'm doing that for an entire site it would make sense to do the same for my 'Notification' templates as well. The rails docs were pretty unhelpful, but a quick dive into the source proved fruitful. If you drop the code below into config/initializers/action_mailer_extensions.rb:

module ActionMailer
  class Base
    class_inheritable_accessor :view_paths
    
    def self.prepend_view_path(path)
      view_paths.unshift(*path)
      ActionView::TemplateFinder.process_view_paths(path)
    end

    def self.append_view_path(path)
      view_paths.push(*path)
      ActionView::TemplateFinder.process_view_paths(path)
    end

    private
    def self.view_paths
      @@view_paths ||= [template_root]
    end
    
    def view_paths
      self.class.view_paths
    end
    
    def initialize_template_class(assigns)
      ActionView::Base.new(view_paths, assigns, self)
    end

  end
end
You can do:
ActionMailer::Base.prepend_view_path('/my/other/path/to/search/first/')
# or
ActionMailer::Base.append_view_path('/my/other/path/to/search/last/')

4 Responses to “ActionMailer: Hacking multiple template paths”

[…] « ActionMailer: Hacking multiple template paths […]

sprewell Says: #

I just had a problem related to this use of templates, where a ruby script that I was using to send periodic emails, outside of rails, stopped working with my host’s upgrade to actionmailer 2.2.2 (with the following error – `create!’: undefined method `[]’ for nil:NilClass (NoMethodError) ). After an hour or so of experimentation, I finally figured out that the only change that was necessary was to add this line to my script

ActionMailer::Base.template_root = “.”

Make sure you add it outside of smtp_settings, as I was blindly doing at first. Actionmailer will then look for templates in the local path and not finding any, will ignore the templates (you may want to set a different directory than . if your local path has a lot of directories/files as it will actually search the whole local path for templates).

Thanks for sharing this! The one problem I’ve run into with this method is that it will remove all helper functionality from ActionMailer (since ActionMailer::Helpers wraps ActionMailer::Base#initialize_template_class). To keep helpers, overwrite initialize_template_class_without_helpers instead.

Thanks for sharing this, but unfortunately it does not work with Rails 2.3.8 – an endless loop is entered, as ActionMailer::Base.template_root calls self.view_paths.
The workaround is to replace @@view_paths||=[template_root] with @@view_paths||=ActionController::Base.view_paths
This fix ends the endless loop, but causes an exception of private view_paths method being called for a subclass. Simply move view_paths (not self.view_paths) outside the private scope. Voila, done.

About

QuirkeyBlog is Aaron Quint's perspective on the ongoing adventure of Code, Life, Work and the Web.

twitter/@aq.

instagram/@quirkey.

github/quirkey.

QuirkeyBlog is proudly powered by WordPress

Categories