inline-style 0.4.6 → 0.4.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/Gemfile.lock +2 -5
- data/{README.txt → README.rdoc} +24 -10
- data/inline-style.gemspec +2 -3
- data/lib/inline-style.rb +62 -44
- data/lib/inline-style/css_parser_wrapper.rb +28 -0
- data/lib/inline-style/{css_parsers/csspool.rb → csspool_wrapper.rb} +4 -9
- data/lib/inline-style/mail/interceptor.rb +17 -19
- data/lib/inline-style/rack/middleware.rb +29 -38
- data/lib/inline-style/selector.rb +41 -0
- data/lib/inline-style/version.rb +2 -4
- data/spec/css_inlining_spec.rb +74 -55
- data/spec/css_parsers_spec.rb +13 -15
- data/spec/fixtures/boletin.html +5 -1
- data/spec/fixtures/inline.html +1 -1
- data/spec/interceptor_spec.rb +16 -6
- data/spec/rack_middleware_spec.rb +4 -4
- data/spec/spec_helper.rb +32 -6
- metadata +21 -11
- data/lib/inline-style/css_parsers.rb +0 -21
- data/lib/inline-style/css_parsers/css_parser.rb +0 -56
- data/spec/factory_spec.rb +0 -38
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
inline-style (0.4.
|
4
|
+
inline-style (0.4.9)
|
5
5
|
css_parser
|
6
6
|
maca-fork-csspool
|
7
7
|
nokogiri
|
@@ -12,7 +12,7 @@ GEM
|
|
12
12
|
activesupport (3.0.4)
|
13
13
|
css_parser (1.1.5)
|
14
14
|
diff-lcs (1.1.2)
|
15
|
-
ffi (1.0.
|
15
|
+
ffi (1.0.7)
|
16
16
|
rake (>= 0.8.7)
|
17
17
|
i18n (0.5.0)
|
18
18
|
maca-fork-csspool (2.0.2)
|
@@ -42,11 +42,8 @@ PLATFORMS
|
|
42
42
|
ruby
|
43
43
|
|
44
44
|
DEPENDENCIES
|
45
|
-
css_parser
|
46
45
|
inline-style!
|
47
|
-
maca-fork-csspool
|
48
46
|
mail
|
49
|
-
nokogiri
|
50
47
|
rack
|
51
48
|
rspec
|
52
49
|
rspec-core
|
data/{README.txt → README.rdoc}
RENAMED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
http://github.com/maca/inline-style
|
4
4
|
|
5
|
-
|
5
|
+
|
6
|
+
== Description
|
6
7
|
|
7
8
|
Will take all css in a page (either from linked stylesheet or from style tag) and will embed it in the style attribute for
|
8
9
|
each refered element taking selector specificity and declarator order.
|
@@ -14,7 +15,13 @@ Useful for html email: some clients (gmail, et all) won't render non inline styl
|
|
14
15
|
inline processing for both mail as well as ActionMailer.
|
15
16
|
* It takes into account selector specificity.
|
16
17
|
|
17
|
-
|
18
|
+
|
19
|
+
== Usage
|
20
|
+
|
21
|
+
|
22
|
+
gem 'maca-fork-csspool' # this is optional if csspool is required inline-style will resort to it for css parsing, otherwise it will use the pure ruby css_parser
|
23
|
+
require 'csspool' # csspool should be faster because it uses librcroco written in C wich can also be an issue, use css_parser if you deploy to heroku
|
24
|
+
|
18
25
|
require 'inline-style'
|
19
26
|
|
20
27
|
html = File.read("#{ dir }/index.html")
|
@@ -83,8 +90,9 @@ Will become:
|
|
83
90
|
<span style='font-family: "Lucida Grande", Lucida, Verdana, sans-serif;margin: 4.0px 3.0px 2.0px 1.0px;padding: 0.0;'>4</span>
|
84
91
|
</li>
|
85
92
|
</ul>
|
93
|
+
|
86
94
|
|
87
|
-
==
|
95
|
+
== Rack Middleware:
|
88
96
|
|
89
97
|
# Process all routes:
|
90
98
|
use InlineStyle::Rack::Middleware
|
@@ -95,7 +103,8 @@ Will become:
|
|
95
103
|
# Restrict processing to some routes:
|
96
104
|
use InlineStyle::Rack::Middleware, :paths => [%r(/mails/.*), "/somepath"]
|
97
105
|
|
98
|
-
|
106
|
+
|
107
|
+
== Mail Interceptor
|
99
108
|
|
100
109
|
If using the mail library the following code will work:
|
101
110
|
|
@@ -106,21 +115,26 @@ If using ActionMailer (which wraps mail):
|
|
106
115
|
|
107
116
|
ActionMailer::Base.register_interceptor \
|
108
117
|
InlineStyle::Mail::Interceptor.new(:stylesheets_path => 'public')
|
118
|
+
|
109
119
|
|
110
|
-
==
|
120
|
+
== Issues:
|
111
121
|
|
112
122
|
* It supports pseudo classes according to W3C specification for style in style attribute: http://www.w3.org/TR/css-style-attr, although browsers
|
113
123
|
doesn't seems to.
|
114
124
|
|
115
|
-
== REQUIREMENTS:
|
116
125
|
|
117
|
-
|
126
|
+
== Requirements:
|
127
|
+
|
128
|
+
nokogiri && (css_parser || maca-fork-csspool)
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
== Install:
|
118
133
|
|
119
|
-
|
134
|
+
sudo gem install inline-style
|
120
135
|
|
121
|
-
sudo gem install inline-style --source http://gemcutter.org
|
122
136
|
|
123
|
-
==
|
137
|
+
== License:
|
124
138
|
|
125
139
|
(The MIT License)
|
126
140
|
|
data/inline-style.gemspec
CHANGED
@@ -4,15 +4,14 @@ require "inline-style/version"
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "inline-style"
|
7
|
-
s.version =
|
7
|
+
s.version = InlineStyle::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Macario Ortega", "Eric Anderson"]
|
10
10
|
s.email = ["macarui@gmail.com"]
|
11
11
|
s.homepage = ""
|
12
12
|
s.summary = %q{Inlines CSS for html email delivery}
|
13
13
|
s.description = %q{Inlines CSS for html email delivery}
|
14
|
-
|
15
|
-
# s.rubyforge_project = "inline-style"
|
14
|
+
s.post_install_message = %{Please read documentation for changes on the default css parser gem, specifically if you use csspool}
|
16
15
|
|
17
16
|
s.files = `git ls-files`.split("\n")
|
18
17
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/lib/inline-style.rb
CHANGED
@@ -1,37 +1,49 @@
|
|
1
1
|
require 'nokogiri'
|
2
2
|
require 'open-uri'
|
3
3
|
|
4
|
-
|
5
|
-
require "#{ File.dirname( __FILE__ ) }/inline-style/rack/middleware"
|
6
|
-
require "#{ File.dirname( __FILE__ ) }/inline-style/mail/interceptor"
|
4
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
require "inline-style/selector"
|
7
|
+
require "inline-style/rack/middleware" # This two may be should be required by user if she needs it
|
8
|
+
require "inline-style/mail/interceptor"
|
9
|
+
|
10
|
+
class InlineStyle
|
11
|
+
CSSParser =
|
12
|
+
if const_defined? :CSSPool
|
13
|
+
require 'inline-style/csspool_wrapper'
|
14
|
+
CSSPoolWrapper
|
15
|
+
else
|
16
|
+
require 'inline-style/css_parser_wrapper'
|
17
|
+
CssParserWrapper
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [String, Nokogiri::HTML::Document] html Html or Nokogiri html to be inlined
|
21
|
+
# @param [Hash] opts Processing options
|
12
22
|
#
|
13
|
-
#
|
23
|
+
# @option opts [String] :stylesheets_path (env['DOCUMENT_ROOT'])
|
24
|
+
# Stylesheets root path or app's public directory where the stylesheets are to be found
|
25
|
+
# @option opts [boolean] :pseudo (false)
|
14
26
|
# If set to true will inline style for pseudo classes according to the W3C specification:
|
15
27
|
# http://www.w3.org/TR/css-style-attr.
|
16
|
-
#
|
17
|
-
# comply with the specification for pseudo class style in the style attribute.
|
28
|
+
# Should probably be left as false because browsers don't seem to comply with the specification for pseudo class style in the style attribute.
|
18
29
|
def self.process html, opts = {}
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
html
|
24
|
-
|
30
|
+
new(html, opts).process
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize html, opts = {}
|
34
|
+
@html = html
|
35
|
+
@stylesheets_path = opts[:stylesheets_path] || ''
|
36
|
+
@pseudo = opts[:pseudo]
|
37
|
+
end
|
38
|
+
|
39
|
+
def process
|
25
40
|
nodes = {}
|
26
41
|
|
27
42
|
css.each_rule_set do |rule_set|
|
28
|
-
rule_set.each_selector do |
|
29
|
-
|
30
|
-
css_selector = "#{ 'body ' unless /^body/ === css_selector }#{ css_selector.gsub /:.*/, '' }"
|
31
|
-
|
32
|
-
html.css(css_selector).each do |node|
|
43
|
+
rule_set.each_selector do |selector|
|
44
|
+
dom.css(selector.search).each do |node|
|
33
45
|
nodes[node] ||= []
|
34
|
-
nodes[node].push
|
46
|
+
nodes[node].push selector
|
35
47
|
|
36
48
|
next unless node['style']
|
37
49
|
|
@@ -39,44 +51,50 @@ module InlineStyle
|
|
39
51
|
path << "##{ node['id'] }" if node['id']
|
40
52
|
path << ".#{ node['class'].scan(/\S+/).join('.') }" if node['class']
|
41
53
|
|
42
|
-
|
43
|
-
rule.each_selector do |
|
44
|
-
nodes[node].push
|
54
|
+
CSSParser.new("#{path}{#{node['style']}}").each_rule_set do |rule|
|
55
|
+
rule.each_selector do |selector_inner|
|
56
|
+
nodes[node].push selector_inner
|
45
57
|
end
|
46
58
|
end
|
47
59
|
end
|
48
60
|
end
|
49
61
|
end
|
50
62
|
|
51
|
-
nodes.each_pair do |node,
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
sets.pop if not pseudo or sets.last.empty?
|
63
|
+
nodes.each_pair do |node, selectors|
|
64
|
+
selectors = selectors.sort_by{ |sel| "#{ sel.specificity }%03d" % selectors.index(sel) }
|
65
|
+
selectors = selectors.reject {|sel| !@pseudo && sel.pseudo? }
|
66
|
+
using_pseudo = selectors.any? &:pseudo?
|
56
67
|
|
57
|
-
node['style'] =
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
68
|
+
node['style'] = selectors.collect do |selector|
|
69
|
+
if using_pseudo && !selector.pseudo?
|
70
|
+
"{#{selector.inline_declarations}}"
|
71
|
+
else
|
72
|
+
selector.inline_declarations
|
62
73
|
end
|
63
|
-
|
64
|
-
index == 0 && sets.size > 1 ? "{#{ set }}" : set.collect(&:strip).join(' ')
|
65
|
-
end.join.strip
|
74
|
+
end.join(' ').strip
|
66
75
|
end
|
67
76
|
|
68
|
-
|
77
|
+
html_already_parsed? ? @dom : @dom.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def dom
|
82
|
+
@dom ||= html_already_parsed? ? @html : Nokogiri.HTML(@html)
|
69
83
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
84
|
+
|
85
|
+
def html_already_parsed?
|
86
|
+
@html.respond_to? :css
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns parsed CSS
|
90
|
+
def css
|
91
|
+
@css ||= CSSParser.new dom.css('style, link').collect { |e|
|
74
92
|
next unless e['media'].nil? || ['screen', 'all'].include?(e['media'])
|
75
93
|
next(e.remove and e.content) if e.name == 'style'
|
76
94
|
next unless e['rel'] == 'stylesheet'
|
77
95
|
e.remove
|
78
96
|
|
79
|
-
uri = %r{^https?://} === e['href'] ? e['href'] : File.join(stylesheets_path, e['href'].sub(
|
97
|
+
uri = %r{^https?://} === e['href'] ? e['href'] : File.join(@stylesheets_path, e['href'].sub(/\?.+$/,''))
|
80
98
|
open(uri).read rescue nil
|
81
99
|
}.join("\n")
|
82
100
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'css_parser'
|
2
|
+
|
3
|
+
class InlineStyle
|
4
|
+
class CssParserWrapper
|
5
|
+
def initialize(css_code)
|
6
|
+
@parser = ::CssParser::Parser.new
|
7
|
+
@parser.add_block! css_code
|
8
|
+
end
|
9
|
+
|
10
|
+
def each_rule_set(&blk)
|
11
|
+
@parser.each_rule_set do |rule_set|
|
12
|
+
yield Ruleset.new(rule_set)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Ruleset
|
17
|
+
def initialize(ruleset)
|
18
|
+
@ruleset = ruleset
|
19
|
+
end
|
20
|
+
|
21
|
+
def each_selector
|
22
|
+
@ruleset.each_selector do |sel, dec, spe|
|
23
|
+
yield InlineStyle::Selector.new(sel, dec, spe)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,8 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
module InlineStyle::CssParsers
|
5
|
-
class Csspool
|
1
|
+
class InlineStyle
|
2
|
+
class CSSPoolWrapper
|
6
3
|
|
7
4
|
def initialize(css_code)
|
8
5
|
@parser = CSSPool.CSS css_code
|
@@ -22,13 +19,11 @@ module InlineStyle::CssParsers
|
|
22
19
|
|
23
20
|
def each_selector(&blk)
|
24
21
|
@ruleset.selectors.each do |selector|
|
25
|
-
yield selector.to_s,
|
22
|
+
yield InlineStyle::Selector.new(selector.to_s,
|
26
23
|
selector.declarations.map{ |d| d.to_s.squeeze(' ') }.join.strip,
|
27
|
-
selector.specificity.inject(0) {|t, s| t+s}
|
24
|
+
selector.specificity.inject(0) {|t, s| t+s})
|
28
25
|
end
|
29
26
|
end
|
30
|
-
|
31
27
|
end
|
32
|
-
|
33
28
|
end
|
34
29
|
end
|
@@ -9,29 +9,27 @@
|
|
9
9
|
#
|
10
10
|
# ActionMailer::Base.register_interceptor \
|
11
11
|
# InlineStyle::Mail::Interceptor.new(:stylesheets_path => 'public')
|
12
|
-
module InlineStyle
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
INLINE_MIME_TYPES = %w(text/html application/xhtml+xml)
|
12
|
+
module InlineStyle::Mail
|
13
|
+
class Interceptor
|
14
|
+
# The mime types we should inline. Basically HTML and XHTML.
|
15
|
+
# If you have something else you can just push it onto the list
|
16
|
+
INLINE_MIME_TYPES = %w(text/html application/xhtml+xml)
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
# Save the options to later pass to InlineStyle.process
|
19
|
+
def initialize(options={})
|
20
|
+
@options = options
|
21
|
+
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
elsif INLINE_MIME_TYPES.any? {|m| part.content_type.starts_with? m}
|
31
|
-
part.body = InlineStyle.process(part.body.to_s, @options)
|
23
|
+
# Mail callback where we actually inline the styles
|
24
|
+
def delivering_email(part)
|
25
|
+
if part.multipart?
|
26
|
+
for part in part.parts
|
27
|
+
delivering_email part
|
32
28
|
end
|
29
|
+
elsif INLINE_MIME_TYPES.any? {|m| part.content_type.starts_with? m}
|
30
|
+
part.body = InlineStyle.process(part.body.to_s, @options)
|
33
31
|
end
|
34
|
-
|
35
32
|
end
|
33
|
+
|
36
34
|
end
|
37
35
|
end
|
@@ -1,42 +1,33 @@
|
|
1
|
-
module InlineStyle
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
#
|
22
|
-
def initialize app, opts = {}
|
23
|
-
@app = app
|
24
|
-
@opts = opts
|
25
|
-
@paths = /^(?:#{ [*opts[:paths]].join('|') })/
|
26
|
-
end
|
1
|
+
module InlineStyle::Rack
|
2
|
+
class Middleware
|
3
|
+
# @param [Hash] opts Middlewar options
|
4
|
+
#
|
5
|
+
# @option opts [String] :stylesheets_path (env['DOCUMENT_ROOT'])
|
6
|
+
# Stylesheets root path or app's public directory where the stylesheets are to be found
|
7
|
+
# @option opts [Regexp, Array, String] :paths
|
8
|
+
# Limit processing to the passed absolute paths
|
9
|
+
# Can be an array of strings or regular expressions, a single string or regular expression
|
10
|
+
# If not passed will process output for every path.
|
11
|
+
# Regexps and strings must comence with '/'
|
12
|
+
# @option opts [Boolean] :pseudo (false)
|
13
|
+
# If set to true will inline style for pseudo classes according to the W3C specification:
|
14
|
+
# http://www.w3.org/TR/css-style-attr.
|
15
|
+
# Should probably be left as false because browsers don't seem to comply with the specification for pseudo class style in the style attribute.
|
16
|
+
def initialize app, opts = {}
|
17
|
+
@app = app
|
18
|
+
@opts = opts
|
19
|
+
@paths = /^(?:#{ [*opts[:paths]].join('|') })/
|
20
|
+
end
|
27
21
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
response.write InlineStyle.process(body, {:stylesheets_path => env['DOCUMENT_ROOT']}.merge(@opts))
|
37
|
-
response.finish
|
38
|
-
end
|
22
|
+
def call env
|
23
|
+
response = @app.call env
|
24
|
+
return response unless @paths === env['PATH_INFO']
|
25
|
+
|
26
|
+
status, headers, body = response
|
27
|
+
|
28
|
+
body = InlineStyle.process(body.first, {:stylesheets_path => env['DOCUMENT_ROOT']}.merge(@opts))
|
29
|
+
[status, headers, [body]]
|
39
30
|
end
|
40
31
|
end
|
41
32
|
end
|
42
|
-
|
33
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
# A simple abstraction of the data we get back from the parsers. CSSPool
|
4
|
+
# actually already does this for us but CSSParser does not so we need
|
5
|
+
# to create the abstraction ourselves.
|
6
|
+
class InlineStyle
|
7
|
+
class Selector < Struct.new :selector_text, :declarations, :specificity
|
8
|
+
# A slightly adjusted version of the selector_text that should be
|
9
|
+
# used for finding nodes. Will remove the pseudo selector and prepend
|
10
|
+
# 'body '.
|
11
|
+
def search
|
12
|
+
selector_text.dup.tap do |s|
|
13
|
+
state_based_pseudo_selectors.each {|p| s.gsub! /:#{p}$/, ''}
|
14
|
+
s.insert(0, 'body ') unless s =~ /^body/
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# For the most part is just declarations unless a pseudo selector.
|
19
|
+
# Then it uses the inline pseudo declarations
|
20
|
+
def inline_declarations
|
21
|
+
if pseudo?
|
22
|
+
"\n#{ selector_text.gsub /\w(?=:)/, '' } {#{ declarations }}"
|
23
|
+
else
|
24
|
+
declarations
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Is this selector using a pseudo class?
|
29
|
+
def pseudo?
|
30
|
+
state_based_pseudo_selectors.any? {|p| selector_text.end_with? ":#{p}"}
|
31
|
+
end
|
32
|
+
|
33
|
+
# A list of state based pseudo selectors (like hover) that should
|
34
|
+
# be handled based on the pseudo option. Unlike position-based
|
35
|
+
# pseudo selectors (like :first-child) which once resolved to the
|
36
|
+
# correct node effectively get inlined like a normal selector.
|
37
|
+
def state_based_pseudo_selectors
|
38
|
+
%w(link visited active hover focus target enabled disabled checked)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/inline-style/version.rb
CHANGED
data/spec/css_inlining_spec.rb
CHANGED
@@ -1,72 +1,91 @@
|
|
1
|
-
require "
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
describe InlineStyle do
|
4
|
-
|
5
|
-
|
6
|
-
:pseudo => false,
|
7
|
-
|
8
|
-
|
4
|
+
shared_examples_for 'inlines styles' do
|
5
|
+
before do
|
6
|
+
processed = InlineStyle.process File.read("#{FIXTURES}/boletin.html"), :pseudo => false, :stylesheets_path => FIXTURES
|
7
|
+
@processed = Nokogiri.HTML(processed)
|
8
|
+
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
it "should extract styles from linked stylesheet with no media specified" do
|
15
|
-
@processed.css('#izq').first['style'].should =~ /color: red;/
|
16
|
-
end
|
17
|
-
|
18
|
-
it "should extract styles from linked stylesheet with media 'all'" do
|
19
|
-
@processed.css('#izq').first['style'].should =~ /padding: 10.0px;/
|
20
|
-
end
|
21
|
-
|
22
|
-
it "should ignore styles from linked stylesheet with media other than screen" do
|
23
|
-
@processed.css('#izq').first['style'].should_not =~ /display: none;/
|
24
|
-
end
|
10
|
+
it "should extract from linked stylesheet" do
|
11
|
+
@processed.css('#izq').first['style'].should match_style /margin: 30.0px;/
|
12
|
+
end
|
25
13
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
it "should should process pseudo classes" do
|
32
|
-
processed = InlineStyle.process Nokogiri.HTML(File.read("#{ FIXTURES }/boletin.html")), :pseudo => true
|
33
|
-
processed.css('a').first['style'].should =~ /:hover/
|
34
|
-
processed.css('a').first['style'].should =~ /\{/
|
35
|
-
end
|
14
|
+
it "should extract styles from linked stylesheet with no media specified" do
|
15
|
+
@processed.css('#izq').first['style'].should match_style /color: red;/
|
16
|
+
end
|
36
17
|
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
# it 'should order selectors by specificity'
|
42
|
-
# it 'should order selectors by specificity and defininition order'
|
43
|
-
# it 'should overwrite rule with less specificity'
|
44
|
-
# it 'should overwrite rule previously defined'
|
45
|
-
# it 'should not overwrite rules defined inline'
|
46
|
-
|
47
|
-
describe 'Box model' do
|
48
|
-
before do
|
49
|
-
@processed = InlineStyle.process( Nokogiri.HTML(File.read("#{ FIXTURES }/box-model.html")) )
|
18
|
+
it "should extract styles from linked stylesheet with media 'all'" do
|
19
|
+
@processed.css('#izq').first['style'].should match_style /padding: 10.0px;/
|
50
20
|
end
|
51
21
|
|
52
|
-
it "should
|
53
|
-
@processed.css('
|
22
|
+
it "should ignore styles from linked stylesheet with media other than screen" do
|
23
|
+
@processed.css('#izq').first['style'].should_not match_style /display: none;/
|
54
24
|
end
|
55
25
|
|
56
|
-
it "should
|
57
|
-
@processed.css('
|
58
|
-
li['style'].should =~ /^color: white; background: blue; margin: 12.0px 12.0px 12.0px 12.0px; padding: 12.0px 0.0px 12.0px 12.0px; list-style: none/
|
59
|
-
end
|
26
|
+
it "should not process pseudo classes" do
|
27
|
+
@processed.css('a').first['style'].should_not match_style /^:hover \{background-color: #8ae0ea; color: #126b5d;\}$/
|
60
28
|
end
|
61
29
|
|
62
|
-
it "should
|
63
|
-
|
64
|
-
|
30
|
+
it "should should process pseudo classes" do
|
31
|
+
processed = InlineStyle.process Nokogiri.HTML(File.read("#{FIXTURES}/boletin.html")), :pseudo => true
|
32
|
+
processed.css('a').first['style'].should match_style /^:hover \{background-color: #8ae0ea; color: #126b5d;\}$/
|
65
33
|
end
|
66
|
-
end
|
67
|
-
end
|
68
34
|
|
35
|
+
it 'should process location-based pseudo classes' do
|
36
|
+
@processed.at_css('#izq')['style'].should match_style /padding: 1.0px;/
|
37
|
+
end
|
69
38
|
|
39
|
+
it 'should apply to #A #B' do
|
40
|
+
@processed.css('#logos #der').first['style'].should match_style /float: right;/
|
41
|
+
end
|
70
42
|
|
43
|
+
# it 'should overwrite rule with less specificity'
|
44
|
+
# it 'should overwrite rule previously defined'
|
45
|
+
# it 'should not overwrite rules defined inline'
|
71
46
|
|
47
|
+
describe 'Box model' do
|
48
|
+
before do
|
49
|
+
@processed = InlineStyle.process(Nokogiri.HTML(File.read("#{FIXTURES}/box-model.html")))
|
50
|
+
end
|
72
51
|
|
52
|
+
it "should inline style for selector ul" do
|
53
|
+
@processed.css('ul').first['style'].should match_style /^background: yellow; margin: 12.0px 12.0px 12.0px 12.0px; padding: 3.0px 3.0px 3.0px 3.0px;$/
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should inline style for selector li" do
|
57
|
+
@processed.css('li').each do |li|
|
58
|
+
li['style'].should match_style /^color: white; background: blue; margin: 12.0px 12.0px 12.0px 12.0px; padding: 12.0px 0.0px 12.0px 12.0px; list-style: none/
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should inline style for selector li.withborder" do
|
63
|
+
@processed.css('li.withborder').first['style'].
|
64
|
+
should match_style /^color: white; background: blue; margin: 12.0px 12.0px 12.0px 12.0px; padding: 12.0px 0.0px 12.0px 12.0px; list-style: none; border-style: dashed; border-width: medium; border-color: lime;$/
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe CssParser do
|
70
|
+
it { InlineStyle::CSSParser.should == InlineStyle::CssParserWrapper }
|
71
|
+
it_should_behave_like 'inlines styles'
|
72
|
+
end
|
73
|
+
|
74
|
+
describe CSSPool do
|
75
|
+
before do
|
76
|
+
class InlineStyle
|
77
|
+
remove_const :CSSParser
|
78
|
+
CSSParser = InlineStyle::CSSPoolWrapper
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
after do
|
83
|
+
class InlineStyle
|
84
|
+
remove_const :CSSParser
|
85
|
+
CSSParser = InlineStyle::CssParserWrapper
|
86
|
+
end
|
87
|
+
end
|
88
|
+
it { InlineStyle::CSSParser.should == InlineStyle::CSSPoolWrapper }
|
89
|
+
it_should_behave_like 'inlines styles'
|
90
|
+
end
|
91
|
+
end
|
data/spec/css_parsers_spec.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
|
-
require "
|
2
|
-
require "#{ DIR }/../lib/inline-style/css_parsers/css_parser"
|
3
|
-
require "#{ DIR }/../lib/inline-style/css_parsers/csspool"
|
1
|
+
require "spec_helper"
|
4
2
|
|
5
|
-
describe InlineStyle::
|
3
|
+
describe InlineStyle::CssParserWrapper do
|
6
4
|
|
7
5
|
it 'should wrap css_parser' do
|
8
6
|
rs_count = 0
|
@@ -10,15 +8,15 @@ describe InlineStyle::CssParsers::CssParser do
|
|
10
8
|
selectors = %w(p b i)
|
11
9
|
decs = ['color: black;', 'color: black;', 'color: green; text-decoration: none;']
|
12
10
|
spes = [1, 1, 1]
|
13
|
-
p = InlineStyle::
|
11
|
+
p = InlineStyle::CssParserWrapper.new "p, b {color: black}\ni {color: green; text-decoration: none}"
|
14
12
|
|
15
13
|
p.each_rule_set do |rs|
|
16
14
|
rs_count += 1
|
17
|
-
rs.each_selector do |sel
|
15
|
+
rs.each_selector do |sel|
|
18
16
|
sel_count += 1
|
19
|
-
sel.should == selectors.shift
|
20
|
-
|
21
|
-
|
17
|
+
sel.selector_text.should == selectors.shift
|
18
|
+
sel.declarations.should == decs.shift
|
19
|
+
sel.specificity.should == spes.shift
|
22
20
|
end
|
23
21
|
end
|
24
22
|
|
@@ -28,7 +26,7 @@ describe InlineStyle::CssParsers::CssParser do
|
|
28
26
|
|
29
27
|
end
|
30
28
|
|
31
|
-
describe InlineStyle::
|
29
|
+
describe InlineStyle::CSSPoolWrapper do
|
32
30
|
|
33
31
|
it 'should wrap csspool' do
|
34
32
|
rs_count = 0
|
@@ -36,15 +34,15 @@ describe InlineStyle::CssParsers::Csspool do
|
|
36
34
|
selectors = %w(p b i)
|
37
35
|
decs = ['color: black;', 'color: black;', 'color: green; text-decoration: none;']
|
38
36
|
spes = [1, 1, 1]
|
39
|
-
p = InlineStyle::
|
37
|
+
p = InlineStyle::CSSPoolWrapper.new "p, b {color: black}\ni {color: green; text-decoration: none}"
|
40
38
|
|
41
39
|
p.each_rule_set do |rs|
|
42
40
|
rs_count += 1
|
43
|
-
rs.each_selector do |sel
|
41
|
+
rs.each_selector do |sel|
|
44
42
|
sel_count += 1
|
45
|
-
sel.should == selectors.shift
|
46
|
-
|
47
|
-
|
43
|
+
sel.selector_text.should == selectors.shift
|
44
|
+
sel.declarations.should == decs.shift
|
45
|
+
sel.specificity.should == spes.shift
|
48
46
|
end
|
49
47
|
end
|
50
48
|
|
data/spec/fixtures/boletin.html
CHANGED
@@ -124,6 +124,10 @@ div#contacto a {
|
|
124
124
|
background-image: url('/images/boletines/Noviembre/logos_fondo.jpg');
|
125
125
|
}
|
126
126
|
|
127
|
+
#logos img:first-child {
|
128
|
+
padding: 1px;
|
129
|
+
}
|
130
|
+
|
127
131
|
#izq {
|
128
132
|
float: left;
|
129
133
|
}
|
@@ -253,4 +257,4 @@ div#contacto a {
|
|
253
257
|
</div></body>
|
254
258
|
|
255
259
|
|
256
|
-
</html>
|
260
|
+
</html>
|
data/spec/fixtures/inline.html
CHANGED
@@ -96,7 +96,7 @@
|
|
96
96
|
contacto <br style="margin: 0.0; padding: 0.0;"><a href="mailto:mail@host.org" style="margin: 0.0; padding: 0.0; color: #185d6b; font-weight: bold; font-size: 1.2em;">mail@host.org</a>
|
97
97
|
</div>
|
98
98
|
<div id="logos" style="margin: 0.0; padding: 0.0; width: 100.0%; margin-top: 40.0px; background-image: url(/images/boletines/Noviembre/logos_fondo.jpg);">
|
99
|
-
<img src="A" id="izq" alt="A" style="margin: 0.0; padding: 0.0; float: left; margin: 30.0px; padding: 10.0px; color: red;"><img src="B" id="der" alt="B" style="margin: 0.0; padding: 0.0; float: right;"><div class="clearer" style="margin: 0.0; padding: 0.0; clear: both;">
|
99
|
+
<img src="A" id="izq" alt="A" style="margin: 0.0; padding: 0.0; float: left; margin: 30.0px; padding: 10.0px; color: red; padding: 1.0px;"><img src="B" id="der" alt="B" style="margin: 0.0; padding: 0.0; float: right;"><div class="clearer" style="margin: 0.0; padding: 0.0; clear: both;">
|
100
100
|
</div>
|
101
101
|
</div>
|
102
102
|
|
data/spec/interceptor_spec.rb
CHANGED
@@ -1,16 +1,27 @@
|
|
1
|
-
require "
|
1
|
+
require "spec_helper"
|
2
|
+
require "inline-style/mail/interceptor"
|
3
|
+
require "mail"
|
2
4
|
|
3
|
-
require 'mail'
|
4
5
|
Mail.defaults do
|
5
6
|
delivery_method :test
|
6
7
|
end
|
7
|
-
Mail.register_interceptor \
|
8
|
-
InlineStyle::Mail::Interceptor.new(:stylesheets_path => FIXTURES)
|
9
8
|
|
10
|
-
|
9
|
+
Mail.register_interceptor InlineStyle::Mail::Interceptor.new(:stylesheets_path => FIXTURES)
|
11
10
|
|
11
|
+
describe InlineStyle::Mail::Interceptor do
|
12
12
|
before do
|
13
13
|
Mail::TestMailer.deliveries.clear
|
14
|
+
class InlineStyle
|
15
|
+
remove_const :CSSParser
|
16
|
+
CSSParser = InlineStyle::CSSPoolWrapper
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
after do
|
21
|
+
class InlineStyle
|
22
|
+
remove_const :CSSParser
|
23
|
+
CSSParser = InlineStyle::CssParserWrapper
|
24
|
+
end
|
14
25
|
end
|
15
26
|
|
16
27
|
it 'should inline html e-mail' do
|
@@ -59,5 +70,4 @@ describe InlineStyle::Mail::Interceptor do
|
|
59
70
|
Mail::TestMailer.deliveries.first.parts[1].body.to_s.
|
60
71
|
should == File.read("#{ FIXTURES }/inline.html")
|
61
72
|
end
|
62
|
-
|
63
73
|
end
|
@@ -1,12 +1,12 @@
|
|
1
|
-
require "
|
1
|
+
require "spec_helper"
|
2
|
+
require "inline-style/rack/middleware"
|
2
3
|
|
3
4
|
describe InlineStyle::Rack::Middleware do
|
4
|
-
|
5
5
|
def get_response path, body, opts = {}
|
6
6
|
content_type = opts.delete(:content_type) || 'text/html'
|
7
7
|
app = Rack::Builder.new do
|
8
8
|
use InlineStyle::Rack::Middleware, opts
|
9
|
-
run lambda { |env| env['DOCUMENT_ROOT'] = FIXTURES; [200, {'Content-Type' => content_type}, body
|
9
|
+
run lambda { |env| env['DOCUMENT_ROOT'] = FIXTURES; [200, {'Content-Type' => content_type}, [body]] }
|
10
10
|
end
|
11
11
|
Nokogiri.HTML Rack::MockRequest.new(app).get(path).body
|
12
12
|
end
|
@@ -20,7 +20,7 @@ describe InlineStyle::Rack::Middleware do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should use external css" do
|
23
|
-
get_response('/',
|
23
|
+
get_response('/', @html, :stylesheets_path => FIXTURES).css('#izq').first['style'].should match_style /margin: 30.0px/
|
24
24
|
end
|
25
25
|
|
26
26
|
describe 'Path inclusion' do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rspec'
|
3
|
-
require "#{ DIR = File.dirname(__FILE__) }/../lib/inline-style"
|
4
3
|
require 'rack'
|
5
4
|
require 'rack/mock'
|
6
5
|
|
7
|
-
|
6
|
+
require "inline-style"
|
7
|
+
require "inline-style/csspool_wrapper"
|
8
|
+
require "csspool"
|
8
9
|
|
9
|
-
|
10
|
+
FIXTURES = "#{File.dirname __FILE__}/fixtures"
|
11
|
+
|
12
|
+
module InlineStyleMatchers
|
10
13
|
class HaveInlineStyle
|
11
14
|
def initialize selector
|
12
15
|
@selector = selector
|
@@ -18,17 +21,40 @@ module HaveInlineStyleMatcher
|
|
18
21
|
end
|
19
22
|
|
20
23
|
def failure_message
|
21
|
-
"expected elements with selector #{
|
24
|
+
"expected elements with selector #{@selector} style attribute not to be nil"
|
25
|
+
end
|
26
|
+
|
27
|
+
def negative_failure_message
|
28
|
+
"expected elements with selector #{@selector} style attribute to be nil"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class MatchStyle
|
33
|
+
def initialize style
|
34
|
+
@style = style
|
35
|
+
end
|
36
|
+
|
37
|
+
def matches? actual
|
38
|
+
@actual = actual.gsub(/([^\.0-9]\d+)(px|;|%|\s)/, '\1.0\2')
|
39
|
+
@actual =~ @style
|
40
|
+
end
|
41
|
+
|
42
|
+
def failure_message
|
43
|
+
"expected #{@style} to match #{@actual}"
|
22
44
|
end
|
23
45
|
|
24
46
|
def negative_failure_message
|
25
|
-
"expected
|
47
|
+
"expected #{@style} not to match #{@actual}"
|
26
48
|
end
|
27
49
|
end
|
28
50
|
|
29
51
|
def have_inline_style_for selector
|
30
52
|
HaveInlineStyle.new selector
|
31
53
|
end
|
54
|
+
|
55
|
+
def match_style style
|
56
|
+
MatchStyle.new style
|
57
|
+
end
|
32
58
|
end
|
33
59
|
|
34
|
-
RSpec.configure { |config| config.include
|
60
|
+
RSpec.configure { |config| config.include InlineStyleMatchers }
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inline-style
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 4
|
8
|
-
-
|
9
|
-
version: 0.4.
|
9
|
+
- 10
|
10
|
+
version: 0.4.10
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Macario Ortega
|
@@ -15,7 +16,7 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date: 2011-03-
|
19
|
+
date: 2011-03-23 00:00:00 -06:00
|
19
20
|
default_executable:
|
20
21
|
dependencies:
|
21
22
|
- !ruby/object:Gem::Dependency
|
@@ -26,6 +27,7 @@ dependencies:
|
|
26
27
|
requirements:
|
27
28
|
- - ">="
|
28
29
|
- !ruby/object:Gem::Version
|
30
|
+
hash: 3
|
29
31
|
segments:
|
30
32
|
- 0
|
31
33
|
version: "0"
|
@@ -39,6 +41,7 @@ dependencies:
|
|
39
41
|
requirements:
|
40
42
|
- - ">="
|
41
43
|
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
42
45
|
segments:
|
43
46
|
- 0
|
44
47
|
version: "0"
|
@@ -52,6 +55,7 @@ dependencies:
|
|
52
55
|
requirements:
|
53
56
|
- - ">="
|
54
57
|
- !ruby/object:Gem::Version
|
58
|
+
hash: 3
|
55
59
|
segments:
|
56
60
|
- 0
|
57
61
|
version: "0"
|
@@ -65,6 +69,7 @@ dependencies:
|
|
65
69
|
requirements:
|
66
70
|
- - ">="
|
67
71
|
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
68
73
|
segments:
|
69
74
|
- 0
|
70
75
|
version: "0"
|
@@ -78,6 +83,7 @@ dependencies:
|
|
78
83
|
requirements:
|
79
84
|
- - ">="
|
80
85
|
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
81
87
|
segments:
|
82
88
|
- 0
|
83
89
|
version: "0"
|
@@ -91,6 +97,7 @@ dependencies:
|
|
91
97
|
requirements:
|
92
98
|
- - ">="
|
93
99
|
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
94
101
|
segments:
|
95
102
|
- 0
|
96
103
|
version: "0"
|
@@ -104,6 +111,7 @@ dependencies:
|
|
104
111
|
requirements:
|
105
112
|
- - ">="
|
106
113
|
- !ruby/object:Gem::Version
|
114
|
+
hash: 3
|
107
115
|
segments:
|
108
116
|
- 0
|
109
117
|
version: "0"
|
@@ -120,24 +128,24 @@ extra_rdoc_files: []
|
|
120
128
|
|
121
129
|
files:
|
122
130
|
- .gitignore
|
131
|
+
- .rspec
|
123
132
|
- Gemfile
|
124
133
|
- Gemfile.lock
|
125
134
|
- History.txt
|
126
135
|
- Manifest.txt
|
127
|
-
- README.
|
136
|
+
- README.rdoc
|
128
137
|
- Rakefile
|
129
138
|
- example.rb
|
130
139
|
- inline-style.gemspec
|
131
140
|
- lib/inline-style.rb
|
132
|
-
- lib/inline-style/
|
133
|
-
- lib/inline-style/
|
134
|
-
- lib/inline-style/css_parsers/csspool.rb
|
141
|
+
- lib/inline-style/css_parser_wrapper.rb
|
142
|
+
- lib/inline-style/csspool_wrapper.rb
|
135
143
|
- lib/inline-style/mail/interceptor.rb
|
136
144
|
- lib/inline-style/rack/middleware.rb
|
145
|
+
- lib/inline-style/selector.rb
|
137
146
|
- lib/inline-style/version.rb
|
138
147
|
- spec/css_inlining_spec.rb
|
139
148
|
- spec/css_parsers_spec.rb
|
140
|
-
- spec/factory_spec.rb
|
141
149
|
- spec/fixtures/all.css
|
142
150
|
- spec/fixtures/boletin.html
|
143
151
|
- spec/fixtures/box-model.html
|
@@ -153,7 +161,7 @@ has_rdoc: true
|
|
153
161
|
homepage: ""
|
154
162
|
licenses: []
|
155
163
|
|
156
|
-
post_install_message:
|
164
|
+
post_install_message: Please read documentation for changes on the default css parser gem, specifically if you use csspool
|
157
165
|
rdoc_options: []
|
158
166
|
|
159
167
|
require_paths:
|
@@ -163,6 +171,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
163
171
|
requirements:
|
164
172
|
- - ">="
|
165
173
|
- !ruby/object:Gem::Version
|
174
|
+
hash: 3
|
166
175
|
segments:
|
167
176
|
- 0
|
168
177
|
version: "0"
|
@@ -171,13 +180,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
171
180
|
requirements:
|
172
181
|
- - ">="
|
173
182
|
- !ruby/object:Gem::Version
|
183
|
+
hash: 3
|
174
184
|
segments:
|
175
185
|
- 0
|
176
186
|
version: "0"
|
177
187
|
requirements: []
|
178
188
|
|
179
189
|
rubyforge_project:
|
180
|
-
rubygems_version: 1.
|
190
|
+
rubygems_version: 1.6.2
|
181
191
|
signing_key:
|
182
192
|
specification_version: 3
|
183
193
|
summary: Inlines CSS for html email delivery
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module InlineStyle
|
2
|
-
|
3
|
-
# A factory for returning a configured CSS parser. Defaults to
|
4
|
-
# :css_parser if not specified. Will also use ENV['CSS_PARSER'].
|
5
|
-
module CssParsers
|
6
|
-
|
7
|
-
# Allows you to specify the CSS parser to use.
|
8
|
-
def self.parser=(parser)
|
9
|
-
@parser = nil
|
10
|
-
@parser_name = parser
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.parser
|
14
|
-
return @parser if @parser
|
15
|
-
@parser_name = ENV['CSS_PARSER'] || :css_parser unless @parser_name
|
16
|
-
require "inline-style/css_parsers/#{@parser_name}"
|
17
|
-
@parser = const_get(@parser_name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase })
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
require 'css_parser'
|
2
|
-
|
3
|
-
module InlineStyle::CssParsers
|
4
|
-
class CssParser
|
5
|
-
|
6
|
-
def initialize(css_code)
|
7
|
-
@parser = ::CssParser::Parser.new
|
8
|
-
@parser.add_block! css_code
|
9
|
-
end
|
10
|
-
|
11
|
-
def each_rule_set(&blk)
|
12
|
-
@parser.each_rule_set do |rule_set|
|
13
|
-
yield Ruleset.new(rule_set)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class Ruleset
|
18
|
-
|
19
|
-
def initialize(ruleset)
|
20
|
-
@ruleset = ruleset
|
21
|
-
end
|
22
|
-
|
23
|
-
def each_selector
|
24
|
-
@ruleset.each_selector do |sel, dec, spe|
|
25
|
-
normalize_for_test! dec
|
26
|
-
yield sel, dec, spe
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
# To make testings work for both parsers we need to conform to
|
33
|
-
# csspool normalizing attributes. This means all numbers have a
|
34
|
-
# decimal place. It also means that urls do not have quotes.
|
35
|
-
#
|
36
|
-
# NOTE: This really doesn't have anything to do with the
|
37
|
-
# correctness of the parser. It just makes testing easier since
|
38
|
-
# each parser can run against the same tests.
|
39
|
-
def normalize_for_test!(dec)
|
40
|
-
|
41
|
-
# Give all numbers a decimal if they do not already have it
|
42
|
-
dec.gsub! '0;', '0.0;'
|
43
|
-
dec.gsub! ' 0 ', ' 0.0 '
|
44
|
-
dec.gsub! /([^\.0-9]\d+)px/, '\1.0px'
|
45
|
-
dec.gsub! /([^\.0-9]\d+)%/, '\1.0%'
|
46
|
-
|
47
|
-
# Remove any quotes in url()
|
48
|
-
dec.gsub! "url('", 'url('
|
49
|
-
dec.gsub! "')", ')'
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|
56
|
-
end
|
data/spec/factory_spec.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
require "#{ File.dirname __FILE__ }/spec_helper"
|
2
|
-
|
3
|
-
describe InlineStyle::CssParsers do
|
4
|
-
|
5
|
-
before do
|
6
|
-
@orig_env = ENV['CSS_PARSER']
|
7
|
-
ENV['CSS_PARSER'] = nil
|
8
|
-
InlineStyle::CssParsers.parser = nil # Clear out any cached value
|
9
|
-
end
|
10
|
-
after do
|
11
|
-
ENV['CSS_PARSER'] = @orig_env
|
12
|
-
InlineStyle::CssParsers.parser = nil # Clear out any cached value
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'should load css_parser by default' do
|
16
|
-
InlineStyle::CssParsers.parser.name.
|
17
|
-
should == 'InlineStyle::CssParsers::CssParser'
|
18
|
-
end
|
19
|
-
|
20
|
-
it "should obey ENV['CSS_PARSER']" do
|
21
|
-
ENV['CSS_PARSER'] = 'css_parser'
|
22
|
-
InlineStyle::CssParsers.parser.name.
|
23
|
-
should == 'InlineStyle::CssParsers::CssParser'
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'should be able to load csspool' do
|
27
|
-
InlineStyle::CssParsers.parser = :csspool
|
28
|
-
InlineStyle::CssParsers.parser.name.
|
29
|
-
should == 'InlineStyle::CssParsers::Csspool'
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'should be able to load css_parser' do
|
33
|
-
InlineStyle::CssParsers.parser = :css_parser
|
34
|
-
InlineStyle::CssParsers.parser.name.
|
35
|
-
should == 'InlineStyle::CssParsers::CssParser'
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|