haml 2.2.1 → 2.2.2

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.

data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.2.1
1
+ 2.2.2
@@ -603,9 +603,8 @@ If N is negative, will remove the spaces instead. Assumes all
603
603
  lines in the region have indentation >= that of the first line."
604
604
  (let ((ci (current-indentation)))
605
605
  (save-excursion
606
- (replace-regexp (concat "^" (make-string ci ? ))
607
- (make-string (max 0 (+ ci n)) ? )
608
- nil (point) (mark)))))
606
+ (while (re-search-forward (concat "^" (make-string ci ?\s)) (mark) t)
607
+ (replace-match (make-string (max 0 (+ ci n)) ?\s))))))
609
608
 
610
609
  (defun haml-electric-backspace (arg)
611
610
  "Delete characters or back-dent the current line.
@@ -100,7 +100,7 @@ module Haml
100
100
  if contains_interpolation?(text)
101
101
  return if options[:suppress_eval]
102
102
 
103
- push_script <<RUBY
103
+ push_script <<RUBY, :escape_html => false
104
104
  find_and_preserve(#{filter.inspect}.render_with_options(#{unescape_interpolation(text)}, _hamlout.options))
105
105
  RUBY
106
106
  return
@@ -14,8 +14,12 @@ module Haml
14
14
  # when it shouldn't be.
15
15
  class ErrorReturn
16
16
  # @param message [String] The error message to raise when \{#to\_s} is called
17
- def initialize(message)
18
- @message = message
17
+ def initialize(method)
18
+ @message = <<MESSAGE
19
+ #{method} outputs directly to the Haml template.
20
+ Disregard its return value and use the - operator,
21
+ or use capture_haml to get the value as a String.
22
+ MESSAGE
19
23
  end
20
24
 
21
25
  # Raises an error.
@@ -23,6 +27,21 @@ module Haml
23
27
  # @raise [Haml::Error] The error
24
28
  def to_s
25
29
  raise Haml::Error.new(@message)
30
+ rescue Haml::Error => e
31
+ e.backtrace.shift
32
+
33
+ # If the ErrorReturn is used directly in the template,
34
+ # we don't want Haml's stuff to get into the backtrace,
35
+ # so we get rid of the format_script line.
36
+ #
37
+ # We also have to subtract one from the Haml line number
38
+ # since the value is passed to format_script the line after
39
+ # it's actually used.
40
+ if e.backtrace.first =~ /^\(eval\):\d+:in `format_script/
41
+ e.backtrace.shift
42
+ e.backtrace.first.gsub!(/^\(haml\):(\d+)/) {|s| "(haml):#{$1.to_i - 1}"}
43
+ end
44
+ raise e
26
45
  end
27
46
 
28
47
  # @return [String] A human-readable string representation
@@ -338,7 +357,7 @@ END
338
357
  # @param text [#to_s] The text to output
339
358
  def haml_concat(text = "")
340
359
  haml_buffer.buffer << haml_indent << text.to_s << "\n"
341
- nil
360
+ ErrorReturn.new("haml_concat")
342
361
  end
343
362
 
344
363
  # @return [String] The indentation string for the current line
@@ -399,11 +418,7 @@ END
399
418
  # @overload haml_tag(name, text, *flags, attributes = {})
400
419
  # @param text [#to_s] The text within the tag
401
420
  def haml_tag(name, *rest, &block)
402
- ret = ErrorReturn.new(<<MESSAGE)
403
- haml_tag outputs directly to the Haml template.
404
- Disregard its return value and use the - operator,
405
- or use capture_haml to get the value as a String.
406
- MESSAGE
421
+ ret = ErrorReturn.new("haml_tag")
407
422
 
408
423
  name = name.to_s
409
424
  text = rest.shift.to_s unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
@@ -2,9 +2,60 @@ require File.dirname(__FILE__) + '/../haml'
2
2
 
3
3
  require 'haml/engine'
4
4
  require 'rubygems'
5
- require 'hpricot'
6
5
  require 'cgi'
7
6
 
7
+ module Haml
8
+ class HTML
9
+ # A module containing utility methods that every Hpricot node
10
+ # should have.
11
+ module Node
12
+ # Returns the Haml representation of the given node.
13
+ #
14
+ # @param tabs [Fixnum] The indentation level of the resulting Haml.
15
+ # @option options (see Haml::HTML#initialize)
16
+ def to_haml(tabs, options)
17
+ parse_text(self.to_s, tabs)
18
+ end
19
+
20
+ private
21
+
22
+ def tabulate(tabs)
23
+ ' ' * tabs
24
+ end
25
+
26
+ def parse_text(text, tabs)
27
+ text.strip!
28
+ if text.empty?
29
+ String.new
30
+ else
31
+ lines = text.split("\n")
32
+
33
+ lines.map do |line|
34
+ line.strip!
35
+ "#{tabulate(tabs)}#{'\\' if Haml::Engine::SPECIAL_CHARACTERS.include?(line[0])}#{line}\n"
36
+ end.join
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ # Haml monkeypatches various Hpricot classes
44
+ # to add methods for conversion to Haml.
45
+ module Hpricot
46
+ # @see Hpricot
47
+ module Node
48
+ include Haml::HTML::Node
49
+ end
50
+
51
+ # @see Hpricot
52
+ class BaseEle
53
+ include Haml::HTML::Node
54
+ end
55
+ end
56
+
57
+ require 'hpricot'
58
+
8
59
  module Haml
9
60
  # Converts HTML documents into Haml templates.
10
61
  # Depends on [Hpricot](http://code.whytheluckystiff.net/hpricot/) for HTML parsing.
@@ -46,67 +97,35 @@ module Haml
46
97
  end
47
98
  alias_method :to_haml, :render
48
99
 
49
- # Haml monkeypatches various Hpricot classes
50
- # to add methods for conversion to Haml.
51
- module ::Hpricot::Node
52
- # Returns the Haml representation of the given node.
53
- #
54
- # @param tabs [Fixnum] The indentation level of the resulting Haml.
55
- # @option options (see Haml::HTML#initialize)
56
- def to_haml(tabs, options)
57
- parse_text(self.to_s, tabs)
58
- end
59
-
60
- private
61
-
62
- def tabulate(tabs)
63
- ' ' * tabs
64
- end
65
-
66
- def parse_text(text, tabs)
67
- text.strip!
68
- if text.empty?
69
- String.new
70
- else
71
- lines = text.split("\n")
72
-
73
- lines.map do |line|
74
- line.strip!
75
- "#{tabulate(tabs)}#{'\\' if Haml::Engine::SPECIAL_CHARACTERS.include?(line[0])}#{line}\n"
76
- end.join
77
- end
78
- end
79
- end
80
-
81
100
  TEXT_REGEXP = /^(\s*).*$/
82
101
 
83
- # @see Hpricot::Node
102
+ # @see Hpricot
84
103
  class ::Hpricot::Doc
85
- # @see Hpricot::Node#to_haml
104
+ # @see Haml::HTML::Node#to_haml
86
105
  def to_haml(tabs, options)
87
106
  (children || []).inject('') {|s, c| s << c.to_haml(0, options)}
88
107
  end
89
108
  end
90
109
 
91
- # @see Hpricot::Node
110
+ # @see Hpricot
92
111
  class ::Hpricot::XMLDecl
93
- # @see Hpricot::Node#to_haml
112
+ # @see Haml::HTML::Node#to_haml
94
113
  def to_haml(tabs, options)
95
114
  "#{tabulate(tabs)}!!! XML\n"
96
115
  end
97
116
  end
98
117
 
99
- # @see Hpricot::Node
118
+ # @see Hpricot
100
119
  class ::Hpricot::CData
101
- # @see Hpricot::Node#to_haml
120
+ # @see Haml::HTML::Node#to_haml
102
121
  def to_haml(tabs, options)
103
122
  "#{tabulate(tabs)}:cdata\n#{parse_text(self.content, tabs + 1)}"
104
123
  end
105
124
  end
106
125
 
107
- # @see Hpricot::Node
126
+ # @see Hpricot
108
127
  class ::Hpricot::DocType
109
- # @see Hpricot::Node#to_haml
128
+ # @see Haml::HTML::Node#to_haml
110
129
  def to_haml(tabs, options)
111
130
  attrs = public_id.scan(/DTD\s+([^\s]+)\s*([^\s]*)\s*([^\s]*)\s*\/\//)[0]
112
131
  if attrs == nil
@@ -137,17 +156,17 @@ module Haml
137
156
  end
138
157
  end
139
158
 
140
- # @see Hpricot::Node
159
+ # @see Hpricot
141
160
  class ::Hpricot::Comment
142
- # @see Hpricot::Node#to_haml
161
+ # @see Haml::HTML::Node#to_haml
143
162
  def to_haml(tabs, options)
144
163
  "#{tabulate(tabs)}/\n#{parse_text(self.content, tabs + 1)}"
145
164
  end
146
165
  end
147
166
 
148
- # @see Hpricot::Node
167
+ # @see Hpricot
149
168
  class ::Hpricot::Elem
150
- # @see Hpricot::Node#to_haml
169
+ # @see Haml::HTML::Node#to_haml
151
170
  def to_haml(tabs, options)
152
171
  output = "#{tabulate(tabs)}"
153
172
  if options[:rhtml] && name[0...5] == 'haml:'
@@ -571,7 +571,10 @@ END
571
571
  attributes = {}
572
572
 
573
573
  scanner.scan(/\(\s*/)
574
- until (name, value = parse_new_attribute(scanner)).first.nil?
574
+ loop do
575
+ name, value = parse_new_attribute(scanner)
576
+ break if name.nil?
577
+
575
578
  if name == false
576
579
  text = (Haml::Shared.balance(line, ?(, ?)) || [line]).first
577
580
  raise Haml::SyntaxError.new("Invalid attribute list: #{text.inspect}.", last_line - 1)
@@ -268,6 +268,7 @@ END
268
268
  continued_rule, child = nil, continued_rule
269
269
  end
270
270
 
271
+ check_for_no_children(child)
271
272
  validate_and_append_child(parent, child, line, root)
272
273
  end
273
274
 
@@ -294,6 +295,22 @@ END
294
295
  end
295
296
  end
296
297
 
298
+ def check_for_no_children(node)
299
+ return unless node.is_a?(Tree::RuleNode) && node.children.empty?
300
+ warning = (node.rules.size == 1) ? <<SHORT : <<LONG
301
+ WARNING:
302
+ Selector #{node.rules.first.inspect} doesn't have any properties and will not be rendered.
303
+ SHORT
304
+
305
+ WARNING:
306
+ Selector
307
+ #{node.rules.join("\n ")}
308
+ doesn't have any properties and will not be rendered.
309
+ LONG
310
+
311
+ warn(warning.strip)
312
+ end
313
+
297
314
  def parse_line(parent, line, root)
298
315
  case line.text[0]
299
316
  when PROPERTY_CHAR
@@ -425,46 +442,24 @@ END
425
442
  nil
426
443
  end
427
444
 
428
- # parses out the arguments between the commas and cleans up the mixin arguments
429
- # returns nil if it fails to parse, otherwise an array.
430
- def parse_mixin_arguments(arg_string)
431
- arg_string = arg_string.strip
432
- return [] if arg_string.empty?
433
- return nil unless (arg_string[0] == ?( && arg_string[-1] == ?))
434
- arg_string = arg_string[1...-1]
435
- arg_string.split(",", -1).map {|a| a.strip}
436
- end
437
-
438
445
  def parse_mixin_definition(line)
439
446
  name, arg_string = line.text.scan(/^=\s*([^(]+)(.*)$/).first
440
- args = parse_mixin_arguments(arg_string)
441
- raise SyntaxError.new("Invalid mixin \"#{line.text[1..-1]}\".", @line) if name.nil? || args.nil?
447
+ raise SyntaxError.new("Invalid mixin \"#{line.text[1..-1]}\".", @line) if name.nil?
448
+
449
+ offset = line.offset + line.text.size - arg_string.size
450
+ args = Script::Parser.new(arg_string.strip, @line, offset).parse_mixin_definition_arglist
442
451
  default_arg_found = false
443
- required_arg_count = 0
444
- args.map! do |arg|
445
- raise SyntaxError.new("Mixin arguments can't be empty.", @line) if arg.empty? || arg == "!"
446
- unless arg[0] == Script::VARIABLE_CHAR
447
- raise SyntaxError.new("Mixin argument \"#{arg}\" must begin with an exclamation point (!).", @line)
448
- end
449
- arg, default = arg.split(/\s*=\s*/, 2)
450
- required_arg_count += 1 unless default
451
- default_arg_found ||= default
452
- raise SyntaxError.new("Invalid variable \"#{arg}\".", @line) unless arg =~ Script::VALIDATE
453
- raise SyntaxError.new("Required arguments must not follow optional arguments \"#{arg}\".", @line) if default_arg_found && !default
454
- default = parse_script(default, :offset => line.offset + line.text.index(default)) if default
455
- [arg[1..-1], default]
456
- end
457
452
  Tree::MixinDefNode.new(name, args)
458
453
  end
459
454
 
460
455
  def parse_mixin_include(line, root)
461
456
  name, arg_string = line.text.scan(/^\+\s*([^(]+)(.*)$/).first
462
- args = parse_mixin_arguments(arg_string)
463
- raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath mixin directives.", @line + 1) unless line.children.empty?
464
- raise SyntaxError.new("Invalid mixin include \"#{line.text}\".", @line) if name.nil? || args.nil?
465
- args.each {|a| raise SyntaxError.new("Mixin arguments can't be empty.", @line) if a.empty?}
457
+ raise SyntaxError.new("Invalid mixin include \"#{line.text}\".", @line) if name.nil?
466
458
 
467
- Tree::MixinNode.new(name, args.map {|s| parse_script(s, :offset => line.offset + line.text.index(s))})
459
+ offset = line.offset + line.text.size - arg_string.size
460
+ args = Script::Parser.new(arg_string.strip, @line, offset).parse_mixin_include_arglist
461
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath mixin directives.", @line + 1) unless line.children.empty?
462
+ Tree::MixinNode.new(name, args)
468
463
  end
469
464
 
470
465
  def parse_script(script, options = {})
@@ -42,7 +42,7 @@ module Sass
42
42
 
43
43
  return Functions::EvaluationContext.new(environment.options).send(name, *args)
44
44
  rescue ArgumentError => e
45
- raise e unless e.backtrace.first =~ /:in `(#{name}|perform)'$/
45
+ raise e unless e.backtrace.first =~ /:in `(block in )?(#{name}|perform)'$/
46
46
  raise Sass::SyntaxError.new("#{e.message} for `#{name}'")
47
47
  end
48
48
  end
@@ -71,7 +71,8 @@ module Sass::Script
71
71
  # A number between 0 and 255 inclusive
72
72
  def rgb(red, green, blue)
73
73
  [red.value, green.value, blue.value].each do |v|
74
- raise ArgumentError.new("Color value #{v} must be between 0 and 255 inclusive") if v <= 0 || v >= 255
74
+ next unless v < 0 || v > 255
75
+ raise ArgumentError.new("Color value #{v} must be between 0 and 255 inclusive")
75
76
  end
76
77
  Color.new([red.value, green.value, blue.value])
77
78
  end
@@ -28,6 +28,7 @@ module Sass
28
28
  '*' => :times,
29
29
  '/' => :div,
30
30
  '%' => :mod,
31
+ '=' => :single_eq,
31
32
  '(' => :lparen,
32
33
  ')' => :rparen,
33
34
  ',' => :comma,
@@ -36,10 +36,42 @@ module Sass
36
36
  # @raise [Sass::SyntaxError] if the expression isn't valid SassScript
37
37
  def parse
38
38
  expr = assert_expr :expr
39
- raise Sass::SyntaxError.new("Unexpected #{@lexer.peek.type} token.") unless @lexer.done?
39
+ assert_done
40
40
  expr
41
41
  end
42
42
 
43
+ # Parses the argument list for a mixin include.
44
+ #
45
+ # @return [Array<Script::Node>] The root nodes of the arguments.
46
+ # @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
47
+ def parse_mixin_include_arglist
48
+ args = []
49
+
50
+ if try_tok(:lparen)
51
+ args = arglist || args
52
+ assert_tok(:rparen)
53
+ end
54
+ assert_done
55
+
56
+ args
57
+ end
58
+
59
+ # Parses the argument list for a mixin definition.
60
+ #
61
+ # @return [Array<Script::Node>] The root nodes of the arguments.
62
+ # @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
63
+ def parse_mixin_definition_arglist
64
+ args = []
65
+
66
+ if try_tok(:lparen)
67
+ args = defn_arglist(false) || args
68
+ assert_tok(:rparen)
69
+ end
70
+ assert_done
71
+
72
+ args
73
+ end
74
+
43
75
  # Parses a SassScript expression.
44
76
  #
45
77
  # @overload parse(str, line, offset, filename = nil)
@@ -120,6 +152,19 @@ END
120
152
  end
121
153
  end
122
154
 
155
+ def defn_arglist(must_have_default)
156
+ return unless c = try_tok(:const)
157
+ var = Script::Variable.new(c.value)
158
+ if try_tok(:single_eq)
159
+ val = assert_expr(:concat)
160
+ elsif must_have_default
161
+ raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.", @line)
162
+ end
163
+
164
+ return [[var, val]] unless try_tok(:comma)
165
+ [[var, val], *defn_arglist(val)]
166
+ end
167
+
123
168
  def arglist
124
169
  return unless e = concat
125
170
  return [e] unless try_tok(:comma)
@@ -167,6 +212,11 @@ END
167
212
  peeked = @lexer.peek
168
213
  peeked && names.include?(peeked.type) && @lexer.next
169
214
  end
215
+
216
+ def assert_done
217
+ return if @lexer.done?
218
+ raise Sass::SyntaxError.new("Unexpected #{@lexer.peek.type} token.")
219
+ end
170
220
  end
171
221
  end
172
222
  end
@@ -5,8 +5,8 @@ module Sass
5
5
  # @see Sass::Tree
6
6
  class MixinDefNode < Node
7
7
  # @param name [String] The mixin name
8
- # @param args [Array<(String, Script::Node)>] The arguments for the mixin.
9
- # Each element is a tuple containing the name of the argument
8
+ # @param args [Array<(Script::Node, Script::Node)>] The arguments for the mixin.
9
+ # Each element is a tuple containing the variable for argument
10
10
  # and the parse tree for the default value of the argument
11
11
  def initialize(name, args)
12
12
  @name = name
@@ -32,14 +32,14 @@ Mixin #{@name} takes #{mixin.args.size} argument#{'s' if mixin.args.size != 1}
32
32
  END
33
33
 
34
34
  environment = mixin.args.zip(@args).
35
- inject(Sass::Environment.new(mixin.environment)) do |env, ((name, default), value)|
36
- env.set_local_var(name,
35
+ inject(Sass::Environment.new(mixin.environment)) do |env, ((var, default), value)|
36
+ env.set_local_var(var.name,
37
37
  if value
38
38
  value.perform(environment)
39
39
  elsif default
40
40
  default.perform(env)
41
41
  end)
42
- raise Sass::SyntaxError.new("Mixin #{@name} is missing parameter !#{name}.") unless env.var(name)
42
+ raise Sass::SyntaxError.new("Mixin #{@name} is missing parameter #{var.inspect}.") unless env.var(var.name)
43
43
  env
44
44
  end
45
45
  mixin.tree.map {|c| c.perform(environment)}.flatten
@@ -425,6 +425,19 @@ HAML
425
425
  assert_equal("<a href='#'></a>\n", render('%a(href="#")'))
426
426
  end
427
427
 
428
+ def test_javascript_filter_with_dynamic_interp_and_escape_html
429
+ assert_equal(<<HTML, render(<<HAML, :escape_html => true))
430
+ <script type='text/javascript'>
431
+ //<![CDATA[
432
+ & < > &
433
+ //]]>
434
+ </script>
435
+ HTML
436
+ :javascript
437
+ & < > \#{"&"}
438
+ HAML
439
+ end
440
+
428
441
  # HTML escaping tests
429
442
 
430
443
  def test_ampersand_equals_should_escape
@@ -8,6 +8,12 @@ class ActionView::Base
8
8
  end
9
9
  end
10
10
 
11
+ module Haml::Helpers
12
+ def something_that_uses_haml_concat
13
+ haml_concat('foo').to_s
14
+ end
15
+ end
16
+
11
17
  class HelperTest < Test::Unit::TestCase
12
18
  Post = Struct.new('Post', :body)
13
19
 
@@ -221,7 +227,29 @@ HAML
221
227
  def test_content_tag_nested
222
228
  assert_equal "<span><div>something</div></span>", render("= nested_tag", :action_view).strip
223
229
  end
224
-
230
+
231
+ def test_error_return
232
+ assert_raise(Haml::Error, <<MESSAGE) {render("= haml_concat 'foo'")}
233
+ haml_concat outputs directly to the Haml template.
234
+ Disregard its return value and use the - operator,
235
+ or use capture_haml to get the value as a String.
236
+ MESSAGE
237
+ end
238
+
239
+ def test_error_return_line
240
+ render("%p foo\n= haml_concat 'foo'\n%p bar")
241
+ assert false, "Expected Haml::Error"
242
+ rescue Haml::Error => e
243
+ assert_equal 2, e.backtrace[0].scan(/:(\d+)/).first.first.to_i
244
+ end
245
+
246
+ def test_error_return_line_in_helper
247
+ render("- something_that_uses_haml_concat")
248
+ assert false, "Expected Haml::Error"
249
+ rescue Haml::Error => e
250
+ assert_equal 13, e.backtrace[0].scan(/:(\d+)/).first.first.to_i
251
+ end
252
+
225
253
  class ActsLikeTag
226
254
  # We want to be able to have people include monkeypatched ActionView helpers
227
255
  # without redefining is_haml?.
@@ -140,8 +140,16 @@ class TemplateTest < Test::Unit::TestCase
140
140
  end
141
141
 
142
142
  def test_action_view_templates_render_correctly
143
- @base.content_for(:layout) {'Lorem ipsum dolor sit amet'}
144
- assert_renders_correctly 'content_for_layout'
143
+ proc = lambda do
144
+ @base.content_for(:layout) {'Lorem ipsum dolor sit amet'}
145
+ assert_renders_correctly 'content_for_layout'
146
+ end
147
+
148
+ if @base.respond_to?(:with_output_buffer)
149
+ @base.with_output_buffer("", &proc)
150
+ else
151
+ proc.call
152
+ end
145
153
  end
146
154
 
147
155
  def test_instance_variables_should_work_inside_templates
@@ -65,14 +65,14 @@ class SassEngineTest < Test::Unit::TestCase
65
65
  "a\n b: c\na\n d: e" => ["The line was indented 2 levels deeper than the previous line.", 4],
66
66
  "a\n b: c\n a\n d: e" => ["The line was indented 3 levels deeper than the previous line.", 4],
67
67
  "a\n \tb: c" => ["Indentation can't use both tabs and spaces.", 2],
68
- "=a(" => 'Invalid mixin "a(".',
69
- "=a(b)" => 'Mixin argument "b" must begin with an exclamation point (!).',
70
- "=a(,)" => "Mixin arguments can't be empty.",
71
- "=a(!)" => "Mixin arguments can't be empty.",
72
- "=a(!foo bar)" => "Invalid variable \"!foo bar\".",
68
+ "=a(" => 'Expected rparen token, was end of text.',
69
+ "=a(b)" => 'Expected rparen token, was ident token.',
70
+ "=a(,)" => "Expected rparen token, was comma token.",
71
+ "=a(!)" => "Syntax error in '(!)' at character 4.",
72
+ "=a(!foo bar)" => "Expected rparen token, was ident token.",
73
73
  "=foo\n bar: baz\n+foo" => ["Properties aren't allowed at the root of a document.", 2],
74
74
  "a-\#{!b\n c: d" => ["Expected end_interpolation token, was end of text.", 1],
75
- "=a(!b = 1, !c)" => "Required arguments must not follow optional arguments \"!c\".",
75
+ "=a(!b = 1, !c)" => "Required argument !c must come before any optional arguments.",
76
76
  "=a(!b = 1)\n :a= !b\ndiv\n +a(1,2)" => "Mixin a takes 1 argument but 2 were passed.",
77
77
  "=a(!b)\n :a= !b\ndiv\n +a" => "Mixin a is missing parameter !b.",
78
78
  "@else\n a\n b: c" => ["@else must come after @if.", 1],
@@ -130,7 +130,7 @@ class SassEngineTest < Test::Unit::TestCase
130
130
  define_method("test_exception (#{key.inspect})") do
131
131
  line = 10
132
132
  begin
133
- Sass::Engine.new(key, :filename => __FILE__, :line => line).render
133
+ silence_warnings {Sass::Engine.new(key, :filename => __FILE__, :line => line).render}
134
134
  rescue Sass::SyntaxError => err
135
135
  value = [value] unless value.is_a?(Array)
136
136
 
@@ -704,8 +704,41 @@ a
704
704
  SASS
705
705
  end
706
706
 
707
+ def test_empty_selector_warning
708
+ assert_warning(<<END) {render("foo bar")}
709
+ WARNING:
710
+ Selector "foo bar" doesn't have any properties and will not be rendered.
711
+ END
712
+
713
+ assert_warning(<<END) {render(<<SASS)}
714
+ WARNING:
715
+ Selector
716
+ foo, bar, baz,
717
+ bang, bip, bop
718
+ doesn't have any properties and will not be rendered.
719
+ END
720
+ foo, bar, baz,
721
+ bang, bip, bop
722
+ SASS
723
+ end
724
+
707
725
  # Regression tests
708
726
 
727
+ def test_parens_in_mixins
728
+ assert_equal(<<CSS, render(<<SASS))
729
+ .foo {
730
+ color: #01ff7f;
731
+ background-color: #000102; }
732
+ CSS
733
+ =foo(!c1, !c2 = rgb(0, 1, 2))
734
+ color = !c1
735
+ background-color = !c2
736
+
737
+ .foo
738
+ +foo(rgb(1,255,127))
739
+ SASS
740
+ end
741
+
709
742
  def test_comment_beneath_prop
710
743
  assert_equal(<<RESULT, render(<<SOURCE))
711
744
  .box {
@@ -91,6 +91,7 @@ class SassFunctionTest < Test::Unit::TestCase
91
91
  def test_rgb
92
92
  assert_equal("#123456", evaluate("rgb(18, 52, 86)"))
93
93
  assert_equal("#beaded", evaluate("rgb(190, 173, 237)"))
94
+ assert_equal("#00ff7f", evaluate("rgb(0, 255, 127)"))
94
95
 
95
96
  assert_error_message("Color value 256 must be between 0 and 255 inclusive for `rgb'",
96
97
  "rgb(256, 1, 1)")
@@ -22,8 +22,10 @@ class SassPluginTest < Test::Unit::TestCase
22
22
  FileUtils.rm_r tempfile_loc(nil,"more_")
23
23
  end
24
24
 
25
- def test_templates_should_render_correctly
26
- @@templates.each { |name| assert_renders_correctly(name) }
25
+ @@templates.each do |name|
26
+ define_method("test_template_renders_correctly (#{name})") do
27
+ assert_renders_correctly(name)
28
+ end
27
29
  end
28
30
 
29
31
  def test_no_update
@@ -243,21 +243,6 @@ WARN
243
243
  Sass::Engine.new(sass, options).render
244
244
  end
245
245
 
246
- def assert_warning(message)
247
- the_real_stderr, $stderr = $stderr, StringIO.new
248
- yield
249
- assert_equal message.strip, $stderr.string.strip
250
- ensure
251
- $stderr = the_real_stderr
252
- end
253
-
254
- def silence_warnings
255
- the_real_stderr, $stderr = $stderr, StringIO.new
256
- yield
257
- ensure
258
- $stderr = the_real_stderr
259
- end
260
-
261
246
  def env(hash = {})
262
247
  env = Sass::Environment.new
263
248
  hash.each {|k, v| env.set_var(k, v)}
@@ -99,8 +99,6 @@ body
99
99
  :clear both
100
100
  .column
101
101
  :float left
102
- .left
103
- .middle
104
102
  .right
105
103
  :float right
106
104
  a:link, a:visited
@@ -7,7 +7,7 @@ $:.unshift lib_dir unless $:.include?(lib_dir)
7
7
  require 'haml'
8
8
  require 'sass'
9
9
 
10
- Sass::RAILS_LOADED = true
10
+ Sass::RAILS_LOADED = true unless defined?(Sass::RAILS_LOADED)
11
11
 
12
12
  # required because of Sass::Plugin
13
13
  unless defined? RAILS_ROOT
@@ -26,4 +26,19 @@ class Test::Unit::TestCase
26
26
  path = File.dirname(__FILE__) + "/../.sass-cache"
27
27
  FileUtils.rm_r(path) if File.exist?(path)
28
28
  end
29
+
30
+ def assert_warning(message)
31
+ the_real_stderr, $stderr = $stderr, StringIO.new
32
+ yield
33
+ assert_equal message.strip, $stderr.string.strip
34
+ ensure
35
+ $stderr = the_real_stderr
36
+ end
37
+
38
+ def silence_warnings
39
+ the_real_stderr, $stderr = $stderr, StringIO.new
40
+ yield
41
+ ensure
42
+ $stderr = the_real_stderr
43
+ end
29
44
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haml
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Weizenbaum
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-07-12 00:00:00 -04:00
13
+ date: 2009-07-20 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency