slim 1.3.0 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.travis.yml +9 -5
  2. data/CHANGES +23 -0
  3. data/Gemfile +13 -2
  4. data/README.md +346 -105
  5. data/Rakefile +21 -9
  6. data/lib/slim.rb +3 -3
  7. data/lib/slim/boolean_attributes.rb +67 -0
  8. data/lib/slim/command.rb +23 -23
  9. data/lib/slim/control_structures.rb +57 -0
  10. data/lib/slim/embedded_engine.rb +80 -43
  11. data/lib/slim/end_inserter.rb +1 -1
  12. data/lib/slim/engine.rb +21 -15
  13. data/lib/slim/filter.rb +0 -6
  14. data/lib/slim/grammar.rb +2 -7
  15. data/lib/slim/interpolation.rb +1 -1
  16. data/lib/slim/logic_less/filter.rb +8 -8
  17. data/lib/slim/logic_less/wrapper.rb +1 -1
  18. data/lib/slim/parser.rb +51 -52
  19. data/lib/slim/splat_attributes.rb +112 -0
  20. data/lib/slim/translator.rb +13 -12
  21. data/lib/slim/version.rb +1 -1
  22. data/slim.gemspec +1 -1
  23. data/test/{slim → core}/helper.rb +3 -7
  24. data/test/{slim → core}/test_code_blocks.rb +0 -0
  25. data/test/{slim → core}/test_code_escaping.rb +4 -4
  26. data/test/{slim → core}/test_code_evaluation.rb +3 -112
  27. data/test/{slim → core}/test_code_output.rb +0 -0
  28. data/test/{slim → core}/test_code_structure.rb +0 -0
  29. data/test/{slim → core}/test_embedded_engines.rb +8 -3
  30. data/test/{slim → core}/test_encoding.rb +0 -0
  31. data/test/core/test_html_attributes.rb +218 -0
  32. data/test/{slim → core}/test_html_escaping.rb +17 -0
  33. data/test/{slim → core}/test_html_structure.rb +13 -98
  34. data/test/{slim → core}/test_parser_errors.rb +24 -15
  35. data/test/{slim → core}/test_pretty.rb +0 -0
  36. data/test/{slim → core}/test_ruby_errors.rb +7 -0
  37. data/test/{slim → core}/test_slim_template.rb +0 -0
  38. data/test/{slim → core}/test_text_interpolation.rb +2 -2
  39. data/test/core/test_thread_options.rb +18 -0
  40. data/test/{slim/logic_less → logic_less}/test_logic_less.rb +21 -0
  41. data/test/{slim/logic_less → logic_less}/test_wrapper.rb +3 -3
  42. data/test/rails/app/controllers/slim_controller.rb +4 -2
  43. data/test/rails/app/views/parents/_form.html.slim +1 -0
  44. data/test/rails/app/views/parents/edit.html.slim +2 -1
  45. data/test/rails/app/views/parents/new.html.slim +2 -1
  46. data/test/rails/app/views/slim/thread_options.html.slim +1 -0
  47. data/test/rails/test/test_slim.rb +6 -4
  48. data/test/{slim/translator → translator}/test_translator.rb +0 -0
  49. metadata +44 -82
  50. data/lib/slim/compiler.rb +0 -194
  51. data/test/rails/app/views/slim/nil.html.slim +0 -1
  52. data/test/slim/test_chain_manipulation.rb +0 -42
@@ -37,4 +37,21 @@ p class="#{x}" test #{content}
37
37
  }
38
38
  assert_html 'Hello World from @env escaped & Hello World from @env', source
39
39
  end
40
+
41
+ def test_html_quoted_attr_escape
42
+ source = %q{
43
+ p id="&" class=="&"
44
+ }
45
+
46
+ assert_html '<p class="&amp;" id="&"></p>', source
47
+ assert_html '<p class="&amp;" id="&amp;"></p>', source, :escape_quoted_attrs => true
48
+ end
49
+
50
+ def test_html_ruby_attr_escape
51
+ source = %q{
52
+ p id=('&'.to_s) class==('&amp;'.to_s)
53
+ }
54
+
55
+ assert_html '<p class="&amp;" id="&amp;"></p>', source
56
+ end
40
57
  end
