premailer 1.23.0 → 1.29.0

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.
@@ -1,9 +1,8 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
2
  require 'htmlentities'
3
3
 
4
4
  # Support functions for Premailer
5
5
  module HtmlToPlainText
6
-
7
6
  # Returns the text in UTF-8 format with all HTML tags removed
8
7
  #
9
8
  # HTML content can be omitted from the output by surrounding it in the following comments:
@@ -13,8 +12,8 @@ module HtmlToPlainText
13
12
  #
14
13
  # TODO: add support for DL, OL
15
14
  # TODO: this is not safe and needs a real html parser to work
16
- def convert_to_text(html, line_length = 65, from_charset = 'UTF-8')
17
- txt = html
15
+ def convert_to_text(html, line_length = 65, _from_charset = 'UTF-8')
16
+ txt = +html
18
17
 
19
18
  # strip text ignored html. Useful for removing
20
19
  # headers and footers that aren't needed in the
@@ -50,7 +49,7 @@ module HtmlToPlainText
50
49
 
51
50
  if text.empty?
52
51
  ''
53
- elsif href.nil? || text.strip.downcase == href.strip.downcase
52
+ elsif href.nil? || text.strip.casecmp(href.strip) == 0
54
53
  text.strip
55
54
  else
56
55
  text.strip + ' ( ' + href.strip + ' )'
@@ -59,7 +58,7 @@ module HtmlToPlainText
59
58
 
60
59
  # handle headings (H1-H6)
61
60
  txt.gsub!(/(<\/h[1-6]>)/i, "\n\\1") # move closing tags to new lines
62
- txt.gsub!(/[\s]*<h([1-6]+)[^>]*>[\s]*(.*)[\s]*<\/h[1-6]+>/i) do |s|
61
+ txt.gsub!(/[\s]*<h([1-6]+)[^>]*>[\s]*(.*)[\s]*<\/h[1-6]+>/i) do |_s|
63
62
  hlevel = $1.to_i
64
63
 
65
64
  htext = $2
@@ -68,17 +67,21 @@ module HtmlToPlainText
68
67
 
69
68
  # determine maximum line length
70
69
  hlength = 0
71
- htext.each_line { |l| llength = l.strip.length; hlength = llength if llength > hlength }
70
+ htext.each_line do |l|
71
+ llength = l.strip.length
72
+ hlength = llength if llength > hlength
73
+ end
72
74
  hlength = line_length if hlength > line_length
73
75
 
74
- case hlevel
75
- when 1 # H1, asterisks above and below
76
- htext = ('*' * hlength) + "\n" + htext + "\n" + ('*' * hlength)
77
- when 2 # H1, dashes above and below
78
- htext = ('-' * hlength) + "\n" + htext + "\n" + ('-' * hlength)
79
- else # H3-H6, dashes below
80
- htext = htext + "\n" + ('-' * hlength)
81
- end
76
+ htext =
77
+ case hlevel
78
+ when 1 # H1, asterisks above and below
79
+ ('*' * hlength) + "\n" + htext + "\n" + ('*' * hlength)
80
+ when 2 # H1, dashes above and below
81
+ ('-' * hlength) + "\n" + htext + "\n" + ('-' * hlength)
82
+ else # H3-H6, dashes below
83
+ htext + "\n" + ('-' * hlength)
84
+ end
82
85
 
83
86
  "\n\n" + htext + "\n\n"
84
87
  end
@@ -117,8 +120,8 @@ module HtmlToPlainText
117
120
  txt.gsub!(/[\n]{3,}/, "\n\n")
118
121
 
119
122
  # the word messes up the parens
120
- txt.gsub!(/\(([ \n])(http[^)]+)([\n ])\)/) do |s|
121
- ($1 == "\n" ? $1 : '' ) + '( ' + $2 + ' )' + ($3 == "\n" ? $1 : '' )
123
+ txt.gsub!(/\(([ \n])(http[^)]+)([\n ])\)/) do |_s|
124
+ ($1 == "\n" ? $1 : '') + '( ' + $2 + ' )' + ($3 == "\n" ? $1 : '')
122
125
  end
123
126
 
124
127
  txt.strip
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Premailer processes HTML and CSS to improve e-mail deliverability.
2
3
  #
3
4
  # Premailer's main function is to render all CSS as inline <tt>style</tt>
