inline-style 0.4.6 → 0.4.10
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/.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
|