haml 1.7.2 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of haml might be problematic. Click here for more details.

Files changed (71) hide show
  1. data/README +17 -9
  2. data/Rakefile +12 -4
  3. data/VERSION +1 -1
  4. data/init.rb +1 -6
  5. data/lib/haml.rb +65 -7
  6. data/lib/haml/buffer.rb +49 -84
  7. data/lib/haml/engine.rb +155 -797
  8. data/lib/haml/error.rb +3 -33
  9. data/lib/haml/exec.rb +86 -65
  10. data/lib/haml/filters.rb +57 -27
  11. data/lib/haml/helpers.rb +52 -9
  12. data/lib/haml/helpers/action_view_mods.rb +1 -1
  13. data/lib/haml/html.rb +20 -5
  14. data/lib/haml/precompiler.rb +671 -0
  15. data/lib/haml/template.rb +20 -73
  16. data/lib/haml/template/patch.rb +51 -0
  17. data/lib/haml/template/plugin.rb +21 -0
  18. data/lib/sass.rb +78 -3
  19. data/lib/sass/constant.rb +45 -19
  20. data/lib/sass/constant.rb.rej +42 -0
  21. data/lib/sass/constant/string.rb +4 -0
  22. data/lib/sass/css.rb +162 -39
  23. data/lib/sass/engine.rb +38 -14
  24. data/lib/sass/plugin.rb +79 -44
  25. data/lib/sass/tree/attr_node.rb +12 -11
  26. data/lib/sass/tree/comment_node.rb +9 -3
  27. data/lib/sass/tree/directive_node.rb +51 -0
  28. data/lib/sass/tree/node.rb +13 -6
  29. data/lib/sass/tree/rule_node.rb +34 -12
  30. data/test/benchmark.rb +85 -52
  31. data/test/haml/engine_test.rb +172 -84
  32. data/test/haml/helper_test.rb +31 -3
  33. data/test/haml/html2haml_test.rb +60 -0
  34. data/test/haml/markaby/standard.mab +52 -0
  35. data/test/haml/results/eval_suppressed.xhtml +4 -1
  36. data/test/haml/results/helpers.xhtml +15 -4
  37. data/test/haml/results/just_stuff.xhtml +9 -1
  38. data/test/haml/results/standard.xhtml +0 -1
  39. data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  40. data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  41. data/test/haml/rhtml/action_view.rhtml +62 -0
  42. data/test/haml/rhtml/standard.rhtml +0 -1
  43. data/test/haml/template_test.rb +41 -21
  44. data/test/haml/templates/_av_partial_1.haml +9 -0
  45. data/test/haml/templates/_av_partial_2.haml +5 -0
  46. data/test/haml/templates/action_view.haml +47 -0
  47. data/test/haml/templates/eval_suppressed.haml +1 -0
  48. data/test/haml/templates/helpers.haml +9 -3
  49. data/test/haml/templates/just_stuff.haml +10 -1
  50. data/test/haml/templates/partials.haml +1 -1
  51. data/test/haml/templates/standard.haml +0 -1
  52. data/test/profile.rb +2 -2
  53. data/test/sass/engine_test.rb +113 -3
  54. data/test/sass/engine_test.rb.rej +18 -0
  55. data/test/sass/plugin_test.rb +34 -11
  56. data/test/sass/results/compact.css +1 -1
  57. data/test/sass/results/complex.css +1 -1
  58. data/test/sass/results/compressed.css +1 -0
  59. data/test/sass/results/constants.css +3 -1
  60. data/test/sass/results/expanded.css +2 -1
  61. data/test/sass/results/import.css +2 -0
  62. data/test/sass/results/nested.css +2 -1
  63. data/test/sass/templates/_partial.sass +2 -0
  64. data/test/sass/templates/compact.sass +2 -0
  65. data/test/sass/templates/complex.sass +1 -0
  66. data/test/sass/templates/compressed.sass +15 -0
  67. data/test/sass/templates/constants.sass +9 -0
  68. data/test/sass/templates/expanded.sass +2 -0
  69. data/test/sass/templates/import.sass +1 -1
  70. data/test/sass/templates/nested.sass +2 -0
  71. metadata +22 -2
