haml 1.8.2 → 2.0.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 (77) hide show
  1. data/FAQ +138 -0
  2. data/MIT-LICENSE +1 -1
  3. data/{README → README.rdoc} +66 -3
  4. data/Rakefile +111 -147
  5. data/VERSION +1 -1
  6. data/bin/css2sass +0 -0
  7. data/bin/haml +0 -0
  8. data/bin/html2haml +0 -0
  9. data/bin/sass +0 -0
  10. data/init.rb +6 -1
  11. data/lib/haml.rb +464 -201
  12. data/lib/haml/buffer.rb +117 -63
  13. data/lib/haml/engine.rb +63 -44
  14. data/lib/haml/error.rb +16 -6
  15. data/lib/haml/exec.rb +37 -7
  16. data/lib/haml/filters.rb +213 -68
  17. data/lib/haml/helpers.rb +95 -60
  18. data/lib/haml/helpers/action_view_extensions.rb +1 -1
  19. data/lib/haml/helpers/action_view_mods.rb +54 -6
  20. data/lib/haml/html.rb +6 -6
  21. data/lib/haml/precompiler.rb +254 -133
  22. data/lib/haml/template.rb +3 -6
  23. data/lib/haml/template/patch.rb +9 -2
  24. data/lib/haml/template/plugin.rb +52 -23
  25. data/lib/sass.rb +157 -12
  26. data/lib/sass/constant.rb +22 -22
  27. data/lib/sass/constant/color.rb +13 -13
  28. data/lib/sass/constant/literal.rb +7 -7
  29. data/lib/sass/constant/number.rb +9 -9
  30. data/lib/sass/constant/operation.rb +4 -4
  31. data/lib/sass/constant/string.rb +3 -3
  32. data/lib/sass/css.rb +104 -31
  33. data/lib/sass/engine.rb +120 -39
  34. data/lib/sass/error.rb +1 -1
  35. data/lib/sass/plugin.rb +14 -3
  36. data/lib/sass/plugin/merb.rb +6 -2
  37. data/lib/sass/tree/attr_node.rb +5 -5
  38. data/lib/sass/tree/directive_node.rb +2 -7
  39. data/lib/sass/tree/node.rb +1 -12
  40. data/lib/sass/tree/rule_node.rb +39 -31
  41. data/lib/sass/tree/value_node.rb +1 -1
  42. data/test/benchmark.rb +67 -80
  43. data/test/haml/engine_test.rb +284 -84
  44. data/test/haml/helper_test.rb +51 -15
  45. data/test/haml/results/content_for_layout.xhtml +1 -2
  46. data/test/haml/results/eval_suppressed.xhtml +2 -4
  47. data/test/haml/results/filters.xhtml +44 -15
  48. data/test/haml/results/helpers.xhtml +2 -3
  49. data/test/haml/results/just_stuff.xhtml +2 -6
  50. data/test/haml/results/nuke_inner_whitespace.xhtml +34 -0
  51. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  52. data/test/haml/results/original_engine.xhtml +3 -7
  53. data/test/haml/results/partials.xhtml +1 -0
  54. data/test/haml/results/tag_parsing.xhtml +1 -6
  55. data/test/haml/results/very_basic.xhtml +2 -4
  56. data/test/haml/results/whitespace_handling.xhtml +13 -21
  57. data/test/haml/template_test.rb +8 -15
  58. data/test/haml/templates/_partial.haml +1 -0
  59. data/test/haml/templates/filters.haml +48 -7
  60. data/test/haml/templates/just_stuff.haml +1 -2
  61. data/test/haml/templates/nuke_inner_whitespace.haml +26 -0
  62. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  63. data/test/haml/templates/tag_parsing.haml +0 -3
  64. data/test/haml/test_helper.rb +15 -0
  65. data/test/sass/engine_test.rb +80 -34
  66. data/test/sass/plugin_test.rb +1 -1
  67. data/test/sass/results/import.css +2 -2
  68. data/test/sass/results/mixins.css +95 -0
  69. data/test/sass/results/multiline.css +24 -0
  70. data/test/sass/templates/import.sass +4 -1
  71. data/test/sass/templates/importee.sass +4 -0
  72. data/test/sass/templates/mixins.sass +76 -0
  73. data/test/sass/templates/multiline.sass +20 -0
  74. metadata +65 -51
  75. data/lib/haml/util.rb +0 -18
  76. data/test/haml/runner.rb +0 -16
  77. data/test/profile.rb +0 -65