@@ -293,10 +293,10 @@ p(id="marvin" class="" data-info="Illudium Q-36")= output_number
293
293
 
294
294
  def test_dynamic_empty_attribute
295
295
  source = %q{
296
- p(id="marvin" class=nil other_empty=("".to_s) data-info="Illudium Q-36")= output_number
296
+ p(id="marvin" class=nil nonempty=("".to_s) data-info="Illudium Q-36")= output_number
297
297
  }
298
298
 
299
- assert_html '<p data-info="Illudium Q-36" id="marvin">1337</p>', source
299
+ assert_html '<p data-info="Illudium Q-36" id="marvin" nonempty="">1337</p>', source
300
300
  end
301
301
 
302
302
  def test_closed_tag
@@ -478,102 +478,17 @@ input[value=succ_x]
478
478
  assert_html %{<input value="1" /><input value="2" />}, source
479
479
  end
480
480
 
481
- def test_shortcut_splat
481
+ def test_html_line_indicator
482
482
  source = %q{
483
- *hash This is my title
484
- }
485
-
486
- assert_html '<div a="The letter a" b="The letter b">This is my title</div>', source
487
- end
488
-
489
- def test_splat
490
- source = %q{
491
- h1 *hash This is my title
492
- }
493
-
494
- assert_html '<h1 a="The letter a" b="The letter b">This is my title</h1>', source
495
- end
496
-
497
- def test_splat_tag_name
498
- source = %q{
499
- *{:tag => 'h1', :id => 'title'} This is my title
500
- }
501
-
502
- assert_html '<h1 id="title">This is my title</h1>', source
503
- end
504
-
505
-
506
- def test_splat_empty_tag_name
507
- source = %q{
508
- *{:tag => '', :id => 'test'} This is my title
509
- }
510
-
511
- assert_html '<div id="test">This is my title</div>', source, :remove_empty_attrs => true
512
- assert_html '<div id="test">This is my title</div>', source, :remove_empty_attrs => false
513
- end
514
-
515
- def test_closed_splat_tag
516
- source = %q{
517
- *hash / This is my title
518
- }
519
-
520
- assert_html '<div a="The letter a" b="The letter b"/>', source
521
- end
522
-
523
- def test_splat_with_id_shortcut
524
- source = %q{
525
- #myid*hash This is my title
526
- }
527
-
528
- assert_html '<div a="The letter a" b="The letter b" id="myid">This is my title</div>', source
529
- end
530
-
531
- def test_splat_with_class_shortcut
532
- source = %q{
533
- .myclass*hash This is my title
534
- }
535
-
536
- assert_html '<div a="The letter a" b="The letter b" class="myclass">This is my title</div>', source
537
- end
538
-
539
- def test_splat_with_id_and_class_shortcuts
540
- source = %q{
541
- #myid.myclass*hash This is my title
542
- }
543
-
544
- assert_html '<div a="The letter a" b="The letter b" class="myclass" id="myid">This is my title</div>', source
545
- end
546
-
547
- def test_splat_with_class_merging
548
- source = %q{
549
- #myid.myclass *{:class => [:secondclass, %w(x y z)]} *hash This is my title
550
- }
551
-
552
- assert_html '<div a="The letter a" b="The letter b" class="myclass secondclass x y z" id="myid">This is my title</div>', source
553
- end
554
-
555
- def test_splat_with_boolean_attribute
556
- source = %q{
557
- *{:disabled => true, :empty1 => false, :empty2 => '', :empty3 => nil} This is my title
558
- }
559
-
560
- assert_html '<div disabled="disabled">This is my title</div>', source
561
- assert_html '<div disabled="disabled" empty1="" empty2="" empty3="">This is my title</div>', source, :remove_empty_attrs => false
562
- end
563
-
564
- def test_splat_merging_with_arrays
565
- source = %q{
566
- *{:a => 1, :b => 2} *[[:c, 3], [:d, 4]] *[[:e, 5], [:f, 6]] This is my title
567
- }
568
-
569
- assert_html '<div a="1" b="2" c="3" d="4" e="5" f="6">This is my title</div>', source
570
- end
571
-
572
- def test_splat_with_other_attributes
573
- source = %q{
574
- h1 data-id="123" *hash This is my title
575
- }
576
-
577
- assert_html '<h1 a="The letter a" b="The letter b" data-id="123">This is my title</h1>', source
483
+ <html>
484
+ head
485
+ meta name="keywords" content=hello_world
486
+ - if true
487
+ <p>#{hello_world}</p>
488
+ span = hello_world
489
+ </html>
490
+ }
491
+
492
+ assert_html '<html><head><meta content="Hello World from @env" name="keywords" /></head><p>Hello World from @env</p><span>Hello World from @env</span></html>', source
578
493
  end