@@ -15,7 +16,7 @@
15
16
  # fout.close
16
17
  #
17
18
  # # Write the plain-text output
18
- # fout = File.open("ouput.txt", "w")
19
+ # fout = File.open("output.txt", "w")
19
20
  # fout.puts premailer.to_plain_text
20
21
  # fout.close
21
22
  #
@@ -38,9 +39,9 @@ class Premailer
38
39
  CLIENT_SUPPORT_FILE = File.dirname(__FILE__) + '/../../misc/client_support.yaml'
39
40
 
40
41
  # Unmergable selectors regexp.
41
- RE_UNMERGABLE_SELECTORS = /(\:(visited|active|hover|focus|after|before|selection|target|first\-(line|letter))|^\@)/i
42
+ RE_UNMERGABLE_SELECTORS = /(:(visited|active|hover|focus|after|before|selection|target|first-(line|letter))|^@)/i
42
43
  # Reset selectors regexp.
43
- RE_RESET_SELECTORS = /^(\:\#outlook|body.*|\.ReadMsgBody|\.ExternalClass|img|\#backgroundTable)$/
44
+ RE_RESET_SELECTORS = /^(:\#outlook|body.*|\.ReadMsgBody|\.ExternalClass|img|\#backgroundTable)$/
44
45
 
45
46
  # list of HTMLEntities to fix
46
47
  # source: http://stackoverflow.com/questions/2812781/how-to-convert-webpage-apostrophe-8217-to-ascii-39-in-ruby-1-
@@ -56,23 +57,23 @@ class Premailer
56
57
  "&#8211;" => '-',
57
58
  "&#8212;" => '--',
58
59
  "&#8213;" => '--'
59
- }
60
+ }.freeze
60
61
 
61
62
  # list of CSS attributes that can be rendered as HTML attributes
62
63
  #
63
64
  # @todo too much repetition
64
65
  # @todo background=""
65
66
  RELATED_ATTRIBUTES = {
66
- 'h1' => {'text-align' => 'align'},
67
- 'h2' => {'text-align' => 'align'},
68
- 'h3' => {'text-align' => 'align'},
69
- 'h4' => {'text-align' => 'align'},
70
- 'h5' => {'text-align' => 'align'},
71
- 'h6' => {'text-align' => 'align'},
72
- 'p' => {'text-align' => 'align'},
73
- 'div' => {'text-align' => 'align'},
74
- 'blockquote' => {'text-align' => 'align'},
75
- 'body' => {'background-color' => 'bgcolor'},
67
+ 'h1' => { 'text-align' => 'align' },
68
+ 'h2' => { 'text-align' => 'align' },
69
+ 'h3' => { 'text-align' => 'align' },
70
+ 'h4' => { 'text-align' => 'align' },
71
+ 'h5' => { 'text-align' => 'align' },
72
+ 'h6' => { 'text-align' => 'align' },
73
+ 'p' => { 'text-align' => 'align' },
74
+ 'div' => { 'text-align' => 'align' },
75
+ 'blockquote' => { 'text-align' => 'align' },
76
+ 'body' => { 'background-color' => 'bgcolor' },
76
77
  'table' => {
77
78
  '-premailer-align' => 'align',
78
79
  'background-color' => 'bgcolor',
@@ -106,7 +107,7 @@ class Premailer
106
107
  '-premailer-width' => 'width',
107
108
  '-premailer-height' => 'height'
108
109
  }
109
- }
110
+ }.freeze
110
111
 
111
112
  # URI of the HTML file used
112
113
  attr_reader :html_file
@@ -141,7 +142,7 @@ class Premailer
141
142
  include Warnings
142
143
 
143
144
  # Waning level names
144
- WARN_LABEL = %w(NONE SAFE POOR RISKY)
145
+ WARN_LABEL = ['NONE', 'SAFE', 'POOR', 'RISKY'].freeze
145
146
 
146
147
  # Create a new Premailer object.
147
148
  #
@@ -181,39 +182,40 @@ class Premailer
181
182
  # @option options [Boolean] :html_fragment Handle HTML fragment without any HTML content wrappers. Default is false.
182
183
  # @option options [Boolean] :drop_unmergeable_css_rules Do not include unmergeable css rules in a <tt><style><tt> tag. Default is false.
183
184
  def initialize(html, options = {})