@@ -19,6 +19,3 @@
19
19
  .foo16 e
20
20
  .123 f
21
21
  .foo2u g
22
- %div.broken
23
- %foo<{ :a => :b }
24
- .foo>{ :c => :d }
@@ -0,0 +1,15 @@
1
+ # allows testing with edge Rails by creating a test/rails symlink
2
+ linked_rails = File.dirname(__FILE__) + '/../rails'
3
+
4
+ if File.exists? linked_rails
5
+ puts "[ using linked Rails ]"
6
+ $:.unshift linked_rails + '/activesupport/lib'
7
+ $:.unshift linked_rails + '/actionpack/lib'
8
+ else
9
+ require 'rubygems'
10
+ end
11
+ require 'action_controller'
12
+ require 'action_view'
13
+
14
+ require 'test/unit'
15
+ require File.dirname(__FILE__) + '/../../lib/haml'
@@ -5,53 +5,73 @@ require File.dirname(__FILE__) + '/../../lib/sass'
5
5
  require 'sass/engine'
6
6
 
7
7
  class SassEngineTest < Test::Unit::TestCase
8
+ # A map of erroneous Sass documents to the error messages they should produce.
9
+ # The error messages may be arrays;
10
+ # if so, the second element should be the line number that should be reported for the error.
11
+ # If this isn't provided, the tests will assume the line number should be the last line of the document.
8
12
  EXCEPTION_MAP = {
9
- "!a = 1 + " => 'Constant arithmetic error: "1 +"',
10
- "!a = 1 + 2 +" => 'Constant arithmetic error: "1 + 2 +"',
11
- "!a = \"b" => 'Unterminated string: "\\"b"',
12
- "!a = #aaa - a" => 'Undefined operation: "#aaaaaa minus a"',
13
- "!a = #aaa / a" => 'Undefined operation: "#aaaaaa div a"',
14
- "!a = #aaa * a" => 'Undefined operation: "#aaaaaa times a"',
15
- "!a = #aaa % a" => 'Undefined operation: "#aaaaaa mod a"',
16
- "!a = 1 - a" => 'Undefined operation: "1 minus a"',
17
- "!a = 1 * a" => 'Undefined operation: "1 times a"',
18
- "!a = 1 / a" => 'Undefined operation: "1 div a"',
19
- "!a = 1 % a" => 'Undefined operation: "1 mod a"',
20
- ":" => 'Invalid attribute: ":"',
21
- ": a" => 'Invalid attribute: ": a"',
22
- ":= a" => 'Invalid attribute: ":= a"',
23
- "a\n :b" => 'Invalid attribute: ":b "',
24
- "a\n :b: c" => 'Invalid attribute: ":b: c"',
25
- "a\n :b:c d" => 'Invalid attribute: ":b:c d"',
26
- "a\n :b=c d" => 'Invalid attribute: ":b=c d"',
27
- "a\n :b c;" => 'Invalid attribute: ":b c;" (This isn\'t CSS!)',
28
- "a\n b : c" => 'Invalid attribute: "b : c"',
29
- "a\n b=c: d" => 'Invalid attribute: "b=c: d"',
13
+ "!a = 1 + " => 'Constant arithmetic error: "1 +".',
14
+ "!a = 1 + 2 +" => 'Constant arithmetic error: "1 + 2 +".',
15
+ "!a = \"b" => 'Unterminated string: "\\"b".',
16
+ "!a = #aaa - a" => 'Undefined operation: "#aaaaaa minus a".',
17
+ "!a = #aaa / a" => 'Undefined operation: "#aaaaaa div a".',
18
+ "!a = #aaa * a" => 'Undefined operation: "#aaaaaa times a".',
19
+ "!a = #aaa % a" => 'Undefined operation: "#aaaaaa mod a".',
20
+ "!a = 1 - a" => 'Undefined operation: "1 minus a".',
21
+ "!a = 1 * a" => 'Undefined operation: "1 times a".',
22
+ "!a = 1 / a" => 'Undefined operation: "1 div a".',
23
+ "!a = 1 % a" => 'Undefined operation: "1 mod a".',
24
+ ":" => 'Invalid attribute: ":".',
25
+ ": a" => 'Invalid attribute: ": a".',
26
+ ":= a" => 'Invalid attribute: ":= a".',
27
+ "a\n :b" => 'Invalid attribute: ":b ".',
28
+ "a\n :b: c" => 'Invalid attribute: ":b: c".',
29
+ "a\n :b:c d" => 'Invalid attribute: ":b:c d".',
30
+ "a\n :b=c d" => 'Invalid attribute: ":b=c d".',
31
+ "a\n :b c;" => 'Invalid attribute: ":b c;" (This isn\'t CSS!).',
32
+ "a\n b : c" => 'Invalid attribute: "b : c".',
33
+ "a\n b=c: d" => 'Invalid attribute: "b=c: d".',
30
34
  ":a" => 'Attributes aren\'t allowed at the root of a document.',
31
- "!" => 'Invalid constant: "!"',
32
- "!a" => 'Invalid constant: "!a"',
33
- "! a" => 'Invalid constant: "! a"',
34
- "!a b" => 'Invalid constant: "!a b"',
35
- "a\n\t:b c" => "Illegal Indentation: Only two space characters are allowed as tabulation.",
36
- "a\n :b c" => "Illegal Indentation: Only two space characters are allowed as tabulation.",
37
- "a\n :b c" => "Illegal Indentation: Only two space characters are allowed as tabulation.",
35
+ "!" => 'Invalid constant: "!".',
36
+ "!a" => 'Invalid constant: "!a".',
37
+ "! a" => 'Invalid constant: "! a".',
38
+ "!a b" => 'Invalid constant: "!a b".',
39
+ "a\n\t:b c" => <<END.strip,
40
+ A tab character was used for indentation. Sass must be indented using two spaces.
41
+ Are you sure you have soft tabs enabled in your editor?
42
+ END
43
+ "a\n :b c" => "1 space was used for indentation. Sass must be indented using two spaces.",
44
+ "a\n :b c" => "4 spaces were used for indentation. Sass must be indented using two spaces.",
38
45
  "a\n :b c\n !d = 3" => "Constants may only be declared at the root of a document.",
39
- "!a = 1b + 2c" => "Incompatible units: b and c",
40
- "& a\n :b c" => "Base-level rules cannot contain the parent-selector-referencing character '&'",
46
+ "!a = 1b + 2c" => "Incompatible units: b and c.",
47
+ "& a\n :b c" => "Base-level rules cannot contain the parent-selector-referencing character '&'.",
41
48
  "a\n :b\n c" => "Illegal nesting: Only attributes may be nested beneath attributes.",
42
49
  "a,\n :b c" => "Rules can\'t end in commas.",
50
+ "a," => "Rules can\'t end in commas.",
51
+ "a,\n!b = c" => "Rules can\'t end in commas.",
43
52
  "!a = b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath constants.",
44
- "@import foo.sass" => "File to import not found or unreadable: foo.sass",
53
+ "@import foo.sass" => "File to import not found or unreadable: foo.sass.",
45
54
  "@import templates/basic\n foo" => "Illegal nesting: Nothing may be nested beneath import directives.",
46
55
  "foo\n @import templates/basic" => "Import directives may only be used at the root of a document.",
47
56
  "!foo = bar baz !" => "Unterminated constant.",
48
57
  "!foo = !(foo)" => "Invalid constant.",
58
+ "=foo\n :color red\n.bar\n +bang" => "Undefined mixin 'bang'.",
59
+ ".bar\n =foo\n :color red\n" => "Mixins may only be defined at the root of a document.",
60
+ "=foo\n :color red\n.bar\n +foo\n :color red" => "Illegal nesting: Nothing may be nested beneath mixin directives.",
61
+
62
+ # Regression tests
63
+ "a\n b:\n c\n d" => ["Illegal nesting: Only attributes may be nested beneath attributes.", 3]
49
64
  }