@@ -13,7 +13,6 @@ class HelperTest < Test::Unit::TestCase
13
13
  include Haml::Helpers
14
14
 
15
15
  def setup
16
- ActionView::Base.register_template_handler("haml", Haml::Template)
17
16
  @base = ActionView::Base.new
18
17
  @base.controller = ActionController::Base.new
19
18
  end
@@ -51,7 +50,8 @@ class HelperTest < Test::Unit::TestCase
51
50
  end
52
51
 
53
52
  def test_tabs
54
- assert_equal(render("foo\n- tab_up\nbar\n- tab_down\nbaz"), "foo\n bar\nbaz\n")
53
+ assert_equal("foo\n bar\nbaz\n", render("foo\n- tab_up\nbar\n- tab_down\nbaz"))
54
+ assert_equal(" <p>tabbed</p>\n", render("- buffer.tabulation=5\n%p tabbed"))
55
55
  end
56
56
 
57
57
  def test_helpers_dont_leak
@@ -77,8 +77,10 @@ class HelperTest < Test::Unit::TestCase
77
77
  def test_action_view_included
78
78
  assert(Haml::Helpers.action_view?)
79
79
  end
80
-
80
+
81
81
  def test_form_tag
82
+ # This is usually provided by ActionController::Base.
83
+ def @base.protect_against_forgery?; false; end
82
84
  result = render("- form_tag 'foo' do\n %p bar\n %strong baz", :action_view)
83
85
  should_be = "<form action=\"foo\" method=\"post\">\n <p>bar</p>\n <strong>baz</strong>\n</form>\n"
84
86
  assert_equal(should_be, result)
@@ -119,5 +121,31 @@ class HelperTest < Test::Unit::TestCase
119
121
 
120
122
  assert_equal("1\n\n2\n\n3\n\n", render("- trc([1, 2, 3]) do |i|\n = i.inspect"))
121
123
  end
124
+
125
+ def test_find_and_preserve_with_block
126
+ assert_equal("<pre>&#x000A; Foo&#x000A; Bar&#x000A;</pre>\nFoo\nBar\n",
127
+ render("= find_and_preserve do\n %pre\n Foo\n Bar\n Foo\n Bar"))
128
+ end
129
+
130
+ def test_preserve_with_block
131
+ assert_equal("<pre>&#x000A; Foo&#x000A; Bar&#x000A;</pre>&#x000A;Foo&#x000A;Bar&#x000A;\n",
132
+ render("= preserve do\n %pre\n Foo\n Bar\n Foo\n Bar"))
133
+ end
134
+
135
+ def test_init_haml_helpers
136
+ context = Object.new
137
+ class << context
138
+ include Haml::Helpers
139
+ end
140
+ context.init_haml_helpers
141
+
142
+ result = context.capture_haml do
143
+ context.open :p, :attr => "val" do
144
+ context.puts "Blah"
145
+ end
146
+ end
147
+
148
+ assert_equal("<p attr='val'>\n Blah\n</p>\n", result)
149
+ end
122
150
  end
