awesomemailer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ 0.0.1
2
+ * Hey look, is that ActionMailer::Base? No? Who is that? HOLY CRAP!
3
+ It's AwesomeMailer::Base!
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'css_parser', '>= 1.2.5'#, :git => 'https://github.com/alexdunae/css_parser.git'
4
+ gem 'hpricot', '>= 0.8'
5
+
6
+ group :test do
7
+ gem 'actionmailer', '>= 3.0'
8
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,54 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionmailer (3.0.2)
6
+ actionpack (= 3.0.2)
7
+ mail (~> 2.2.9)
8
+ actionpack (3.0.2)
9
+ activemodel (= 3.0.2)
10
+ activesupport (= 3.0.2)
11
+ builder (~> 2.1.2)
12
+ erubis (~> 2.6.6)
13
+ i18n (~> 0.4.1)
14
+ rack (~> 1.2.1)
15
+ rack-mount (~> 0.6.13)
16
+ rack-test (~> 0.5.6)
17
+ tzinfo (~> 0.3.23)
18
+ activemodel (3.0.2)
19
+ activesupport (= 3.0.2)
20
+ builder (~> 2.1.2)
21
+ i18n (~> 0.4.1)
22
+ activesupport (3.0.2)
23
+ addressable (2.2.6)
24
+ builder (2.1.2)
25
+ css_parser (1.2.5)
26
+ addressable
27
+ erubis (2.6.6)
28
+ abstract (>= 1.0.0)
29
+ hpricot (0.8.5)
30
+ i18n (0.4.2)
31
+ mail (2.2.19)
32
+ activesupport (>= 2.3.6)
33
+ i18n (>= 0.4.0)
34
+ mime-types (~> 1.16)
35
+ treetop (~> 1.4.8)
36
+ mime-types (1.17.2)
37
+ polyglot (0.3.3)
38
+ rack (1.2.5)
39
+ rack-mount (0.6.14)
40
+ rack (>= 1.0.0)
41
+ rack-test (0.5.7)
42
+ rack (>= 1.0)
43
+ treetop (1.4.10)
44
+ polyglot
45
+ polyglot (>= 0.3.1)
46
+ tzinfo (0.3.31)
47
+
48
+ PLATFORMS
49
+ ruby
50
+
51
+ DEPENDENCIES
52
+ actionmailer (>= 3.0)
53
+ css_parser (>= 1.2.5)
54
+ hpricot (>= 0.8)
data/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # AwesomeMailer
2
+
3
+ AwesomeMailer is an ActionMailer extension that supports rad stuff like inline CSS embedded through `stylesheet_link_tag` or just, you know, stylesheets.
4
+
5
+ ## Installation
6
+
7
+ Add this to your Gemfile:
8
+
9
+ gem 'awesome_mailer'
10
+
11
+ Or if you're old-fashioned, do this:
12
+
13
+ gem install awesome_mailer
14
+
15
+ Then `require 'awesome_mailer'` and follow the example below.
16
+
17
+ ## Example
18
+
19
+ Suppose you have the following mailer:
20
+
21
+ class UserMailer < ActionMailer::Base
22
+ def signup(user_id)
23
+ @user = User.find(user_id)
24
+ mail(:to => @user.email, :from => "no-reply@example.com")
25
+ end
26
+ end
27
+
28
+ ... and you have a template `app/views/user_mailer/signup.html.erb`. It might look something like this:
29
+
30
+ <html>
31
+ <%= stylesheet_link_tag 'email' %>
32
+ <body>
33
+ <div id="header"><%= link_to raw(image_tag('logo.png')), root_url %></div>
34
+ <div id="content">
35
+ <p>Welcome to AwesomeMailer, <%= @user.name %>! We think you might be neat.</p>
36
+ </div>
37
+ <div id="footer">
38
+ Copyright &copy 2012 <a href="http://www.delightfulwidgets.com">Delightful Widgets</a>
39
+ </div>
40
+ </body>
41
+ </html>
42
+
43
+ ... and your spreadsheet (email.css) might be kinda like this:
44
+
45
+ body {
46
+ background: #f0f0f0;
47
+ font: 12pt Arial normal;
48
+ }
49
+
50
+ a img {
51
+ border-width: 0;
52
+ }
53
+
54
+ #header {
55
+ border-bottom: 1px solid black;
56
+ margin-bottom: 1em;
57
+ }
58
+
59
+ #content {
60
+ font-family: Helvetica;
61
+ padding: 1em 0;
62
+ }
63
+
64
+ #content p {
65
+ line-height: 1.3em;
66
+ }
67
+
68
+ #footer {
69
+ border-top: 1px dotted orange;
70
+ font-size: 10pt;
71
+ }
72
+
73
+ ... you might be unhappy because most mail viewers couldn't care less that you included a stylesheet. But wait!
74
+ There's ActionMailer! Just change your mailer to look like this:
75
+
76
+ class UserMailer < AwesomeMailer::Base
77
+
78
+ ... and voila! Now your templates will render like this:
79
+
80
+ <html>
81
+ <body style="background: #f0f0f0; font: 12pt Arial normal;">
82
+ <div id="header" style="border-bottom: 1px solid black; margin-bottom: 1em;">
83
+ <a href="http://www.delightfulwidgets.com/">
84
+ <img src="http://www.delightfulwidgets.com/assets/logo.png" style="border-width: 0;" />
85
+ </a>
86
+ </div>
87
+ <div id="content" style="font-family: Helvetica; padding: 1em 0;">
88
+ <p style="line-height: 1.3em;">Welcome to AwesomeMailer, <%= @user.name %>! We think you might be neat.</p>
89
+ </div>
90
+ <div id="footer" style="border-top: 1px dotted orange; font-size: 10pt;">
91
+ Copyright &copy 2012 <a href="http://www.delightfulwidgets.com">Delightful Widgets</a>
92
+ </div>
93
+ </body>
94
+ </html>
95
+
96
+ WOW!
97
+
98
+ ## Additional Features
99
+
100
+ ### @import
101
+
102
+ AwesomeMailer (or really, the library it relies on, CSS parser) is smart enough to load up stylesheets through
103
+ @import statements. So go ahead and add `@import url('global.css')` to email.css, and we'll handle the rest.
104
+
105
+ ### Pseudo-classes
106
+
107
+ AwesomeMailer supports more than just inline styles. If you define pseudo-classes like :hover, :after, etc, it'll
108
+ make sure they get included in a &lt;style&gt; tag in the &lt;head&gt; of your e-mail. Don&#x27;t have a &lt;head&gt;? That&#x27;s cool;
109
+ AwesomeMailer will add one.
110
+
111
+ ### @font-face
112
+
113
+ AwesomeMailer will also load up font-face declarations, if'n you have 'em. That means you can add custom fonts to
114
+ your e-mails the same way you do with your websites, and if your user's mail client supports them, UP they'll go!
115
+
116
+ ## Bugs
117
+ File bugs using the issues tab in Github. **Don't** e-mail me. _Please_.
118
+
119
+ ## LEGAL FUNSIES
120
+
121
+ AwesomeMailer is copyright (c) 2011 Delightful Widgets Inc.
122
+
123
+ It was built by Flip Sasser (flip@x451.com) using libraries from Alex Dunae
124
+ ([https://github.com/alexdunae/css_parser](https://github.com/alexdunae/css_parser)) and, as far as I know, Nick Sieger
125
+ ([https://github.com/hpricot/hpricot](https://github.com/hpricot/hpricot)). Those guys are AWESOME. Be their friends.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "awesomemailer"
5
+ gemspec.summary = "An ActionMailer extension that embeds CSS inline in e-mails"
6
+ gemspec.description = %{
7
+ AwesomeMailer embeds your e-mail CSS inline, allowing you to write e-mail templates without worrying too much about stylesheets
8
+ }
9
+ gemspec.email = "flip@x451.com"
10
+ gemspec.homepage = "http://github.com/Plinq/awesome_mailer"
11
+ gemspec.authors = ["Flip Sasser"]
12
+ end
13
+ rescue LoadError
14
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,4 @@
1
+ require 'autotest/fsevent'
2
+ require 'autotest/growl'
3
+
4
+ Autotest.add_discovery { "rspec2" }
@@ -0,0 +1,54 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "awesomemailer"
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Flip Sasser"]
12
+ s.date = "2012-01-05"
13
+ s.description = "\n AwesomeMailer embeds your e-mail CSS inline, allowing you to write e-mail templates without worrying too much about stylesheets\n "
14
+ s.email = "flip@x451.com"
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ "CHANGELOG",
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "README.md",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "autotest/discover.rb",
26
+ "lib/awesome_mailer.rb",
27
+ "lib/awesome_mailer/base.rb",
28
+ "spec/lib/awesome_mailer_spec.rb",
29
+ "spec/spec.opts",
30
+ "spec/spec_helper.rb",
31
+ "spec/support/test_mailer/test_email.html.erb",
32
+ "spec/support/test_mailer/test_email.text.erb"
33
+ ]
34
+ s.homepage = "http://github.com/Plinq/awesome_mailer"
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = "1.8.10"
37
+ s.summary = "An ActionMailer extension that embeds CSS inline in e-mails"
38
+
39
+ if s.respond_to? :specification_version then
40
+ s.specification_version = 3
41
+
42
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
43
+ s.add_runtime_dependency(%q<css_parser>, [">= 1.2.5"])
44
+ s.add_runtime_dependency(%q<hpricot>, [">= 0.8"])
45
+ else
46
+ s.add_dependency(%q<css_parser>, [">= 1.2.5"])
47
+ s.add_dependency(%q<hpricot>, [">= 0.8"])
48
+ end
49
+ else
50
+ s.add_dependency(%q<css_parser>, [">= 1.2.5"])
51
+ s.add_dependency(%q<hpricot>, [">= 0.8"])
52
+ end
53
+ end
54
+
@@ -0,0 +1,81 @@
1
+ require 'rubygems'
2
+ require 'action_mailer'
3
+ require 'hpricot'
4
+ require 'css_parser'
5
+
6
+ module AwesomeMailer
7
+ class Base < ActionMailer::Base
8
+ abstract!
9
+
10
+ def render(*arguments)
11
+ html_string = super
12
+ document = Hpricot(html_string)
13
+ stylesheets = document.search('link[@rel=stylesheet]')
14
+ stylesheets.each do |stylesheet|
15
+ if stylesheet['media'] =~ /^(all|handheld|screen)$/ # Must be intended for digital screens!
16
+ apply_stylesheet!(document, stylesheet)
17
+ end
18
+ end
19
+ stylesheets.remove
20
+ document.to_html
21
+ end
22
+
23
+ private
24
+ def append_styles!(document, selector, declarations)
25
+ unless head = document.at('head')
26
+ head = Hpricot::Elem.new('head')
27
+ document.children.unshift(head)
28
+ end
29
+ unless style = head.at('style[@type=text/css]')
30
+ style = Hpricot::Elem.new('style')
31
+ style['type'] = 'text/css'
32
+ style.inner_html = "\n"
33
+ head.children.unshift(style)
34
+ end
35
+ style.inner_html += "#{selector} { #{declarations} }\n"
36
+ end
37
+
38
+ def apply_rules!(document, css_parser, url)
39
+ css_parser.each_selector do |selector, declarations, specificity|
40
+ if url
41
+ # Rewrite relative URLs to match their parent CSS's URL path
42
+ path_url = Addressable::URI.parse(url)
43
+ path_url.path = File.dirname(path_url.path)
44
+ declarations.scan(/(url\(?["']+(.[^'"]*)["']\))/i).each do |url_command, item|
45
+ next if item =~ /^http(s){0,1}:\/\//
46
+ item_url = path_url.dup
47
+ item_url.path = File.join(item_url.path, item)
48
+ new_url_command = url_command.gsub(item, item_url.to_s)
49
+ declarations[url_command] = new_url_command
50
+ end
51
+ else
52
+ declarations.reject {|item| item.match(/url\s*\(/) }
53
+ end
54
+ if selector =~ /(^@)/
55
+ append_styles!(document, selector, declarations.to_s) if url
56
+ elsif selector !~ /:/
57
+ document.search(selector).each do |element|
58
+ element['style'] = [element['style'], *declarations].compact.join(';')
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def apply_stylesheet!(document, stylesheet)
65
+ css_parser = CssParser::Parser.new
66
+ clean_href = stylesheet['href'].split('?').shift
67
+ url = nil
68
+ case stylesheet['href']
69
+ when /^\/assets/
70
+ dirname = File.dirname(clean_href).split('/').reject(&:blank?)[1..-1]
71
+ css_parser.load_file!(File.join(Rails.root, 'app', 'assets', 'stylesheets', dirname, File.basename(clean_href)))
72
+ when /^\//
73
+ css_parser.load_file!(File.join(Rails.root, 'public', clean_href))
74
+ else
75
+ css_parser.load_uri!(stylesheet['href'])
76
+ url = clean_href
77
+ end
78
+ apply_rules!(document, css_parser, url)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1 @@
1
+ require 'awesome_mailer/base'
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ require 'awesome_mailer'
3
+
4
+ AwesomeMailer::Base.prepend_view_path 'spec/support'
5
+ AwesomeMailer::Base.config.assets_dir = 'spec/support'
6
+
7
+ class TestMailer < AwesomeMailer::Base
8
+ def test_email(subject = "Hello!")
9
+ mail(
10
+ :from => "flip@x451.com",
11
+ :to => "flip@x451.com",
12
+ :subject => subject
13
+ )
14
+ end
15
+
16
+ def test_multipart_email(subject = "Hello")
17
+ mail(
18
+ :from => "flip@x451.com",
19
+ :to => "flip@x451.com",
20
+ :subject => subject
21
+ ) do |format|
22
+ format.html { render :test_email }
23
+ format.text { render :test_email }
24
+ end
25
+ end
26
+ end
27
+
28
+ describe AwesomeMailer::Base do
29
+ it "should render messages like ActionMailer::Base" do
30
+ TestMailer.test_email("Howdy!").should be_instance_of Mail::Message
31
+ end
32
+
33
+ it "should automatically parse the body of HTML e-mails" do
34
+ raise TestMailer.test_email("Howdy!").html_part.body.inspect
35
+ end
36
+
37
+ it "should automatically parse the body of multipart e-mails" do
38
+ raise TestMailer.test_multipart_email("Howdy!").html_part.body.inspect
39
+ end
40
+ end
41
+
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --backtrace
@@ -0,0 +1 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
@@ -0,0 +1,2 @@
1
+ <%= stylesheet_link_tag 'test.css' %>
2
+ <div>welcome!</div>
@@ -0,0 +1 @@
1
+ welcome!
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: awesomemailer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Flip Sasser
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: css_parser
16
+ requirement: &70279699185020 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.2.5
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70279699185020
25
+ - !ruby/object:Gem::Dependency
26
+ name: hpricot
27
+ requirement: &70279699182000 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0.8'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70279699182000
36
+ description: ! "\n AwesomeMailer embeds your e-mail CSS inline, allowing you
37
+ to write e-mail templates without worrying too much about stylesheets\n "
38
+ email: flip@x451.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files:
42
+ - README.md
43
+ files:
44
+ - CHANGELOG
45
+ - Gemfile
46
+ - Gemfile.lock
47
+ - README.md
48
+ - Rakefile
49
+ - VERSION
50
+ - autotest/discover.rb
51
+ - awesomemailer.gemspec
52
+ - lib/awesome_mailer.rb
53
+ - lib/awesome_mailer/base.rb
54
+ - spec/lib/awesome_mailer_spec.rb
55
+ - spec/spec.opts
56
+ - spec/spec_helper.rb
57
+ - spec/support/test_mailer/test_email.html.erb
58
+ - spec/support/test_mailer/test_email.text.erb
59
+ homepage: http://github.com/Plinq/awesome_mailer
60
+ licenses: []
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 1.8.10
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: An ActionMailer extension that embeds CSS inline in e-mails
83
+ test_files: []