Skip to content
Browse files

Boohoo. Noone liked the name Shemail

  • Loading branch information...
1 parent 5b91b95 commit cd25d3c3e1b2c81fa2b1381daf02bfb3bafbe2be @jimneath jimneath committed Dec 30, 2009
Showing with 160 additions and 11 deletions.
  1. +1 −1 init.rb
  2. +5 −0 lib/mail_style.rb
  3. +144 −0 lib/mail_style/inline_styles.rb
  4. +7 −7 readme.textile
  5. +2 −2 spec/inline_styles_spec.rb
  6. +1 −1 spec/spec_helper.rb
View
2 init.rb
@@ -1 +1 @@
-require 'shemail'
+require 'mail_style'
View
5 lib/mail_style.rb
@@ -0,0 +1,5 @@
+require 'mail_style/inline_styles'
+
+module MailStyle
+ class CSSFileNotFound < StandardError; end
+end
View
144 lib/mail_style/inline_styles.rb
@@ -0,0 +1,144 @@
+require 'uri'
+require 'nokogiri'
+require 'css_parser'
+
+module MailStyle
+ module InlineStyles
+ DOCTYPE = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
+
+ module InstanceMethods
+ def deliver_with_inline_styles!(mail = @mail)
+ write_inline_styles(mail) if @css.present?
+ deliver_without_inline_styles!(mail = @mail)
+ end
+
+ protected
+
+ def write_inline_styles(mail)
+ # Parse only text/html parts
+ mail.parts.select{|p| p.content_type == 'text/html'}.each do |part|
+ part.body = parse_html_part(part)
+ end
+ end
+
+ def parse_html_part(part)
+ # Parse original html
+ html_document = create_html_document(part.body)
+ html_document = absolutize_image_sources(html_document)
+
+ # Write inline styles
+ css_parser.each_selector do |selector, declaration, specificity|
+ html_document.css(selector).each do |element|
+ # Styles
+ current_style = element['style'].to_s.split(';').sort
+ new_style = declaration.to_s.split(';').sort.map do |style|
+ style = update_image_urls(style)
+ end
+
+ # Concat styles
+ style = (current_style + new_style).compact.uniq.map(&:strip)
+
+ # Set new styles
+ element['style'] = style.join(';')
+ end
+ end
+
+ # Strip all references to classes.
+ html_document.css('*').remove_class
+ html_document.to_html
+ end
+
+ def absolutize_image_sources(document)
+ document.css('img').each do |img|
+ src = img['src']
+ img['src'] = src.gsub(src, absolutize_url(src))
+ end
+
+ document
+ end
+
+ # Create Nokogiri html document from part contents and add/amend certain elements.
+ # Reference: http://www.creativeglo.co.uk/email-design/html-email-design-and-coding-tips-part-2/
+ def create_html_document(body)
+ # Add doctype to html along with body
+ document = Nokogiri::HTML.parse(DOCTYPE + body)
+
+ # Set some meta stuff
+ html = document.at_css('html')
+ html['xmlns'] = 'http://www.w3.org/1999/xhtml'
+
+ # Create <head> element if missing
+ head = document.at_css('head')
+
+ unless head.present?
+ head = Nokogiri::XML::Node.new('head', document)
+ document.at_css('body').add_previous_sibling(head)
+ end
+
+ # Add utf-8 content type meta tag
+ meta = Nokogiri::XML::Node.new('meta', document)
+ meta['http-equiv'] = 'Content-Type'
+ meta['content'] = 'text/html; charset=utf-8'
+ head.add_child(meta)
+
+ # Return document
+ document
+ end
+
+ # Update image urls
+ def update_image_urls(style)
+ if default_url_options[:host].present?
+ # Replace urls in stylesheets
+ style.gsub!($1, absolutize_url($1, 'stylesheets')) if style[/url\(['"]?(.*)['"]?\)/i]
+ end
+
+ style
+ end
+
+ # Absolutize URL (Absolutize? Seriously?)
+ def absolutize_url(url, base_path = '')
+ original_url = url
+
+ unless original_url[URI::regexp(%w[http https])]
+ # Calculate new path
+ host = default_url_options[:host]
+ url = URI.join("http://#{host}/", File.join(base_path, original_url)).to_s
+ end
+
+ url
+ end
+
+ # Css Parser
+ def css_parser
+ parser = CssParser::Parser.new
+ parser.add_block!(css_rules)
+ parser
+ end
+
+ # Css Rules
+ def css_rules
+ File.open(css_file).read
+ end
+
+ # Find the css file
+ def css_file
+ if @css.present?
+ css = @css.to_s
+ css = css[/\.css$/] ? css : "#{css}.css"
+ path = File.join(RAILS_ROOT, 'public', 'stylesheets', css)
+ File.exist?(path) ? path : raise(CSSFileNotFound)
+ end
+ end
+ end
+
+ def self.included(receiver)
+ receiver.send :include, InstanceMethods
+ receiver.class_eval do
+ adv_attr_accessor :css
+ alias_method_chain :deliver!, :inline_styles
+ end
+ end
+ end
+end
+
+ActionMailer::Base.send :include, MailStyle::InlineStyles
View
14 readme.textile
@@ -1,8 +1,8 @@
-h1. Shemail
+h1. MailStyle
-Shemail (Simple Html Email, or something) tries to make sending HTML emails a little less painful.
+MailStyle tries to make sending HTML emails a little less painful.
-For a more in depth introduction check out the "Blog Post":http://blog.purifyapp.com/2009/12/30/shemail-a-html-email-plugin-for-ruby-on-rails/
+For a more in depth introduction check out the "Blog Post":http://blog.purifyapp.com/2009/12/30/mailstyle-a-html-email-plugin-for-ruby-on-rails/
It was developed for use on "Purify: Awesome Bug & Issue Tracking":http://purifyapp.com
@@ -12,9 +12,9 @@ First install the dependencies:
<pre><code>sudo gem install nokogiri css_parser</code></pre>
-Then install Shemail to your rails app:
+Then install MailStyle to your rails app:
-<pre><code>script/plugin install http://github.com/purify/shemail</code></pre>
+<pre><code>script/plugin install http://github.com/purify/mailstyle</code></pre>
h2. Usage
@@ -35,14 +35,14 @@ This will look for a css file called _email.css_ in your _public/stylesheets_ fo
h2. Image URL Correcting
-If you have _default_url_options[:host]_ set in your mailer, then Shemail will do it's best to make the urls of images absolute.
+If you have _default_url_options[:host]_ set in your mailer, then MailStyle will do it's best to make the urls of images absolute.
h4. In your mailer
<pre><code>ActionMailer::Base.default_url_options[:host] = "example.com"</code></pre>
h2. Troubleshooting / FAQ
-* Shemail does not touch single part emails. It will only render inline styles for html parts of emails (_eg. template_name.text.html.erb_)
+* MailStyle does not touch single part emails. It will only render inline styles for html parts of emails (_eg. template_name.text.html.erb_)
h2. Bugs / Todo
View
4 spec/inline_styles_spec.rb
@@ -130,10 +130,10 @@ def setup_email(css_file = nil)
html_part(@email).should eql('<p class=3D"text">Hello World</p>=')
end
- it "should raise Shemail::CSSFileNotFound if css file does not exist" do
+ it "should raise MailStyle::CSSFileNotFound if css file does not exist" do
lambda {
TestMailer.deliver_test_multipart(:fake)
- }.should raise_error(Shemail::CSSFileNotFound)
+ }.should raise_error(MailStyle::CSSFileNotFound)
end
end
View
2 spec/spec_helper.rb
@@ -3,7 +3,7 @@
require 'rubygems'
require 'spec'
require 'actionmailer'
-require 'shemail'
+require 'mail_style'
# Extract HTML Part
def html_part(email)

0 comments on commit cd25d3c

Please sign in to comment.
Something went wrong with that request. Please try again.