slim 1.3.9 → 2.0.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -8
  3. data/.travis.yml +8 -7
  4. data/CHANGES +35 -0
  5. data/Gemfile +9 -9
  6. data/README.md +94 -176
  7. data/Rakefile +7 -14
  8. data/benchmarks/run-benchmarks.rb +9 -37
  9. data/doc/logic_less.md +140 -0
  10. data/doc/translator.md +31 -0
  11. data/lib/slim.rb +3 -1
  12. data/lib/slim/code_attributes.rb +20 -20
  13. data/lib/slim/command.rb +16 -6
  14. data/lib/slim/do_inserter.rb +33 -0
  15. data/lib/slim/embedded.rb +0 -6
  16. data/lib/slim/end_inserter.rb +2 -2
  17. data/lib/slim/engine.rb +9 -23
  18. data/lib/slim/erb_converter.rb +14 -0
  19. data/lib/slim/filter.rb +2 -2
  20. data/lib/slim/logic_less/context.rb +8 -0
  21. data/lib/slim/logic_less/filter.rb +12 -11
  22. data/lib/slim/parser.rb +57 -113
  23. data/lib/slim/splat/builder.rb +79 -0
  24. data/lib/slim/splat/filter.rb +93 -0
  25. data/lib/slim/version.rb +1 -1
  26. data/slim.gemspec +1 -1
  27. data/test/core/helper.rb +1 -3
  28. data/test/core/test_code_blocks.rb +51 -0
  29. data/test/core/test_code_evaluation.rb +4 -12
  30. data/test/core/test_embedded_engines.rb +18 -44
  31. data/test/core/test_encoding.rb +8 -1
  32. data/test/core/test_erb_converter.rb +67 -0
  33. data/test/core/test_html_attributes.rb +19 -25
  34. data/test/core/test_html_escaping.rb +10 -2
  35. data/test/core/test_html_structure.rb +6 -6
  36. data/test/core/test_parser_errors.rb +1 -1
  37. data/test/core/test_ruby_errors.rb +2 -6
  38. data/test/core/test_thread_options.rb +4 -4
  39. data/test/core/test_unicode.rb +18 -0
  40. data/test/literate/TESTS.md +193 -34
  41. data/test/logic_less/test_logic_less.rb +17 -0
  42. data/test/rails/app/controllers/application_controller.rb +0 -1
  43. data/test/rails/app/controllers/entries_controller.rb +5 -0
  44. data/test/rails/app/controllers/slim_controller.rb +3 -0
  45. data/test/rails/app/models/entry.rb +16 -0
  46. data/test/rails/app/views/entries/edit.html.slim +3 -0
  47. data/test/rails/app/views/slim/form_for.html.slim +2 -0
  48. data/test/rails/app/views/slim/xml.slim +1 -0
  49. data/test/rails/config/application.rb +2 -2
  50. data/test/rails/config/environments/test.rb +1 -1
  51. data/test/rails/config/routes.rb +1 -1
  52. data/test/rails/test/helper.rb +0 -3
  53. data/test/rails/test/test_slim.rb +13 -8
  54. data/test/translator/test_translator.rb +13 -2
  55. metadata +17 -14
  56. data/lib/slim/splat_attributes.rb +0 -113
  57. data/test/rails/app/controllers/parents_controller.rb +0 -85
  58. data/test/rails/app/models/child.rb +0 -3
  59. data/test/rails/app/models/parent.rb +0 -4
  60. data/test/rails/app/views/parents/_form.html.slim +0 -8
  61. data/test/rails/app/views/parents/edit.html.slim +0 -2
  62. data/test/rails/app/views/parents/new.html.slim +0 -2
  63. data/test/rails/app/views/parents/show.html.slim +0 -5
  64. data/test/rails/config/database.yml +0 -4
  65. data/test/rails/db/migrate/20101220223037_parents_and_children.rb +0 -17
