awesomemailer 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +3 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +54 -0
- data/README.md +125 -0
- data/Rakefile +14 -0
- data/VERSION +1 -0
- data/autotest/discover.rb +4 -0
- data/awesomemailer.gemspec +54 -0
- data/lib/awesome_mailer/base.rb +81 -0
- data/lib/awesome_mailer.rb +1 -0
- data/spec/lib/awesome_mailer_spec.rb +41 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/test_mailer/test_email.html.erb +2 -0
- data/spec/support/test_mailer/test_email.text.erb +1 -0
- metadata +83 -0
data/CHANGELOG
ADDED
data/Gemfile
ADDED
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 © 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 © 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 <style> tag in the <head> of your e-mail. Don't have a <head>? That'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,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
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
@@ -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: []
|