roadie 1.0.0.pre2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -1
- data/.yardopts +1 -0
- data/Gemfile.lock +43 -34
- data/README.textile +4 -0
- data/lib/roadie.rb +22 -13
- data/lib/roadie/action_mailer_extensions.rb +16 -9
- data/lib/roadie/css_file_not_found.rb +12 -0
- data/lib/roadie/inliner.rb +65 -32
- data/lib/roadie/style_declaration.rb +34 -0
- data/lib/roadie/version.rb +1 -1
- data/spec/fixtures/views/integration_mailer/marketing.html.erb +2 -0
- data/spec/integration_spec.rb +16 -0
- data/spec/lib/roadie/action_mailer_extensions_spec.rb +11 -17
- data/spec/lib/roadie/css_file_not_found_spec.rb +13 -0
- data/spec/lib/roadie/inliner_spec.rb +46 -27
- data/spec/lib/roadie/style_declaration_spec.rb +45 -0
- data/spec/lib/roadie_spec.rb +1 -1
- data/spec/spec_helper.rb +3 -68
- data/spec/support/have_attribute_matcher.rb +28 -0
- data/spec/support/have_selector_matcher.rb +6 -0
- data/spec/support/have_styling_matcher.rb +36 -0
- metadata +23 -13
- data/.rspec +0 -2
- data/VERSION +0 -1
data/.gitignore
CHANGED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private
|
data/Gemfile.lock
CHANGED
@@ -10,57 +10,66 @@ GEM
|
|
10
10
|
remote: http://rubygems.org/
|
11
11
|
specs:
|
12
12
|
abstract (1.0.0)
|
13
|
-
actionmailer (3.0.
|
14
|
-
actionpack (= 3.0.
|
15
|
-
mail (~> 2.2.
|
16
|
-
actionpack (3.0.
|
17
|
-
activemodel (= 3.0.
|
18
|
-
activesupport (= 3.0.
|
13
|
+
actionmailer (3.0.3)
|
14
|
+
actionpack (= 3.0.3)
|
15
|
+
mail (~> 2.2.9)
|
16
|
+
actionpack (3.0.3)
|
17
|
+
activemodel (= 3.0.3)
|
18
|
+
activesupport (= 3.0.3)
|
19
19
|
builder (~> 2.1.2)
|
20
20
|
erubis (~> 2.6.6)
|
21
|
-
i18n (~> 0.4
|
21
|
+
i18n (~> 0.4)
|
22
22
|
rack (~> 1.2.1)
|
23
|
-
rack-mount (~> 0.6.
|
24
|
-
rack-test (~> 0.5.
|
23
|
+
rack-mount (~> 0.6.13)
|
24
|
+
rack-test (~> 0.5.6)
|
25
25
|
tzinfo (~> 0.3.23)
|
26
|
-
activemodel (3.0.
|
27
|
-
activesupport (= 3.0.
|
26
|
+
activemodel (3.0.3)
|
27
|
+
activesupport (= 3.0.3)
|
28
28
|
builder (~> 2.1.2)
|
29
|
-
i18n (~> 0.4
|
30
|
-
activesupport (3.0.
|
29
|
+
i18n (~> 0.4)
|
30
|
+
activesupport (3.0.3)
|
31
31
|
builder (2.1.2)
|
32
|
-
css_parser (1.
|
32
|
+
css_parser (1.1.5)
|
33
33
|
diff-lcs (1.1.2)
|
34
34
|
erubis (2.6.6)
|
35
35
|
abstract (>= 1.0.0)
|
36
|
-
i18n (0.
|
37
|
-
mail (2.2.
|
36
|
+
i18n (0.5.0)
|
37
|
+
mail (2.2.15)
|
38
38
|
activesupport (>= 2.3.6)
|
39
|
-
|
40
|
-
|
39
|
+
i18n (>= 0.4.0)
|
40
|
+
mime-types (~> 1.16)
|
41
|
+
treetop (~> 1.4.8)
|
41
42
|
mime-types (1.16)
|
42
|
-
nokogiri (1.4.
|
43
|
+
nokogiri (1.4.4)
|
43
44
|
polyglot (0.3.1)
|
44
45
|
rack (1.2.1)
|
45
46
|
rack-mount (0.6.13)
|
46
47
|
rack (>= 1.0.0)
|
47
|
-
rack-test (0.5.
|
48
|
+
rack-test (0.5.7)
|
48
49
|
rack (>= 1.0)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
rspec-
|
58
|
-
rspec-
|
59
|
-
rspec-
|
60
|
-
|
61
|
-
|
50
|
+
railties (3.0.3)
|
51
|
+
actionpack (= 3.0.3)
|
52
|
+
activesupport (= 3.0.3)
|
53
|
+
rake (>= 0.8.7)
|
54
|
+
thor (~> 0.14.4)
|
55
|
+
rake (0.8.7)
|
56
|
+
rspec (2.5.0)
|
57
|
+
rspec-core (~> 2.5.0)
|
58
|
+
rspec-expectations (~> 2.5.0)
|
59
|
+
rspec-mocks (~> 2.5.0)
|
60
|
+
rspec-core (2.5.1)
|
61
|
+
rspec-expectations (2.5.0)
|
62
|
+
diff-lcs (~> 1.1.2)
|
63
|
+
rspec-mocks (2.5.0)
|
64
|
+
rspec-rails (2.5.0)
|
65
|
+
actionpack (~> 3.0)
|
66
|
+
activesupport (~> 3.0)
|
67
|
+
railties (~> 3.0)
|
68
|
+
rspec (~> 2.5.0)
|
69
|
+
thor (0.14.6)
|
70
|
+
treetop (1.4.9)
|
62
71
|
polyglot (>= 0.3.1)
|
63
|
-
tzinfo (0.3.
|
72
|
+
tzinfo (0.3.24)
|
64
73
|
|
65
74
|
PLATFORMS
|
66
75
|
ruby
|
data/README.textile
CHANGED
@@ -73,6 +73,10 @@ You can set a special <code>data-immutable="true"</code> attribute on @style@ ta
|
|
73
73
|
|
74
74
|
Style elements with <code>media="print"</code> are always ignored.
|
75
75
|
|
76
|
+
h2. Documentation
|
77
|
+
|
78
|
+
* "Online documentation for master":http://rubydoc.info/github/Mange/roadie/master/frames
|
79
|
+
|
76
80
|
h2. Bugs / TODO
|
77
81
|
|
78
82
|
* Improve overall performance
|
data/lib/roadie.rb
CHANGED
@@ -1,26 +1,35 @@
|
|
1
1
|
module Roadie
|
2
|
-
|
3
|
-
|
4
|
-
def self.inline_css(*args)
|
2
|
+
# Shortcut for inlining CSS using {Inliner}
|
3
|
+
# @see Inliner
|
4
|
+
def self.inline_css(*args)
|
5
5
|
Roadie::Inliner.new(*args).execute
|
6
6
|
end
|
7
7
|
|
8
|
+
# Tries to load the CSS "names" specified in the +targets+ parameter inside the +root+ path.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# Roadie.load_css(Rails.root, %w[application newsletter])
|
12
|
+
#
|
13
|
+
# @param [Pathname] root The root path of your stylesheets
|
14
|
+
# @param [Array<String, Symbol>] targets Stylesheet names - <b>without extensions</b>
|
15
|
+
# @return [String] The combined contents of the CSS files
|
16
|
+
# @raise [CSSFileNotFound] When a target cannot be found under +[root]/[target].css+
|
8
17
|
def self.load_css(root, targets)
|
9
|
-
|
10
|
-
|
18
|
+
css_files_from_targets(root, targets).map do |file|
|
19
|
+
raise CSSFileNotFound, file unless file.exist?
|
20
|
+
file.read
|
21
|
+
end.join("\n")
|
22
|
+
end
|
11
23
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
else
|
16
|
-
raise CSSFileNotFound, "Could not find #{target_file}"
|
17
|
-
end
|
24
|
+
private
|
25
|
+
def self.css_files_from_targets(root, targets)
|
26
|
+
targets.map { |target| root.join("#{target}.css") }
|
18
27
|
end
|
19
|
-
loaded_css.join("\n")
|
20
|
-
end
|
21
28
|
end
|
22
29
|
|
23
30
|
require 'roadie/version'
|
31
|
+
require 'roadie/css_file_not_found'
|
32
|
+
require 'roadie/style_declaration'
|
24
33
|
require 'roadie/inliner'
|
25
34
|
|
26
35
|
require 'action_mailer'
|
@@ -3,6 +3,9 @@ require 'nokogiri'
|
|
3
3
|
require 'css_parser'
|
4
4
|
|
5
5
|
module Roadie
|
6
|
+
# This module adds the Roadie functionality to ActionMailer 3 when included in ActionMailer::Base.
|
7
|
+
#
|
8
|
+
# If you want to add Roadie to any other mail framework, take a look at how this module is implemented.
|
6
9
|
module ActionMailerExtensions
|
7
10
|
def self.included(base)
|
8
11
|
base.class_eval do
|
@@ -14,24 +17,28 @@ module Roadie
|
|
14
17
|
protected
|
15
18
|
def mail_with_inline_styles(headers = {}, &block)
|
16
19
|
@inline_style_css_targets = headers[:css]
|
17
|
-
mail_without_inline_styles(headers
|
18
|
-
email
|
20
|
+
mail_without_inline_styles(headers, &block).tap do |email|
|
21
|
+
email.header.fields.delete_if { |field| field.name == 'css' }
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
22
25
|
def collect_responses_and_parts_order_with_inline_styles(headers, &block)
|
23
26
|
responses, order = collect_responses_and_parts_order_without_inline_styles(headers, &block)
|
24
|
-
|
25
|
-
responses.each do |response|
|
26
|
-
new_responses << inline_style_response(response)
|
27
|
-
end
|
28
|
-
[new_responses, order]
|
27
|
+
[responses.map { |response| inline_style_response(response) }, order]
|
29
28
|
end
|
30
29
|
|
31
30
|
private
|
31
|
+
def url_options
|
32
|
+
Rails.application.config.action_mailer.default_url_options
|
33
|
+
end
|
34
|
+
|
35
|
+
def stylesheet_root
|
36
|
+
Rails.root.join('public', 'stylesheets')
|
37
|
+
end
|
38
|
+
|
32
39
|
def inline_style_response(response)
|
33
40
|
if response[:content_type] == 'text/html'
|
34
|
-
response.merge :body => Roadie.inline_css(css_rules, response[:body],
|
41
|
+
response.merge :body => Roadie.inline_css(css_rules, response[:body], url_options)
|
35
42
|
else
|
36
43
|
response
|
37
44
|
end
|
@@ -43,7 +50,7 @@ module Roadie
|
|
43
50
|
end
|
44
51
|
|
45
52
|
def css_rules
|
46
|
-
@css_rules ||= Roadie.load_css(
|
53
|
+
@css_rules ||= Roadie.load_css(stylesheet_root, css_targets) if css_targets.present?
|
47
54
|
end
|
48
55
|
end
|
49
56
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Roadie
|
2
|
+
# Raised when a stylesheet specified for inlining is not present.
|
3
|
+
# You can access the target filename via #filename.
|
4
|
+
class CSSFileNotFound < StandardError
|
5
|
+
attr_reader :filename
|
6
|
+
|
7
|
+
def initialize(filename)
|
8
|
+
@filename = filename
|
9
|
+
super("Could not find #{filename}")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/roadie/inliner.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Roadie
|
4
|
+
# This class is the core of Roadie as it does all the actual work. You just give it
|
5
|
+
# the CSS rules, the HTML and the url_options for rewriting URLs and let it go on
|
6
|
+
# doing all the heavy lifting and building.
|
2
7
|
class Inliner
|
8
|
+
# Regexp matching all the url() declarations in CSS
|
9
|
+
#
|
10
|
+
# It matches without any quotes and with both single and double quotes
|
11
|
+
# inside the parenthesis. There's much room for improvement, of course.
|
3
12
|
CSS_URL_REGEXP = %r{
|
4
13
|
url\(
|
5
14
|
(["']?)
|
@@ -8,12 +17,15 @@ module Roadie
|
|
8
17
|
(?:\([^)]*\))* # Texts containing parens pairs
|
9
18
|
[^(]+ # Texts without parens - required
|
10
19
|
)
|
11
|
-
\1
|
20
|
+
\1 # Closing quote
|
12
21
|
\)
|
13
22
|
}x
|
14
23
|
|
15
|
-
|
16
|
-
|
24
|
+
# Initialize a new Inliner with the given CSS, HTML and url_options.
|
25
|
+
#
|
26
|
+
# @param [String] css
|
27
|
+
# @param [String] html
|
28
|
+
# @param [Hash] url_options Supported keys: +:host+, +:port+ and +:protocol+
|
17
29
|
def initialize(css, html, url_options)
|
18
30
|
@css = css
|
19
31
|
@inline_css = []
|
@@ -21,17 +33,23 @@ module Roadie
|
|
21
33
|
@url_options = url_options
|
22
34
|
end
|
23
35
|
|
36
|
+
# Start the inlining and return the final HTML output
|
37
|
+
# @return [String]
|
24
38
|
def execute
|
25
39
|
adjust_html do |document|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
40
|
+
@document = document
|
41
|
+
add_missing_structure
|
42
|
+
extract_inline_style_elements
|
43
|
+
inline_css_rules
|
44
|
+
make_image_urls_absolute
|
45
|
+
make_style_urls_absolute
|
46
|
+
@document = nil
|
31
47
|
end
|
32
48
|
end
|
33
49
|
|
34
50
|
private
|
51
|
+
attr_reader :css, :html, :url_options, :document
|
52
|
+
|
35
53
|
def inline_css
|
36
54
|
@inline_css.join("\n")
|
37
55
|
end
|
@@ -49,7 +67,7 @@ module Roadie
|
|
49
67
|
end.to_html
|
50
68
|
end
|
51
69
|
|
52
|
-
def add_missing_structure
|
70
|
+
def add_missing_structure
|
53
71
|
html_node = document.at_css('html')
|
54
72
|
html_node['xmlns'] ||= 'http://www.w3.org/1999/xhtml'
|
55
73
|
|
@@ -68,7 +86,7 @@ module Roadie
|
|
68
86
|
end
|
69
87
|
end
|
70
88
|
|
71
|
-
def extract_inline_style_elements
|
89
|
+
def extract_inline_style_elements
|
72
90
|
document.css("style").each do |style|
|
73
91
|
next if style['media'] == 'print' or style['data-immutable']
|
74
92
|
@inline_css << style.content
|
@@ -76,45 +94,60 @@ module Roadie
|
|
76
94
|
end
|
77
95
|
end
|
78
96
|
|
79
|
-
def inline_css_rules
|
80
|
-
|
81
|
-
|
97
|
+
def inline_css_rules
|
98
|
+
elements_with_declarations.each do |element, declarations|
|
99
|
+
ordered_declarations = []
|
100
|
+
seen_properties = Set.new
|
101
|
+
declarations.sort.reverse_each do |declaration|
|
102
|
+
next if seen_properties.include?(declaration.property)
|
103
|
+
ordered_declarations.unshift(declaration)
|
104
|
+
seen_properties << declaration.property
|
105
|
+
end
|
82
106
|
|
83
|
-
|
84
|
-
|
85
|
-
element['style'] = [rules_string, element['style']].compact.join('; ')
|
107
|
+
rules_string = ordered_declarations.map { |declaration| declaration.to_s }.join(';')
|
108
|
+
element['style'] = [rules_string, element['style']].compact.join(';')
|
86
109
|
end
|
87
110
|
end
|
88
111
|
|
89
|
-
def
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
112
|
+
def elements_with_declarations
|
113
|
+
Hash.new { |hash, key| hash[key] = [] }.tap do |element_declarations|
|
114
|
+
parsed_css.each_rule_set do |rule_set|
|
115
|
+
each_selector_without_psuedo(rule_set) do |selector, specificity|
|
116
|
+
each_element_in_selector(selector) do |element|
|
117
|
+
style_declarations_in_rule_set(specificity, rule_set) do |declaration|
|
118
|
+
element_declarations[element] << declaration
|
119
|
+
end
|
120
|
+
end
|
94
121
|
end
|
95
122
|
end
|
96
123
|
end
|
97
124
|
end
|
98
125
|
|
99
|
-
def
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
126
|
+
def each_selector_without_psuedo(rules)
|
127
|
+
rules.selectors.reject { |selector| selector.include?(':') }.each do |selector|
|
128
|
+
yield selector, CssParser.calculate_specificity(selector)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def each_element_in_selector(selector)
|
133
|
+
document.css(selector.strip).each do |element|
|
134
|
+
yield element
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def style_declarations_in_rule_set(specificity, rule_set)
|
139
|
+
rule_set.each_declaration do |property, value, important|
|
140
|
+
yield StyleDeclaration.new(property, value, important, specificity)
|
108
141
|
end
|
109
142
|
end
|
110
143
|
|
111
|
-
def make_image_urls_absolute
|
144
|
+
def make_image_urls_absolute
|
112
145
|
document.css('img').each do |img|
|
113
146
|
img['src'] = ensure_absolute_url(img['src']) if img['src']
|
114
147
|
end
|
115
148
|
end
|
116
149
|
|
117
|
-
def make_style_urls_absolute
|
150
|
+
def make_style_urls_absolute
|
118
151
|
document.css('*[style]').each do |element|
|
119
152
|
styling = element['style']
|
120
153
|
element['style'] = styling.gsub(CSS_URL_REGEXP) { "url(#{$1}#{ensure_absolute_url($2, '/stylesheets')}#{$1})" }
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Roadie
|
2
|
+
class StyleDeclaration
|
3
|
+
include Comparable
|
4
|
+
attr_reader :property, :value, :important, :specificity
|
5
|
+
|
6
|
+
def initialize(property, value, important, specificity)
|
7
|
+
@property = property
|
8
|
+
@value = value
|
9
|
+
@important = important
|
10
|
+
@specificity = specificity
|
11
|
+
end
|
12
|
+
|
13
|
+
def important?
|
14
|
+
@important
|
15
|
+
end
|
16
|
+
|
17
|
+
def <=>(other)
|
18
|
+
if important == other.important
|
19
|
+
specificity <=> other.specificity
|
20
|
+
else
|
21
|
+
important ? 1 : -1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
[property, value].join(':')
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
extra = [important ? '!important' : nil, specificity].compact
|
31
|
+
"#{to_s} (#{extra.join(' , ')})"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/roadie/version.rb
CHANGED
data/spec/integration_spec.rb
CHANGED
@@ -15,6 +15,11 @@ describe "roadie integration" do
|
|
15
15
|
@reason = reason
|
16
16
|
mail(:subject => 'Notification for you', :to => to) { |format| format.html; format.text }
|
17
17
|
end
|
18
|
+
|
19
|
+
def marketing(to)
|
20
|
+
headers('X-Spam' => 'No way! Trust us!')
|
21
|
+
mail(:subject => 'Buy cheap v1agra', :to => to)
|
22
|
+
end
|
18
23
|
end
|
19
24
|
|
20
25
|
before(:each) do
|
@@ -41,6 +46,17 @@ describe "roadie integration" do
|
|
41
46
|
plain_part.body.decoded.should_not match(/<.*>/)
|
42
47
|
end
|
43
48
|
|
49
|
+
# If we deliver mails we can catch weird problems with headers being invalid
|
44
50
|
email.deliver
|
45
51
|
end
|
52
|
+
|
53
|
+
it "should not add headers for the roadie options" do
|
54
|
+
email = IntegrationMailer.notification('doe@example.com', 'no berries left in chest')
|
55
|
+
email.header.fields.map(&:name).should_not include('css')
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should keep custom headers in place" do
|
59
|
+
email = IntegrationMailer.marketing('everyone@inter.net')
|
60
|
+
email.header['X-Spam'].should be_present
|
61
|
+
end
|
46
62
|
end
|
@@ -12,17 +12,6 @@ describe Roadie::ActionMailerExtensions, "inlining styles" do
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
# Not sure how to implement this one.
|
16
|
-
# TODO: Either remove or implement
|
17
|
-
def nested_multipart_mixed(css_file = nil)
|
18
|
-
raise "Nested multipart mixed is not implemented"
|
19
|
-
content_type "multipart/mixed"
|
20
|
-
part :content_type => "multipart/alternative", :content_disposition => "inline" do |p|
|
21
|
-
p.part :content_type => 'text/html', :body => 'Hello HTML'
|
22
|
-
p.part :content_type => 'text/plain', :body => 'Hello Text'
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
15
|
def singlepart_html
|
27
16
|
mail(:subject => "HTML email") do |format|
|
28
17
|
format.html { render :text => 'Hello HTML' }
|
@@ -38,7 +27,7 @@ describe Roadie::ActionMailerExtensions, "inlining styles" do
|
|
38
27
|
|
39
28
|
before(:each) do
|
40
29
|
Roadie.stub!(:load_css => 'loaded css')
|
41
|
-
Roadie.stub!(:inline_css => 'unexpected value
|
30
|
+
Roadie.stub!(:inline_css => 'unexpected value passed to inline_css')
|
42
31
|
end
|
43
32
|
|
44
33
|
describe "for singlepart text/plain" do
|
@@ -63,8 +52,8 @@ describe Roadie::ActionMailerExtensions, "inlining styles" do
|
|
63
52
|
it "should inline css to the email's html part" do
|
64
53
|
Roadie.should_receive(:inline_css).with(anything, 'Hello HTML', anything).and_return('html')
|
65
54
|
email = InliningMailer.multipart
|
66
|
-
email.
|
67
|
-
email.
|
55
|
+
email.html_part.body.decoded.should == 'html'
|
56
|
+
email.text_part.body.decoded.should == 'Hello Text'
|
68
57
|
end
|
69
58
|
end
|
70
59
|
end
|
@@ -90,13 +79,18 @@ describe Roadie::ActionMailerExtensions, "loading css files" do
|
|
90
79
|
Roadie.stub!(:inline_css => 'html')
|
91
80
|
end
|
92
81
|
|
82
|
+
it "should load css from Rails' stylesheet root" do
|
83
|
+
Roadie.should_receive(:load_css).with(Rails.root.join('public', 'stylesheets'), anything).and_return('')
|
84
|
+
CssLoadingMailer.use_default
|
85
|
+
end
|
86
|
+
|
93
87
|
it "should load the css specified in the default mailer settings" do
|
94
|
-
Roadie.should_receive(:load_css).with(
|
88
|
+
Roadie.should_receive(:load_css).with(anything, ['default_value']).and_return('')
|
95
89
|
CssLoadingMailer.use_default
|
96
90
|
end
|
97
91
|
|
98
92
|
it "should load the css specified in the specific mailer action instead of the default choice" do
|
99
|
-
Roadie.should_receive(:load_css).with(
|
93
|
+
Roadie.should_receive(:load_css).with(anything, ['specific']).and_return('')
|
100
94
|
CssLoadingMailer.override(:specific)
|
101
95
|
end
|
102
96
|
|
@@ -106,7 +100,7 @@ describe Roadie::ActionMailerExtensions, "loading css files" do
|
|
106
100
|
end
|
107
101
|
|
108
102
|
it "should load multiple css files when given an array" do
|
109
|
-
Roadie.should_receive(:load_css).with(
|
103
|
+
Roadie.should_receive(:load_css).with(anything, ['specific', 'other']).and_return('')
|
110
104
|
CssLoadingMailer.override([:specific, :other])
|
111
105
|
end
|
112
106
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Roadie
|
4
|
+
describe CSSFileNotFound do
|
5
|
+
it "is initialized with a filename" do
|
6
|
+
CSSFileNotFound.new('file.css').filename.should == 'file.css'
|
7
|
+
end
|
8
|
+
|
9
|
+
it "has a message" do
|
10
|
+
CSSFileNotFound.new('style.css').message.should == 'Could not find style.css'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -15,19 +15,38 @@ describe Roadie::Inliner do
|
|
15
15
|
|
16
16
|
it "should inline simple attributes" do
|
17
17
|
use_css 'p { color: green }'
|
18
|
-
rendering('<p></p>').should have_styling('color' => 'green')
|
18
|
+
rendering('<p></p>').should have_styling('color' => 'green')
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should keep the order of the styles that was inlined" do
|
22
|
+
use_css 'h1 { padding: 2px; margin: 5px; }'
|
23
|
+
rendering('<h1></h1>').should have_styling([['padding', '2px'], ['margin', '5px']])
|
19
24
|
end
|
20
25
|
|
21
26
|
it "should combine multiple selectors into one" do
|
22
|
-
use_css
|
23
|
-
.tip { float: right; }
|
24
|
-
rendering('<p class="tip"></p>').should have_styling('color' => 'green', 'float' => 'right')
|
27
|
+
use_css 'p { color: green; }
|
28
|
+
.tip { float: right; }'
|
29
|
+
rendering('<p class="tip"></p>').should have_styling('color' => 'green', 'float' => 'right')
|
25
30
|
end
|
26
31
|
|
27
32
|
it "should use the ones attributes with the highest specificality when conflicts arises" do
|
28
33
|
use_css "p { color: red; }
|
29
|
-
.safe { color: green;
|
30
|
-
rendering('<p class="safe"></p>').should have_styling('color' => 'green'
|
34
|
+
.safe { color: green; }"
|
35
|
+
rendering('<p class="safe"></p>').should have_styling('color' => 'green')
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should sort styles by specificity order" do
|
39
|
+
use_css 'p { margin: 2px; }
|
40
|
+
#big { margin: 10px; }
|
41
|
+
.down { margin-bottom: 5px; }'
|
42
|
+
|
43
|
+
rendering('<p class="down"></p>').should have_styling([
|
44
|
+
['margin', '2px'], ['margin-bottom', '5px']
|
45
|
+
])
|
46
|
+
|
47
|
+
rendering('<p class="down" id="big"></p>').should have_styling([
|
48
|
+
['margin-bottom', '5px'], ['margin', '10px']
|
49
|
+
])
|
31
50
|
end
|
32
51
|
|
33
52
|
it "should support multiple selectors for the same rules" do
|
@@ -41,27 +60,27 @@ describe Roadie::Inliner do
|
|
41
60
|
it "should respect !important properties" do
|
42
61
|
use_css "a { text-decoration: underline !important; }
|
43
62
|
a.hard-to-spot { text-decoration: none; }"
|
44
|
-
rendering('<a class="hard-to-spot"></a>').should have_styling('text-decoration' => 'underline')
|
63
|
+
rendering('<a class="hard-to-spot"></a>').should have_styling('text-decoration' => 'underline')
|
45
64
|
end
|
46
65
|
|
47
66
|
it "should combine with already present inline styles" do
|
48
67
|
use_css "p { color: green }"
|
49
|
-
rendering('<p style="font-size: 1.1em"></p>').should have_styling('color'
|
68
|
+
rendering('<p style="font-size: 1.1em"></p>').should have_styling([['color', 'green'], ['font-size', '1.1em']])
|
50
69
|
end
|
51
70
|
|
52
|
-
it "should not
|
71
|
+
it "should not touch already present inline styles" do
|
53
72
|
use_css "p { color: red }"
|
54
|
-
rendering('<p style="color: green"></p>').should have_styling('color'
|
73
|
+
rendering('<p style="color: green"></p>').should have_styling([['color', 'red'], ['color', 'green']])
|
55
74
|
end
|
56
75
|
|
57
76
|
it "should ignore selectors with :psuedo-classes" do
|
58
77
|
use_css 'p:hover { color: red }'
|
59
|
-
rendering('<p></p>').should_not have_styling('color' => 'red')
|
78
|
+
rendering('<p></p>').should_not have_styling('color' => 'red')
|
60
79
|
end
|
61
80
|
|
62
81
|
describe "inline <style> elements" do
|
63
82
|
it "should be used for inlined styles" do
|
64
|
-
rendering(<<-HTML).should have_styling('color'
|
83
|
+
rendering(<<-HTML).should have_styling([['color', 'green'], ['font-size', '1.1em']])
|
65
84
|
<html>
|
66
85
|
<head>
|
67
86
|
<style type="text/css">p { color: green; }</style>
|
@@ -93,7 +112,7 @@ describe Roadie::Inliner do
|
|
93
112
|
<p></p>
|
94
113
|
HTML
|
95
114
|
document.should have_selector('style[data-immutable=true]')
|
96
|
-
document.should_not have_styling('color' => 'red')
|
115
|
+
document.should_not have_styling('color' => 'red')
|
97
116
|
end
|
98
117
|
|
99
118
|
it "should not be touched when media=print" do
|
@@ -117,41 +136,41 @@ describe Roadie::Inliner do
|
|
117
136
|
|
118
137
|
describe "making urls absolute" do
|
119
138
|
it "should work on image sources" do
|
120
|
-
rendering('<img src="/images/foo.jpg" />').should have_attribute('src' => 'http://example.com/images/foo.jpg')
|
121
|
-
rendering('<img src="../images/foo.jpg" />').should have_attribute('src' => 'http://example.com/images/foo.jpg')
|
122
|
-
rendering('<img src="foo.jpg" />').should have_attribute('src' => 'http://example.com/foo.jpg')
|
139
|
+
rendering('<img src="/images/foo.jpg" />').should have_attribute('src' => 'http://example.com/images/foo.jpg')
|
140
|
+
rendering('<img src="../images/foo.jpg" />').should have_attribute('src' => 'http://example.com/images/foo.jpg')
|
141
|
+
rendering('<img src="foo.jpg" />').should have_attribute('src' => 'http://example.com/foo.jpg')
|
123
142
|
end
|
124
143
|
|
125
144
|
it "should not touch image sources that are already absolute" do
|
126
|
-
rendering('<img src="http://other.example.org/images/foo.jpg" />').should have_attribute('src' => 'http://other.example.org/images/foo.jpg')
|
145
|
+
rendering('<img src="http://other.example.org/images/foo.jpg" />').should have_attribute('src' => 'http://other.example.org/images/foo.jpg')
|
127
146
|
end
|
128
147
|
|
129
148
|
it "should work on inlined style attributes" do
|
130
|
-
rendering('<p style="background: url(/paper.png)"></p>').should have_styling('background' => 'url(http://example.com/paper.png)')
|
131
|
-
rendering('<p style="background: url("/paper.png")"></p>').should have_styling('background' => 'url("http://example.com/paper.png")')
|
149
|
+
rendering('<p style="background: url(/paper.png)"></p>').should have_styling('background' => 'url(http://example.com/paper.png)')
|
150
|
+
rendering('<p style="background: url("/paper.png")"></p>').should have_styling('background' => 'url("http://example.com/paper.png")')
|
132
151
|
end
|
133
152
|
|
134
153
|
it "should work on external style declarations" do
|
135
154
|
use_css "p { background-image: url(/paper.png); }
|
136
155
|
table { background-image: url('/paper.png'); }
|
137
156
|
div { background-image: url(\"/paper.png\"); }"
|
138
|
-
rendering('<p></p>').should have_styling('background-image' => 'url(http://example.com/paper.png)')
|
139
|
-
rendering('<table></table>').should have_styling('background-image' => "url('http://example.com/paper.png')")
|
140
|
-
rendering('<div></div>').should have_styling('background-image' => 'url("http://example.com/paper.png")')
|
157
|
+
rendering('<p></p>').should have_styling('background-image' => 'url(http://example.com/paper.png)')
|
158
|
+
rendering('<table></table>').should have_styling('background-image' => "url('http://example.com/paper.png')")
|
159
|
+
rendering('<div></div>').should have_styling('background-image' => 'url("http://example.com/paper.png")')
|
141
160
|
end
|
142
161
|
|
143
162
|
it "should not touch style urls that are already absolute" do
|
144
163
|
external_url = 'url(http://other.example.org/paper.png)'
|
145
164
|
use_css "p { background-image: #{external_url}; }"
|
146
|
-
rendering('<p></p>').should have_styling('background-image' => external_url)
|
147
|
-
rendering(%(<div style="background-image: #{external_url}"></div>)).should have_styling('background-image' => external_url)
|
165
|
+
rendering('<p></p>').should have_styling('background-image' => external_url)
|
166
|
+
rendering(%(<div style="background-image: #{external_url}"></div>)).should have_styling('background-image' => external_url)
|
148
167
|
end
|
149
168
|
|
150
169
|
it "should not touch the urls when no url options are defined" do
|
151
170
|
use_css "img { background: url(/a.jpg); }"
|
152
171
|
rendering('<img src="/b.jpg" />', :url_options => nil).tap do |document|
|
153
172
|
document.should have_attribute('src' => '/b.jpg').at_selector('img')
|
154
|
-
document.should have_styling('background' => 'url(/a.jpg)')
|
173
|
+
document.should have_styling('background' => 'url(/a.jpg)')
|
155
174
|
end
|
156
175
|
end
|
157
176
|
|
@@ -159,13 +178,13 @@ describe Roadie::Inliner do
|
|
159
178
|
use_css "img { background: url(/a.jpg); }"
|
160
179
|
rendering('<img src="/b.jpg" />', :url_options => {:host => 'example.com', :protocol => 'https', :port => '8080'}).tap do |document|
|
161
180
|
document.should have_attribute('src' => 'https://example.com:8080/b.jpg').at_selector('img')
|
162
|
-
document.should have_styling('background' => 'url(https://example.com:8080/a.jpg)')
|
181
|
+
document.should have_styling('background' => 'url(https://example.com:8080/a.jpg)')
|
163
182
|
end
|
164
183
|
end
|
165
184
|
|
166
185
|
it "should not touch data: URIs" do
|
167
186
|
use_css "div { background: url(data:abcdef); }"
|
168
|
-
rendering('<div></div>').should have_styling('background' => 'url(data:abcdef)')
|
187
|
+
rendering('<div></div>').should have_styling('background' => 'url(data:abcdef)')
|
169
188
|
end
|
170
189
|
end
|
171
190
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Roadie
|
4
|
+
describe StyleDeclaration do
|
5
|
+
it "should be initialized with a property, value, if it is marked as important, and the specificity" do
|
6
|
+
StyleDeclaration.new('color', 'green', true, 45).tap do |declaration|
|
7
|
+
declaration.property.should == 'color'
|
8
|
+
declaration.value.should == 'green'
|
9
|
+
declaration.should be_important
|
10
|
+
declaration.specificity.should == 45
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "string representation" do
|
15
|
+
it "should be the property and the value joined with a colon" do
|
16
|
+
StyleDeclaration.new('color', 'green', false, 1).to_s.should == 'color:green'
|
17
|
+
StyleDeclaration.new('font-size', '1.1em', false, 1).to_s.should == 'font-size:1.1em'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "comparing" do
|
22
|
+
def declaration(specificity, important = false)
|
23
|
+
StyleDeclaration.new('color', 'green', important, specificity)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should compare on specificity" do
|
27
|
+
declaration(5).should be == declaration(5)
|
28
|
+
declaration(4).should be < declaration(5)
|
29
|
+
declaration(6).should be > declaration(5)
|
30
|
+
end
|
31
|
+
|
32
|
+
context "with an important declaration" do
|
33
|
+
it "should be less than the important declaration regardless of the specificity" do
|
34
|
+
declaration(99, false).should be < declaration(1, true)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should compare like normal when both declarations are important" do
|
38
|
+
declaration(5, true).should be == declaration(5, true)
|
39
|
+
declaration(4, true).should be < declaration(5, true)
|
40
|
+
declaration(6, true).should be > declaration(5, true)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/spec/lib/roadie_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe Roadie do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
describe ".load_css(root, targets)" do
|
12
|
-
let(:fixtures_root) { Pathname.new(__FILE__).dirname.join('..', 'fixtures') }
|
12
|
+
let(:fixtures_root) { Pathname.new(__FILE__).dirname.join('..', 'fixtures', 'public', 'stylesheets') }
|
13
13
|
|
14
14
|
it "should load files matching the target names under root/public/stylesheets" do
|
15
15
|
Roadie.load_css(fixtures_root, ['foo']).should == 'contents of foo'
|
data/spec/spec_helper.rb
CHANGED
@@ -13,6 +13,9 @@ rescue Bundler::BundlerError => e
|
|
13
13
|
end
|
14
14
|
|
15
15
|
require 'rspec'
|
16
|
+
|
17
|
+
Dir['./spec/support/**/*'].each { |file| require file }
|
18
|
+
|
16
19
|
require 'action_mailer'
|
17
20
|
require 'roadie'
|
18
21
|
|
@@ -31,71 +34,3 @@ else
|
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
34
|
-
RSpec::Matchers.define :have_styling do |rules|
|
35
|
-
chain :at_selector do |selector|
|
36
|
-
@selector = selector
|
37
|
-
end
|
38
|
-
|
39
|
-
match do |document|
|
40
|
-
styles = parsed_styles(document)
|
41
|
-
if rules.nil?
|
42
|
-
styles.blank?
|
43
|
-
else
|
44
|
-
rules.stringify_keys.should == parsed_styles(document)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
describe { "have styles #{rules.inspect} at selector #{@selector.inspect}" }
|
49
|
-
failure_message_for_should { |document| "expected styles at #{@selector.inspect} to be #{rules.inspect} but was #{parsed_styles(document).inspect}" }
|
50
|
-
failure_message_for_should_not { "expected styles at #{@selector.inspect} to not be #{rules.inspect}" }
|
51
|
-
|
52
|
-
def element_styles(document)
|
53
|
-
node = document.css(@selector).first
|
54
|
-
node && node['style']
|
55
|
-
end
|
56
|
-
|
57
|
-
def parsed_styles(document)
|
58
|
-
return @parsed_styles if defined?(@parsed_styles)
|
59
|
-
if (styles = element_styles(document)).present?
|
60
|
-
@parsed_styles = styles.split(';').inject({}) do |styles, item|
|
61
|
-
attribute, value = item.split(':', 2)
|
62
|
-
styles.merge!(attribute.strip => value.strip)
|
63
|
-
end
|
64
|
-
else
|
65
|
-
@parsed_styles = nil
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
RSpec::Matchers.define :have_attribute do |attribute|
|
71
|
-
chain :at_selector do |selector|
|
72
|
-
@selector = selector
|
73
|
-
end
|
74
|
-
|
75
|
-
match do |document|
|
76
|
-
name, expected = attribute.first
|
77
|
-
expected == attribute(document, name)
|
78
|
-
end
|
79
|
-
|
80
|
-
describe { "have attribute #{attribute.inspect} at selector #{@selector.inspect}" }
|
81
|
-
failure_message_for_should do |document|
|
82
|
-
name, expected = attribute.first
|
83
|
-
"expected #{name} attribute at #{@selector.inspect} to be #{expected.inspect} but was #{attribute(document, name).inspect}"
|
84
|
-
end
|
85
|
-
failure_message_for_should_not do |document|
|
86
|
-
name, expected = attribute.first
|
87
|
-
"expected #{name} attribute at #{@selector.inspect} to not be #{expected.inspect}"
|
88
|
-
end
|
89
|
-
|
90
|
-
def attribute(document, attribute_name)
|
91
|
-
node = document.css(@selector).first
|
92
|
-
node && node[attribute_name]
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
RSpec::Matchers.define :have_selector do |selector|
|
97
|
-
match { |document| document.css(selector).present? }
|
98
|
-
failure_message_for_should { "expected document to #{name_to_sentence}#{expected_to_sentence}"}
|
99
|
-
failure_message_for_should_not { "expected document to not #{name_to_sentence}#{expected_to_sentence}"}
|
100
|
-
end
|
101
|
-
|
@@ -0,0 +1,28 @@
|
|
1
|
+
RSpec::Matchers.define :have_attribute do |attribute|
|
2
|
+
@selector = 'body > *:first'
|
3
|
+
|
4
|
+
chain :at_selector do |selector|
|
5
|
+
@selector = selector
|
6
|
+
end
|
7
|
+
|
8
|
+
match do |document|
|
9
|
+
name, expected = attribute.first
|
10
|
+
expected == attribute(document, name)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe { "have attribute #{attribute.inspect} at selector #{@selector.inspect}" }
|
14
|
+
failure_message_for_should do |document|
|
15
|
+
name, expected = attribute.first
|
16
|
+
"expected #{name} attribute at #{@selector.inspect} to be #{expected.inspect} but was #{attribute(document, name).inspect}"
|
17
|
+
end
|
18
|
+
failure_message_for_should_not do |document|
|
19
|
+
name, expected = attribute.first
|
20
|
+
"expected #{name} attribute at #{@selector.inspect} to not be #{expected.inspect}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def attribute(document, attribute_name)
|
24
|
+
node = document.css(@selector).first
|
25
|
+
node && node[attribute_name]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,6 @@
|
|
1
|
+
RSpec::Matchers.define :have_selector do |selector|
|
2
|
+
match { |document| document.css(selector).present? }
|
3
|
+
failure_message_for_should { "expected document to #{name_to_sentence}#{expected_to_sentence}"}
|
4
|
+
failure_message_for_should_not { "expected document to not #{name_to_sentence}#{expected_to_sentence}"}
|
5
|
+
end
|
6
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
RSpec::Matchers.define :have_styling do |rules|
|
2
|
+
@selector = 'body > *:first'
|
3
|
+
|
4
|
+
chain :at_selector do |selector|
|
5
|
+
@selector = selector
|
6
|
+
end
|
7
|
+
|
8
|
+
match do |document|
|
9
|
+
if rules.nil?
|
10
|
+
parsed_styles(document).blank?
|
11
|
+
else
|
12
|
+
rules.to_a.should == parsed_styles(document)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe { "have styles #{rules.inspect} at selector #{@selector.inspect}" }
|
17
|
+
failure_message_for_should { |document| "expected styles at #{@selector.inspect} to be #{rules.inspect} but was #{parsed_styles(document).inspect}" }
|
18
|
+
failure_message_for_should_not { "expected styles at #{@selector.inspect} to not be #{rules.inspect}" }
|
19
|
+
|
20
|
+
def parsed_styles(document)
|
21
|
+
parse_styles(element_style(document))
|
22
|
+
end
|
23
|
+
|
24
|
+
def element_style(document)
|
25
|
+
node = document.css(@selector).first
|
26
|
+
node && node['style']
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_styles(styles)
|
30
|
+
return [] if styles.blank?
|
31
|
+
styles.split(';').inject([]) do |array, item|
|
32
|
+
array << item.split(':', 2).map(&:strip)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roadie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
9
|
- 0
|
10
|
-
|
11
|
-
version: 1.0.0.pre2
|
10
|
+
version: 1.0.0
|
12
11
|
platform: ruby
|
13
12
|
authors:
|
14
13
|
- Magnus Bergmark
|
@@ -16,7 +15,7 @@ autorequire:
|
|
16
15
|
bindir: bin
|
17
16
|
cert_chain: []
|
18
17
|
|
19
|
-
date: 2011-
|
18
|
+
date: 2011-03-09 00:00:00 +01:00
|
20
19
|
default_executable:
|
21
20
|
dependencies:
|
22
21
|
- !ruby/object:Gem::Dependency
|
@@ -91,29 +90,36 @@ extra_rdoc_files:
|
|
91
90
|
files:
|
92
91
|
- .autotest
|
93
92
|
- .gitignore
|
94
|
-
- .
|
93
|
+
- .yardopts
|
95
94
|
- Gemfile
|
96
95
|
- Gemfile.lock
|
97
96
|
- MIT-LICENSE
|
98
97
|
- README.textile
|
99
98
|
- Rakefile
|
100
|
-
- VERSION
|
101
99
|
- autotest/discover.rb
|
102
100
|
- lib/roadie.rb
|
103
101
|
- lib/roadie/action_mailer_extensions.rb
|
102
|
+
- lib/roadie/css_file_not_found.rb
|
104
103
|
- lib/roadie/inliner.rb
|
104
|
+
- lib/roadie/style_declaration.rb
|
105
105
|
- lib/roadie/version.rb
|
106
106
|
- roadie.gemspec
|
107
107
|
- spec/fixtures/public/stylesheets/bar.css
|
108
108
|
- spec/fixtures/public/stylesheets/foo.css
|
109
109
|
- spec/fixtures/public/stylesheets/integration.css
|
110
|
+
- spec/fixtures/views/integration_mailer/marketing.html.erb
|
110
111
|
- spec/fixtures/views/integration_mailer/notification.html.erb
|
111
112
|
- spec/fixtures/views/integration_mailer/notification.text.erb
|
112
113
|
- spec/integration_spec.rb
|
113
114
|
- spec/lib/roadie/action_mailer_extensions_spec.rb
|
115
|
+
- spec/lib/roadie/css_file_not_found_spec.rb
|
114
116
|
- spec/lib/roadie/inliner_spec.rb
|
117
|
+
- spec/lib/roadie/style_declaration_spec.rb
|
115
118
|
- spec/lib/roadie_spec.rb
|
116
119
|
- spec/spec_helper.rb
|
120
|
+
- spec/support/have_attribute_matcher.rb
|
121
|
+
- spec/support/have_selector_matcher.rb
|
122
|
+
- spec/support/have_styling_matcher.rb
|
117
123
|
has_rdoc: true
|
118
124
|
homepage: http://github.com/Mange/roadie
|
119
125
|
licenses: []
|
@@ -135,14 +141,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
135
141
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
142
|
none: false
|
137
143
|
requirements:
|
138
|
-
- - "
|
144
|
+
- - ">="
|
139
145
|
- !ruby/object:Gem::Version
|
140
|
-
hash:
|
146
|
+
hash: 3
|
141
147
|
segments:
|
142
|
-
-
|
143
|
-
|
144
|
-
- 1
|
145
|
-
version: 1.3.1
|
148
|
+
- 0
|
149
|
+
version: "0"
|
146
150
|
requirements: []
|
147
151
|
|
148
152
|
rubyforge_project:
|
@@ -154,10 +158,16 @@ test_files:
|
|
154
158
|
- spec/fixtures/public/stylesheets/bar.css
|
155
159
|
- spec/fixtures/public/stylesheets/foo.css
|
156
160
|
- spec/fixtures/public/stylesheets/integration.css
|
161
|
+
- spec/fixtures/views/integration_mailer/marketing.html.erb
|
157
162
|
- spec/fixtures/views/integration_mailer/notification.html.erb
|
158
163
|
- spec/fixtures/views/integration_mailer/notification.text.erb
|
159
164
|
- spec/integration_spec.rb
|
160
165
|
- spec/lib/roadie/action_mailer_extensions_spec.rb
|
166
|
+
- spec/lib/roadie/css_file_not_found_spec.rb
|
161
167
|
- spec/lib/roadie/inliner_spec.rb
|
168
|
+
- spec/lib/roadie/style_declaration_spec.rb
|
162
169
|
- spec/lib/roadie_spec.rb
|
163
170
|
- spec/spec_helper.rb
|
171
|
+
- spec/support/have_attribute_matcher.rb
|
172
|
+
- spec/support/have_selector_matcher.rb
|
173
|
+
- spec/support/have_styling_matcher.rb
|
data/.rspec
DELETED
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1.0.0.pre1
|