184
- @options = {:warn_level => Warnings::SAFE,
185
- :line_length => 65,
186
- :link_query_string => nil,
187
- :base_url => nil,
188
- :rgb_to_hex_attributes => true,
189
- :remove_classes => false,
190
- :remove_ids => false,
191
- :remove_comments => false,
192
- :remove_scripts => true,
193
- :reset_contenteditable => true,
194
- :css => [],
195
- :css_to_attributes => true,
196
- :preserve_style_attribute => false,
197
- :with_html_string => false,
198
- :css_string => nil,
199
- :preserve_styles => false,
200
- :preserve_reset => true,
201
- :verbose => false,
202
- :debug => false,
203
- :io_exceptions => false,
204
- :rule_set_exceptions => true,
205
- :include_link_tags => true,
206
- :include_style_tags => true,
207
- :input_encoding => 'ASCII-8BIT',
208
- :output_encoding => nil,
209
- :replace_html_entities => false,
210
- :escape_url_attributes => true,
211
- :unescaped_ampersand => false,
212
- :create_shorthands => true,
213
- :html_fragment => false,
214
- :adapter => Adapter.use,
215
- :drop_unmergeable_css_rules => false
216
- }.merge(options)
185
+ @options = {
186
+ :warn_level => Warnings::SAFE,
187
+ :line_length => 65,
188
+ :link_query_string => nil,
189
+ :base_url => nil,
190
+ :rgb_to_hex_attributes => true,
191
+ :remove_classes => false,
192
+ :remove_ids => false,
193
+ :remove_comments => false,
194
+ :remove_scripts => true,
195
+ :reset_contenteditable => true,
196
+ :css => [],
197
+ :css_to_attributes => true,
198
+ :preserve_style_attribute => false,
199
+ :with_html_string => false,
200
+ :css_string => nil,
201
+ :preserve_styles => false,
202
+ :preserve_reset => true,
203
+ :verbose => false,
204
+ :debug => false,
205
+ :io_exceptions => false,
206
+ :rule_set_exceptions => true,
207
+ :include_link_tags => true,
208
+ :include_style_tags => true,
209
+ :input_encoding => 'ASCII-8BIT',
210
+ :output_encoding => nil,
211
+ :replace_html_entities => false,
212
+ :escape_url_attributes => true,
213
+ :unescaped_ampersand => false,
214
+ :create_shorthands => true,
215
+ :html_fragment => false,
216
+ :adapter => Adapter.use,
217
+ :drop_unmergeable_css_rules => false
218
+ }.merge(options)
217
219
 
218
220
  @html_file = html
219
221
  @is_local_file = @options[:with_html_string] || Premailer.local_data?(html)
@@ -228,20 +230,22 @@ class Premailer
228
230
 
229
231
  if @options[:base_url]
230
232
  @base_url = Addressable::URI.parse(@options.delete(:base_url))
231
- elsif not @is_local_file
233
+ elsif !@is_local_file
232
234
  @base_url = Addressable::URI.parse(@html_file)
233
235
  end
234
236
 
235
- @css_parser = CssParser::Parser.new({
236
- :absolute_paths => true,
237
- :import => true,
238
- :io_exceptions => @options[:io_exceptions],
239
- :rule_set_exceptions => @options[:rule_set_exceptions]
240
- })
237
+ @css_parser = CssParser::Parser.new(
238
+ {
239
+ :absolute_paths => true,
240
+ :import => true,
241
+ :io_exceptions => @options[:io_exceptions],
242
+ :rule_set_exceptions => @options[:rule_set_exceptions]
243
+ }
244
+ )
241
245
 
242
246
  @adapter_class = Adapter.find @options[:adapter]
243
247
 
244
- self.extend(@adapter_class)
248
+ extend(@adapter_class)
245
249
 
246
250
  @doc = load_html(@html_file)
247
251
 
@@ -262,25 +266,26 @@ class Premailer
262
266
  @css_warnings
263
267
  end
264
268
 
265
- protected
269
+ protected
270
+
266
271
  def load_css_from_local_file!(path)
267
- css_block = ''
268
- path.gsub!(/\Afile:/, '')
272
+ css_block = +''
273
+ path.delete_prefix!('file:')
269
274
  begin
270
275
  File.open(path, "r") do |file|
271
- while line = file.gets
276
+ while (line = file.gets)
272
277
  css_block << line