@@ -0,0 +1,79 @@
1
+ module Slim
2
+ module Splat
3
+ # @api private
4
+ class Builder
5
+ def initialize(options)
6
+ @options = options
7
+ @attrs = {}
8
+ end
9
+
10
+ def code_attr(name, escape, value)
11
+ if delim = @options[:merge_attrs][name]
12
+ value = Array === value ? value.join(delim) : value.to_s
13
+ attr(name, escape ? Temple::Utils.escape_html(value) : value) unless value.empty?
14
+ elsif @options[:hyphen_attrs].include?(name) && Hash === value
15
+ hyphen_attr(name, escape, value)
16
+ else
17
+ case value
18
+ when false, nil
19
+ # Boolean false attribute
20
+ return
21
+ when true
22
+ # Boolean true attribute
23
+ value = name
24
+ else
25
+ value = value.to_s
26
+ end
27
+ attr(name, escape ? Temple::Utils.escape_html(value) : value)
28
+ end
29
+ end
30
+
31
+ def splat_attrs(splat)
32
+ splat.each do |name, value|
33
+ code_attr(name.to_s, true, value)
34
+ end
35
+ end
36
+
37
+ def attr(name, value)
38
+ if @attrs[name]
39
+ if delim = @options[:merge_attrs][name]
40
+ @attrs[name] << delim << value
41
+ else
42
+ raise("Multiple #{name} attributes specified")
43
+ end
44
+ else
45
+ @attrs[name] = value
46
+ end
47
+ end
48
+
49
+ def build_tag
50
+ tag = @attrs.delete('tag').to_s
51
+ tag = @options[:default_tag] if tag.empty?
52
+ if block_given?
53
+ "<#{tag}#{build_attrs}>#{yield}</#{tag}>"
54
+ else
55
+ "<#{tag}#{build_attrs} />"
56
+ end
57
+ end
58
+
59
+ def build_attrs
60
+ attrs = @options[:sort_attrs] ? @attrs.sort_by(&:first) : @attrs
61
+ attrs.map do |k, v|
62
+ " #{k}=#{@options[:attr_quote]}#{v}#{@options[:attr_quote]}"
63
+ end.join
64
+ end
65
+
66
+ private
67
+
68
+ def hyphen_attr(name, escape, value)
69
+ if Hash === value
70
+ value.each do |n, v|
71
+ hyphen_attr("#{name}-#{n.to_s.gsub('_', '-')}", escape, v)
72
+ end
73
+ else
74
+ attr(name, escape ? Temple::Utils.escape_html(value) : value.to_s)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,93 @@
1
+ module Slim
2
+ module Splat
3
+ # @api private
4
+ class Filter < ::Slim::Filter
5
+ OPTIONS = [:merge_attrs, :attr_quote, :sort_attrs, :default_tag, :hyphen_attrs]
6
+ define_options OPTIONS
7
+ default_options[:hyphen_attrs] = %w(data aria)
8
+
9
+ def call(exp)
10
+ @splat_options = nil
11
+ exp = compile(exp)
12
+ if @splat_options
13
+ opts = options.to_hash.reject {|k,v| !OPTIONS.include?(k) }.inspect
14
+ [:multi, [:code, "#{@splat_options} = #{opts}"], exp]
15
+ else
16
+ exp
17
+ end
18
+ end
19
+
20
+ # Handle tag expression `[:html, :tag, name, attrs, content]`
21
+ #
22
+ # @param [String] name Tag name
23
+ # @param [Array] attrs Temple expression
24
+ # @param [Array] content Temple expression
25
+ # @return [Array] Compiled temple expression
26
+ def on_html_tag(name, attrs, content = nil)
27
+ if name != '*'
28
+ super
29
+ elsif content
30
+ builder, block = make_builder(attrs[2..-1])
31
+ [:multi,
32
+ block,
33
+ [:slim, :output, false, "#{builder}.build_tag do",
34
+ compile(content)]]
35
+ else
36
+ builder, block = make_builder(attrs[2..-1])
37
+ [:multi,
38
+ block,
39
+ [:dynamic, "#{builder}.build_tag"]]
40
+ end
41
+ end
42
+
43
+ # Handle attributes expression `[:html, :attrs, *attrs]`
44
+ #
45
+ # @param [Array] attrs Array of temple expressions
46
+ # @return [Array] Compiled temple expression
47
+ def on_html_attrs(*attrs)
48
+ if attrs.any? {|attr| splat?(attr) }
49
+ builder, block = make_builder(attrs)
50
+ [:multi,
51
+ block,
52
+ [:dynamic, "#{builder}.build_attrs"]]
53
+ else
54
+ super
55
+ end
56
+ end
57
+
58
+ protected
59
+
60
+ def splat?(attr)
61
+ # Splat attribute given
62
+ attr[0] == :slim && attr[1] == :splat ||
63
+ # Hyphenated attribute also needs splat handling
64
+ (attr[0] == :html && attr[1] == :attr && options[:hyphen_attrs].include?(attr[2]) &&
65
+ attr[3][0] == :slim && attr[3][1] == :attrvalue)
66
+ end
67
+
68
+ def make_builder(attrs)
69
+ @splat_options ||= unique_name
70
+ builder = unique_name
71
+ result = [:multi, [:code, "#{builder} = ::Slim::Splat::Builder.new(#{@splat_options})"]]
72
+ attrs.each do |attr|
73
+ result <<
74
+ if attr[0] == :html && attr[1] == :attr
75
+ if attr[3][0] == :slim && attr[3][1] == :attrvalue
76
+ [:code, "#{builder}.code_attr(#{attr[2].inspect}, #{attr[3][2]}, (#{attr[3][3]}))"]
77
+ else
78
+ tmp = unique_name
79
+ [:multi,
80
+ [:capture, tmp, compile(attr[3])],
81
+ [:code, "#{builder}.attr(#{attr[2].inspect}, #{tmp})"]]
82
+ end
83
+ elsif attr[0] == :slim && attr[1] == :splat
84
+ [:code, "#{builder}.splat_attrs((#{attr[2]}))"]
85
+ else
86
+ attr
87
+ end
88
+ end
89
+ return builder, result
90
+ end
91
+ end
92
+ end
93
+ 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.9'
4
+ VERSION = '2.0.0'
5
5
  end
