eyeliner 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/.gitignore +4 -0
- data/Gemfile +8 -0
- data/Rakefile +12 -0
- data/eyeliner.gemspec +22 -0
- data/lib/eyeliner/version.rb +3 -0
- data/lib/eyeliner.rb +46 -0
- data/spec/eyeliner_spec.rb +94 -0
- data/spec/spec_helper.rb +3 -0
- metadata +92 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/eyeliner.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "eyeliner/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
|
7
|
+
s.name = "eyeliner"
|
8
|
+
s.version = Eyeliner::VERSION
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.authors = ["Mike Williams", "Rob Mitchell"]
|
11
|
+
s.email = ["mike@cogentconsulting.com.au", "rob@cogentconsulting.com.au"]
|
12
|
+
s.homepage = "http://github.com/cogent/eyeliner"
|
13
|
+
s.summary = %q{Eyeliner puts CSS makeup on your HTML emails.}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_runtime_dependency("nokogiri", ">= 1.4.4")
|
20
|
+
s.add_runtime_dependency("css_parser", ">= 1.1.9")
|
21
|
+
|
22
|
+
end
|
data/lib/eyeliner.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'css_parser'
|
3
|
+
|
4
|
+
module Eyeliner
|
5
|
+
|
6
|
+
class Inliner
|
7
|
+
|
8
|
+
attr_accessor :css
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@css = ""
|
12
|
+
end
|
13
|
+
|
14
|
+
StyleRule = Struct.new(:declarations, :specificity) do
|
15
|
+
|
16
|
+
def <=>(other)
|
17
|
+
specificity <=> other.specificity
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
declarations
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def inline(input)
|
27
|
+
fragment = Nokogiri::HTML.fragment(input)
|
28
|
+
css_parser = CssParser::Parser.new
|
29
|
+
css_parser.add_block!(css)
|
30
|
+
styles_by_element = Hash.new do |h,k|
|
31
|
+
h[k] = []
|
32
|
+
end
|
33
|
+
css_parser.each_selector do |selector, declarations, specificity|
|
34
|
+
fragment.css(selector).each do |element|
|
35
|
+
styles_by_element[element] << StyleRule.new(declarations, specificity)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
styles_by_element.each do |element, rules|
|
39
|
+
element["style"] = rules.sort.join(" ")
|
40
|
+
end
|
41
|
+
fragment.to_html
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Eyeliner::Inliner do
|
4
|
+
|
5
|
+
let(:eyeliner) { Eyeliner::Inliner.new }
|
6
|
+
|
7
|
+
def should_not_modify(input)
|
8
|
+
eyeliner.inline(input).should == input
|
9
|
+
end
|
10
|
+
|
11
|
+
def should_modify(input, options)
|
12
|
+
eyeliner.inline(input).should == options[:to]
|
13
|
+
end
|
14
|
+
|
15
|
+
context "with no CSS" do
|
16
|
+
|
17
|
+
describe "#inline" do
|
18
|
+
|
19
|
+
context "with empty input" do
|
20
|
+
|
21
|
+
it "returns empty output" do
|
22
|
+
should_not_modify("")
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with an HTML fragment" do
|
28
|
+
|
29
|
+
it "returns the same output" do
|
30
|
+
should_not_modify("<div>abc</div>")
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with some explicit CSS" do
|
40
|
+
|
41
|
+
before do
|
42
|
+
eyeliner.css << %{
|
43
|
+
.box { border: 1px solid green; }
|
44
|
+
.small { font-size: 8px; }
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#inline" do
|
49
|
+
|
50
|
+
it "leaves non-matching elements alone" do
|
51
|
+
should_not_modify("<p>xyz</p>")
|
52
|
+
end
|
53
|
+
|
54
|
+
it "adds style attributes to matching elements" do
|
55
|
+
should_modify %(<p class="box">xyz</p>),
|
56
|
+
:to => %(<p class="box" style="border: 1px solid green;">xyz</p>)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "iterates into nested elements" do
|
60
|
+
should_modify %(<p><span class="small">xyz</span></p>),
|
61
|
+
:to => %(<p><span class="small" style="font-size: 8px;">xyz</span></p>)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "combines all matching rules" do
|
65
|
+
should_modify %(<p class="small box">xyz</p>),
|
66
|
+
:to => %(<p class="small box" style="border: 1px solid green; font-size: 8px;">xyz</p>)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
context "where CSS rules conflict" do
|
74
|
+
|
75
|
+
before do
|
76
|
+
eyeliner.css << %{
|
77
|
+
p { color: red; }
|
78
|
+
p.small { text-decoration: underline; }
|
79
|
+
.small { font-size: 8px; }
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "#inline" do
|
84
|
+
|
85
|
+
it "applies styles in order of specificity" do
|
86
|
+
should_modify %(<p class="small">xyz</p>),
|
87
|
+
:to => %(<p class="small" style="color: red; font-size: 8px; text-decoration: underline;">xyz</p>)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: eyeliner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mike Williams
|
9
|
+
- Rob Mitchell
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
|
14
|
+
date: 2011-07-13 00:00:00 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: nokogiri
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.4.4
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: css_parser
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.1.9
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: *id002
|
38
|
+
description:
|
39
|
+
email:
|
40
|
+
- mike@cogentconsulting.com.au
|
41
|
+
- rob@cogentconsulting.com.au
|
42
|
+
executables: []
|
43
|
+
|
44
|
+
extensions: []
|
45
|
+
|
46
|
+
extra_rdoc_files: []
|
47
|
+
|
48
|
+
files:
|
49
|
+
- .gitignore
|
50
|
+
- Gemfile
|
51
|
+
- Rakefile
|
52
|
+
- eyeliner.gemspec
|
53
|
+
- lib/eyeliner.rb
|
54
|
+
- lib/eyeliner/version.rb
|
55
|
+
- spec/eyeliner_spec.rb
|
56
|
+
- spec/spec_helper.rb
|
57
|
+
homepage: http://github.com/cogent/eyeliner
|
58
|
+
licenses: []
|
59
|
+
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
|
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
|
+
hash: 3190977879137818689
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
hash: 3190977879137818689
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.7.2
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Eyeliner puts CSS makeup on your HTML emails.
|
90
|
+
test_files:
|
91
|
+
- spec/eyeliner_spec.rb
|
92
|
+
- spec/spec_helper.rb
|