273
278
  end
274
279
  end
275
280
 
276
281
  load_css_from_string(css_block)
277
- rescue => e
282
+ rescue StandardError => e
278
283
  raise e if @options[:io_exceptions]
279
284
  end
280
285
  end
281
286
 
282
287
  def load_css_from_string(css_string)
283
- @css_parser.add_block!(css_string, {:base_uri => @base_url, :base_dir => @base_dir, :only_media_types => [:screen, :handheld]})
288
+ @css_parser.add_block!(css_string, { :base_uri => @base_url, :base_dir => @base_dir, :only_media_types => [:screen, :handheld] })
284
289
  end
285
290
 
286
291
  # @private
@@ -296,20 +301,20 @@ protected
296
301
  end
297
302
  end
298
303
 
299
- # Load CSS included in <tt>style</tt> and <tt>link</tt> tags from an HTML document.
304
+ # Load CSS included in <tt>style</tt> and <tt>link</tt> tags from an HTML document.
300
305
  def load_css_from_html! # :nodoc:
301
306
  tags = @doc.search("link[@rel='stylesheet']:not([@data-premailer='ignore']), style:not([@data-premailer='ignore'])")
302
307
  if tags
303
308
  tags.each do |tag|
304
- if tag.to_s.strip =~ /^\<link/i && tag.attributes['href'] && media_type_ok?(tag.attributes['media']) && @options[:include_link_tags]
309
+ if tag.to_s.strip =~ /^<link/i && tag.attributes['href'] && media_type_ok?(tag.attributes['media']) && @options[:include_link_tags]
305
310
  # A user might want to <link /> to a local css file that is also mirrored on the site
306
311
  # but the local one is different (e.g. newer) than the live file, premailer will now choose the local file
307
312
 
308
- if tag.attributes['href'].to_s.include? @base_url.to_s and @html_file.kind_of?(String)
309
- if @options[:with_html_string]
310
- link_uri = tag.attributes['href'].to_s.sub(@base_url.to_s, '')
313
+ if tag.attributes['href'].to_s.include?(@base_url.to_s) && @html_file.is_a?(String)
314
+ link_uri = if @options[:with_html_string]
315
+ tag.attributes['href'].to_s.sub(@base_url.to_s, '')
311
316
  else
312
- link_uri = File.join(File.dirname(@html_file), tag.attributes['href'].to_s.sub!(@base_url.to_s, ''))
317
+ File.join(File.dirname(@html_file), tag.attributes['href'].to_s.sub!(@base_url.to_s, ''))
313
318
  end
314
319
  # if the file does not exist locally, try to grab the remote reference
315
320
  unless File.exist?(link_uri)
@@ -320,14 +325,14 @@ protected
320
325
  end
321
326
 
322
327
  if Premailer.local_data?(link_uri)
323
- $stderr.puts "Loading css from local file: " + link_uri if @options[:verbose]
328
+ warn "Loading css from local file: " + link_uri if @options[:verbose]
324
329
  load_css_from_local_file!(link_uri)
325
330
  else
326
- $stderr.puts "Loading css from uri: " + link_uri if @options[:verbose]
327
- @css_parser.load_uri!(link_uri, {:only_media_types => [:screen, :handheld]})
331
+ warn "Loading css from uri: " + link_uri if @options[:verbose]
332
+ @css_parser.load_uri!(link_uri, { :only_media_types => [:screen, :handheld] })
328
333
  end
329
334
 
330
- elsif tag.to_s.strip =~ /^\<style/i && @options[:include_style_tags]
335
+ elsif tag.to_s.strip =~ /^<style/i && @options[:include_style_tags]
331
336
  @css_parser.add_block!(tag.inner_html, :base_uri => @base_url, :base_dir => @base_dir, :only_media_types => [:screen, :handheld])
332
337
  end
333
338
  end
@@ -335,10 +340,31 @@ protected
335
340
  end
336
341
  end
337
342
 
343
+ # here be deprecated methods
344
+ public
338
345
 
