haml 4.0.7 → 5.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +42 -4
  4. data/FAQ.md +4 -14
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +85 -42
  7. data/REFERENCE.md +108 -57
  8. data/Rakefile +46 -54
  9. data/lib/haml/attribute_builder.rb +163 -0
  10. data/lib/haml/attribute_compiler.rb +215 -0
  11. data/lib/haml/attribute_parser.rb +144 -0
  12. data/lib/haml/buffer.rb +22 -132
  13. data/lib/haml/compiler.rb +87 -295
  14. data/lib/haml/engine.rb +25 -41
  15. data/lib/haml/error.rb +3 -0
  16. data/lib/haml/escapable.rb +49 -0
  17. data/lib/haml/exec.rb +33 -19
  18. data/lib/haml/filters.rb +18 -24
  19. data/lib/haml/generator.rb +41 -0
  20. data/lib/haml/helpers/action_view_extensions.rb +3 -2
  21. data/lib/haml/helpers/action_view_mods.rb +36 -58
  22. data/lib/haml/helpers/action_view_xss_mods.rb +1 -0
  23. data/lib/haml/helpers/safe_erubi_template.rb +27 -0
  24. data/lib/haml/helpers/safe_erubis_template.rb +4 -1
  25. data/lib/haml/helpers/xss_mods.rb +18 -12
  26. data/lib/haml/helpers.rb +133 -90
  27. data/lib/haml/options.rb +38 -47
  28. data/lib/haml/parser.rb +278 -216
  29. data/lib/haml/{template/plugin.rb → plugin.rb} +8 -15
  30. data/lib/haml/railtie.rb +21 -12
  31. data/lib/haml/sass_rails_filter.rb +17 -4
  32. data/lib/haml/template/options.rb +12 -2
  33. data/lib/haml/template.rb +12 -6
  34. data/lib/haml/temple_engine.rb +120 -0
  35. data/lib/haml/temple_line_counter.rb +29 -0
  36. data/lib/haml/util.rb +80 -199
  37. data/lib/haml/version.rb +2 -1
  38. data/lib/haml.rb +1 -0
  39. data/test/attribute_parser_test.rb +101 -0
  40. data/test/engine_test.rb +287 -176
  41. data/test/filters_test.rb +32 -19
  42. data/test/gemfiles/Gemfile.rails-4.0.x +9 -3
  43. data/test/gemfiles/Gemfile.rails-4.0.x.lock +87 -0
  44. data/test/gemfiles/Gemfile.rails-4.1.x +5 -0
  45. data/test/gemfiles/Gemfile.rails-4.2.x +5 -0
  46. data/test/gemfiles/Gemfile.rails-5.0.x +4 -0
  47. data/test/helper_test.rb +224 -112
  48. data/test/options_test.rb +22 -0
  49. data/test/parser_test.rb +71 -4
  50. data/test/results/bemit.xhtml +4 -0
  51. data/test/results/eval_suppressed.xhtml +4 -4
  52. data/test/results/helpers.xhtml +43 -41
  53. data/test/results/helpful.xhtml +6 -3
  54. data/test/results/just_stuff.xhtml +21 -20
  55. data/test/results/list.xhtml +9 -9
  56. data/test/results/nuke_inner_whitespace.xhtml +22 -22
  57. data/test/results/nuke_outer_whitespace.xhtml +84 -92
  58. data/test/results/original_engine.xhtml +17 -17
  59. data/test/results/partial_layout.xhtml +4 -3
  60. data/test/results/partial_layout_erb.xhtml +4 -3
  61. data/test/results/partials.xhtml +11 -10
  62. data/test/results/silent_script.xhtml +63 -63
  63. data/test/results/standard.xhtml +156 -159
  64. data/test/results/tag_parsing.xhtml +19 -19
  65. data/test/results/very_basic.xhtml +2 -2
  66. data/test/results/whitespace_handling.xhtml +77 -76
  67. data/test/template_test.rb +24 -56
  68. data/test/template_test_helper.rb +38 -0
  69. data/test/templates/bemit.haml +3 -0
  70. data/test/templates/just_stuff.haml +1 -0
  71. data/test/templates/standard_ugly.haml +1 -0
  72. data/test/templates/with_bom.haml +1 -0
  73. data/test/temple_line_counter_test.rb +40 -0
  74. data/test/test_helper.rb +26 -8
  75. data/test/util_test.rb +6 -47
  76. metadata +53 -43
  77. data/test/gemfiles/Gemfile.rails-3.0.x +0 -5
  78. data/test/gemfiles/Gemfile.rails-3.1.x +0 -6
  79. data/test/gemfiles/Gemfile.rails-3.2.x +0 -5
  80. data/test/haml-spec/LICENSE +0 -14
  81. data/test/haml-spec/README.md +0 -106
  82. data/test/haml-spec/lua_haml_spec.lua +0 -38
  83. data/test/haml-spec/perl_haml_test.pl +0 -81
  84. data/test/haml-spec/ruby_haml_test.rb +0 -23
  85. data/test/haml-spec/tests.json +0 -660
  86. data/test/templates/_av_partial_1_ugly.haml +0 -9
  87. data/test/templates/_av_partial_2_ugly.haml +0 -5
  88. data/test/templates/action_view_ugly.haml +0 -47
  89. data/test/templates/standard_ugly.haml +0 -43