123
151
 
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require File.dirname(__FILE__) + '/../../lib/haml'
5
+ require 'haml/html'
6
+
7
+ class Html2HamlTest < Test::Unit::TestCase
8
+
9
+ def test_empty_render_should_remain_empty
10
+ assert_equal '', render('')
11
+ end
12
+
13
+ def test_id_and_class_should_be_removed_from_hash
14
+ assert_equal '%span#foo.bar', render('<span id="foo" class="bar"> </span>')
15
+ end
16
+
17
+ def test_no_tag_name_for_div_if_class_or_id_is_present
18
+ assert_equal '#foo', render('<div id="foo"> </div>')
19
+ assert_equal '.foo', render('<div class="foo"> </div>')
20
+ end
21
+
22
+ def test_multiple_class_names
23
+ assert_equal '.foo.bar.baz', render('<div class=" foo bar baz "> </div>')
24
+ end
25
+
26
+ def test_should_have_pretty_attributes
27
+ assert_equal_attributes('%input{ :type => "text", :name => "login" }/',
28
+ render('<input type="text" name="login" />'))
29
+ assert_equal_attributes('%meta{ "http-equiv" => "Content-Type", :content => "text/html" }/',
30
+ render('<meta http-equiv="Content-Type" content="text/html" />'))
31
+ assert_equal_attributes('%div{ "xml:lang" => "hr" }/',
32
+ render('<div xml:lang="hr" />'))
33
+ end
34
+
35
+ def test_sqml_comment
36
+ assert_equal "/\n IE sucks", render('<!-- IE sucks -->')
37
+ end
38
+
39
+ def test_rhtml
40
+ assert_equal '- foo = bar', render_rhtml('<% foo = bar %>')
41
+ assert_equal '- foo = bar', render_rhtml('<% foo = bar -%>')
42
+ assert_equal '= h @item.title', render_rhtml('<%=h @item.title %>')
43
+ assert_equal '= h @item.title', render_rhtml('<%=h @item.title -%>')
44
+ end
45
+
46
+ protected
47
+
48
+ def render(text, options = {})
49
+ Haml::HTML.new(text, options).render.rstrip
50
+ end
51
+
52
+ def render_rhtml(text)
53
+ render(text, :rhtml => true)
54
+ end
55
+
56
+ def assert_equal_attributes(expected, result)
57
+ assert_equal *[expected, result].map { |s| s.gsub!(/\{ (.+) \}/, ''); $1.split(', ').sort }
58
+ assert_equal expected, result
59
+ end
60
+ end
@@ -0,0 +1,52 @@
1
+ self << '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
2
+ html(:xmlns=>'http://www.w3.org/1999/xhtml', 'xml:lang'=>'en-US') do
3
+ head do
4
+ title "Hampton Catlin Is Totally Awesome"
5
+ meta("http-equiv" => "Content-Type", :content => "text/html; charset=utf-8")
6
+ end
7
+ body do
8
+ # You're In my house now!
9
+ div :class => "header" do
10
+ self << %|Yes, ladies and gentileman. He is just that egotistical.
11
+ Fantastic! This should be multi-line output
12
+ The question is if this would translate! Ahah!|
13
+ self << 1 + 9 + 8 + 2 #numbers should work and this should be ignored
14
+ end
15
+ div(:id => "body") { self << "Quotes should be loved! Just like people!"}
16
+ 120.times do |number|
17
+ number
18
+ end
19
+ self << "Wow.|"
20
+ p do
21
+ self << "Holy cow " +
22
+ "multiline " +
23
+ "tags! " +
24
+ "A pipe (|) even!"
25
+ self << [1, 2, 3].collect { |n| "PipesIgnored|" }
26
+ self << [1, 2, 3].collect { |n|
27
+ n.to_s
28
+ }.join("|")
29
+ end
30
+ div(:class => "silent") do
31
+ foo = String.new
32
+ foo << "this"
33
+ foo << " shouldn't"
34
+ foo << " evaluate"
35
+ self << foo + " but now it should!"
36
+ # Woah crap a comment!
37
+ end
38
+ # That was a line that shouldn't close everything.
39
+ ul(:class => "really cool") do
40
+ ('a'..'f').each do |a|
41
+ li a
42
+ end
43
+ end
44
+ div((@should_eval = "with this text"), :id => "combo", :class => "of_divs_with_underscore")
45
+ [ 104, 101, 108, 108, 111 ].map do |byte|
46
+ byte.chr
47
+ end
48
+ div(:class => "footer") do
49
+ strong("This is a really long ruby quote. It should be loved and wrapped because its more than 50 characters. This value may change in the future and this test may look stupid. \nSo, I'm just making it *really* long. God, I hope this works", :class => "shout")
50
+ end
51
+ end
52
+ end
@@ -1,4 +1,7 @@
1
- <p></p>
1
+ <p>
2
+ </p>
3
+ <p>
4
+ </p>
2
5
  <h1>Me!</h1>