346
+ # Free native Nokogiri/libxml2 memory held by this instance.
347
+ #
348
+ # After calling +to_inline_css+ or +to_plain_text+, the Premailer instance
349
+ # retains live Nokogiri documents that pin native libxml2 memory. Ruby's GC
350
+ # cannot see this native memory pressure, so in high-throughput scenarios
351
+ # (e.g. email rendering in Sidekiq workers) native memory can accumulate
352
+ # unboundedly. Call +cleanup!+ when you are done with the instance to
353
+ # release that memory immediately.
354
+ #
355
+ # After calling this method the instance is no longer usable.
356
+ def cleanup!
357
+ @processed_doc&.unlink
358
+ @doc&.unlink
359
+ @doc = nil
360
+ @processed_doc = nil
361
+ @css_parser = nil
362
+ @unmergable_rules = nil
363
+ @html_file = nil
364
+ @css_warnings = nil
365
+ @css_files = nil
366
+ end
339
367
 
340
- # here be deprecated methods
341
- public
342
368
  # @private
343
369
  # @deprecated
344
370
  def local_uri?(uri) # :nodoc:
@@ -346,70 +372,70 @@ public
346
372
  Premailer.local_data?(uri)
347
373
  end
348
374
 
349
- # here be instance methods
375
+ # here be instance methods
350
376
 
351
377
  # @private
352
378
  def media_type_ok?(media_types)
353
379
  media_types = media_types.to_s
354
- return true if media_types.nil? or media_types.empty?
380
+ return true if media_types.nil? || media_types.empty?
355
381
  media_types.split(/[\s]+|,/).any? { |media_type| media_type.strip =~ /screen|handheld|all/i }
356
382
  end
357
383
 
358
- def append_query_string(doc, qs)
359
- return doc if qs.nil?
384
+ def append_query_string(doc, queries)
385
+ return doc if queries.nil?
360
386
 
361
- qs.to_s.gsub!(/^[\?]*/, '').strip!
362
- return doc if qs.empty?
387
+ queries = +queries
388
+ queries.to_s.gsub!(/^[\?]*/, '').strip!
389
+ return doc if queries.empty?
363
390
 
364
391
  begin
365
392
  current_host = @base_url.host
366
- rescue
393
+ rescue StandardError
367
394
  current_host = nil
368
395
  end
369
396
 
370
- $stderr.puts "Attempting to append_query_string: #{qs}" if @options[:verbose]
397
+ warn "Attempting to append_query_string: #{queries}" if @options[:verbose]
371
398
 
372
- doc.search('a').each do|el|
399
+ doc.search('a').each do |el|
373
400
  href = el.attributes['href'].to_s.strip
374
- next if href.nil? or href.empty?
401
+ next if href.nil? || href.empty?
375
402
 