data/slim.gemspec CHANGED
@@ -17,6 +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.6.3'])
20
+ s.add_runtime_dependency('temple', ['~> 0.6.5'])
21
21
  s.add_runtime_dependency('tilt', ['~> 1.3', '>= 1.3.3'])
22
22
  end
data/test/core/helper.rb CHANGED
@@ -1,11 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'minitest/unit'
3
+ require 'minitest/autorun'
4
4
  require 'slim'
5
5
  require 'slim/grammar'
6
6
 
7
- MiniTest::Unit.autorun
8
-
9
7
  Slim::Engine.after Slim::Parser, Temple::Filters::Validator, :grammar => Slim::Grammar
10
8
  Slim::Engine.before :Pretty, Temple::Filters::Validator
11
9
 
@@ -11,6 +11,16 @@ p
11
11
  assert_html '<p>Hello Ruby! Hello from within a block! Hello Ruby!</p>', source
12
12
  end
13
13
 
14
+ def test_render_with_output_code_block_without_do
15
+ source = %q{
16
+ p
17
+ = hello_world "Hello Ruby!"
18
+ | Hello from within a block!
19
+ }
20
+
21
+ assert_html '<p>Hello Ruby! Hello from within a block! Hello Ruby!</p>', source
22
+ end
23
+
14
24
  def test_render_with_output_code_within_block