579
494
  end
@@ -7,7 +7,7 @@ doctype 5
7
7
  div Invalid
8
8
  }
9
9
 
10
- assert_syntax_error "Unexpected indentation\n test.slim, Line 3\n div Invalid\n ^\n", source, :file => 'test.slim'
10
+ assert_syntax_error "Unexpected indentation\n test.slim, Line 3, Column 2\n div Invalid\n ^\n", source, :file => 'test.slim'
11
11
  end
12
12
 
13
13
  def test_unexpected_indentation
@@ -16,7 +16,7 @@ doctype 5
16
16
  div Invalid
17
17
  }
18
18
 
19
- assert_syntax_error "Unexpected indentation\n (__TEMPLATE__), Line 3\n div Invalid\n ^\n", source
19
+ assert_syntax_error "Unexpected indentation\n (__TEMPLATE__), Line 3, Column 2\n div Invalid\n ^\n", source
20
20
  end
21
21
 
22
22
  def test_unexpected_text_indentation
@@ -26,7 +26,7 @@ p
26
26
  text
27
27
  }
28
28
 
29
- assert_syntax_error "Text line not indented deep enough.\nThe first text line defines the necessary text indentation.\n (__TEMPLATE__), Line 4\n text\n ^\n", source
29
+ assert_syntax_error "Text line not indented deep enough.\nThe first text line defines the necessary text indentation.\n (__TEMPLATE__), Line 4, Column 3\n text\n ^\n", source
30
30
  end
31
31
 
32
32
  def test_unexpected_text_indentation_in_tag
@@ -42,7 +42,7 @@ ul
42
42
  li b
43
43
  }
44
44
 
45
- assert_syntax_error "Text line not indented deep enough.\nThe first text line defines the necessary text indentation.\nAre you trying to nest a child tag in a tag containing text? Use | for the text block!\n (__TEMPLATE__), Line 4\n ul\n ^\n", source
45
+ assert_syntax_error "Text line not indented deep enough.\nThe first text line defines the necessary text indentation.\nAre you trying to nest a child tag in a tag containing text? Use | for the text block!\n (__TEMPLATE__), Line 4, Column 4\n ul\n ^\n", source
46
46
  end
47
47
 
48
48
  def test_malformed_indentation
@@ -52,7 +52,7 @@ p
52
52
  div Invalid
53
53
  }
54
54
 
55
- assert_syntax_error "Malformed indentation\n (__TEMPLATE__), Line 4\n div Invalid\n ^\n", source
55
+ assert_syntax_error "Malformed indentation\n (__TEMPLATE__), Line 4, Column 1\n div Invalid\n ^\n", source
56
56
  end
57
57
 
58
58
  def test_unknown_line_indicator
@@ -64,7 +64,7 @@ p
64
64
  ?invalid
65
65
  }
66
66
 
67
- assert_syntax_error "Unknown line indicator\n (__TEMPLATE__), Line 6\n ?invalid\n ^\n", source
67
+ assert_syntax_error "Unknown line indicator\n (__TEMPLATE__), Line 6, Column 2\n ?invalid\n ^\n", source
68
68
  end
69
69
 
70
70
  def test_expected_closing_delimiter
@@ -73,7 +73,16 @@ p
73
73
  img(src="img.jpg" title={title}
74
74
  }
75
75
 
