premailer 1.8.7 → 1.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -5
- data/lib/premailer/adapter/nokogiri_fast.rb +354 -0
- data/lib/premailer/adapter.rb +4 -0
- data/lib/premailer/executor.rb +1 -1
- data/lib/premailer/version.rb +1 -1
- metadata +17 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e05556033aa604e7c14baa5250b5d7d5baf90e1e
|
4
|
+
data.tar.gz: 302aff11e259bce3d183f3ec57c387da51ad67a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78658bc3655cf3eae6f2c11e1bd2cd9974b1eda62518818620e84d222429cbe6bab8e2f827962342059c19df7179d26e534d54f169b3c6adcc6c564473b88594
|
7
|
+
data.tar.gz: 981be77ba31ab22cb7199c8b885a75be53d28c79f1c8e1c9a38d0dd29d2a74763236c9824ebfbc16464b18c1f485d0f26e1fa4ea4865ca4997ee3bf851c19e6e
|
data/README.md
CHANGED
@@ -36,16 +36,17 @@ require 'premailer'
|
|
36
36
|
|
37
37
|
premailer = Premailer.new('http://example.com/myfile.html', :warn_level => Premailer::Warnings::SAFE)
|
38
38
|
|
39
|
-
# Write the HTML output
|
40
|
-
File.open("output.html", "w") do |fout|
|
41
|
-
fout.puts premailer.to_inline_css
|
42
|
-
end
|
43
|
-
|
44
39
|
# Write the plain-text output
|
40
|
+
# This must come before to_inline_css (https://github.com/premailer/premailer/issues/201)
|
45
41
|
File.open("output.txt", "w") do |fout|
|
46
42
|
fout.puts premailer.to_plain_text
|
47
43
|
end
|
48
44
|
|
45
|
+
# Write the HTML output
|
46
|
+
File.open("output.html", "w") do |fout|
|
47
|
+
fout.puts premailer.to_inline_css
|
48
|
+
end
|
49
|
+
|
49
50
|
# Output any CSS warnings
|
50
51
|
premailer.warnings.each do |w|
|
51
52
|
puts "#{w[:message]} (#{w[:level]}) may not render properly in #{w[:clients]}"
|
@@ -0,0 +1,354 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
class Premailer
|
4
|
+
module Adapter
|
5
|
+
# NokogiriFast adapter
|
6
|
+
module NokogiriFast
|
7
|
+
|
8
|
+
# Merge CSS into the HTML document.
|
9
|
+
#
|
10
|
+
# @return [String] an HTML.
|
11
|
+
def to_inline_css
|
12
|
+
doc = @processed_doc
|
13
|
+
@unmergable_rules = CssParser::Parser.new
|
14
|
+
|
15
|
+
# Give all styles already in style attributes a specificity of 1000
|
16
|
+
# per http://www.w3.org/TR/CSS21/cascade.html#specificity
|
17
|
+
doc.search("*[@style]").each do |el|
|
18
|
+
el['style'] = '[SPEC=1000[' + el.attributes['style'] + ']]'
|
19
|
+
end
|
20
|
+
|
21
|
+
# Create an index for nodes by tag name/id/class
|
22
|
+
# Also precompute the map of nodes to descendants
|
23
|
+
index, all_nodes, descendants = make_index(doc)
|
24
|
+
|
25
|
+
# Iterate through the rules and merge them into the HTML
|
26
|
+
@css_parser.each_selector(:all) do |selector, declaration, specificity, media_types|
|
27
|
+
|
28
|
+
# Save un-mergable rules separately
|
29
|
+
selector.gsub!(/:link([\s]*)+/i) { |m| $1 }
|
30
|
+
|
31
|
+
# Convert element names to lower case
|
32
|
+
selector.gsub!(/([\s]|^)([\w]+)/) { |m| $1.to_s + $2.to_s.downcase }
|
33
|
+
|
34
|
+
if Premailer.is_media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS
|
35
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]
|
36
|
+
else
|
37
|
+
begin
|
38
|
+
if selector =~ Premailer::RE_RESET_SELECTORS
|
39
|
+
# this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/
|
40
|
+
# however, this doesn't mean for testing pur
|
41
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration)) unless !@options[:preserve_reset]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Try the new index based technique. If not supported, fall back to the old brute force one.
|
45
|
+
nodes = match_selector(index, all_nodes, descendants, selector) || doc.search(selector)
|
46
|
+
nodes.each do |el|
|
47
|
+
if el.elem? and (el.name != 'head' and el.parent.name != 'head')
|
48
|
+
# Add a style attribute or append to the existing one
|
49
|
+
block = "[SPEC=#{specificity}[#{declaration}]]"
|
50
|
+
el['style'] = (el.attributes['style'].to_s ||= '') + ' ' + block
|
51
|
+
end
|
52
|
+
end
|
53
|
+
rescue ::Nokogiri::SyntaxError, RuntimeError, ArgumentError
|
54
|
+
$stderr.puts "CSS syntax error with selector: #{selector}" if @options[:verbose]
|
55
|
+
next
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Remove script tags
|
61
|
+
doc.search("script").remove if @options[:remove_scripts]
|
62
|
+
|
63
|
+
# Read STYLE attributes and perform folding
|
64
|
+
doc.search("*[@style]").each do |el|
|
65
|
+
style = el.attributes['style'].to_s
|
66
|
+
|
67
|
+
declarations = []
|
68
|
+
style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/).each do |declaration|
|
69
|
+
rs = CssParser::RuleSet.new(nil, declaration[1].to_s, declaration[0].to_i)
|
70
|
+
declarations << rs
|
71
|
+
end
|
72
|
+
|
73
|
+
# Perform style folding
|
74
|
+
merged = CssParser.merge(declarations)
|
75
|
+
merged.expand_shorthand!
|
76
|
+
|
77
|
+
# Duplicate CSS attributes as HTML attributes
|
78
|
+
if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]
|
79
|
+
Premailer::RELATED_ATTRIBUTES[el.name].each do |css_att, html_att|
|
80
|
+
el[html_att] = merged[css_att].gsub(/url\(['|"](.*)['|"]\)/, '\1').gsub(/;$|\s*!important/, '').strip if el[html_att].nil? and not merged[css_att].empty?
|
81
|
+
merged.instance_variable_get("@declarations").tap do |declarations|
|
82
|
+
declarations.delete(css_att)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
# Collapse multiple rules into one as much as possible.
|
87
|
+
merged.create_shorthand! if @options[:create_shorthands]
|
88
|
+
|
89
|
+
# write the inline STYLE attribute
|
90
|
+
# split by ';' but ignore those in brackets
|
91
|
+
attributes = Premailer.escape_string(merged.declarations_to_s).split(/;(?![^(]*\))/).map(&:strip)
|
92
|
+
attributes = attributes.map { |attr| [attr.split(':').first, attr] }.sort_by { |pair| pair.first }.map { |pair| pair[1] }
|
93
|
+
el['style'] = attributes.join('; ') + ";"
|
94
|
+
end
|
95
|
+
|
96
|
+
doc = write_unmergable_css_rules(doc, @unmergable_rules)
|
97
|
+
|
98
|
+
if @options[:remove_classes] or @options[:remove_comments]
|
99
|
+
doc.traverse do |el|
|
100
|
+
if el.comment? and @options[:remove_comments]
|
101
|
+
el.remove
|
102
|
+
elsif el.element?
|
103
|
+
el.remove_attribute('class') if @options[:remove_classes]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
if @options[:remove_ids]
|
109
|
+
# find all anchor's targets and hash them
|
110
|
+
targets = []
|
111
|
+
doc.search("a[@href^='#']").each do |el|
|
112
|
+
target = el.get_attribute('href')[1..-1]
|
113
|
+
targets << target
|
114
|
+
el.set_attribute('href', "#" + Digest::MD5.hexdigest(target))
|
115
|
+
end
|
116
|
+
# hash ids that are links target, delete others
|
117
|
+
doc.search("*[@id]").each do |el|
|
118
|
+
id = el.get_attribute('id')
|
119
|
+
if targets.include?(id)
|
120
|
+
el.set_attribute('id', Digest::MD5.hexdigest(id))
|
121
|
+
else
|
122
|
+
el.remove_attribute('id')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
if @options[:reset_contenteditable]
|
128
|
+
doc.search('*[@contenteditable]').each do |el|
|
129
|
+
el.remove_attribute('contenteditable')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
@processed_doc = doc
|
134
|
+
if is_xhtml?
|
135
|
+
# we don't want to encode carriage returns
|
136
|
+
@processed_doc.to_xhtml(:encoding => @options[:output_encoding]).gsub(/&\#(xD|13);/i, "\r")
|
137
|
+
else
|
138
|
+
@processed_doc.to_html(:encoding => @options[:output_encoding])
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Create a <tt>style</tt> element with un-mergable rules (e.g. <tt>:hover</tt>)
|
143
|
+
# and write it into the <tt>body</tt>.
|
144
|
+
#
|
145
|
+
# <tt>doc</tt> is an Nokogiri document and <tt>unmergable_css_rules</tt> is a Css::RuleSet.
|
146
|
+
#
|
147
|
+
# @return [::Nokogiri::XML] a document.
|
148
|
+
def write_unmergable_css_rules(doc, unmergable_rules) # :nodoc:
|
149
|
+
styles = unmergable_rules.to_s
|
150
|
+
|
151
|
+
unless styles.empty?
|
152
|
+
style_tag = "<style type=\"text/css\">\n#{styles}</style>"
|
153
|
+
unless (body = doc.search('body')).empty?
|
154
|
+
if doc.at_css('body').children && !doc.at_css('body').children.empty?
|
155
|
+
doc.at_css('body').children.before(::Nokogiri::XML.fragment(style_tag))
|
156
|
+
else
|
157
|
+
doc.at_css('body').add_child(::Nokogiri::XML.fragment(style_tag))
|
158
|
+
end
|
159
|
+
else
|
160
|
+
doc.inner_html = style_tag += doc.inner_html
|
161
|
+
end
|
162
|
+
end
|
163
|
+
doc
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
# Converts the HTML document to a format suitable for plain-text e-mail.
|
168
|
+
#
|
169
|
+
# If present, uses the <body> element as its base; otherwise uses the whole document.
|
170
|
+
#
|
171
|
+
# @return [String] a plain text.
|
172
|
+
def to_plain_text
|
173
|
+
html_src = ''
|
174
|
+
begin
|
175
|
+
html_src = @doc.at("body").inner_html
|
176
|
+
rescue;
|
177
|
+
end
|
178
|
+
|
179
|
+
html_src = @doc.to_html unless html_src and not html_src.empty?
|
180
|
+
convert_to_text(html_src, @options[:line_length], @html_encoding)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Gets the original HTML as a string.
|
184
|
+
# @return [String] HTML.
|
185
|
+
def to_s
|
186
|
+
if is_xhtml?
|
187
|
+
@doc.to_xhtml(:encoding => nil)
|
188
|
+
else
|
189
|
+
@doc.to_html(:encoding => nil)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Load the HTML file and convert it into an Nokogiri document.
|
194
|
+
#
|
195
|
+
# @return [::Nokogiri::XML] a document.
|
196
|
+
def load_html(input) # :nodoc:
|
197
|
+
thing = nil
|
198
|
+
|
199
|
+
# TODO: duplicate options
|
200
|
+
if @options[:with_html_string] or @options[:inline] or input.respond_to?(:read)
|
201
|
+
thing = input
|
202
|
+
elsif @is_local_file
|
203
|
+
@base_dir = File.dirname(input)
|
204
|
+
thing = File.open(input, 'r')
|
205
|
+
else
|
206
|
+
thing = open(input)
|
207
|
+
end
|
208
|
+
|
209
|
+
if thing.respond_to?(:read)
|
210
|
+
thing = thing.read
|
211
|
+
end
|
212
|
+
|
213
|
+
return nil unless thing
|
214
|
+
doc = nil
|
215
|
+
|
216
|
+
# Handle HTML entities
|
217
|
+
if @options[:replace_html_entities] == true and thing.is_a?(String)
|
218
|
+
HTML_ENTITIES.map do |entity, replacement|
|
219
|
+
thing.gsub! entity, replacement
|
220
|
+
end
|
221
|
+
end
|
222
|
+
# Default encoding is ASCII-8BIT (binary) per http://groups.google.com/group/nokogiri-talk/msg/0b81ef0dc180dc74
|
223
|
+
# However, we really don't want to hardcode this. ASCII-8BIT should be the default, but not the only option.
|
224
|
+
if thing.is_a?(String) and RUBY_VERSION =~ /1.9/
|
225
|
+
thing = thing.force_encoding(@options[:input_encoding]).encode!
|
226
|
+
doc = ::Nokogiri::HTML(thing, nil, @options[:input_encoding]) { |c| c.recover }
|
227
|
+
else
|
228
|
+
default_encoding = RUBY_PLATFORM == 'java' ? nil : 'BINARY'
|
229
|
+
doc = ::Nokogiri::HTML(thing, nil, @options[:input_encoding] || default_encoding) { |c| c.recover }
|
230
|
+
end
|
231
|
+
|
232
|
+
# Fix for removing any CDATA tags from both style and script tags inserted per
|
233
|
+
# https://github.com/sparklemotion/nokogiri/issues/311 and
|
234
|
+
# https://github.com/premailer/premailer/issues/199
|
235
|
+
%w(style script).each do |tag|
|
236
|
+
doc.search(tag).children.each do |child|
|
237
|
+
child.swap(child.text()) if child.cdata?
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
doc
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
# For very large documents, it is useful to trade off some memory for performance.
|
247
|
+
# We can build an index of the nodes so we can quickly select by id/class/tagname
|
248
|
+
# instead of search the tree again and again.
|
249
|
+
#
|
250
|
+
# @param page The Nokogiri HTML document to index.
|
251
|
+
# @return [index, set_of_all_nodes, descendants] The index is a hash from key to set of nodes.
|
252
|
+
# The "descendants" is a hash mapping a node to the set of its descendant nodes.
|
253
|
+
def make_index(page)
|
254
|
+
index = {} # Contains a map of tag/class/id names to set of nodes.
|
255
|
+
all_nodes = [] # A plain array of all nodes in the doc. The superset.
|
256
|
+
descendants = {} # Maps node -> set of descendants
|
257
|
+
|
258
|
+
page.traverse do |node|
|
259
|
+
all_nodes.push(node)
|
260
|
+
|
261
|
+
if node != page then
|
262
|
+
index_ancestry(page, node, node.parent, descendants)
|
263
|
+
end
|
264
|
+
|
265
|
+
# Index the node by tag name. This is the least selective
|
266
|
+
# of the three index types empirically.
|
267
|
+
index[node.name] = (index[node.name] || Set.new).add(node)
|
268
|
+
|
269
|
+
# Index the node by all class attributes it possesses.
|
270
|
+
# Classes are modestly selective. Usually more than tag names
|
271
|
+
# but less selective than ids.
|
272
|
+
if node.has_attribute?("class") then
|
273
|
+
node.get_attribute("class").split(/\s+/).each do |c|
|
274
|
+
c = '.' + c
|
275
|
+
index[c] = (index[c] || Set.new).add(node)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# Index the node by its "id" attribute if it has one.
|
280
|
+
# This is usually the most selective of the three.
|
281
|
+
if node.has_attribute?("id") then
|
282
|
+
id = '#' + node.get_attribute("id")
|
283
|
+
index[id] = (index[id] || Set.new).add(node)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# If an index key isn't there, then we should treat it as an empty set.
|
288
|
+
# This makes the index total and we don't need to special case presence.
|
289
|
+
# Note that the default value will never be modified. So we don't need
|
290
|
+
# default_proc.
|
291
|
+
index.default = Set.new
|
292
|
+
descendants.default = Set.new
|
293
|
+
|
294
|
+
return index, Set.new(all_nodes), descendants
|
295
|
+
end
|
296
|
+
|
297
|
+
# @param doc The top level document
|
298
|
+
# @param elem The element whose ancestry is to be captured
|
299
|
+
# @param parent the current parent in the process of capturing. Should be set to elem.parent for starters.
|
300
|
+
# @param descendants The running hash map of node -> set of nodes that maps descendants of a node.
|
301
|
+
# @return The descendants argument after updating it.
|
302
|
+
def index_ancestry(doc, elem, parent, descendants)
|
303
|
+
if parent then
|
304
|
+
descendants[parent] = (descendants[parent] || Set.new).add(elem)
|
305
|
+
if doc != parent then
|
306
|
+
index_ancestry(doc, elem, parent.parent, descendants)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
descendants
|
310
|
+
end
|
311
|
+
|
312
|
+
# @param index An index hash returned by make_index
|
313
|
+
# @param base The base set of nodes within which the given spec is to be matched.
|
314
|
+
# @param intersection_selector A CSS intersection selector string of the form
|
315
|
+
# "hello.world" or "#blue.diamond". This should not contain spaces.
|
316
|
+
# @return Set of nodes matching the given spec that are present in the base set.
|
317
|
+
def narrow_down_nodes(index, base, intersection_selector)
|
318
|
+
intersection_selector.split(/(?=[.#])/).reduce(base) do |acc, sel|
|
319
|
+
acc = index[sel].intersection(acc)
|
320
|
+
acc
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# @param index An index returned by make_index
|
325
|
+
# @param allNodes The set of all nodes in the DOM to search
|
326
|
+
# @param selector A simple CSS tree matching selector of the form "div.container p.item span"
|
327
|
+
# @return Set of matching nodes
|
328
|
+
#
|
329
|
+
# Note that fancy CSS selector syntax is not supported. Anything
|
330
|
+
# not matching the regex /^[-a-zA-Z0-9\s_.#]*$/ should not be passed.
|
331
|
+
# It will return nil when such a selector is passed, so you can take
|
332
|
+
# action on the falsity of the return value.
|
333
|
+
def match_selector(index, all_nodes, descendants, selector)
|
334
|
+
if /[^-a-zA-Z0-9_\s.#]/.match(selector) then
|
335
|
+
return nil
|
336
|
+
end
|
337
|
+
|
338
|
+
take_children = false
|
339
|
+
selector.split(/\s+/).reduce(all_nodes) do |base, spec|
|
340
|
+
desc = base
|
341
|
+
if take_children then
|
342
|
+
desc = Set.new
|
343
|
+
base.each do |n|
|
344
|
+
desc.merge(descendants[n])
|
345
|
+
end
|
346
|
+
else
|
347
|
+
take_children = true
|
348
|
+
end
|
349
|
+
narrow_down_nodes(index, desc, spec)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
data/lib/premailer/adapter.rb
CHANGED
@@ -4,17 +4,20 @@ class Premailer
|
|
4
4
|
# Manages the adapter classes. Currently supports:
|
5
5
|
#
|
6
6
|
# * nokogiri
|
7
|
+
# * nokogiri_fast
|
7
8
|
# * nokogumbo
|
8
9
|
# * hpricot
|
9
10
|
module Adapter
|
10
11
|
|
11
12
|
autoload :Hpricot, 'premailer/adapter/hpricot'
|
12
13
|
autoload :Nokogiri, 'premailer/adapter/nokogiri'
|
14
|
+
autoload :NokogiriFast, 'premailer/adapter/nokogiri_fast'
|
13
15
|
autoload :Nokogumbo, 'premailer/adapter/nokogumbo'
|
14
16
|
|
15
17
|
# adapter to required file mapping.
|
16
18
|
REQUIREMENT_MAP = [
|
17
19
|
["nokogiri", :nokogiri],
|
20
|
+
["nokogiri", :nokogiri_fast],
|
18
21
|
["nokogumbo", :nokogumbo],
|
19
22
|
["hpricot", :hpricot],
|
20
23
|
]
|
@@ -32,6 +35,7 @@ class Premailer
|
|
32
35
|
# @raise [RuntimeError] unless suitable adapter found.
|
33
36
|
def self.default
|
34
37
|
return :nokogiri if defined?(::Nokogiri)
|
38
|
+
return :nokogiri_fast if defined?(::NokogiriFast)
|
35
39
|
return :nokogumbo if defined?(::Nokogumbo)
|
36
40
|
return :hpricot if defined?(::Hpricot)
|
37
41
|
|
data/lib/premailer/executor.rb
CHANGED
@@ -45,7 +45,7 @@ opts = OptionParser.new do |opts|
|
|
45
45
|
end
|
46
46
|
|
47
47
|
opts.on("-j", "--remove-scripts", "Remove <script> elements") do |v|
|
48
|
-
options[:
|
48
|
+
options[:remove_scripts] = v
|
49
49
|
end
|
50
50
|
|
51
51
|
opts.on("-l", "--line-length N", Integer, "Line length for plaintext (default: #{options[:line_length].to_s})") do |v|
|
data/lib/premailer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: premailer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Dunae
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: css_parser
|
@@ -56,7 +56,7 @@ dependencies:
|
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0.8'
|
62
62
|
- - "!="
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
prerelease: false
|
67
67
|
version_requirements: !ruby/object:Gem::Requirement
|
68
68
|
requirements:
|
69
|
-
- - "
|
69
|
+
- - ">"
|
70
70
|
- !ruby/object:Gem::Version
|
71
71
|
version: '0.8'
|
72
72
|
- - "!="
|
@@ -93,6 +93,9 @@ dependencies:
|
|
93
93
|
- - ">="
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: 1.4.4
|
96
|
+
- - "<="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: 1.6.8
|
96
99
|
type: :development
|
97
100
|
prerelease: false
|
98
101
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -100,20 +103,23 @@ dependencies:
|
|
100
103
|
- - ">="
|
101
104
|
- !ruby/object:Gem::Version
|
102
105
|
version: 1.4.4
|
106
|
+
- - "<="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: 1.6.8
|
103
109
|
- !ruby/object:Gem::Dependency
|
104
110
|
name: yard
|
105
111
|
requirement: !ruby/object:Gem::Requirement
|
106
112
|
requirements:
|
107
|
-
- - "
|
113
|
+
- - ">="
|
108
114
|
- !ruby/object:Gem::Version
|
109
|
-
version: 0
|
115
|
+
version: '0'
|
110
116
|
type: :development
|
111
117
|
prerelease: false
|
112
118
|
version_requirements: !ruby/object:Gem::Requirement
|
113
119
|
requirements:
|
114
|
-
- - "
|
120
|
+
- - ">="
|
115
121
|
- !ruby/object:Gem::Version
|
116
|
-
version: 0
|
122
|
+
version: '0'
|
117
123
|
- !ruby/object:Gem::Dependency
|
118
124
|
name: redcarpet
|
119
125
|
requirement: !ruby/object:Gem::Requirement
|
@@ -199,6 +205,7 @@ files:
|
|
199
205
|
- lib/premailer/adapter.rb
|
200
206
|
- lib/premailer/adapter/hpricot.rb
|
201
207
|
- lib/premailer/adapter/nokogiri.rb
|
208
|
+
- lib/premailer/adapter/nokogiri_fast.rb
|
202
209
|
- lib/premailer/adapter/nokogumbo.rb
|
203
210
|
- lib/premailer/executor.rb
|
204
211
|
- lib/premailer/html_to_plain_text.rb
|
@@ -207,7 +214,8 @@ files:
|
|
207
214
|
- misc/client_support.yaml
|
208
215
|
homepage: http://premailer.dialect.ca/
|
209
216
|
licenses: []
|
210
|
-
metadata:
|
217
|
+
metadata:
|
218
|
+
yard.run: yri
|
211
219
|
post_install_message:
|
212
220
|
rdoc_options: []
|
213
221
|
require_paths:
|
@@ -229,4 +237,3 @@ signing_key:
|
|
229
237
|
specification_version: 4
|
230
238
|
summary: Preflight for HTML e-mail.
|
231
239
|
test_files: []
|
232
|
-
has_rdoc: true
|