15
25
  source = %q{
16
26
  p
@@ -21,6 +31,16 @@ p
21
31
  assert_html '<p>Hello Ruby! Hello from within a block! Hello Ruby!</p>', source
22
32
  end
23
33
 
34
+ def test_render_with_output_code_within_block_without_do
35
+ source = %q{
36
+ p
37
+ = hello_world "Hello Ruby!"
38
+ = hello_world "Hello from within a block!"
39
+ }
40
+
41
+ assert_html '<p>Hello Ruby! Hello from within a block! Hello Ruby!</p>', source
42
+ end
43
+
24
44
  def test_render_with_output_code_within_block_2
25
45
  source = %q{
26
46
  p
@@ -32,6 +52,17 @@ p
32
52
  assert_html '<p>Hello Ruby! Hello from within a block! And another one! Hello from within a block! Hello Ruby!</p>', source
33
53
  end
34
54
 
55
+ def test_render_with_output_code_within_block_2_without_do
56
+ source = %q{
57
+ p
58
+ = hello_world "Hello Ruby!"
59
+ = hello_world "Hello from within a block!"
60
+ = hello_world "And another one!"
61
+ }
62
+
63
+ assert_html '<p>Hello Ruby! Hello from within a block! And another one! Hello from within a block! Hello Ruby!</p>', source
64
+ end
65
+
35
66
  def test_output_block_with_arguments
36
67
  source = %q{
37
68
  p
@@ -56,6 +87,16 @@ p
56
87
  assert_html '<p>Hey!Hey!Hey!</p>', source
57
88
  end
58
89
 
90
+ def test_render_with_control_code_loop_without_do
91
+ source = %q{
92
+ p
93
+ - 3.times
94
+ | Hey!
95
+ }
96
+
97
+ assert_html '<p>Hey!Hey!Hey!</p>', source
98
+ end
99
+
59
100
  def test_captured_code_block_with_conditional
60
101
  source = %q{
61
102
  = hello_world "Hello Ruby!" do
@@ -65,4 +106,14 @@ p
65
106
 
66
107
  assert_html 'Hello Ruby! Hello from within a block! Hello Ruby!', source
67
108
  end
109
+
110
+ def test_captured_code_block_with_conditional_without_do
111
+ source = %q{
112
+ = hello_world "Hello Ruby!"
113
+ - if true
114
+ | Hello from within a block!
115
+ }
116
+
117
+ assert_html 'Hello Ruby! Hello from within a block! Hello Ruby!', source
118
+ end
68
119
  end
@@ -116,7 +116,7 @@ p(id=hash[:a]) Test it
116
116
 
117
117
  def test_hash_call_in_attribute_with_ruby_evaluation
118
118
  source = %q{
119
- p id={hash[:a] + hash[:a]} Test it
119
+ p id=(hash[:a] + hash[:a]) Test it
120
120
  }
121
121
 
122
122
  assert_html '<p id="The letter aThe letter a">Test it</p>', source
@@ -140,23 +140,15 @@ p[id=(hash[:a] + hash[:a])] Test it
140
140
 
141
141
  def test_hash_call_in_delimited_attribute_with_ruby_evaluation_3
142
142
  source = %q{
143
- p(id=[hash[:a] + hash[:a]]) Test it
144
- }
145
-
146
- assert_html '<p id="The letter aThe letter a">Test it</p>', source
147
- end
148
-
149
- def test_hash_call_in_delimited_attribute_with_ruby_evaluation_4
150
- source = %q{
151
- p(id=[hash[:a] + hash[:a]] class=[hash[:a]]) Test it
143
+ p(id=(hash[:a] + hash[:a]) class=hash[:a]) Test it
152
144
  }
153
145
 
154
146
  assert_html '<p class="The letter a" id="The letter aThe letter a">Test it</p>', source
155
147
  end
156
148
 
157
- def test_hash_call_in_delimited_attribute_with_ruby_evaluation_5
149
+ def test_hash_call_in_delimited_attribute_with_ruby_evaluation_4_
158
150
  source = %q{
159
- p(id=hash[:a] class=[hash[:a]]) Test it
151
+ p(id=hash[:a] class=hash[:a]) Test it
160
152
  }
161
153
 
162
154
  assert_html '<p class="The letter a" id="The letter a">Test it</p>', source
@@ -25,44 +25,9 @@ asciidoc:
25
25
  * one
26
26
  * two
27
27
  }
28
-
29
- expected = <<-EOS
30
- <div class="sect1">
31
- <h2 id="_header">Header</h2>
32
- <div class="sectionbody">
33
- <div class="paragraph">
34
- <p>Hello from AsciiDoc!</p>
35
- </div>
36
- <div class="paragraph">
37
- <p>3</p>
38
- </div>
39
- <div class="ulist">
40
- <ul>
41
- <li>
42
- <p>one</p>
43
- </li>
44
- <li>
45
- <p>two</p>
46
- </li>
47
- </ul>
48
- </div>
49
- </div>
50
- </div>
51
- EOS
52
- # render, then remove blank lines and unindent the remaining lines
53
- output = render(source).gsub(/^ *(\n|(?=[^ ]))/, '')
54
-
55
- assert_equal expected, output
56
-
57
- Slim::Embedded.with_options(:asciidoc => {:compact => true, :attributes => {'sectids!' => ''}}) do
58
- # render, then unindent lines
59
- output = render(source).gsub(/^ *(?=[^ ])/, '')
60
- assert_equal expected.gsub('<h2 id="_header">', '<h2>'), output
61
- end
62
-
63
- # render again, then remove blank lines and unindent the remaining lines
64
- output = render(source).gsub(/^ *(\n|(?=[^ ]))/, '')
65
- assert_equal expected, output
28
+ output = render(source)
29
+ assert_match 'sect1', output
30
+ assert_match 'Hello from AsciiDoc!', output
66
31
  end
67
32
 
68
33
  def test_render_with_markdown
@@ -76,13 +41,22 @@ markdown:
76
41
  * one
77
42
  * two
78
43
  }
79
- assert_html "<h1 id=\"header\">Header</h1>\n<p>Hello from Markdown!</p>\n\n<p>3</p>\n\n<ul>\n <li>one</li>\n <li>two</li>\n</ul>\n", source
80
-
81
- Slim::EmbeddedEngine.with_options(:markdown => {:auto_ids => false}) do
82
- assert_html "<h1>Header</h1>\n<p>Hello from Markdown!</p>\n\n<p>3</p>\n\n<ul>\n <li>one</li>\n <li>two</li>\n</ul>\n", source
44
+ if ::Tilt['md'].name =~ /Redcarpet/
45
+ # redcarpet
46
+ assert_html "<h1>Header</h1>\n\n<p>Hello from Markdown!</p>\n\n<p>3</p>\n\n<ul>\n<li>one</li>\n<li>two</li>\n</ul>\n", source
47
+ elsif ::Tilt['md'].name =~ /RDiscount/
48
+ # rdiscount
49
+ assert_html "<h1>Header</h1>\n\n<p>Hello from Markdown!</p>\n\n<p>3</p>\n\n<ul>\n<li>one</li>\n<li>two</li>\n</ul>\n\n", source
50
+ else
51
+ # kramdown, :auto_ids by default
52
+ assert_html "<h1 id=\"header\">Header</h1>\n<p>Hello from Markdown!</p>\n\n<p>3</p>\n\n<ul>\n <li>one</li>\n <li>two</li>\n</ul>\n", source
53
+
54
+ Slim::Embedded.with_options(:markdown => {:auto_ids => false}) do
55
+ assert_html "<h1>Header</h1>\n<p>Hello from Markdown!</p>\n\n<p>3</p>\n\n<ul>\n <li>one</li>\n <li>two</li>\n</ul>\n", source
56
+ end
57
+
58
+ assert_html "<h1 id=\"header\">Header</h1>\n<p>Hello from Markdown!</p>\n\n<p>3</p>\n\n<ul>\n <li>one</li>\n <li>two</li>\n</ul>\n", source
83
59
  end
84
-
85
- assert_html "<h1 id=\"header\">Header</h1>\n<p>Hello from Markdown!</p>\n\n<p>3</p>\n\n<ul>\n <li>one</li>\n <li>two</li>\n</ul>\n", source
86
60
  end
87
61
 
88
62
  def test_render_with_creole
@@ -9,8 +9,15 @@ class TestSlimEncoding < TestSlim
9
9
 
10
10
  def test_binary
11
11
  source = "| \xFF\xFF"
12
+ source.force_encoding(Encoding::BINARY) if source.respond_to? :force_encoding
13
+
12
14
  result = "\xFF\xFF"
13
- assert_html result, source
15
+ result.force_encoding(Encoding::BINARY) if source.respond_to? :force_encoding
16
+
17
+ out = render(source, :default_encoding => 'binary')
18
+ out.force_encoding(Encoding::BINARY) if source.respond_to? :force_encoding
19
+
20
+ assert_equal result, out
14
21
  end
15
22
 
16
23
  def test_bom
@@ -0,0 +1,67 @@
1
+ require 'helper'
2
+ require 'slim/erb_converter'
3
+
4
+ class TestSlimERBConverter < TestSlim
5
+ def test_converter
6
+ source = %q{
7
+ doctype 5
8
+ html
9
+ head
10
+ title Hello World!
11
+ /! Meta tags
12
+ with long explanatory
13
+ multiline comment
14
+ meta name="description" content="template language"
15
+ /! Stylesheets
16
+ link href="style.css" media="screen" rel="stylesheet" type="text/css"
17
+ link href="colors.css" media="screen" rel="stylesheet" type="text/css"
18
+ /! Javascripts
19
+ script src="jquery.js"
20
+ script src="jquery.ui.js"
21
+ /[if lt IE 9]
22
+ script src="old-ie1.js"
23
+ script src="old-ie2.js"
24
+ sass:
25
+ body
26
+ background-color: red
27
+ body
28
+ #container
29
+ p Hello
30
+ World!
31
+ p= "dynamic text with\nnewline"
32
+ }
33
+
34
+ result = %q{<%
35
+ %><!DOCTYPE html><%
36
+ %><html><%
37
+ %><head><%
38
+ %><title>Hello World!</title><%
39
+ %><!--Meta tags<%
40
+ %>
41
+ with long explanatory<%
42
+ %>
43
+ multiline comment--><%
44
+ %><meta content="template language" name="description" /><%
45
+ %><!--Stylesheets--><%
46
+ %><link href="style.css" media="screen" rel="stylesheet" type="text/css" /><%
47
+ %><link href="colors.css" media="screen" rel="stylesheet" type="text/css" /><%
48
+ %><!--Javascripts--><%
49
+ %><script src="jquery.js"><%
50
+ %></script><script src="jquery.ui.js"><%
51
+ %></script><!--[if lt IE 9]><%
52
+ %><script src="old-ie1.js"><%
53
+ %></script><script src="old-ie2.js"><%
54
+ %></script><![endif]--><style type="text/css">body{background-color:red}<%
55
+ %><%
56
+ %></style><%
57
+ %></head><body><%
58
+ %><div id="container"><%
59
+ %><p>Hello<%
60
+ %>
61
+ World!</p><%
62
+ %><p><%= ::Temple::Utils.escape_html(("dynamic text with\nnewline")) %><%
63
+ %></p></div></body></html>}
64
+
65
+ assert_equal result, Slim::ERBConverter.new.call(source)
66
+ end
67
+ end