76
- assert_syntax_error "Expected closing delimiter )\n (__TEMPLATE__), Line 3\n img(src=\"img.jpg\" title={title}\n ^\n", source
76
+ assert_syntax_error "Expected closing delimiter )\n (__TEMPLATE__), Line 3, Column 33\n img(src=\"img.jpg\" title={title}\n ^\n", source
77
+ end
78
+
79
+ def test_expected_closing_quote
80
+ source = %q{
81
+ p
82
+ img(src="img.jpg
83
+ }
84
+
85
+ assert_syntax_error "Expected closing quote \"\n (__TEMPLATE__), Line 3, Column 18\n img(src=\"img.jpg\n ^\n", source
77
86
  end
78
87
 
79
88
  def test_expected_closing_attribute_delimiter
@@ -82,7 +91,7 @@ p
82
91
  img src=[hash[1] + hash[2]
83
92
  }
84
93
 
85
- assert_syntax_error "Expected closing delimiter ]\n (__TEMPLATE__), Line 3\n img src=[hash[1] + hash[2]\n ^\n", source
94
+ assert_syntax_error "Expected closing delimiter ]\n (__TEMPLATE__), Line 3, Column 28\n img src=[hash[1] + hash[2]\n ^\n", source
86
95
  end
87
96
 
88
97
  def test_expected_attribute
@@ -91,7 +100,7 @@ p
91
100
  img(src='img.png' whatsthis?!)
92
101
  }
93
102
 
94
- assert_syntax_error "Expected attribute\n (__TEMPLATE__), Line 3\n img(src='img.png' whatsthis?!)\n ^\n", source
103
+ assert_syntax_error "Expected attribute\n (__TEMPLATE__), Line 3, Column 20\n img(src='img.png' whatsthis?!)\n ^\n", source
95
104
  end
96
105
 
97
106
  def test_invalid_empty_attribute
@@ -100,7 +109,7 @@ p
100
109
  img{src= }
101
110
  }
102
111
 
103
- assert_syntax_error "Invalid empty attribute\n (__TEMPLATE__), Line 3\n img{src= }\n ^\n", source
112
+ assert_syntax_error "Invalid empty attribute\n (__TEMPLATE__), Line 3, Column 10\n img{src= }\n ^\n", source
104
113
  end
105
114
 
106
115
  def test_invalid_empty_attribute2
@@ -109,7 +118,7 @@ p
109
118
  img{src=}
110
119
  }
111
120
 
112
- assert_syntax_error "Invalid empty attribute\n (__TEMPLATE__), Line 3\n img{src=}\n ^\n", source
121
+ assert_syntax_error "Invalid empty attribute\n (__TEMPLATE__), Line 3, Column 10\n img{src=}\n ^\n", source
113
122
  end
114
123
 
115
124
  def test_invalid_empty_attribute3
@@ -118,7 +127,7 @@ p
118
127
  img src=
119
128
  }
120
129
 
121
- assert_syntax_error "Invalid empty attribute\n (__TEMPLATE__), Line 3\n img src=\n ^\n", source
130
+ assert_syntax_error "Invalid empty attribute\n (__TEMPLATE__), Line 3, Column 10\n img src=\n ^\n", source
122
131
  end
123
132
 
124
133
  def test_missing_tag_in_block_expansion
@@ -126,18 +135,18 @@ p
126
135
  html: body:
127
136
  }
128
137
 
129
- assert_syntax_error "Expected tag\n (__TEMPLATE__), Line 2\n html: body:\n ^\n", source
138
+ assert_syntax_error "Expected tag\n (__TEMPLATE__), Line 2, Column 11\n html: body:\n ^\n", source
130
139
  end
131
140
 
132
141
  def test_invalid_tag_in_block_expansion
133
142
  source = %{
134
143
  html: body: /comment
135
144
  }
136
- assert_syntax_error "Expected tag\n (__TEMPLATE__), Line 2\n html: body: /comment\n ^\n", source
145
+ assert_syntax_error "Expected tag\n (__TEMPLATE__), Line 2, Column 12\n html: body: /comment\n ^\n", source
137
146
 
138
147
  source = %{
139
148
  html: body:/comment
140
149
  }
