slim 1.3.6 → 1.3.8

Sign up to get free protection for your applications and to get access to all the features.
data/lib/slim/engine.rb CHANGED
@@ -5,27 +5,42 @@ module Slim
5
5
  # This overwrites some Temple default options or sets default options for Slim specific filters.
6
6
  # It is recommended to set the default settings only once in the code and avoid duplication. Only use
7
7
  # `define_options` when you have to override some default settings.
8
- define_options :pretty => false,
8
+ define_options :attr_quote,
9
+ :merge_attrs,
10
+ :pretty => false,
9
11
  :sort_attrs => true,
10
- :attr_wrapper => '"',
11
- :attr_delimiter => {'class' => ' '},
12
12
  :generator => Temple::Generators::ArrayBuffer,
13
- :default_tag => 'div'
13
+ :default_tag => 'div',
14
+ :attr_quote => '"',
15
+ :merge_attrs => {'class' => ' '},
16
+ :escape_quoted_attrs => false
14
17
 
15
- # TODO: Remove these options in 1.4.0
16
- define_deprecated_options :remove_empty_attrs, :chain
18
+ # Removed in 2.0
19
+ define_deprecated_options :remove_empty_attrs,
20
+ :chain,
21
+ :escape_quoted_attrs,
22
+ :attr_wrapper,
23
+ :attr_delimiter
24
+
25
+ def initialize(opts = {})
26
+ super
27
+ deprecated = {}
28
+ deprecated[:merge_attrs] = options[:attr_delimiter] if options.include? :attr_delimiter
29
+ deprecated[:attr_quote] = options[:attr_wrapper] if options.include? :attr_wrapper
30
+ @options = Temple::ImmutableHash.new(deprecated, @options) unless deprecated.empty?
31
+ end
17
32
 
18
33
  use Slim::Parser, :file, :tabsize, :encoding, :shortcut, :default_tag, :escape_quoted_attrs
19
- use Slim::EmbeddedEngine, :enable_engines, :disable_engines, :pretty
34
+ use Slim::Embedded, :enable_engines, :disable_engines, :pretty
20
35
  use Slim::Interpolation
21
36
  use Slim::EndInserter
22
- use Slim::ControlStructures, :disable_capture
23
- use Slim::SplatAttributes, :attr_delimiter, :attr_wrapper, :sort_attrs, :default_tag
37
+ use Slim::Controls, :disable_capture
38
+ use Slim::SplatAttributes, :merge_attrs, :attr_quote, :sort_attrs, :default_tag
24
39
  html :AttributeSorter, :sort_attrs
25
- html :AttributeMerger, :attr_delimiter
26
- use Slim::CodeAttributes, :attr_delimiter
27
- use(:AttributeRemover) { Temple::HTML::AttributeRemover.new(:remove_empty_attrs => options[:attr_delimiter].keys) }
28
- html :Pretty, :format, :attr_wrapper, :pretty, :indent
40
+ html :AttributeMerger, :merge_attrs
41
+ use Slim::CodeAttributes, :merge_attrs
42
+ use(:AttributeRemover) { Temple::HTML::AttributeRemover.new(:remove_empty_attrs => options[:merge_attrs].keys) }
43
+ html :Pretty, :format, :attr_quote, :pretty, :indent, :js_wrapper
29
44
  filter :Escapable, :use_html_safe, :disable_escape
30
45
  filter :ControlFlow
31
46
  filter :MultiFlattener
data/lib/slim/filter.rb CHANGED
@@ -14,7 +14,7 @@ module Slim
14
14
 
15
15
  # Pass-through handler
16
16
  def on_slim_embedded(type, content)
17
- [:slim, :embedded, code, compile(content)]
17
+ [:slim, :embedded, type, compile(content)]
18
18
  end
19
19
 
20
20
  # Pass-through handler
@@ -14,7 +14,7 @@ module Slim
14
14
  super
15
15
  access = options[:dictionary_access]
16
16
  if access == :wrapped