3
6
  <div id='foo'>
4
7
  <p id='bar'>All</p>
@@ -45,15 +45,23 @@
45
45
  click
46
46
  <a href='thing'>here</a>.
47
47
  <p>baz</p>
48
- <p>boom</p>
48
+ <p>boom</p>
49
49
  foo
50
50
  <p>
51
- <form action="hello/world" method="post">
51
+ <form action="" method="post">
52
52
  </p>
53
- <form action="heeheeaform" method="post">
53
+ <form action="" method="post">
54
54
  <div><input name="commit" type="submit" value="save" /></div>
55
+ <p>
56
+ @foo =
57
+ value one
58
+ </p>
59
+ <p>
60
+ @foo =
61
+ value three
62
+ </p>
55
63
  </form>
56
- <form action="article_url" method="post">
64
+ <form action="" method="post">
57
65
  Title:
58
66
  <input id="article_title" name="article[title]" size="30" type="text" value="Hello" />
59
67
  Body:
@@ -82,3 +90,6 @@ foo
82
90
  </td>
83
91
  </tr>
84
92
  </table>
93
+ <hr />
94
+ <div>
95
+ </div>
@@ -10,6 +10,13 @@
10
10
  Embedded? false!
11
11
  Embedded? true!
12
12
  Embedded? true!
13
+ Embedded? twice! true!
14
+ Embedded? one af"t"er another!
15
+ <p>Embedded? false!</p>
16
+ <p>Embedded? true!</p>
17
+ <p>Embedded? true!</p>
18
+ <p>Embedded? twice! true!</p>
19
+ <p>Embedded? one af"t"er another!</p>
13
20
  <div class='render'><em>wow!</em></div>
14
21
  stuff followed by whitespace
15
22
  <strong>block with whitespace</strong>
@@ -29,7 +36,8 @@ stuff followed by whitespace
29
36
  <strong>there can even be sub-tags!</strong>
30
37
  Or script!
31
38
  -->
32
- <p>class attribute shouldn't appear!</p>
39
+ <p class=''>class attribute should appear!</p>
40
+ <p>this attribute shouldn't appear</p>
33
41
  <!--[if lte IE6]> conditional comment! <![endif]-->
34
42
  <!--[if gte IE7]>
35
43
  <p>Block conditional comment</p>
@@ -6,7 +6,6 @@
6
6
  </head>
7
7
  <body>
8
8
  <!-- You're In my house now! -->
9
- foo
10
9
  <div class='header'>
11
10
  Yes, ladies and gentileman. He is just that egotistical.
12
11
  Fantastic! This should be multi-line output
