inline-style 0.4.2 → 0.4.6
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 +4 -0
- data/Gemfile.lock +52 -0
- data/Manifest.txt +9 -1
- data/{README.rdoc → README.txt} +15 -2
- data/Rakefile +9 -26
- data/inline-style.gemspec +30 -0
- data/lib/inline-style.rb +20 -20
- data/lib/inline-style/css_parsers.rb +21 -0
- data/lib/inline-style/css_parsers/css_parser.rb +56 -0
- data/lib/inline-style/css_parsers/csspool.rb +34 -0
- data/lib/inline-style/mail/interceptor.rb +37 -0
- data/lib/inline-style/rack/middleware.rb +3 -3
- data/lib/inline-style/version.rb +5 -0
- data/spec/css_parsers_spec.rb +55 -0
- data/spec/factory_spec.rb +38 -0
- data/spec/fixtures/inline.html +2 -2
- data/spec/interceptor_spec.rb +63 -0
- data/spec/{as_middleware_spec.rb → rack_middleware_spec.rb} +5 -2
- data/spec/spec_helper.rb +2 -3
- metadata +111 -48
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
inline-style (0.4.6)
|
5
|
+
css_parser
|
6
|
+
maca-fork-csspool
|
7
|
+
nokogiri
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
activesupport (3.0.4)
|
13
|
+
css_parser (1.1.5)
|
14
|
+
diff-lcs (1.1.2)
|
15
|
+
ffi (1.0.6)
|
16
|
+
rake (>= 0.8.7)
|
17
|
+
i18n (0.5.0)
|
18
|
+
maca-fork-csspool (2.0.2)
|
19
|
+
ffi
|
20
|
+
mail (2.2.15)
|
21
|
+
activesupport (>= 2.3.6)
|
22
|
+
i18n (>= 0.4.0)
|
23
|
+
mime-types (~> 1.16)
|
24
|
+
treetop (~> 1.4.8)
|
25
|
+
mime-types (1.16)
|
26
|
+
nokogiri (1.4.4)
|
27
|
+
polyglot (0.3.1)
|
28
|
+
rack (1.2.1)
|
29
|
+
rake (0.8.7)
|
30
|
+
rspec (2.4.0)
|
31
|
+
rspec-core (~> 2.4.0)
|
32
|
+
rspec-expectations (~> 2.4.0)
|
33
|
+
rspec-mocks (~> 2.4.0)
|
34
|
+
rspec-core (2.4.0)
|
35
|
+
rspec-expectations (2.4.0)
|
36
|
+
diff-lcs (~> 1.1.2)
|
37
|
+
rspec-mocks (2.4.0)
|
38
|
+
treetop (1.4.9)
|
39
|
+
polyglot (>= 0.3.1)
|
40
|
+
|
41
|
+
PLATFORMS
|
42
|
+
ruby
|
43
|
+
|
44
|
+
DEPENDENCIES
|
45
|
+
css_parser
|
46
|
+
inline-style!
|
47
|
+
maca-fork-csspool
|
48
|
+
mail
|
49
|
+
nokogiri
|
50
|
+
rack
|
51
|
+
rspec
|
52
|
+
rspec-core
|
data/Manifest.txt
CHANGED
@@ -1,12 +1,19 @@
|
|
1
1
|
History.txt
|
2
2
|
Manifest.txt
|
3
|
-
README.
|
3
|
+
README.txt
|
4
4
|
Rakefile
|
5
5
|
example.rb
|
6
|
+
inline-style.gemspec
|
6
7
|
lib/inline-style.rb
|
8
|
+
lib/inline-style/css_parsers.rb
|
9
|
+
lib/inline-style/css_parsers/css_parser.rb
|
10
|
+
lib/inline-style/css_parsers/csspool.rb
|
11
|
+
lib/inline-style/mail/interceptor.rb
|
7
12
|
lib/inline-style/rack/middleware.rb
|
8
13
|
spec/as_middleware_spec.rb
|
9
14
|
spec/css_inlining_spec.rb
|
15
|
+
spec/css_parsers_spec.rb
|
16
|
+
spec/factory_spec.rb
|
10
17
|
spec/fixtures/all.css
|
11
18
|
spec/fixtures/boletin.html
|
12
19
|
spec/fixtures/box-model.html
|
@@ -15,4 +22,5 @@ spec/fixtures/none.css
|
|
15
22
|
spec/fixtures/print.css
|
16
23
|
spec/fixtures/selectors.html
|
17
24
|
spec/fixtures/style.css
|
25
|
+
spec/interceptor_spec.rb
|
18
26
|
spec/spec_helper.rb
|
data/{README.rdoc → README.txt}
RENAMED
@@ -10,6 +10,8 @@ each refered element taking selector specificity and declarator order.
|
|
10
10
|
Useful for html email: some clients (gmail, et all) won't render non inline styles.
|
11
11
|
|
12
12
|
* Includes a Rack middleware for using with Rails, Sinatra, etc...
|
13
|
+
* Includes a interceptor for the mail gem which allows automatic
|
14
|
+
inline processing for both mail as well as ActionMailer.
|
13
15
|
* It takes into account selector specificity.
|
14
16
|
|
15
17
|
== USAGE
|
@@ -92,12 +94,23 @@ Will become:
|
|
92
94
|
|
93
95
|
# Restrict processing to some routes:
|
94
96
|
use InlineStyle::Rack::Middleware, :paths => [%r(/mails/.*), "/somepath"]
|
97
|
+
|
98
|
+
== MAIL INTERCEPTOR
|
99
|
+
|
100
|
+
If using the mail library the following code will work:
|
101
|
+
|
102
|
+
Mail.register_interceptor \
|
103
|
+
InlineStyle::Mail::Interceptor.new(:stylesheets_path => 'public')
|
104
|
+
|
105
|
+
If using ActionMailer (which wraps mail):
|
106
|
+
|
107
|
+
ActionMailer::Base.register_interceptor \
|
108
|
+
InlineStyle::Mail::Interceptor.new(:stylesheets_path => 'public')
|
95
109
|
|
96
110
|
== ISSUES:
|
97
111
|
|
98
112
|
* It supports pseudo classes according to W3C specification for style in style attribute: http://www.w3.org/TR/css-style-attr, although browsers
|
99
113
|
doesn't seems to.
|
100
|
-
* It strips any numeric character (Rails may add) at the end of the stylesheet file name, anyway stylesheets should end with .css extension
|
101
114
|
|
102
115
|
== REQUIREMENTS:
|
103
116
|
|
@@ -130,4 +143,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
130
143
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
131
144
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
132
145
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
133
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
146
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -1,26 +1,9 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
['nokogiri','>= 1.3.3'],
|
11
|
-
['maca-fork-csspool', '>= 2.0.2']
|
12
|
-
]
|
13
|
-
p.extra_dev_deps = [
|
14
|
-
['newgem', ">= #{::Newgem::VERSION}"]
|
15
|
-
]
|
16
|
-
|
17
|
-
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
18
|
-
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
19
|
-
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
20
|
-
p.rsync_args = '-av --delete --ignore-errors'
|
21
|
-
end
|
22
|
-
|
23
|
-
require 'newgem/tasks'
|
24
|
-
Dir['tasks/**/*.rake'].each { |t| load t }
|
25
|
-
|
26
|
-
|
1
|
+
require 'bundler'
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
task :default => [:spec]
|
5
|
+
|
6
|
+
desc "Run specs"
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
8
|
+
|
9
|
+
Bundler::GemHelper.install_tasks
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "inline-style/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "inline-style"
|
7
|
+
s.version = Inline::Style::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Macario Ortega", "Eric Anderson"]
|
10
|
+
s.email = ["macarui@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Inlines CSS for html email delivery}
|
13
|
+
s.description = %q{Inlines CSS for html email delivery}
|
14
|
+
|
15
|
+
# s.rubyforge_project = "inline-style"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_development_dependency 'rspec'
|
23
|
+
s.add_development_dependency 'rack'
|
24
|
+
s.add_development_dependency 'rspec-core'
|
25
|
+
s.add_development_dependency 'mail'
|
26
|
+
|
27
|
+
s.add_dependency 'nokogiri'
|
28
|
+
s.add_dependency 'css_parser'
|
29
|
+
s.add_dependency 'maca-fork-csspool'
|
30
|
+
end
|
data/lib/inline-style.rb
CHANGED
@@ -1,14 +1,11 @@
|
|
1
1
|
require 'nokogiri'
|
2
2
|
require 'open-uri'
|
3
3
|
|
4
|
-
|
5
|
-
require 'csspool'
|
6
|
-
|
4
|
+
require "#{ File.dirname( __FILE__ ) }/inline-style/css_parsers"
|
7
5
|
require "#{ File.dirname( __FILE__ ) }/inline-style/rack/middleware"
|
6
|
+
require "#{ File.dirname( __FILE__ ) }/inline-style/mail/interceptor"
|
8
7
|
|
9
8
|
module InlineStyle
|
10
|
-
VERSION = '0.4.2'
|
11
|
-
|
12
9
|
# Options:
|
13
10
|
# +:stylesheets_path+
|
14
11
|
# Stylesheets root path, can also be a URL
|
@@ -27,41 +24,44 @@ module InlineStyle
|
|
27
24
|
css = extract_css html, stylesheets_path
|
28
25
|
nodes = {}
|
29
26
|
|
30
|
-
css.
|
31
|
-
rule_set.
|
32
|
-
|
27
|
+
css.each_rule_set do |rule_set|
|
28
|
+
rule_set.each_selector do |css_selector, declarations, specificity|
|
29
|
+
orig_selector = css_selector.dup
|
33
30
|
css_selector = "#{ 'body ' unless /^body/ === css_selector }#{ css_selector.gsub /:.*/, '' }"
|
34
31
|
|
35
32
|
html.css(css_selector).each do |node|
|
36
33
|
nodes[node] ||= []
|
37
|
-
nodes[node].push
|
34
|
+
nodes[node].push [css_selector, declarations, specificity, orig_selector]
|
38
35
|
|
39
36
|
next unless node['style']
|
40
37
|
|
41
38
|
path = node.css_path
|
42
39
|
path << "##{ node['id'] }" if node['id']
|
43
40
|
path << ".#{ node['class'].scan(/\S+/).join('.') }" if node['class']
|
44
|
-
|
45
|
-
|
41
|
+
|
42
|
+
InlineStyle::CssParsers.parser.new("#{ path }{#{ node['style'] }}").each_rule_set do |rule|
|
43
|
+
rule.each_selector do |css_selector_inner, declarations_inner, specificity_inner|
|
44
|
+
nodes[node].push [css_selector_inner, declarations_inner, specificity_inner, css_selector_inner]
|
45
|
+
end
|
46
|
+
end
|
46
47
|
end
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
50
51
|
nodes.each_pair do |node, style|
|
51
|
-
style = style.sort_by{ |sel| "#{
|
52
|
-
sets = style.partition{ |sel| not /:\w+/ ===
|
52
|
+
style = style.sort_by{ |(sel, dec, spe, orig)| "#{ spe }%03d" % style.index([sel, dec, spe, orig]) }
|
53
|
+
sets = style.partition{ |(sel, dec, spe, orig)| not /:\w+/ === orig }
|
53
54
|
|
54
55
|
sets.pop if not pseudo or sets.last.empty?
|
55
56
|
|
56
57
|
node['style'] = sets.collect do |selectors|
|
57
58
|
index = sets.index selectors
|
58
59
|
|
59
|
-
set = selectors.map do |
|
60
|
-
declarations
|
61
|
-
index == 0 ? declarations : "\n#{ selector.to_s.gsub /\w(?=:)/, '' } {#{ declarations }}"
|
60
|
+
set = selectors.map do |(css_selector, declarations, specificity, orig_selector)|
|
61
|
+
index == 0 ? declarations : "\n#{ orig_selector.gsub /\w(?=:)/, '' } {#{ declarations }}"
|
62
62
|
end
|
63
63
|
|
64
|
-
index == 0 && sets.size > 1 ? "{#{ set }}" : set.join
|
64
|
+
index == 0 && sets.size > 1 ? "{#{ set }}" : set.collect(&:strip).join(' ')
|
65
65
|
end.join.strip
|
66
66
|
end
|
67
67
|
|
@@ -70,8 +70,8 @@ module InlineStyle
|
|
70
70
|
|
71
71
|
# Returns CSSPool::Document
|
72
72
|
def self.extract_css html, stylesheets_path = ''
|
73
|
-
|
74
|
-
next unless e['media'].nil?
|
73
|
+
InlineStyle::CssParsers.parser.new html.css('style, link').collect { |e|
|
74
|
+
next unless e['media'].nil? || ['screen', 'all'].include?(e['media'])
|
75
75
|
next(e.remove and e.content) if e.name == 'style'
|
76
76
|
next unless e['rel'] == 'stylesheet'
|
77
77
|
e.remove
|
@@ -80,4 +80,4 @@ module InlineStyle
|
|
80
80
|
open(uri).read rescue nil
|
81
81
|
}.join("\n")
|
82
82
|
end
|
83
|
-
end
|
83
|
+
end
|
@@ -0,0 +1,21 @@
|
|
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
|
@@ -0,0 +1,56 @@
|
|
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
|
@@ -0,0 +1,34 @@
|
|
1
|
+
gem 'maca-fork-csspool'
|
2
|
+
require 'csspool'
|
3
|
+
|
4
|
+
module InlineStyle::CssParsers
|
5
|
+
class Csspool
|
6
|
+
|
7
|
+
def initialize(css_code)
|
8
|
+
@parser = CSSPool.CSS css_code
|
9
|
+
end
|
10
|
+
|
11
|
+
def each_rule_set
|
12
|
+
@parser.rule_sets.each 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(&blk)
|
24
|
+
@ruleset.selectors.each do |selector|
|
25
|
+
yield selector.to_s,
|
26
|
+
selector.declarations.map{ |d| d.to_s.squeeze(' ') }.join.strip,
|
27
|
+
selector.specificity.inject(0) {|t, s| t+s}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# A interceptor for +mail+ (https://github.com/mikel/mail) to
|
2
|
+
# automatically inline the styles of outgoing e-mails. To use:
|
3
|
+
#
|
4
|
+
# Mail.register_interceptor \
|
5
|
+
# InlineStyle::Mail::Interceptor.new(:stylesheets_path => 'public')
|
6
|
+
#
|
7
|
+
# Rails 3's ActionMailer wraps around the +mail+ and also supports
|
8
|
+
# interceptors. Example usage:
|
9
|
+
#
|
10
|
+
# ActionMailer::Base.register_interceptor \
|
11
|
+
# InlineStyle::Mail::Interceptor.new(:stylesheets_path => 'public')
|
12
|
+
module InlineStyle
|
13
|
+
module Mail
|
14
|
+
class Interceptor
|
15
|
+
# The mime types we should inline. Basically HTML and XHTML.
|
16
|
+
# If you have something else you can just push it onto the list
|
17
|
+
INLINE_MIME_TYPES = %w(text/html application/xhtml+xml)
|
18
|
+
|
19
|
+
# Save the options to later pass to InlineStyle.process
|
20
|
+
def initialize(options={})
|
21
|
+
@options = options
|
22
|
+
end
|
23
|
+
|
24
|
+
# Mail callback where we actually inline the styles
|
25
|
+
def delivering_email(part)
|
26
|
+
if part.multipart?
|
27
|
+
for part in part.parts
|
28
|
+
delivering_email part
|
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)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -3,8 +3,8 @@ module InlineStyle
|
|
3
3
|
class Middleware
|
4
4
|
#
|
5
5
|
# Options:
|
6
|
-
# +
|
7
|
-
#
|
6
|
+
# +stylesheets_path+
|
7
|
+
# Stylesheets root path or app's public directory where the stylesheets are to be found, defaults to
|
8
8
|
# env['DOCUMENT_ROOT']
|
9
9
|
#
|
10
10
|
# +paths+
|
@@ -33,7 +33,7 @@ module InlineStyle
|
|
33
33
|
response = ::Rack::Response.new '', status, headers
|
34
34
|
body = content.respond_to?(:body) ? content.body : content
|
35
35
|
|
36
|
-
response.write InlineStyle.process(body, {:
|
36
|
+
response.write InlineStyle.process(body, {:stylesheets_path => env['DOCUMENT_ROOT']}.merge(@opts))
|
37
37
|
response.finish
|
38
38
|
end
|
39
39
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "#{ File.dirname __FILE__ }/spec_helper"
|
2
|
+
require "#{ DIR }/../lib/inline-style/css_parsers/css_parser"
|
3
|
+
require "#{ DIR }/../lib/inline-style/css_parsers/csspool"
|
4
|
+
|
5
|
+
describe InlineStyle::CssParsers::CssParser do
|
6
|
+
|
7
|
+
it 'should wrap css_parser' do
|
8
|
+
rs_count = 0
|
9
|
+
sel_count = 0
|
10
|
+
selectors = %w(p b i)
|
11
|
+
decs = ['color: black;', 'color: black;', 'color: green; text-decoration: none;']
|
12
|
+
spes = [1, 1, 1]
|
13
|
+
p = InlineStyle::CssParsers::CssParser.new "p, b {color: black}\ni {color: green; text-decoration: none}"
|
14
|
+
|
15
|
+
p.each_rule_set do |rs|
|
16
|
+
rs_count += 1
|
17
|
+
rs.each_selector do |sel, dec, spe|
|
18
|
+
sel_count += 1
|
19
|
+
sel.should == selectors.shift
|
20
|
+
dec.should == decs.shift
|
21
|
+
spe.should == spes.shift
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
rs_count.should == 2
|
26
|
+
sel_count.should == 3
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
describe InlineStyle::CssParsers::Csspool do
|
32
|
+
|
33
|
+
it 'should wrap csspool' do
|
34
|
+
rs_count = 0
|
35
|
+
sel_count = 0
|
36
|
+
selectors = %w(p b i)
|
37
|
+
decs = ['color: black;', 'color: black;', 'color: green; text-decoration: none;']
|
38
|
+
spes = [1, 1, 1]
|
39
|
+
p = InlineStyle::CssParsers::Csspool.new "p, b {color: black}\ni {color: green; text-decoration: none}"
|
40
|
+
|
41
|
+
p.each_rule_set do |rs|
|
42
|
+
rs_count += 1
|
43
|
+
rs.each_selector do |sel, dec, spe|
|
44
|
+
sel_count += 1
|
45
|
+
sel.should == selectors.shift
|
46
|
+
dec.should == decs.shift
|
47
|
+
spe.should == spes.shift
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
rs_count.should == 2
|
52
|
+
sel_count.should == 3
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,38 @@
|
|
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
|
data/spec/fixtures/inline.html
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2
2
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
3
3
|
<head>
|
4
|
-
<meta
|
4
|
+
<meta content="text/html; charset=utf-8" http-equiv="Content-type">
|
5
5
|
<title> Boletín CENART - November
|
6
6
|
</title>
|
7
7
|
<link href="/print.css?1248460539" rel="stylesheet" media="print" type="text/css">
|
@@ -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;"><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;"><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
|
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "#{ File.dirname __FILE__ }/spec_helper"
|
2
|
+
|
3
|
+
require 'mail'
|
4
|
+
Mail.defaults do
|
5
|
+
delivery_method :test
|
6
|
+
end
|
7
|
+
Mail.register_interceptor \
|
8
|
+
InlineStyle::Mail::Interceptor.new(:stylesheets_path => FIXTURES)
|
9
|
+
|
10
|
+
describe InlineStyle::Mail::Interceptor do
|
11
|
+
|
12
|
+
before do
|
13
|
+
Mail::TestMailer.deliveries.clear
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should inline html e-mail' do
|
17
|
+
Mail.deliver do
|
18
|
+
to 'joe@example.com'
|
19
|
+
from 'joe@example.com'
|
20
|
+
subject 'HTML e-mail'
|
21
|
+
content_type 'text/html'
|
22
|
+
body File.read("#{ FIXTURES }/boletin.html")
|
23
|
+
end
|
24
|
+
|
25
|
+
Mail::TestMailer.deliveries.first.body.to_s.
|
26
|
+
should == File.read("#{ FIXTURES }/inline.html")
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should not inline non-html' do
|
30
|
+
Mail.deliver do
|
31
|
+
to 'joe@example.com'
|
32
|
+
from 'joe@example.com'
|
33
|
+
subject 'Plain text e-mail (with HTML looking content)'
|
34
|
+
content_type 'text/plain'
|
35
|
+
body File.read("#{ FIXTURES }/boletin.html")
|
36
|
+
end
|
37
|
+
|
38
|
+
Mail::TestMailer.deliveries.first.body.to_s.
|
39
|
+
should == File.read("#{ FIXTURES }/boletin.html")
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should inline html part of multipart/alternative' do
|
43
|
+
Mail.deliver do
|
44
|
+
to 'joe@example.com'
|
45
|
+
from 'joe@example.com'
|
46
|
+
subject 'Multipart/alternative e-mail'
|
47
|
+
text_part do
|
48
|
+
body File.read("#{ FIXTURES }/boletin.html")
|
49
|
+
end
|
50
|
+
html_part do
|
51
|
+
content_type 'text/html; charset=UTF-8'
|
52
|
+
body File.read("#{ FIXTURES }/boletin.html")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Mail::TestMailer.deliveries.first.parts[0].body.to_s.
|
57
|
+
should == File.read("#{ FIXTURES }/boletin.html")
|
58
|
+
|
59
|
+
Mail::TestMailer.deliveries.first.parts[1].body.to_s.
|
60
|
+
should == File.read("#{ FIXTURES }/inline.html")
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -19,8 +19,11 @@ describe InlineStyle::Rack::Middleware do
|
|
19
19
|
get_response('/', @html, :stylesheets_path => FIXTURES).should have_inline_style_for('#A')
|
20
20
|
end
|
21
21
|
|
22
|
+
it "should use external css" do
|
23
|
+
get_response('/', Nokogiri.HTML(@html), :stylesheets_path => FIXTURES).css('#izq').first['style'].should =~ /margin: 30.0px/
|
24
|
+
end
|
25
|
+
|
22
26
|
describe 'Path inclusion' do
|
23
|
-
|
24
27
|
it "should inline style for string path" do
|
25
28
|
paths = "/some/path"
|
26
29
|
get_response('/some/path', @html, :stylesheets_path => FIXTURES, :paths => paths).should have_inline_style_for('#A')
|
@@ -54,4 +57,4 @@ describe InlineStyle::Rack::Middleware do
|
|
54
57
|
get_response('/other/path', @html, :stylesheets_path => FIXTURES, :paths => paths).should_not have_inline_style_for('#A')
|
55
58
|
end
|
56
59
|
end
|
57
|
-
end
|
60
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '
|
2
|
+
require 'rspec'
|
3
3
|
require "#{ DIR = File.dirname(__FILE__) }/../lib/inline-style"
|
4
4
|
require 'rack'
|
5
5
|
require 'rack/mock'
|
6
6
|
|
7
7
|
FIXTURES = "#{ DIR }/fixtures"
|
8
8
|
|
9
|
-
|
10
9
|
module HaveInlineStyleMatcher
|
11
10
|
class HaveInlineStyle
|
12
11
|
def initialize selector
|
@@ -32,4 +31,4 @@ module HaveInlineStyleMatcher
|
|
32
31
|
end
|
33
32
|
end
|
34
33
|
|
35
|
-
|
34
|
+
RSpec.configure { |config| config.include HaveInlineStyleMatcher }
|
metadata
CHANGED
@@ -1,85 +1,143 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inline-style
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 4
|
8
|
+
- 6
|
9
|
+
version: 0.4.6
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Macario Ortega
|
13
|
+
- Eric Anderson
|
8
14
|
autorequire:
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date:
|
18
|
+
date: 2011-03-07 00:00:00 -06:00
|
13
19
|
default_executable:
|
14
20
|
dependencies:
|
15
21
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
20
26
|
requirements:
|
21
27
|
- - ">="
|
22
28
|
- !ruby/object:Gem::Version
|
23
|
-
|
24
|
-
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
25
34
|
- !ruby/object:Gem::Dependency
|
26
|
-
name:
|
27
|
-
|
28
|
-
|
29
|
-
|
35
|
+
name: rack
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
30
39
|
requirements:
|
31
40
|
- - ">="
|
32
41
|
- !ruby/object:Gem::Version
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
name: newgem
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
37
45
|
type: :development
|
38
|
-
|
39
|
-
|
46
|
+
version_requirements: *id002
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec-core
|
49
|
+
prerelease: false
|
50
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
40
52
|
requirements:
|
41
53
|
- - ">="
|
42
54
|
- !ruby/object:Gem::Version
|
43
|
-
|
44
|
-
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id003
|
45
60
|
- !ruby/object:Gem::Dependency
|
46
|
-
name:
|
61
|
+
name: mail
|
62
|
+
prerelease: false
|
63
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
47
71
|
type: :development
|
48
|
-
|
49
|
-
|
72
|
+
version_requirements: *id004
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: nokogiri
|
75
|
+
prerelease: false
|
76
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
50
78
|
requirements:
|
51
79
|
- - ">="
|
52
80
|
- !ruby/object:Gem::Version
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
type: :runtime
|
85
|
+
version_requirements: *id005
|
86
|
+
- !ruby/object:Gem::Dependency
|
87
|
+
name: css_parser
|
88
|
+
prerelease: false
|
89
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
segments:
|
95
|
+
- 0
|
96
|
+
version: "0"
|
97
|
+
type: :runtime
|
98
|
+
version_requirements: *id006
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
name: maca-fork-csspool
|
101
|
+
prerelease: false
|
102
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
segments:
|
108
|
+
- 0
|
109
|
+
version: "0"
|
110
|
+
type: :runtime
|
111
|
+
version_requirements: *id007
|
112
|
+
description: Inlines CSS for html email delivery
|
63
113
|
email:
|
64
114
|
- macarui@gmail.com
|
65
115
|
executables: []
|
66
116
|
|
67
117
|
extensions: []
|
68
118
|
|
69
|
-
extra_rdoc_files:
|
70
|
-
|
71
|
-
- Manifest.txt
|
72
|
-
- README.rdoc
|
119
|
+
extra_rdoc_files: []
|
120
|
+
|
73
121
|
files:
|
122
|
+
- .gitignore
|
123
|
+
- Gemfile
|
124
|
+
- Gemfile.lock
|
74
125
|
- History.txt
|
75
126
|
- Manifest.txt
|
76
|
-
- README.
|
127
|
+
- README.txt
|
77
128
|
- Rakefile
|
78
129
|
- example.rb
|
130
|
+
- inline-style.gemspec
|
79
131
|
- lib/inline-style.rb
|
132
|
+
- lib/inline-style/css_parsers.rb
|
133
|
+
- lib/inline-style/css_parsers/css_parser.rb
|
134
|
+
- lib/inline-style/css_parsers/csspool.rb
|
135
|
+
- lib/inline-style/mail/interceptor.rb
|
80
136
|
- lib/inline-style/rack/middleware.rb
|
81
|
-
-
|
137
|
+
- lib/inline-style/version.rb
|
82
138
|
- spec/css_inlining_spec.rb
|
139
|
+
- spec/css_parsers_spec.rb
|
140
|
+
- spec/factory_spec.rb
|
83
141
|
- spec/fixtures/all.css
|
84
142
|
- spec/fixtures/boletin.html
|
85
143
|
- spec/fixtures/box-model.html
|
@@ -88,35 +146,40 @@ files:
|
|
88
146
|
- spec/fixtures/print.css
|
89
147
|
- spec/fixtures/selectors.html
|
90
148
|
- spec/fixtures/style.css
|
149
|
+
- spec/interceptor_spec.rb
|
150
|
+
- spec/rack_middleware_spec.rb
|
91
151
|
- spec/spec_helper.rb
|
92
152
|
has_rdoc: true
|
93
|
-
homepage:
|
153
|
+
homepage: ""
|
94
154
|
licenses: []
|
95
155
|
|
96
156
|
post_install_message:
|
97
|
-
rdoc_options:
|
98
|
-
|
99
|
-
- README.rdoc
|
157
|
+
rdoc_options: []
|
158
|
+
|
100
159
|
require_paths:
|
101
160
|
- lib
|
102
161
|
required_ruby_version: !ruby/object:Gem::Requirement
|
162
|
+
none: false
|
103
163
|
requirements:
|
104
164
|
- - ">="
|
105
165
|
- !ruby/object:Gem::Version
|
166
|
+
segments:
|
167
|
+
- 0
|
106
168
|
version: "0"
|
107
|
-
version:
|
108
169
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
170
|
+
none: false
|
109
171
|
requirements:
|
110
172
|
- - ">="
|
111
173
|
- !ruby/object:Gem::Version
|
174
|
+
segments:
|
175
|
+
- 0
|
112
176
|
version: "0"
|
113
|
-
version:
|
114
177
|
requirements: []
|
115
178
|
|
116
|
-
rubyforge_project:
|
117
|
-
rubygems_version: 1.3.
|
179
|
+
rubyforge_project:
|
180
|
+
rubygems_version: 1.3.7
|
118
181
|
signing_key:
|
119
182
|
specification_version: 3
|
120
|
-
summary:
|
183
|
+
summary: Inlines CSS for html email delivery
|
121
184
|
test_files: []
|
122
185
|
|