50
65
 
51
66
  def test_basic_render
52
67
  renders_correctly "basic", { :style => :compact }
53
68
  end
54
69
 
70
+ def test_multiple_calls_to_render
71
+ sass = Sass::Engine.new("a\n b: c")
72
+ assert_equal sass.render, sass.render
73
+ end
74
+
55
75
  def test_alternate_styles
56
76
  renders_correctly "expanded", { :style => :expanded }
57
77
  renders_correctly "compact", { :style => :compact }
@@ -64,8 +84,10 @@ class SassEngineTest < Test::Unit::TestCase
64
84
  begin
65
85
  Sass::Engine.new(key).render
66
86
  rescue Sass::SyntaxError => err
67
- assert_equal(value, err.message)
68
- assert(err.sass_line, "Line: #{key}")
87
+ value = [value] unless value.is_a?(Array)
88
+
89
+ assert_equal(value.first, err.message, "Line: #{key}")
90
+ assert_equal(value[1] || key.split("\n").length, err.sass_line, "Line: #{key}")
69
91
  assert_match(/\(sass\):[0-9]+/, err.backtrace[0], "Line: #{key}")
70
92
  else
71
93
  assert(false, "Exception not raised for\n#{key}")
@@ -98,16 +120,27 @@ class SassEngineTest < Test::Unit::TestCase
98
120
  end