@@ -0,0 +1,12 @@
1
+ <h2>This is a pretty complicated partial</h2>
2
+ <div class="partial">
3
+ <p>It has several nested partials,</p>
4
+ <ul>
5
+ <% 5.times do %>
6
+ <li>
7
+ <strong>Partial:</strong>
8
+ <% @nesting = 5 %>
9
+ <%= render :partial => 'haml/rhtml/av_partial_2' %>
10
+ <% end %>
11
+ </ul>
12
+ </div>
@@ -0,0 +1,8 @@
1
+ <% @nesting -= 1 %>
2
+ <div class="partial" level="<%= @nesting %>">
3
+ <h3>This is a crazy deep-nested partial.</h3>
4
+ <p>Nesting level <%= @nesting %></p>
5
+ <% if @nesting > 0 %>
6
+ <%= render :partial => 'haml/rhtml/av_partial_2' %>
7
+ <% end %>
8
+ </div>
@@ -0,0 +1,62 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US'>
3
+ <head>
4
+ <title>Hampton Catlin Is Totally Awesome</title>
5
+ <meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
6
+ </head>
7
+ <body>
8
+ <h1>
9
+ This is very much like the standard template,
10
+ except that it has some ActionView-specific stuff.
11
+ It's only used for benchmarking.
12
+ </h1>
13
+ <div class="crazy_partials">
14
+ <%= render :partial => 'haml/rhtml/av_partial_1' %>
15
+ </div>
16
+ <!-- You're In my house now! -->
17
+ <div class='header'>
18
+ Yes, ladies and gentileman. He is just that egotistical.
19
+ Fantastic! This should be multi-line output
20
+ The question is if this would translate! Ahah!
21
+ <%= 1 + 9 + 8 + 2 %>
22
+ <%# numbers should work and this should be ignored %>
23
+ </div>
24
+ <% 120.times do |number| -%>
25
+ <%= number %>
26
+ <% end -%>
27
+ <div id='body'><%= " Quotes should be loved! Just like people!" %></div>
28
+ Wow.
29
+ <p>
30
+ <%= "Holy cow " +
31
+ "multiline " +
32
+ "tags! " +
33
+ "A pipe (|) even!" %>
34
+ <%= [1, 2, 3].collect { |n| "PipesIgnored|" } %>
35
+ <%= [1, 2, 3].collect { |n|
36
+ n.to_s
37
+ }.join("|") %>
38
+ </p>
39
+ <div class='silent'>
40
+ <% foo = String.new
41
+ foo << "this"
42
+ foo << " shouldn't"
43
+ foo << " evaluate" %>
44
+ <%= foo + "but now it should!" %>
45
+ <%# Woah crap a comment! %>
46
+ </div>
47
+ <ul class='really cool'>
48
+ <% ('a'..'f').each do |a|%>
49
+ <li><%= a %>
50
+ <% end %>
51
+ <div class='of_divs_with_underscore' id='combo'><%= @should_eval = "with this text" %></div>
52
+ <%= [ 104, 101, 108, 108, 111 ].map do |byte|
53
+ byte.chr
54
+ end %>
55
+ <div class='footer'>
56
+ <strong class='shout'>
57
+ <%= "This is a really long ruby quote. It should be loved and wrapped because its more than 50 characters. This value may change in the future and this test may look stupid.\n" +
58
+ " So, I'm just making it *really* long. God, I hope this works" %>
59
+ </strong>
60
+ </div>
61
+ </body>
62
+ </html>
@@ -6,7 +6,6 @@
6
6
  </head>
7
7
  <body>
8
8
  <!-- You're In my house now! -->
9
- <% concat("Foo", Proc.new {}) %>
10
9
  <div class='header'>
11
10
  Yes, ladies and gentileman. He is just that egotistical.
12
11
  Fantastic! This should be multi-line output
@@ -2,9 +2,7 @@
2
2
 
3
3
  require 'test/unit'
4
4
  require 'rubygems'
5
- require 'active_support'
6
- require 'action_controller'
7
- require 'action_view'
5
+ require 'action_pack'
8
6
 
9
7
  require File.dirname(__FILE__) + '/../../lib/haml'
10
8
  require 'haml/template'
@@ -27,9 +25,13 @@ class TemplateTest < Test::Unit::TestCase
27
25
  filters }
28
26
 
29
27
  def setup
30
- ActionView::Base.register_template_handler("haml", Haml::Template)
31
28
  Haml::Template.options = { :filters => { 'test'=>TestFilter } }
32
29
  @base = ActionView::Base.new(File.dirname(__FILE__) + "/templates/", {'article' => Article.new, 'foo' => 'value one'})
30
+ @base.send(:evaluate_assigns)
31
+
32
+ # This is used by form_for.
33
+ # It's usually provided by ActionController::Base.
34
+ def @base.protect_against_forgery?; false; end
33
35
  end