17
- warn 'Slim::LogicLess - Wrapped dictionary access is deprecated'
17
+ warn 'Slim::LogicLess - Wrapped dictionary access is deprecated and unsupported in Slim 2.0'
18
18
  access = DEFAULT_ACCESS_ORDER
19
19
  else
20
20
  access = [access].flatten.compact
data/lib/slim/parser.rb CHANGED
@@ -4,7 +4,7 @@ module Slim
4
4
  class Parser < Temple::Parser
5
5
  define_options :file,
6
6
  :default_tag,
7
- :escape_quoted_attrs => false,
7
+ :escape_quoted_attrs,
8
8
  :tabsize => 4,
9
9
  :encoding => 'utf-8',
10
10
  :shortcut => {
@@ -24,7 +24,7 @@ module Slim
24
24
  end
25
25
 
26
26
  def to_s
27
- line = @line.strip
27
+ line = @line.lstrip
28
28
  column = @column + line.size - @line.size
29
29
  %{#{error}
30
30
  #{file}, Line #{lineno}, Column #{@column}
@@ -36,7 +36,14 @@ module Slim
36
36
 
37
37
  def initialize(opts = {})
38
38
  super
39
- @tab = ' ' * options[:tabsize]
39
+ tabsize = options[:tabsize]
40
+ if tabsize > 1
41
+ @tab_re = /\G((?: {#{tabsize}})*) {0,#{tabsize-1}}\t/
42
+ @tab = '\1' + ' ' * tabsize
43
+ else
44
+ @tab_re = "\t"
45
+ @tab = ' '
46
+ end
40
47
  @tag_shortcut, @attr_shortcut = {}, {}
41
48
  options[:shortcut].each do |k,v|
42
49
  v = deprecated_shortcut(v) if String === v
@@ -47,8 +54,8 @@ module Slim
47
54
  raise ArgumentError, 'You can only use special characters for attribute shortcuts' if k =~ /[\w-]/
48
55
  end
49
56
  end
50
- @attr_shortcut_regex = /\A(#{shortcut_regex @attr_shortcut})(\w[\w-]*\w|\w+)/
51
- @tag_regex = /\A(?:#{shortcut_regex @tag_shortcut}|\*(?=[^\s]+)|(\w[\w:-]*\w|\w+))/
57
+ @attr_shortcut_regex = /\A(#{Regexp.union @attr_shortcut.keys})(\w[\w-]*\w|\w+)/
58
+ @tag_regex = /\A(?:#{Regexp.union @tag_shortcut.keys}|\*(?=[^\s]+)|(\w[\w:-]*\w|\w+))/
52
59
  end
53
60
 
54
61
  # Compile string to Temple expression
@@ -75,14 +82,17 @@ module Slim
75
82
  '{' => '}',
76
83
  }.freeze
77
84
 
85
+ ATTR_DELIM_REGEX = /\A\s*[#{Regexp.escape DELIMITERS.keys.join}]/
78
86
  DELIMITER_REGEX = /\A[#{Regexp.escape DELIMITERS.keys.join}]/
79
87
  ATTR_NAME = '\A\s*(\w[:\w-]*)'
80
88
  QUOTED_ATTR_REGEX = /#{ATTR_NAME}=(=?)("|')/
81
89
  CODE_ATTR_REGEX = /#{ATTR_NAME}=(=?)/
90
+ QUOTED_ATTR_REGEX_20 = /#{ATTR_NAME}\s*=(=?)\s*("|')/
91
+ CODE_ATTR_REGEX_20 = /#{ATTR_NAME}\s*=(=?)\s*/
82
92
 
83
93
  # Convert deprecated string shortcut to hash
84
94
  def deprecated_shortcut(v)
85
- warn "Slim :shortcut string values are deprecated, use hash like { '#' => { :tag => 'div', :attr => 'id' } }"
95
+ warn "Slim :shortcut string values are deprecated and unsupported in Slim 2.0, use hash like { '#' => { :tag => 'div', :attr => 'id' } }"
86
96
  if v =~ /\A([^\s]+)\s+([^\s]+)\Z/
87
97
  { :tag => $1, :attr => $2 }
88
98
  else
@@ -90,11 +100,6 @@ module Slim
90
100
  end
91
101
  end
92
102
 
93
- # Compile shortcut regular expression
94
- def shortcut_regex(shortcut)
95
- shortcut.map { |k,v| Regexp.escape(k) }.join('|')
96
- end
97
-
98
103
  # Set string encoding if option is set
99
104
  def set_encoding(s)
100
105
  if options[:encoding] && s.respond_to?(:encoding)
@@ -102,7 +107,10 @@ module Slim
102
107
  s = s.dup if s.frozen?
103
108
  s.force_encoding(options[:encoding])
104
109
  # Fall back to old encoding if new encoding is invalid
105
- s.force_encoding(old_enc) unless s.valid_encoding?
110
+ unless s.valid_encoding?
111
+ s.force_encoding(old_enc)
112
+ s.force_encoding(Encoding::BINARY) unless s.valid_encoding?
113
+ end
106
114
  end
107
115
  s
108
116
  end
@@ -160,7 +168,7 @@ module Slim
160
168
  def get_indent(line)
161
169
  # Figure out the indentation. Kinda ugly/slow way to support tabs,
162
170
  # but remember that this is only done at parsing time.
163
- line[/\A[ \t]*/].gsub("\t", @tab).size
171
+ line[/\A[ \t]*/].gsub(@tab_re, @tab).size
164
172
  end
165
173
 
166
174
  def parse_line
@@ -354,8 +362,10 @@ module Slim
354
362
  tag << [:slim, :output, $1 != '=', parse_broken_line, block]
355
363
  @stacks.last << [:static, ' '] unless $2.empty?
356
364
  @stacks << block
357
- when /\A\s*\//
365
+ when /\A\s*\/\s*/
358
366
  # Closed tag. Do nothing
367
+ @line = $'
368
+ syntax_error!('Unexpected text after closed tag') unless @line.empty?
359
369
  when /\A\s*\Z/
360
370
  # Empty content
361
371
  content = [:multi]
@@ -380,9 +390,13 @@ module Slim
380
390
 
381
391
  # Check to see if there is a delimiter right after the tag name
382
392
  delimiter = nil
383
- if @line =~ DELIMITER_REGEX
384
- delimiter = DELIMITERS[$&]
385
- @line.slice!(0)
393
+ if @line =~ ATTR_DELIM_REGEX
394
+ if $&.size > 1
395
+ warn "#{options[:file]}:#{@lineno} - spaces around attribute delimiters will be allowed in Slim 2.0. Your code is incompatible."
396
+ else
397
+ delimiter = DELIMITERS[$&]
398
+ @line.slice!(0)
399
+ end
386
400
  end
387
401
 
388
402
  if delimiter
@@ -399,9 +413,16 @@ module Slim
399
413
  when QUOTED_ATTR_REGEX
400
414
  # Value is quoted (static)
401
415
  @line = $'
402
- attributes << [:html, :attr, $1,
403
- [:escape, options[:escape_quoted_attrs] && $2.empty?,
404
- [:slim, :interpolate, parse_quoted_attribute($3)]]]
416
+ name = $1
417
+ escape = $2.empty?
418
+ quote = $3
419
+ value = parse_quoted_attribute(quote)
420
+ if escape && !options[:escape_quoted_attrs] && value =~ /&(amp|quot|gt|lt);/
421
+ warn "#{options[:file]}:#{@lineno} - quoted attribute value '#{value}' might be double escaped in Slim 2.0. Remove manually escaped entities and set :escape_quoted_attrs => true! :escaped_quoted_attrs is activated on Slim 2.0 by default."
422
+ end
423
+ attributes << [:html, :attr, name,
424
+ [:escape, options[:escape_quoted_attrs] && escape,
425
+ [:slim, :interpolate, value]]]
405
426
  when CODE_ATTR_REGEX
406
427
  # Value is ruby code
407
428
  @line = $'
@@ -410,11 +431,17 @@ module Slim
410
431
  value = parse_ruby_code(delimiter)
411
432
  # Remove attribute wrapper which doesn't belong to the ruby code
412
433
  # e.g id=[hash[:a] + hash[:b]]
413
- value = value[1..-2] if value =~ DELIMITER_REGEX &&
414
- DELIMITERS[$&] == value[-1, 1]
434
+ if value =~ /\A[\[\{]/ && DELIMITERS[$&] == value[-1, 1]
435
+ warn "#{options[:file]}:#{@lineno} - ruby attribute value #{value} with curly braces/brackets is deprecated and unsupported in Slim 2.0. Use parentheses!"
436
+ value = value[1..-2]
437
+ end
415
438
  syntax_error!('Invalid empty attribute') if value.empty?
416
439
  attributes << [:html, :attr, name, [:slim, :attrvalue, escape, value]]
417
440
  else
441
+ if @line =~ QUOTED_ATTR_REGEX_20 || @line =~ CODE_ATTR_REGEX_20
442
+ warn "#{options[:file]}:#{@lineno} - you have spaces around =, this will be interpreted as attribute in Slim 2.0."
443
+ end
444
+
418
445
  break unless delimiter
419
446
 
420
447
  case @line
@@ -1,13 +1,13 @@
1
1
  module Slim
2
2
  # @api private
3
3
  class SplatAttributes < Filter
4
- define_options :attr_delimiter, :attr_wrapper, :sort_attrs, :default_tag
4
+ define_options :sort_attrs, :default_tag, :merge_attrs, :attr_quote
5
5
 
6
6
  def call(exp)
7
- @attr_delimiter, @splat_used = unique_name, false
7
+ @merge_attrs, @splat_used = unique_name, false
8
8
  exp = compile(exp)
9
9
  if @splat_used
10
- [:multi, [:code, "#{@attr_delimiter} = #{@options[:attr_delimiter].inspect}"], exp]
10
+ [:multi, [:code, "#{@merge_attrs} = #{@options[:merge_attrs].inspect}"], exp]
11
11
  else
12
12
  exp
13
13
  end
@@ -82,14 +82,14 @@ module Slim
82
82
  merger << [:block, "#{hash}.keys.each do |#{name}|",
83
83
  [:multi,
84
84
  [:code, "#{value} = #{hash}[#{name}]"],
85
- [:if, "#{@attr_delimiter}[#{name}]",
85
+ [:if, "#{@merge_attrs}[#{name}]",
86
86
  [:multi,
87
87
  [:code, "#{value}.flatten!"],
88
88
  [:code, "#{value}.map!(&:to_s)"],
89
89
  [:code, "#{value}.reject!(&:empty?)"],
90
90
  [:if, "#{value}.empty?",
91
91
  [:code, "#{hash}.delete(#{name})"],
92
- [:code, "#{hash}[#{name}] = #{value}.join(#{@attr_delimiter}[#{name}].to_s)"]]],
92
+ [:code, "#{hash}[#{name}] = #{value}.join(#{@merge_attrs}[#{name}].to_s)"]]],
93
93
  [:multi,
94
94
  [:if, "#{value}.size > 1",
95
95
  [:code, %{raise("Multiple #\{#{name}\} attributes specified")}]],
@@ -101,9 +101,9 @@ module Slim
101
101
  attr = [:multi,
102
102
  [:static, ' '],
103
103
  [:dynamic, name],
104
- [:static, "=#{options[:attr_wrapper]}"],
104
+ [:static, "=#{options[:attr_quote]}"],
105
105
  [:escape, true, [:dynamic, value]],
106
- [:static, options[:attr_wrapper]]]
106
+ [:static, options[:attr_quote]]]
107
107
  enumerator = options[:sort_attrs] ? "#{hash}.sort_by {|#{name},#{value}| #{name} }" : hash
108
108
  formatter = [:block, "#{enumerator}.each do |#{name},#{value}|", attr]
109
109
 
data/lib/slim/template.rb CHANGED
@@ -3,7 +3,7 @@ module Slim
3
3
  # @api public
4
4
  Template = Temple::Templates::Tilt(Slim::Engine, :register_as => :slim)
5
5
 
6
- if Object.const_defined?(:Rails)
6
+ if defined?(::ActionView)
7
7
  # Rails template implementation for Slim
8
8
  # @api public
9
9
  RailsTemplate = Temple::Templates::Rails(Slim::Engine,
@@ -14,6 +14,6 @@ module Slim
14
14
  # Disable the internal slim capturing.
15
15
  # Rails takes care of the capturing by itself.
16
16
  :disable_capture => true,
17
- :streaming => Object.const_defined?(:Fiber))
17
+ :streaming => defined?(::Fiber))
18
18
  end
19
19
  end
@@ -6,13 +6,13 @@ module Slim
6
6
  :tr_fn => '_',
7
7
  :tr => false
8
8
 
9
- if Object.const_defined?(:I18n)
9
+ if defined?(::I18n)
10
10
  set_default_options :tr_fn => '::Slim::Translator.i18n_text',
11
11
  :tr => true
12
- elsif Object.const_defined?(:GetText)
12
+ elsif defined?(::GetText)
13
13
  set_default_options :tr_fn => '::GetText._',
14
14
  :tr => true
15
- elsif Object.const_defined?(:FastGettext)
15
+ elsif defined?(::FastGettext)
16
16
  set_default_options :tr_fn => '::FastGettext::Translation._',
17
17
  :tr => true
18
18
  end
data/lib/slim/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Slim
2
2
  # Slim version string
3
3
  # @api public
4
- VERSION = '1.3.6'
4
+ VERSION = '1.3.8'
5
5
  end
data/slim.gemspec CHANGED
@@ -17,13 +17,6 @@ Gem::Specification.new do |s|
17
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
18
  s.require_paths = %w(lib)
19
19
 
20
- s.add_runtime_dependency('temple', ['~> 0.5.5'])
20
+ s.add_runtime_dependency('temple', ['~> 0.6.3'])
21
21
  s.add_runtime_dependency('tilt', ['~> 1.3.3'])
22
-
23
- s.add_development_dependency('rake', ['>= 0.8.7'])
24
- s.add_development_dependency('sass', ['>= 3.1.0'])
25
- s.add_development_dependency('minitest', ['>= 0'])
26
- s.add_development_dependency('kramdown', ['>= 0'])
27
- s.add_development_dependency('creole', ['>= 0'])
28
- s.add_development_dependency('builder', ['>= 0'])
29
22
  end
data/test/core/helper.rb CHANGED
@@ -16,7 +16,27 @@ class TestSlim < MiniTest::Unit::TestCase
16
16
 
17
17
  def render(source, options = {}, &block)
18
18
  scope = options.delete(:scope)
19
- Slim::Template.new(options[:file], options) { source }.render(scope || @env, &block)
19
+ locals = options.delete(:locals)
20
+ Slim::Template.new(options[:file], options) { source }.render(scope || @env, locals, &block)
21
+ end
22
+
23
+ class HtmlSafeString < String
24
+ def html_safe?
25
+ true
26
+ end
27
+
28
+ def to_s
29
+ self
30
+ end
31
+ end
32
+
33
+ def with_html_safe
34
+ String.send(:define_method, :html_safe?) { false }
35
+ String.send(:define_method, :html_safe) { HtmlSafeString.new(self) }
36
+ yield
37
+ ensure
38
+ String.send(:undef_method, :html_safe?) if String.method_defined?(:html_safe?)
39
+ String.send(:undef_method, :html_safe) if String.method_defined?(:html_safe)
20
40
  end
21
41
 
22
42
  def assert_html(expected, source, options = {}, &block)
@@ -69,18 +89,6 @@ end
69
89
  class Env
70
90
  attr_reader :var, :x
71
91
 
72
- class ::HtmlSafeString < String
73
- def html_safe?
74
- true
75
- end
76
- end
77
-
78
- class ::HtmlUnsafeString < String
79
- def html_safe?
80
- false
81
- end
82
- end
83
-
84
92
  def initialize
85
93
  @var = 'instance'
86
94
  @x = 0
@@ -19,18 +19,22 @@ p = "<strong>Hello World\\n, meet \\"Slim\\"</strong>."
19
19
 
20
20
  def test_render_with_html_safe_false
21
21
  source = %q{
22
- p = HtmlUnsafeString.new("<strong>Hello World\\n, meet \\"Slim\\"</strong>.")
22
+ p = "<strong>Hello World\\n, meet \\"Slim\\"</strong>."
23
23
  }
24
24
 
25
- assert_html "<p>&lt;strong&gt;Hello World\n, meet \&quot;Slim\&quot;&lt;/strong&gt;.</p>", source, :use_html_safe => true
25
+ with_html_safe do
26
+ assert_html "<p>&lt;strong&gt;Hello World\n, meet \&quot;Slim\&quot;&lt;/strong&gt;.</p>", source, :use_html_safe => true
27
+ end
26
28
  end
27
29
 
28
30
  def test_render_with_html_safe_true
29
31
  source = %q{
30
- p = HtmlSafeString.new("<strong>Hello World\\n, meet \\"Slim\\"</strong>.")
32
+ p = "<strong>Hello World\\n, meet \\"Slim\\"</strong>.".html_safe
31
33
  }
32
34
 
33
- assert_html "<p><strong>Hello World\n, meet \"Slim\"</strong>.</p>", source, :use_html_safe => true
35
+ with_html_safe do
36
+ assert_html "<p><strong>Hello World\n, meet \"Slim\"</strong>.</p>", source, :use_html_safe => true
37
+ end
34
38
  end
35
39
 
36
40
  def test_render_with_disable_escape_false
@@ -50,4 +54,58 @@ p = HtmlSafeString.new("<strong>Hello World\\n, meet \\"Slim\\"</strong>.")
50
54
 
51
55
  assert_html "<p>Hello</p><p>World</p>", source, :disable_escape => true
52
56
  end
57
+
58
+ def test_escaping_evil_method_with_pretty
59
+ source = %q{
60
+ p = evil_method
61
+ }
62
+
63
+ assert_html "<p>\n &lt;script&gt;do_something_evil();&lt;/script&gt;\n</p>", source, :pretty => true
64
+ end
65
+
66
+ def test_render_without_html_safe_with_pretty
67
+ source = %q{
68
+ p = "<strong>Hello World\\n, meet \\"Slim\\"</strong>."
69
+ }
70
+
71
+ assert_html "<p>\n &lt;strong&gt;Hello World\n , meet \&quot;Slim\&quot;&lt;/strong&gt;.\n</p>", source, :pretty => true
72
+ end
73
+
74
+ def test_render_with_html_safe_false_with_pretty
75
+ source = %q{
76
+ p = "<strong>Hello World\\n, meet \\"Slim\\"</strong>."
77
+ }
78
+
79
+ with_html_safe do
80
+ assert_html "<p>\n &lt;strong&gt;Hello World\n , meet \&quot;Slim\&quot;&lt;/strong&gt;.\n</p>", source, :use_html_safe => true, :pretty => true
81
+ end
82
+ end
83
+
84
+ def test_render_with_html_safe_true_with_pretty
85
+ source = %q{
86
+ p = "<strong>Hello World\\n, meet \\"Slim\\"</strong>.".html_safe
87
+ }
88
+
89
+ with_html_safe do
90
+ assert_html "<p>\n <strong>Hello World\n , meet \"Slim\"</strong>.\n</p>", source, :use_html_safe => true, :pretty => true
91
+ end
92
+ end
93
+
94
+ def test_render_with_disable_escape_false_with_pretty
95
+ source = %q{
96
+ = "<p>Hello</p>"
97
+ == "<p>World</p>"
98
+ }
99
+
100
+ assert_html "&lt;p&gt;Hello&lt;/p&gt;<p>World</p>", source, :pretty => true
101
+ end
102
+
103
+ def test_render_with_disable_escape_true_with_pretty
104
+ source = %q{
105
+ = "<p>Hello</p>"
106
+ == "<p>World</p>"
107
+ }
108
+
109
+ assert_html "<p>Hello</p><p>World</p>", source, :disable_escape => true, :pretty => true
110
+ end
53
111
  end