141
- assert_syntax_error "Expected tag\n (__TEMPLATE__), Line 2\n html: body:/comment\n ^\n", source
150
+ assert_syntax_error "Expected tag\n (__TEMPLATE__), Line 2, Column 11\n html: body:/comment\n ^\n", source
142
151
  end
143
152
  end
File without changes
@@ -207,4 +207,11 @@ div
207
207
  }
208
208
  assert_runtime_error 'Multiple id attributes specified', source
209
209
  end
210
+
211
+ # def test_invalid_option
212
+ # render('', :foobar => 42)
213
+ # raise Exception, 'ArgumentError expected'
214
+ # rescue ArgumentError => ex
215
+ # assert_equal 'Option :foobar is not supported by Slim::Engine', ex.message
216
+ # end
210
217
  end
@@ -59,7 +59,7 @@ p Message: #{message('hello', "user #{output_number}")}
59
59
  | #{evil_method}
60
60
  }
61
61
 
62
- assert_html '&lt;script&gt;do_something_evil();&lt;&#47;script&gt;', source
62
+ assert_html '&lt;script&gt;do_something_evil();&lt;/script&gt;', source
63
63
  end
64
64
 
65
65
  def test_interpolation_without_escaping
@@ -74,6 +74,6 @@ p Message: #{message('hello', "user #{output_number}")}
74
74
  source = %q{
75
75
  | #{(evil_method)}
76
76
  }
77
- assert_html '&lt;script&gt;do_something_evil();&lt;&#47;script&gt;', source
77
+ assert_html '&lt;script&gt;do_something_evil();&lt;/script&gt;', source
78
78
  end
79
79
  end
@@ -0,0 +1,18 @@
1
+ require 'helper'
2
+
3
+ class TestSlimThreadOptions < TestSlim
4
+ def test_thread_options
5
+ source = %q{p.test}
6
+
7
+ assert_html '<p class="test"></p>', source
8
+ assert_html "<p class='test'></p>", source, :attr_wrapper => "'"
9
+
10
+ Slim::Engine.with_options(:attr_wrapper => "'") do
11
+ assert_html "<p class='test'></p>", source
12
+ assert_html '<p class="test"></p>', source, :attr_wrapper => '"'
13
+ end
14
+
15
+ assert_html '<p class="test"></p>', source
16
+ assert_html "<p class='test'></p>", source, :attr_wrapper => "'"
17
+ end
18
+ end
@@ -2,6 +2,17 @@ require 'helper'
2
2
  require 'slim/logic_less'
3
3
 
4
4
  class TestSlimLogicLess < TestSlim
5
+ class Scope
6
+ def initialize
7
+ @hash = {
8
+ :person => [
9
+ { :name => 'Joe', },
10
+ { :name => 'Jack', }
11
+ ]
12
+ }
13
+ end
14
+ end
15
+
5
16
  def test_symbol_access
6
17
  source = %q{
7
18
  p
@@ -19,6 +30,16 @@ p
19
30
  assert_html '<p><div class="name">Joe</div><div class="name">Jack</div></p>', source, :scope => hash, :dictionary_access => :symbol
20
31
  end
21
32
 
33
+ def test_dictionary_option
34
+ source = %q{
35
+ p
36
+ - person
37
+ .name = name
38
+ }
39
+
40
+ assert_html '<p><div class="name">Joe</div><div class="name">Jack</div></p>', source, :scope => Scope.new, :dictionary => '@hash', :dictionary_access => :symbol
41
+ end
42
+
22
43
  def test_string_access
23
44
  source = %q{
24
45
  p
@@ -8,7 +8,7 @@ p
8
8
  - person
9
9
  .name = name
10
10
  }
11
- assert_html '<p><div class="name">Joe</div><div class="name">Jack</div></p>', source, :sections => true, :dictionary => 'ViewEnv.new'
11
+ assert_html '<p><div class="name">Joe</div><div class="name">Jack</div></p>', source, :dictionary => 'ViewEnv.new'
12
12
  end
13
13
 
14
14
  def test_with_array
@@ -18,14 +18,14 @@ ul
18
18
  li = name
19
19
  li = city
20
20
  }