99
121
  end
100
122
 
123
+ def test_css_import
124
+ assert_equal("@import url(./fonts.css) screen;", render("@import url(./fonts.css) screen"))
125
+ assert_equal("@import \"./fonts.css\" screen;", render("@import \"./fonts.css\" screen"))
126
+ end
127
+
128
+ def test_sass_import
129
+ renders_correctly "import", { :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"] }
130
+ end
131
+
101
132
  def test_default_function
102
133
  assert_equal("foo {\n bar: url(foo.png); }\n",
103
134
  render("foo\n bar = url(foo.png)\n"));
104
135
  end
105
136
 
106
- def test_multiline_selector
137
+ def test_basic_multiline_selector
107
138
  assert_equal("#foo #bar,\n#baz #boom {\n foo: bar; }\n",
108
139
  render("#foo #bar,\n#baz #boom\n :foo bar"))
109
140
  assert_equal("#foo #bar,\n#foo #baz {\n foo: bar; }\n",
110
141
  render("#foo\n #bar,\n #baz\n :foo bar"))
142
+ assert_equal("#foo,\n#bar {\n foo: bar; }\n #foo #baz,\n #bar #baz {\n foo: bar; }\n",
143
+ render("#foo,\n#bar\n :foo bar\n #baz\n :foo bar"))
111
144
  assert_equal("#foo #bar, #baz #boom { foo: bar; }\n",
112
145
  render("#foo #bar,\n#baz #boom\n :foo bar", :style => :compact))
113
146
 
@@ -115,6 +148,10 @@ class SassEngineTest < Test::Unit::TestCase
115
148
  render("#foo #bar,\n#baz #boom\n :foo bar", :style => :compressed))
116
149
  end
117
150
 
151
+ def test_complex_multiline_selector
152
+ renders_correctly "multiline"
153
+ end
154
+
118
155
  def test_colon_only
119
156
  begin
120
157
  render("a\n b: c", :attribute_syntax => :normal)
@@ -208,7 +245,16 @@ END
208
245
  def test_cr_newline
209
246
  assert_equal("foo {\n a: b;\n c: d;\n e: f; }\n", render("foo\r a: b\r\n c: d\n\r e: f"))
210
247
  end
248
+
249
+ def test_or_eq
250
+ assert_equal("foo {\n a: b; }\n", render("!foo = b\n!foo ||= c\nfoo\n a = !foo"))
251
+ assert_equal("foo {\n a: b; }\n", render("!foo ||= b\nfoo\n a = !foo"))
252
+ end
211
253
 