data/test/helper_test.rb CHANGED
@@ -1,32 +1,13 @@
1
1
  require 'test_helper'
2
+ require "active_model/naming"
2
3
 
3
- class ActionView::Base
4
- def nested_tag
5
- content_tag(:span) {content_tag(:div) {"something"}}
6
- end
7
-
8
- def wacky_form
9
- form_tag("/foo") {"bar"}
10
- end
4
+ class FormModel
5
+ extend ActiveModel::Naming
11
6
  end
12
7
 
13
- module Haml::Helpers
14
- def something_that_uses_haml_concat
15
- haml_concat('foo').to_s
16
- end
17
-
18
- def render_something_with_haml_concat
19
- haml_concat "<p>"
20
- end
21
-
22
- def render_something_with_haml_tag_and_concat
23
- haml_tag 'p' do
24
- haml_concat '<foo>'
25
- end
26
- end
27
- end
8
+ class HelperTest < Haml::TestCase
9
+ TEXT_AREA_CONTENT_REGEX = /<(textarea)[^>]*>\n(.*?)<\/\1>/im
28
10
 
29
- class HelperTest < MiniTest::Unit::TestCase
30
11
  Post = Struct.new('Post', :body, :error_field, :errors)
31
12
  class PostErrors
32
13
  def on(name)
@@ -41,16 +22,18 @@ class HelperTest < MiniTest::Unit::TestCase
41
22
  end
42
23
 
43
24
  def setup
44
- @base = ActionView::Base.new
25
+ @base = Class.new(ActionView::Base) {
26
+ def nested_tag
27
+ content_tag(:span) {content_tag(:div) {"something"}}
28
+ end
29
+
30
+ def wacky_form
31
+ form_tag("/foo") {"bar"}
32
+ end
33
+ }.new
45
34
  @base.controller = ActionController::Base.new
46
35
  @base.view_paths << File.expand_path("../templates", __FILE__)
47
-
48
- if defined?(ActionController::Response)
49
- # This is needed for >=3.0.0
50
- @base.controller.response = ActionController::Response.new
51
- end
52
-
53
- @base.instance_variable_set('@post', Post.new("Foo bar\nbaz", nil, PostErrors.new))
36
+ @base.instance_variable_set(:@post, Post.new("Foo bar\nbaz", nil, PostErrors.new))
54
37
  end
55
38
 
56
39
  def render(text, options = {})
@@ -59,12 +42,29 @@ class HelperTest < MiniTest::Unit::TestCase
59
42
  end
60
43
 
61
44
  def test_rendering_with_escapes
45
+ def @base.render_something_with_haml_concat
46
+ haml_concat "<p>"
47
+ end
48
+ def @base.render_something_with_haml_tag_and_concat
49
+ haml_tag 'p' do
50
+ haml_concat '<foo>'
51
+ end
52
+ end
53
+
62
54
  output = render(<<-HAML, :action_view)
63
55
  - render_something_with_haml_concat
64
56
  - render_something_with_haml_tag_and_concat
65
57
  - render_something_with_haml_concat
66
58
  HAML
67
- assert_equal("&lt;p&gt;\n<p>\n <foo>\n</p>\n&lt;p&gt;\n", output)
59
+ assert_equal("&lt;p&gt;\n<p>\n &lt;foo&gt;\n</p>\n&lt;p&gt;\n", output)
60
+ end
61
+
62
+ def test_with_raw_haml_concat
63
+ haml = <<HAML
64
+ - with_raw_haml_concat do
65
+ - haml_concat "<>&"
66
+ HAML
67
+ assert_equal("<>&\n", render(haml, :action_view))
68
68
  end