376
- next if href[0,1] =~ /[\#\{\[\<\%]/ # don't bother with anchors or special-looking links
403
+ next if /[\#\{\[\<\%]/.match?(href[0, 1]) # don't bother with anchors or special-looking links
377
404
 
378
405
  begin
379
406
  href = Addressable::URI.parse(href)
380
407
 
381
- if current_host and href.host != nil and href.host != current_host
382
- $stderr.puts "Skipping append_query_string for: #{href.to_s} because host is no good" if @options[:verbose]
408
+ if current_host && !href.host.nil? && (href.host != current_host)
409
+ warn "Skipping append_query_string for: #{href} because host is no good" if @options[:verbose]
383
410
  next
384
411
  end
385
412
 
386
- if href.scheme and href.scheme != 'http' and href.scheme != 'https'
387
- puts "Skipping append_query_string for: #{href.to_s} because scheme is no good" if @options[:verbose]
413
+ if href.scheme && (href.scheme != 'http') && (href.scheme != 'https')
414
+ puts "Skipping append_query_string for: #{href} because scheme is no good" if @options[:verbose]
388
415
  next
389
416
  end
390
417
 
391
- if href.query and not href.query.empty?
418
+ if href.query && !href.query.empty?
392
419
  amp = @options[:unescaped_ampersand] ? '&' : '&amp;'
393
- href.query = href.query + amp + qs
420
+ href.query = href.query + amp + queries
394
421
  else
395
- href.query = qs
422
+ href.query = queries
396
423
  end
397
424
 
398
425
  el['href'] = href.to_s
399
426
  rescue Addressable::URI::InvalidURIError => e
400
- $stderr.puts "Skipping append_query_string for: #{href.to_s} (#{e.message})" if @options[:verbose]
427
+ warn "Skipping append_query_string for: #{href} (#{e.message})" if @options[:verbose]
401
428
  next
402
429
  end
403
-
404
430
  end
405
431
  doc
406
432
  end
407
433
 
408
434
  # Check for an XHTML doctype
409
- def is_xhtml?
435
+ def xhtml?
410
436
  intro = @doc.to_xhtml.strip.split("\n")[0..2].join(' ')
411
437
  is_xhtml = !!(intro =~ /w3c\/\/[\s]*dtd[\s]+xhtml/i)
412
- $stderr.puts "Is XHTML? #{is_xhtml.inspect}\nChecked:\n#{intro}" if @options[:debug]
438
+ warn "Is XHTML? #{is_xhtml.inspect}\nChecked:\n#{intro}" if @options[:debug]
413
439
  is_xhtml
414
440
  end
415
441
 
@@ -422,9 +448,8 @@ public
422
448
  #
423
449
  # Returns a document.
424
450
  def convert_inline_links(doc, base_uri) # :nodoc:
425
- base_uri = Addressable::URI.parse(base_uri) unless base_uri.kind_of?(Addressable::URI)
451
+ base_uri = Addressable::URI.parse(base_uri) unless base_uri.is_a?(Addressable::URI)
426
452
 
427
- append_qs = @options[:link_query_string] || ''
428
453
  escape_attrs = @options[:escape_url_attributes]
429
454
 
430
455
  ['href', 'src', 'background'].each do |attribute|
@@ -435,30 +460,30 @@ public
435
460
  tags.each do |tag|
436
461
  # skip links that look like they have merge tags
437
462
  # and mailto, ftp, etc...
438
- if tag.attributes[attribute].to_s =~ /^([\%\<\{\#\[]|data:|tel:|file:|sms:|callto:|facetime:|mailto:|ftp:|gopher:|cid:)/i
463
+ if /^([\%\<\{\#\[]|data:|tel:|file:|sms:|callto:|facetime:|mailto:|ftp:|gopher:|cid:)/i.match?(tag.attributes[attribute].to_s)
439
464
  next
440
465
  end
441
466
 
442
- if tag.attributes[attribute].to_s =~ /^http/i
467
+ if /^http/i.match?(tag.attributes[attribute].to_s)
443
468
  begin
444
469
  merged = Addressable::URI.parse(tag.attributes[attribute])
445
- rescue; next; end
470
+ rescue StandardError; next; end
446
471
  else
447
472
  begin
448
473
  merged = Premailer.resolve_link(tag.attributes[attribute].to_s, base_uri)
449
- rescue
474
+ rescue StandardError
450
475
  begin
451
476
  next unless escape_attrs
452
477
  merged = Premailer.resolve_link(Addressable::URI.escape(tag.attributes[attribute].to_s), base_uri)
453
- rescue; end
478
+ rescue StandardError; end
454
479
  end
455
480
  end
456
481
 
457
482
  # make sure 'merged' is a URI
458
- merged = Addressable::URI.parse(merged.to_s) unless merged.kind_of?(Addressable::URI)
483
+ merged = Addressable::URI.parse(merged.to_s) unless merged.is_a?(Addressable::URI)
459
484
  tag[attribute] = merged.to_s
460
- end # end of each tag
461
- end # end of each attrs
485
+ end
486
+ end
462
487
 
463
488
  doc.search("*[@style]").each do |el|
464
489
  el['style'] = CssParser.convert_uris(el.attributes['style'].to_s, base_uri)
@@ -467,21 +492,21 @@ public
467
492
  end
468
493
 
469
494
  # @private
470
- def self.is_media_query?(media_types)
471
- media_types && media_types.any?{|mt| mt.to_s.count('()') >= 2 }
495
+ def self.media_query?(media_types)
496
+ media_types&.any? { |mt| mt.to_s.count('()') >= 2 }
472
497
  end
473
498
 
474
499
  # @private
475
500
  def self.resolve_link(path, base_path) # :nodoc:
501
+ path = +path
476
502
  path.strip!
477
- resolved = nil
478
- if path =~ /\A(?:(https?|ftp|file):)\/\//i
503
+ if /\A(?:(https?|ftp|file):)\/\//i.match?(path)
479
504
  resolved = path
480
505
  Premailer.canonicalize(resolved)
481
- elsif base_path.kind_of?(Addressable::URI)
506
+ elsif base_path.is_a?(Addressable::URI)
482
507
  resolved = base_path.join(path)
483
508
  Premailer.canonicalize(resolved)
484
- elsif base_path.kind_of?(String) and base_path =~ /\A(?:(?:https?|ftp|file):)\/\//i
509
+ elsif base_path.is_a?(String) && base_path =~ (/\A(?:(?:https?|ftp|file):)\/\//i)
485
510
  resolved = Addressable::URI.parse(base_path)
486
511
  resolved = resolved.join(path)
487
512
  Premailer.canonicalize(resolved)
@@ -494,13 +519,13 @@ public
494
519
  #
495
520
  # IO objects return true, as do strings that look like URLs.
496
521
  def self.local_data?(data)
497
- return false if data.kind_of?(String) && data =~ /\A(?:(https?|ftp):)\/\//i
522
+ return false if data.is_a?(String) && data =~ /\A(?:(https?|ftp):)\/\//i
498
523
  true
499
524
  end
500
525
 
501
526
  # from http://www.ruby-forum.com/topic/140101
502
527
  def self.canonicalize(uri) # :nodoc:
503
- u = uri.kind_of?(Addressable::URI) ? uri : Addressable::URI.parse(uri.to_s)
528
+ u = uri.is_a?(Addressable::URI) ? uri : Addressable::URI.parse(uri.to_s)
504
529
  u.normalize!
505
530
  u.to_s
506
531
  end
@@ -515,7 +540,7 @@ public
515
540
 
516
541
  # Get a list off CSS properties
517
542
  @processed_doc.search("*[@style]").each do |el|
518
- style_url = el.attributes['style'].to_s.gsub(/([\w\-]+)[\s]*\:/i) do |s|
543
+ el.attributes['style'].to_s.gsub(/([\w\-]+)[\s]*:/i) do |_s|
519
544
  properties.push($1)
520
545
  end
521
546
  end
@@ -524,30 +549,42 @@ public
524
549
 
525
550
  property_support = @client_support['css_properties']
526
551
  properties.each do |prop|
527
- if property_support.include?(prop) and
528
- property_support[prop].include?('support') and
529
- property_support[prop]['support'] >= @options[:warn_level]
530
- warnings.push({:message => "#{prop} CSS property",
552
+ if property_support.include?(prop) &&
553
+ property_support[prop].include?('support') &&
554
+ (property_support[prop]['support'] >= @options[:warn_level])
555
+ warnings.push(
556
+ {
557
+ :message => "#{prop} CSS property",
531
558
  :level => WARN_LABEL[property_support[prop]['support']],
532
- :clients => property_support[prop]['unsupported_in'].join(', ')})
559
+ :clients => property_support[prop]['unsupported_in'].join(', ')
560
+ }
561
+ )
533
562
  end
534
563
  end
535
564
 
536
565
  @client_support['attributes'].each do |attribute, data|
537
566
  next unless data['support'] >= @options[:warn_level]
538
567
  if @doc.search("*[@#{attribute}]").length > 0
539
- warnings.push({:message => "#{attribute} HTML attribute",
568
+ warnings.push(
569
+ {
570
+ :message => "#{attribute} HTML attribute",
540
571
  :level => WARN_LABEL[data['support']],
541
- :clients => data['unsupported_in'].join(', ')})
572
+ :clients => data['unsupported_in'].join(', ')
573
+ }
574
+ )
542
575
  end
543
576
  end
544
577
 
545
578
  @client_support['elements'].each do |element, data|
546
579
  next unless data['support'] >= @options[:warn_level]
547
580
  if @doc.search(element).length > 0
548
- warnings.push({:message => "#{element} HTML element",
581
+ warnings.push(
582
+ {
583
+ :message => "#{element} HTML element",
549
584
  :level => WARN_LABEL[data['support']],
550
- :clients => data['unsupported_in'].join(', ')})
585
+ :clients => data['unsupported_in'].join(', ')
586
+ }
587
+ )
551
588
  end
552
589
  end
553
590
 
@@ -1,4 +1,5 @@
1
+ # frozen_string_literal: true
1
2
  class Premailer
2
3
  # Premailer version.
3
- VERSION = '1.23.0'.freeze
4
+ VERSION = '1.29.0'
4
5
  end
data/lib/premailer.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'yaml'
2
3
  require 'open-uri'
3
4
  require 'digest/sha2'