Jens
Jens

Reputation: 1397

Rails and multipart emails with user-defined part selection and html to text autoconversion

Our Rails (3.0.14) application has user profiles where users can choose whether they want to receive HTML formatted emails. To keep things DRY, I would like to only setup one set of templates for all mails (HTML) and then use my own String extension dehtml (basically strip_tags with some formatting modifications) on the text/plain part. Also, I would like to keep the mailer code DRY.

So far, our mailer methods look like this:

def signup_confirmation(user)
  @user = user
  mail(:to => @user.email, :subject => ..., ...)
end

1. DRY templates: If possible, I would like to avoid having to create 200 additional mail templates, and autocreate the text/plain part from the HTML template. This is the basic idea (dehtml is my own String extension):

def signup_confirmation(user)
  @user = user
  mail(:to => @user.email, :subject => ..., ...) do |format|
    format.html
    format.text { render(:file => 'signup_notification.html').dehtml }
  end
end

However, this fails with a 'missing template' error. How do I tell Rails to use the HTML template in both cases? I tried appending :formats => :html and :handler => :html but this didn't help.

I don't have a solution here right now. Any ideas?

2: DRY mailer methods: Since our users should be able to decide whether they want to have HTML or not, the above method will look something like

def signup_confirmation(user)
  @user = user
  attachments.inline["email-header.jpg"] = File.read(...) if @user.wants_html
  mail(:to => @user.email, :subject => ..., ...) do |format|
    format.html if @user.wants_html
    format.text { render(:file => 'signup_notification.html').dehtml }
  end
end

Altogether, this triples the LOC in each method. I would like to DRY this up (since it will have to be inserted into at least 200 mailer methods) as far as possible. One idea would be to write my own mail method (let's call it mymail) as something like

def mymail(user, p={})
  attachments.inline["email-header.jpg"] = File.read(...) if user.wants_html
  mail(p) do |format|
    format.html if user.wants_html
    format.text
  end
end

(ignoring the above text template problem for now) and then change each call to mail to mymail, as in

def signup_confirmation(user)
  @user = user
  attachments.inline["email-header.jpg"] = File.read(...) if @user.wants_html
  mymail(user, { :to => @user.email, :subject => ..., ... })
end

This works. But is it good practice? Where do I best put mymail - in a helper?

Any insights and recommendations welcome!

Upvotes: 1

Views: 526

Answers (1)

doesterr
doesterr

Reputation: 3965

I did something very similar to your first solution a long time ago. I don't really remember why it hat to be the way it is, but this is working for me:

def my_mail
  mail(:to => @user.email ...) do |format|
    format.text { convert_html_to_plain(__method__) } # first text
    format.html                                       # then html
  end
end

def convert_html_to_plain(method)
  old_formats = self.formats
  self.formats = ["html"]
  rendered = render "#{method}", :layout => false
  self.formats = old_formats

  # strip tags, reformat, etc. from rendered
  rendered << render(:partial => "plaintext_footer", :locals => {:user => @user}, :formats => [:text] )
end

Upvotes: 1

Related Questions