34
36
 
35
37
  def render(text)
@@ -42,18 +44,22 @@ class TemplateTest < Test::Unit::TestCase
42
44
  @result
43
45
  end
44
46
 
45
- def assert_renders_correctly(name)
47
+ def assert_renders_correctly(name, &render_method)
48
+ render_method ||= proc { |name| @base.render(name) }
46
49
  test = Proc.new do |rendered|
47
50
  load_result(name).split("\n").zip(rendered.split("\n")).each_with_index do |pair, line|
48
51
  message = "template: #{name}\nline: #{line}"
49
52
  assert_equal(pair.first, pair.last, message)
50
53
  end
51
54
  end
52
- test.call(@base.render(name))
53
-
54
- # If eval's suppressed, the partial won't render anyway :p.
55
- unless Haml::Template.options[:suppress_eval]
56
- test.call(@base.render(:file => "partialize", :locals => { :name => name }))
55
+ begin
56
+ test.call(render_method[name])
57
+ rescue ActionView::TemplateError => e
58
+ if e.message =~ /Can't run [\w:]+ filter; required (one of|file) ((?:'\w+'(?: or )?)+)(, but none were found| not found)/
59
+ puts "\nCouldn't require #{$2}; skipping a test."
60
+ else
61
+ raise e
62
+ end
57
63
  end
58
64
  end
59
65
 
@@ -67,6 +73,27 @@ class TemplateTest < Test::Unit::TestCase
67
73
  end
68
74
  end
69
75
 
76
+ def test_templates_should_render_correctly_with_render_proc
77
+ @@templates.each do |template|
78
+ assert_renders_correctly(template) do |name|
79
+ engine = Haml::Engine.new(File.read(File.dirname(__FILE__) + "/templates/#{name}.haml"), :filters => { 'test'=>TestFilter })
80
+ engine.render_proc(@base).call
81
+ end
82
+ end
83
+ end
84
+
85
+ def test_templates_should_render_correctly_with_def_method
86
+ @@templates.each do |template|
87
+ assert_renders_correctly(template) do |name|
88
+ method = "render_haml_" + name.gsub(/[^a-zA-Z0-9]/, '_')
89
+
90
+ engine = Haml::Engine.new(File.read(File.dirname(__FILE__) + "/templates/#{name}.haml"), :filters => { 'test'=>TestFilter })
91
+ engine.def_method(@base, method)
92
+ @base.send(method)
93
+ end
94
+ end
95
+ end
96
+
70
97
  def test_action_view_templates_render_correctly
71
98
  @base.instance_variable_set("@content_for_layout", 'Lorem ipsum dolor sit amet')
72
99
  assert_renders_correctly 'content_for_layout'
@@ -116,18 +143,11 @@ class TemplateTest < Test::Unit::TestCase
116
143
  end
117
144
 
118
145
  def test_exceptions_should_work_correctly
119
- begin
120
- Haml::Template.new(@base).render(File.dirname(__FILE__) + '/templates/breakage.haml')
121
- rescue Exception => e
122
- assert_equal("./test/haml/templates/breakage.haml:4", e.backtrace[0])
123
- else
124
- assert false
125
- end
126
-
127
146
  begin
128
147
  render("- raise 'oops!'")
129
148
  rescue Exception => e
130
- assert_equal("(haml):1", e.backtrace[0])
149
+ assert_equal("oops!", e.message)
150
+ assert_match(/^\(haml\):1/, e.backtrace[0])
131
151
  else
132
152
  assert false
133
153
  end
@@ -147,9 +167,9 @@ END
147
167
  begin
148
168
  render(template.chomp)
149
169
  rescue Exception => e
150
- assert_equal("(haml):5", e.backtrace[0])
170
+ assert_match(/^\(haml\):5/, e.backtrace[0])
151
171
  else
152
172
  assert false
153
173
  end
154
- end
174
+ end
155
175
  end