254
+ def test_mixins
255
+ renders_correctly "mixins", { :style => :expanded }
256
+ end
257
+
212
258
  private
213
259
 
214
260
  def render(sass, options = {})
@@ -58,7 +58,7 @@ class SassPluginTest < Test::Unit::TestCase
58
58
  File.delete(tempfile_loc('bork'))
59
59
  Sass::Plugin.update_stylesheets
60
60
  File.open(tempfile_loc('bork')) do |file|
61
- assert_equal("/*\nSass::SyntaxError: Undefined constant: \"!bork\"\non line 2 of #{File.dirname(__FILE__) + '/templates/bork.sass'}\n\n1: bork\n2: :bork= !bork", file.read.split("\n")[0...6].join("\n"))
61
+ assert_equal("/*\nSass::SyntaxError: Undefined constant: \"!bork\".\non line 2 of #{File.dirname(__FILE__) + '/templates/bork.sass'}\n\n1: bork\n2: :bork= !bork", file.read.split("\n")[0...6].join("\n"))
62
62
  end
63
63
  File.delete(tempfile_loc('bork'))
64
64
  end
@@ -1,4 +1,4 @@
1
- imported { otherconst: hello; myconst: goodbye; }
1
+ imported { otherconst: hello; myconst: goodbye; pre-mixin: here; }
2
2
 
3
3
  body { font: Arial; background: blue; }
4
4
 
@@ -26,4 +26,4 @@ body { font: Arial; background: blue; }
26
26
  @import url(../results/complex.css);
27
27
  #foo { background-color: #baf; }
28
28
 
29
- nonimported { myconst: hello; otherconst: goodbye; }
29
+ nonimported { myconst: hello; otherconst: goodbye; post-mixin: here; }
@@ -0,0 +1,95 @@
1
+ #main {
2
+ width: 15em;
3
+ color: #0000ff;
4
+ }
5
+ #main p {
6
+ border-top-width: 2px;
7
+ border-top-color: #ffcc00;
8
+ border-left-width: 1px;
9
+ border-left-color: #000;
10
+ -moz-border-radius: 10px;
11
+ border-style: dotted;
12
+ border-width: 2px;
13
+ }
14
+ #main .cool {
15
+ width: 100px;
16
+ }
17
+
18
+ #left {
19
+ border-top-width: 2px;
20
+ border-top-color: #ffcc00;
21
+ border-left-width: 1px;
22
+ border-left-color: #000;
23
+ -moz-border-radius: 10px;
24
+ font-size: 2em;
25
+ font-weight: bold;
26
+ float: left;
27
+ }
28
+
29
+ #right {
30
+ border-top-width: 2px;
31
+ border-top-color: #ffcc00;
32
+ border-left-width: 1px;
33
+ border-left-color: #000;
34
+ -moz-border-radius: 10px;
35
+ color: #f00;
36
+ font-size: 20px;
37
+ float: right;
38
+ }
39
+
40
+ .bordered {
41
+ border-top-width: 2px;
42
+ border-top-color: #ffcc00;
43
+ border-left-width: 1px;
44
+ border-left-color: #000;
45
+ -moz-border-radius: 10px;
46
+ }
47
+
48
+ .complex {
49
+ color: #f00;
50
+ font-size: 20px;
51
+ text-decoration: none;
52
+ }
53
+ .complex:after {
54
+ content: ".";
55
+ display: block;
56
+ height: 0;
57
+ clear: both;
58
+ visibility: hidden;
59
+ }
60
+ * html .complex {
61
+ height: 1px;
62
+ color: #f00;
63
+ font-size: 20px;
64
+ }
65
+
66
+ .more-complex {
67
+ color: #f00;
68
+ font-size: 20px;
69
+ text-decoration: none;
70
+ display: inline;
71
+ -webkit-nonsense-top-right: 1px;
72
+ -webkit-nonsense-bottom-left: 1px;
73
+ }
74
+ .more-complex:after {
75
+ content: ".";
76
+ display: block;
77
+ height: 0;
78
+ clear: both;
79
+ visibility: hidden;
80
+ }
81
+ * html .more-complex {
82
+ height: 1px;
83
+ color: #f00;
84
+ font-size: 20px;
85
+ }
86
+ .more-complex a:hover {
87
+ text-decoration: underline;
88
+ color: #f00;
89
+ font-size: 20px;
90
+ border-top-width: 2px;
91
+ border-top-color: #ffcc00;
92
+ border-left-width: 1px;
93
+ border-left-color: #000;
94
+ -moz-border-radius: 10px;
95
+ }
@@ -0,0 +1,24 @@
1
+ #main,
2
+ #header {
3
+ height: 50px; }
4
+ #main div,
5
+ #header div {
6
+ width: 100px; }
7
+ #main div a span,
8
+ #main div em span,
9
+ #header div a span,
10
+ #header div em span {
11
+ color: pink; }
12
+
13
+ #one div.nested,
14
+ #one span.nested,
15
+ #one p.nested,
16
+ #two div.nested,
17
+ #two span.nested,
18
+ #two p.nested,
19
+ #three div.nested,
20
+ #three span.nested,
21
+ #three p.nested {
22
+ font-weight: bold;
23
+ border-color: red;
24
+ display: block; }
@@ -1,8 +1,11 @@
1
1
  !preconst = hello