21
- assert_html '<ul><li>Andy</li><li>Atlanta</li><li>Fred</li><li>Melbourne</li><li>Daniel</li><li>Karlsruhe</li></ul>', source, :sections => true, :dictionary => 'ViewEnv.new'
21
+ assert_html '<ul><li>Andy</li><li>Atlanta</li><li>Fred</li><li>Melbourne</li><li>Daniel</li><li>Karlsruhe</li></ul>', source, :dictionary => 'ViewEnv.new'
22
22
  end
23
23
 
24
24
  def test_method
25
25
  source = %q{
26
26
  a href=output_number Link
27
27
  }
28
- assert_html '<a href="1337">Link</a>', source, :sections => true, :dictionary => 'ViewEnv.new'
28
+ assert_html '<a href="1337">Link</a>', source, :dictionary => 'ViewEnv.new'
29
29
  end
30
30
 
31
31
  end
@@ -22,8 +22,10 @@ class SlimController < ApplicationController
22
22
  @integer = 1337
23
23
  end
24
24
 
25
- def nil
26
- @nil = nil
25
+ def thread_options
26
+ Slim::Engine.with_options(:shortcut => {'@' => "div #{params[:attr]}"}) do
27
+ render
28
+ end
27
29
  end
28
30
 
29
31
  def content_for
@@ -1,3 +1,4 @@
1
+ h2 Form
1
2
  = form_for @parent, :html => {:multipart => true} do |f|
2
3
  h1 Parent
3
4
  = f.text_field :name
@@ -1 +1,2 @@
1
- = render "form"
1
+ h1 Edit
2
+ = render "form"
@@ -1 +1,2 @@
1
- = render "form"
1
+ h1 New
2
+ = render "form"
@@ -45,9 +45,11 @@ class TestSlim < ActionDispatch::IntegrationTest
45
45
  assert_html "<p>1337</p>"
46
46
  end
47
47
 
48
- test "render nil" do
49
- get "slim/nil"
50
- assert_html "<p></p>"
48
+ test "render thread_options" do
49
+ get "slim/thread_options", :attr => 'role'
50
+ assert_html '<p role="empty">Test</p>'
51
+ get "slim/thread_options", :attr => 'id' # Overwriting doesn't work because of caching
52
+ assert_html '<p role="empty">Test</p>'
51
53
  end
52
54
 
53
55
  test "content_for" do
@@ -59,7 +61,7 @@ class TestSlim < ActionDispatch::IntegrationTest
59
61
  post "parents", 'parent[name]' => "p1", 'parent[children_attributes][0][name]' => "c1"
60
62
  get "parents/1/edit"
61
63
 
62
- assert_match(%r{<form accept-charset="UTF-8" action="/parents/1" class="edit_parent" enctype="multipart/form-data" id="edit_parent_1" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /><input name="_method" type="hidden" value="[^"]+" /></div><h1>Parent</h1><input id="parent_name" name="parent\[name\]" size="30" type="text" value="p1" /><h2>Children</h2><ul><li><input id="parent_children_attributes_0_name" name="parent\[children_attributes\]\[0\]\[name\]" size="30" type="text" value="c1" /></li><input id="parent_children_attributes_0_id" name="parent\[children_attributes\]\[0\]\[id\]" type="hidden" value="1" /></ul></form>}, @response.body)
64
+ assert_match(%r{<div class="content"><h1>Edit</h1><h2>Form</h2><form accept-charset="UTF-8" action="/parents/1" class="edit_parent" enctype="multipart/form-data" id="edit_parent_1" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /><input name="_method" type="hidden" value="[^"]+" /></div><h1>Parent</h1><input id="parent_name" name="parent\[name\]" size="30" type="text" value="p1" /><h2>Children</h2><ul><li><input id="parent_children_attributes_0_name" name="parent\[children_attributes\]\[0\]\[name\]" size="30" type="text" value="c1" /></li><input id="parent_children_attributes_0_id" name="parent\[children_attributes\]\[0\]\[id\]" type="hidden" value="1" /></ul></form></div>}, @response.body)
63
65
  end
64
66
 
65
67
  protected