69
69
 
70
70
  def test_flatten
@@ -79,13 +79,13 @@ HAML
79
79
  end
80
80
 
81
81
  def test_list_of_should_render_correctly
82
- assert_equal("<li>1</li>\n<li>2</li>\n", render("= list_of([1, 2]) do |i|\n = i"))
83
- assert_equal("<li>[1]</li>\n", render("= list_of([[1]]) do |i|\n = i.inspect"))
84
- assert_equal("<li>\n <h1>Fee</h1>\n <p>A word!</p>\n</li>\n<li>\n <h1>Fi</h1>\n <p>A word!</p>\n</li>\n<li>\n <h1>Fo</h1>\n <p>A word!</p>\n</li>\n<li>\n <h1>Fum</h1>\n <p>A word!</p>\n</li>\n",
82
+ assert_equal("<li>1</li>\n<li>2</li>", render("= list_of([1, 2]) do |i|\n = i"))
83
+ assert_equal("<li>[1]</li>", render("= list_of([[1]]) do |i|\n = i.inspect"))
84
+ assert_equal("<li>\n <h1>Fee</h1>\n <p>A word!</p>\n</li>\n<li>\n <h1>Fi</h1>\n <p>A word!</p>\n</li>\n<li>\n <h1>Fo</h1>\n <p>A word!</p>\n</li>\n<li>\n <h1>Fum</h1>\n <p>A word!</p>\n</li>",
85
85
  render("= list_of(['Fee', 'Fi', 'Fo', 'Fum']) do |title|\n %h1= title\n %p A word!"))
86
- assert_equal("<li c='3'>1</li>\n<li c='3'>2</li>\n", render("= list_of([1, 2], {:c => 3}) do |i|\n = i"))
87
- assert_equal("<li c='3'>[1]</li>\n", render("= list_of([[1]], {:c => 3}) do |i|\n = i.inspect"))
88
- assert_equal("<li c='3'>\n <h1>Fee</h1>\n <p>A word!</p>\n</li>\n<li c='3'>\n <h1>Fi</h1>\n <p>A word!</p>\n</li>\n<li c='3'>\n <h1>Fo</h1>\n <p>A word!</p>\n</li>\n<li c='3'>\n <h1>Fum</h1>\n <p>A word!</p>\n</li>\n",
86
+ assert_equal("<li c='3'>1</li>\n<li c='3'>2</li>", render("= list_of([1, 2], {:c => 3}) do |i|\n = i"))
87
+ assert_equal("<li c='3'>[1]</li>", render("= list_of([[1]], {:c => 3}) do |i|\n = i.inspect"))
88
+ assert_equal("<li c='3'>\n <h1>Fee</h1>\n <p>A word!</p>\n</li>\n<li c='3'>\n <h1>Fi</h1>\n <p>A word!</p>\n</li>\n<li c='3'>\n <h1>Fo</h1>\n <p>A word!</p>\n</li>\n<li c='3'>\n <h1>Fum</h1>\n <p>A word!</p>\n</li>",
89
89
  render("= list_of(['Fee', 'Fi', 'Fo', 'Fum'], {:c => 3}) do |title|\n %h1= title\n %p A word!"))
90
90
  end
91
91
 
@@ -95,15 +95,15 @@ HAML
95
95
  end
96
96
 
97
97
  def test_tabs
98
- assert_equal("foo\n bar\nbaz\n", render("foo\n- tab_up\nbar\n- tab_down\nbaz"))
99
- assert_equal(" <p>tabbed</p>\n", render("- buffer.tabulation=5\n%p tabbed"))
98
+ assert_equal("foo\nbar\nbaz\n", render("foo\n- tab_up\nbar\n- tab_down\nbaz"))
99
+ assert_equal("<p>tabbed</p>\n", render("- buffer.tabulation=5\n%p tabbed"))
100
100
  end
101
101
 
102
102
  def test_with_tabs
103
103
  assert_equal(<<HTML, render(<<HAML))
104
104
  Foo
105
- Bar
106
- Baz
105
+ Bar
106
+ Baz
107
107
  Baz
108
108
  HTML
109
109
  Foo
@@ -120,7 +120,7 @@ HAML
120
120
 
121
121
  begin
122
122
  ActionView::Base.new.render(:inline => "<%= flatten('Foo\\nBar') %>")
123
- rescue NoMethodError, Haml::Util.av_template_class(:Error)
123
+ rescue NoMethodError, ActionView::Template::Error
124
124
  proper_behavior = true
125
125
  end
126
126
  assert(proper_behavior)
@@ -138,18 +138,26 @@ HAML
138
138
  end
139
139
 
140
140
  def test_form_tag
141
- # This is usually provided by ActionController::Base.
142
141
  def @base.protect_against_forgery?; false; end
143
-
144
142
  rendered = render(<<HAML, :action_view)
145
143
  = form_tag 'foo' do
146
- %p bar
147
- %strong baz
144
+ %p bar
145
+ %strong baz
148
146
  HAML
149
- fragment = Nokogiri::HTML.fragment(rendered)
150
- assert_equal 'foo', fragment.css('form').first.attributes['action'].to_s
151
- assert_equal 'bar', fragment.css('form p').first.text.strip
152
- assert_equal 'baz', fragment.css('form strong').first.text.strip
147
+ fragment = Nokogiri::HTML.fragment(rendered)
148
+ assert_equal 'foo', fragment.css('form').first.attributes['action'].to_s
149
+ assert_equal 'bar', fragment.css('form p').first.text.strip
150
+ assert_equal 'baz', fragment.css('form strong').first.text.strip
151
+ end
152
+
153
+ def test_form_for
154
+ # FIXME: current HAML doesn't do proper indentation with form_for (it's the capture { output } in #form_for).
155
+ def @base.protect_against_forgery?; false; end
156
+ rendered = render(<<HAML, :action_view)
157
+ = form_for OpenStruct.new, url: 'foo', as: :post do |f|
158
+ = f.text_field :name
159
+ HAML
160
+ assert_match(/<(form|div)[^>]+><input/, rendered)
153
161
  end
154
162
 
155
163
  def test_pre
@@ -157,56 +165,40 @@ HAML
157
165
  render('= content_tag "pre", "Foo bar\n baz"', :action_view))
158
166
  end
159
167
 
160
- # Rails >= 3.2.3 adds a newline after opening textarea tags.
161
- def self.rails_text_area_helpers_emit_a_newline?
162
- major, minor, tiny = ActionPack::VERSION::MAJOR, ActionPack::VERSION::MINOR, ActionPack::VERSION::TINY
163
- major == 4 || ((major == 3) && (minor >= 2) && (tiny >= 3))
164
- end
165
-
166
- def text_area_content_regex
167
- @text_area_content_regex ||= if self.class.rails_text_area_helpers_emit_a_newline?
168
- /<(textarea)[^>]*>\n(.*?)<\/\1>/im
169
- else
170
- /<(textarea)[^>]*>(.*?)<\/\1>/im
171
- end
172
- end
173
-
174
168
  def test_text_area_tag
175
169
  output = render('= text_area_tag "body", "Foo\nBar\n Baz\n Boom"', :action_view)
176
- match_data = output.match(text_area_content_regex)
170
+ match_data = output.match(TEXT_AREA_CONTENT_REGEX)
177
171
  assert_equal "Foo&#x000A;Bar&#x000A; Baz&#x000A; Boom", match_data[2]
178
172
  end
179
173
 
180
174
  def test_text_area
181
175
  output = render('= text_area :post, :body', :action_view)
182
- match_data = output.match(text_area_content_regex)
176
+ match_data = output.match(TEXT_AREA_CONTENT_REGEX)
183
177
  assert_equal "Foo bar&#x000A;baz", match_data[2]
184
178
  end
185
179
 
186
180
  def test_partials_should_not_cause_textareas_to_be_indented
187
181
  # non-indentation of textareas rendered inside partials
188
- @base.instance_variable_set('@post', Post.new("Foo", nil, PostErrors.new))
182
+ @base.instance_variable_set(:@post, Post.new("Foo", nil, PostErrors.new))
189
183
  output = render(".foo\n .bar\n = render '/text_area_helper'", :action_view)
190
- match_data = output.match(text_area_content_regex)
184
+ match_data = output.match(TEXT_AREA_CONTENT_REGEX)
191
185
  assert_equal 'Foo', match_data[2]
192
186
  end
193
187
 
194
- if rails_text_area_helpers_emit_a_newline?
195
- def test_textareas_should_prerve_leading_whitespace
196
- # leading whitespace preservation
197
- @base.instance_variable_set('@post', Post.new(" Foo", nil, PostErrors.new))
198
- output = render(".foo\n = text_area :post, :body", :action_view)
199
- match_data = output.match(text_area_content_regex)
200
- assert_equal '&#x0020; Foo', match_data[2]
201
- end
188
+ def test_textareas_should_preserve_leading_whitespace
189
+ # leading whitespace preservation
190
+ @base.instance_variable_set(:@post, Post.new(" Foo", nil, PostErrors.new))
191
+ output = render(".foo\n = text_area :post, :body", :action_view)
192
+ match_data = output.match(TEXT_AREA_CONTENT_REGEX)
193
+ assert_equal '&#x0020; Foo', match_data[2]
194
+ end
202
195
 
203
- def test_textareas_should_prerve_leading_whitespace_in_partials
204
- # leading whitespace in textareas rendered inside partials
205
- @base.instance_variable_set('@post', Post.new(" Foo", nil, PostErrors.new))
206
- output = render(".foo\n .bar\n = render '/text_area_helper'", :action_view)
207
- match_data = output.match(text_area_content_regex)
208
- assert_equal '&#x0020; Foo', match_data[2]
209
- end
196
+ def test_textareas_should_preserve_leading_whitespace_in_partials
197
+ # leading whitespace in textareas rendered inside partials
198
+ @base.instance_variable_set(:@post, Post.new(" Foo", nil, PostErrors.new))
199
+ output = render(".foo\n .bar\n = render '/text_area_helper'", :action_view)
200
+ match_data = output.match(TEXT_AREA_CONTENT_REGEX)
201
+ assert_equal '&#x0020; Foo', match_data[2]
210
202
  end
211
203
 
212
204
  def test_capture_haml
@@ -306,8 +298,12 @@ HAML
306
298
  assert_equal("<p id='foo&amp;bar'>baz</p>\n", render("%p{:id => 'foo&bar'} baz", :escape_html => true))
307
299
  end
308
300
 
309
- def test_haml_tag_autoclosed_tags_are_closed
310
- assert_equal("<br class='foo' />\n", render("- haml_tag :br, :class => 'foo'"))
301
+ def test_haml_tag_autoclosed_tags_are_closed_xhtml
302
+ assert_equal("<br class='foo' />\n", render("- haml_tag :br, :class => 'foo'", :format => :xhtml))
303
+ end
304
+
305
+ def test_haml_tag_autoclosed_tags_are_closed_html
306
+ assert_equal("<br class='foo'>\n", render("- haml_tag :br, :class => 'foo'", :format => :html5))
311
307
  end
312
308
 
313
309
  def test_haml_tag_with_class_array
@@ -338,7 +334,8 @@ HAML
338
334
  end
339
335
 
340
336
  def test_haml_tag_flags
341
- assert_equal("<p />\n", render("- haml_tag :p, :/"))
337
+ assert_equal("<p />\n", render("- haml_tag :p, :/", :format => :xhtml))
338
+ assert_equal("<p>\n", render("- haml_tag :p, :/", :format => :html5))
342
339
  assert_equal("<p>kumquat</p>\n", render("- haml_tag :p, :< do\n kumquat"))
343
340
 
344
341
  assert_raises(Haml::Error) { render("- haml_tag :p, 'foo', :/") }
@@ -361,12 +358,19 @@ HTML
361
358
  HAML
362
359
  end
363
360
 
361
+ def test_haml_concat_inside_haml_tag_escaped_with_xss
362
+ assert_equal("<p>\n &lt;&gt;&amp;\n</p>\n", render(<<HAML, :action_view))
363
+ - haml_tag :p do
364
+ - haml_concat "<>&"
365
+ HAML
366
+ end
367
+
364
368
  def test_haml_concat_with_multiline_string
365
369
  assert_equal(<<HTML, render(<<HAML))
366
370
  <p>
367
- foo
368
- bar
369
- baz
371
+ foo
372
+ bar
373
+ baz
370
374
  </p>
371
375
  HTML
372
376
  %p
@@ -374,10 +378,10 @@ HTML
374
378
  HAML
375
379
  end
376
380
 
377
- def test_haml_tag_with_ugly
378
- assert_equal(<<HTML, render(<<HAML, :ugly => true))
381
+ def test_haml_tag
382
+ assert_equal(<<HTML, render(<<HAML))
379
383
  <p>
380
- <strong>Hi!</strong>
384
+ <strong>Hi!</strong>
381
385
  </p>
382
386
  HTML
383
387
  - haml_tag :p do
@@ -385,6 +389,41 @@ HTML
385
389
  HAML
386
390
  end
387
391
 
392
+ def test_haml_tag_if_positive
393
+ assert_equal(<<HTML, render(<<HAML))
394
+ <div class='conditional'>
395
+ <p>A para</p>
396
+ </div>
397
+ HTML
398
+ - haml_tag_if true, '.conditional' do
399
+ %p A para
400
+ HAML
401
+ end
402
+
403
+ def test_haml_tag_if_positive_with_attributes
404
+ assert_equal(<<HTML, render(<<HAML))
405
+ <div class='conditional' foo='bar'>
406
+ <p>A para</p>
407
+ </div>
408
+ HTML
409
+ - haml_tag_if true, '.conditional', {:foo => 'bar'} do
410
+ %p A para
411
+ HAML
412
+ end
413
+
414
+ def test_haml_tag_if_negative
415
+ assert_equal(<<HTML, render(<<HAML))
416
+ <p>A para</p>
417
+ HTML
418
+ - haml_tag_if false, '.conditional' do
419
+ %p A para
420
+ HAML
421
+ end
422
+
423
+ def test_haml_tag_if_error_return
424
+ assert_raises(Haml::Error) { render("= haml_tag_if false, '.conditional' do\n %p Hello") }
425
+ end
426
+
388
427
  def test_is_haml
389
428
  assert(!ActionView::Base.new.is_haml?)
390
429
  assert_equal("true\n", render("= is_haml?"))
@@ -406,15 +445,14 @@ HAML
406
445
  end
407
446
 
408
447
  def test_capture_deals_properly_with_collections
409
- Haml::Helpers.module_eval do
410
- def trc(collection, &block)
411
- collection.each do |record|
412
- haml_concat capture_haml(record, &block)
413
- end
448
+ obj = Object.new
449
+ def obj.trc(collection, &block)
450
+ collection.each do |record|
451
+ haml_concat capture_haml(record, &block)
414
452
  end
415
453
  end
416
454
 
417
- assert_equal("1\n\n2\n\n3\n\n", render("- trc([1, 2, 3]) do |i|\n = i.inspect"))
455
+ assert_equal("1\n\n2\n\n3\n\n", render("- trc([1, 2, 3]) do |i|\n = i.inspect", scope: obj))
418
456
  end
419
457
 
420
458
  def test_capture_with_string_block
@@ -422,17 +460,57 @@ HAML
422
460
  end
423
461
 
424
462
  def test_capture_with_non_string_value_reurns_nil
425
- Haml::Helpers.module_eval do
426
- def check_capture_returns_nil(&block)
427
- contents = capture(&block)
463
+ def @base.check_capture_returns_nil(&block)
464
+ contents = capture(&block)
428
465
 
429
- contents << "ERROR" if contents
430
- end
466
+ contents << "ERROR" if contents
431
467
  end
432
468
 
433
469
  assert_equal("\n", render("= check_capture_returns_nil { 2 }", :action_view))
434
470
  end
435
471
 
472
+
473
+ class HomemadeViewContext
474
+ include ActionView::Context
475
+ include ActionView::Helpers::FormHelper
476
+
477
+ def initialize
478
+ _prepare_context
479
+ end
480
+
481
+ def url_for(*)
482
+ "/"
483
+ end
484
+
485
+ def dom_class(*)
486
+ end
487
+
488
+ def dom_id(*)
489
+ end
490
+
491
+ def m # I have to inject the model into the view using an instance method, using locals doesn't work.
492
+ FormModel.new
493
+ end
494
+
495
+ def protect_against_forgery?
496
+ end
497
+
498
+ # def capture(*args, &block)
499
+ # capture_haml(*args, &block)
500
+ # end
501
+ end
502
+
503
+ def test_form_for_with_homemade_view_context
504
+ handler = ActionView::Template.handler_for_extension("haml")
505
+ template = ActionView::Template.new(<<HAML, "inline template", handler, {})
506
+ = form_for(m, :url => "/") do
507
+ %b Bold!
508
+ HAML
509
+
510
+ # see if Bold is within form tags:
511
+ assert_match(/<form.*>.*<b>Bold!<\/b>.*<\/form>/m, template.render(HomemadeViewContext.new, {}))
512
+ end
513
+
436
514
  def test_find_and_preserve_with_block
437
515
  assert_equal("<pre>Foo&#x000A;Bar</pre>\nFoo\nBar\n",
438
516
  render("= find_and_preserve do\n %pre\n Foo\n Bar\n Foo\n Bar"))
@@ -444,7 +522,7 @@ HAML
444
522
  end
445
523
 
446
524
  def test_preserve_with_block
447
- assert_equal("<pre>Foo&#x000A;Bar</pre>&#x000A;Foo&#x000A;Bar\n",
525
+ assert_equal("<pre>Foo&#x000A;Bar</pre>&#x000A;Foo&#x000A;Bar",
448
526
  render("= preserve do\n %pre\n Foo\n Bar\n Foo\n Bar"))
449
527
  end
450
528
 
@@ -481,17 +559,22 @@ MESSAGE
481
559
  end
482
560
 
483
561
  def test_error_return_line
484
- render("%p foo\n= haml_concat 'foo'\n%p bar")
562
+ render("%p foo\n= haml_concat('foo').to_s\n%p bar")
485
563
  assert false, "Expected Haml::Error"
486
564
  rescue Haml::Error => e
487
- assert_equal 2, e.backtrace[1].scan(/:(\d+)/).first.first.to_i
565
+ assert_equal 2, e.backtrace[0].scan(/:(\d+)/).first.first.to_i
488
566
  end
489
567
 
490
568
  def test_error_return_line_in_helper
491
- render("- something_that_uses_haml_concat")
569
+ obj = Object.new
570
+ def obj.something_that_uses_haml_concat
571
+ haml_concat('foo').to_s
572
+ end
573
+
574
+ render("- something_that_uses_haml_concat", scope: obj)
492
575
  assert false, "Expected Haml::Error"
493
576
  rescue Haml::Error => e
494
- assert_equal 15, e.backtrace[0].scan(/:(\d+)/).first.first.to_i
577
+ assert_equal __LINE__ - 6, e.backtrace[0].scan(/:(\d+)/).first.first.to_i
495
578
  end
496
579
 
497
580
  class ActsLikeTag
@@ -559,7 +642,7 @@ HAML
559
642
 
560
643
  def test_escape_once_leaves_numeric_references
561
644
  assert_equal "&quot;&gt;&lt;&amp; &#160;", Haml::Helpers.escape_once('"><& &#160;') #decimal
562
- #assert_equal "&quot;&gt;&lt;&amp; &#x00a0;", Haml::Helpers.escape_once('"><& &#x00a0;') #hexadecimal
645
+ assert_equal "&quot;&gt;&lt;&amp; &#x00a0;", Haml::Helpers.escape_once('"><& &#x00a0;') #hexadecimal
563
646
  end
564
647
 
565
648
  def test_escape_once_encoding
@@ -571,6 +654,36 @@ HAML
571
654
  $stderr = old_stderr
572
655
  end
573
656
 
657
+ def test_html_attrs_xhtml
658
+ assert_equal("<html lang='en-US' xml:lang='en-US' xmlns='http://www.w3.org/1999/xhtml'></html>\n",
659
+ render("%html{html_attrs}", :format => :xhtml))
660
+ end
661
+
662
+ def test_html_attrs_html4
663
+ assert_equal("<html lang='en-US'></html>\n",
664
+ render("%html{html_attrs}", :format => :html4))
665
+ end
666
+
667
+ def test_html_attrs_html5
668
+ assert_equal("<html lang='en-US'></html>\n",
669
+ render("%html{html_attrs}", :format => :html5))
670
+ end
671
+
672
+ def test_html_attrs_xhtml_other_lang
673
+ assert_equal("<html lang='es-AR' xml:lang='es-AR' xmlns='http://www.w3.org/1999/xhtml'></html>\n",
674
+ render("%html{html_attrs('es-AR')}", :format => :xhtml))
675
+ end
676
+
677
+ def test_html_attrs_html4_other_lang
678
+ assert_equal("<html lang='es-AR'></html>\n",
679
+ render("%html{html_attrs('es-AR')}", :format => :html4))
680
+ end
681
+
682
+ def test_html_attrs_html5_other_lang
683
+ assert_equal("<html lang='es-AR'></html>\n",
684
+ render("%html{html_attrs('es-AR')}", :format => :html5))
685
+ end
686
+
574
687
  def test_escape_once_should_work_on_frozen_strings
575
688
  begin
576
689
  Haml::Helpers.escape_once('foo'.freeze)
@@ -580,4 +693,3 @@ HAML
580
693
  end
581
694
 
582
695
  end
583
-
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ module Haml
4
+ class OptionsTest < Haml::TestCase
5
+ def test_buffer_defaults_have_only_buffer_option_keys
6
+ assert_equal(
7
+ Haml::Options.buffer_option_keys.sort,
8
+ Haml::Options.buffer_defaults.keys.sort,
9
+ )
10
+ end
11
+
12
+ def test_buffer_defaults_values_are_the_same_as_rails_defaults
13
+ rails_defaults = Haml::Options.defaults.merge(Haml::Template.options)
14
+ Haml::Options.buffer_option_keys.each do |key|
15
+ assert_equal(
16
+ rails_defaults[key],
17
+ Haml::Options.buffer_defaults[key], "key: #{key}"
18
+ )
19
+ end
20
+ end
21
+ end
22
+ end
data/test/parser_test.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'test_helper'
2
2
 
3
3
  module Haml
4
- class ParserTest < MiniTest::Unit::TestCase
4
+ class ParserTest < Haml::TestCase
5
5
 
6
6
  test "should raise error for 'else' at wrong indent level" do
7
7
  begin
@@ -94,12 +94,79 @@ module Haml
94
94
  end
95
95
  end
96
96
 
97
+ test "revealed conditional comments are detected" do
98
+ text = "some revealed text"
99
+ cond = "[cond]"
100
+
101
+ node = parse("/!#{cond} #{text}").children[0]
102
+
103
+ assert_equal text, node.value[:text]
104
+ assert_equal cond, node.value[:conditional]
105
+ assert node.value[:revealed]
106
+ end
107
+
108
+ test "hidden conditional comments are detected" do
109
+ text = "some revealed text"
110
+ cond = "[cond]"
111
+
112
+ node = parse("/#{cond} #{text}").children[0]
113
+
114
+ assert_equal text, node.value[:text]
115
+ assert_equal cond, node.value[:conditional]
116
+ refute node.value[:revealed]
117
+ end
118
+
119
+ test "only script lines are checked for continuation keywords" do
120
+ haml = "- if true\n setup\n- else\n else\n"
121
+ node = parse(haml).children[0]
122
+ assert_equal(3, node.children.size)
123
+ end
124
+
125
+ # see #830. Strictly speaking the pipe here is not necessary, but there
126
+ # shouldn't be an error if it is there.
127
+ test "multiline Ruby with extra trailing pipe doesn't raise error" do
128
+ haml = "%p= foo bar, |\n baz"
129
+ begin
130
+ parse haml
131
+ rescue Haml::SyntaxError
132
+ flunk "Should not have raised SyntaxError"
133
+ end
134
+ end
135
+
136
+ test "empty filter doesn't hide following lines" do
137
+ root = parse "%p\n :plain\n %p\n"
138
+ p_element = root.children[0]
139
+ assert_equal 2, p_element.children.size
140
+ assert_equal :filter, p_element.children[0].type
141
+ assert_equal :tag, p_element.children[1].type
142
+ end
143
+
144
+ # Previously blocks under a haml_comment would be rejected if any line was
145
+ # indented by a value that wasn't a multiple of the document indentation.
146
+ test "haml_comment accepts any indentation in content" do
147
+ begin
148
+ parse "-\#\n Indented two spaces\n Indented three spaces"
149
+ rescue Haml::SyntaxError
150
+ flunk "haml_comment should accept any combination of indentation"
151
+ end
152
+ end
153
+
154
+ test "block haml_comment includes text" do
155
+ root = parse "-#\n Hello\n Hello\n"
156
+ assert_equal "Hello\n Hello\n", root.children[0].value[:text]
157
+ end
158
+
159
+ test "block haml_comment includes first line if present" do
160
+ root = parse "-# First line\n Hello\n Hello\n"
161
+ assert_equal " First line\nHello\n Hello\n", root.children[0].value[:text]
162
+ end
163
+
97
164
  private
98
165
 
99
166
  def parse(haml, options = nil)
100
167
  options ||= Options.new
101
- parser = Parser.new(haml, options)
102
- parser.parse
168
+ parser = Parser.new(options)
169
+ parser.call(haml)
103
170
  end
104
171
  end
105
- end
172
+ end
@@ -0,0 +1,4 @@
1
+ <div class='o-media@md c-user c-user--premium'>
2
+ <img alt='' class='o-media__img@md c-user__photo c-avatar' src='' />
3
+ <p class='o-media__body@md c-user__bio'>...</p>
4
+ </div>
@@ -2,8 +2,8 @@
2
2
  <p></p>
3
3
  <h1>Me!</h1>
4
4
  <div id='foo'>
5
- <p id='bar'>All</p>
6
- <br />
7
- <p class='baz'>This</p>
8
- Should render
5
+ <p id='bar'>All</p>
6
+ <br />
7
+ <p class='baz'>This</p>
8
+ Should render
9
9
  </div>