2
2
 
3
+ =premixin
4
+ pre-mixin: here
5
+
3
6
  @import importee, basic, basic.css, ../results/complex.css, partial
4
7
 
5
8
  nonimported
6
9
  :myconst = !preconst
7
10
  :otherconst = !postconst
8
-
11
+ +postmixin
@@ -1,8 +1,12 @@
1
1
  !postconst = goodbye
2
2
 
3
+ =postmixin
4
+ post-mixin: here
5
+
3
6
  imported
4
7
  :otherconst = !preconst
5
8
  :myconst = !postconst
9
+ +premixin
6
10
 
7
11
  @import basic
8
12
 
@@ -0,0 +1,76 @@
1
+ !yellow = #fc0
2
+
3
+ =bordered
4
+ :border
5
+ :top
6
+ :width 2px
7
+ :color = !yellow
8
+ :left
9
+ :width 1px
10
+ :color #000
11
+ -moz-border-radius: 10px
12
+
13
+ =header-font
14
+ :color #f00
15
+ :font
16
+ :size 20px
17
+
18
+ =compound
19
+ +header-font
20
+ +bordered
21
+
22
+ =complex
23
+ +header-font
24
+ text:
25
+ decoration: none
26
+ &:after
27
+ content: "."
28
+ display: block
29
+ height: 0
30
+ clear: both
31
+ visibility: hidden
32
+ * html &
33
+ height: 1px
34
+ +header-font
35
+ =deep
36
+ a:hover
37
+ :text-decoration underline
38
+ +compound
39
+
40
+
41
+ #main
42
+ :width 15em
43
+ :color #0000ff
44
+ p
45
+ +bordered
46
+ :border
47
+ :style dotted
48
+ :width 2px
49
+ .cool
50
+ :width 100px
51
+
52
+ #left
53
+ +bordered
54
+ :font
55
+ :size 2em
56
+ :weight bold
57
+ :float left
58
+
59
+ #right
60
+ +bordered
61
+ +header-font
62
+ :float right
63
+
64
+ .bordered
65
+ +bordered
66
+
67
+ .complex
68
+ +complex
69
+
70
+ .more-complex
71
+ +complex
72
+ +deep
73
+ display: inline
74
+ -webkit-nonsense:
75
+ top-right: 1px
76
+ bottom-left: 1px
@@ -0,0 +1,20 @@
1
+ #main,
2
+ #header
3
+ height: 50px
4
+ div
5
+ width: 100px
6
+ a,
7
+ em
8
+ span
9
+ color: pink
10
+
11
+ #one,
12
+ #two,
13
+ #three
14
+ div.nested,
15
+ span.nested,
16
+ p.nested
17
+ :font
18
+ :weight bold
19
+ :border-color red
20
+ :display block