haml 2.0.9 → 2.0.10

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/Rakefile CHANGED
@@ -64,7 +64,8 @@ at_exit { File.delete('REVISION') rescue nil }
64
64
  desc "Install Haml as a gem."
65
65
  task :install => [:package] do
66
66
  sudo = RUBY_PLATFORM =~ /win32/ ? '' : 'sudo'
67
- sh %{#{sudo} gem install --no-ri pkg/haml-#{File.read('VERSION').strip}}
67
+ gem = RUBY_PLATFORM =~ /java/ ? 'jgem' : 'gem'
68
+ sh %{#{sudo} #{gem} install --no-ri pkg/haml-#{File.read('VERSION').strip}}
68
69
  end
69
70
 
70
71
  desc "Release a new Haml package to Rubyforge. Requires the NAME and VERSION flags."
@@ -181,4 +182,4 @@ namespace :test do
181
182
  `rm -rf test/rails`
182
183
  end
183
184
  end
184
- end
185
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.9
1
+ 2.0.10
@@ -18,6 +18,10 @@ module Haml
18
18
  # It's nil at the top level (see #toplevel?).
19
19
  attr_accessor :upper
20
20
 
21
+ # nil if there's no capture_haml block running,
22
+ # and the position at which it's beginning the capture if there is one.
23
+ attr_accessor :capture_position
24
+
21
25
  # See #active?
22
26
  attr_writer :active
23
27
 
@@ -178,6 +182,17 @@ module Haml
178
182
  @real_tabs += 1 unless self_closing || nuke_inner_whitespace
179
183
  end
180
184
 
185
+ # Remove the whitespace from the right side of the buffer string.
186
+ # Doesn't do anything if we're at the beginning of a capture_haml block.
187
+ def rstrip!
188
+ if capture_position.nil?
189
+ buffer.rstrip!
190
+ return
191
+ end
192
+
193
+ buffer << buffer.slice!(capture_position..-1).rstrip
194
+ end
195
+
181
196
  def self.merge_attrs(to, from)
182
197
  if to['id'] && from['id']
183
198
  to['id'] << '_' << from.delete('id')
@@ -7,6 +7,23 @@ module Haml
7
7
  # that a Haml template is parsed in, so all these methods are at your
8
8
  # disposal from within the template.
9
9
  module Helpers
10
+ # An object that raises an error when #to_s is called.
11
+ # It's used to raise an error when the return value of a helper is used
12
+ # when it shouldn't be.
13
+ class ErrorReturn
14
+ def initialize(message)
15
+ @message = message
16
+ end
17
+
18
+ def to_s
19
+ raise Haml::Error.new(@message)
20
+ end
21
+
22
+ def inspect
23
+ "Haml::Helpers::ErrorReturn(#{@message.inspect})"
24
+ end
25
+ end
26
+
10
27
  self.extend self
11
28
 
12
29
  @@action_view_defined = defined?(ActionView)
@@ -65,7 +82,7 @@ module Haml
65
82
  #
66
83
  # Uses preserve to convert any newlines inside whitespace-sensitive tags
67
84
  # into the HTML entities for endlines.
68
- # @tags@ is an array of tags to preserve.
85
+ # +tags+ is an array of tags to preserve.
69
86
  # It defaults to the value of the <tt>:preserve</tt> option.
70
87
  def find_and_preserve(input = '', tags = haml_buffer.options[:preserve], &block)
71
88
  return find_and_preserve(capture_haml(&block)) if block
@@ -258,6 +275,7 @@ module Haml
258
275
  with_haml_buffer(buffer) do
259
276
  position = haml_buffer.buffer.length
260
277
 
278
+ haml_buffer.capture_position = position
261
279
  block.call(*args)
262
280
 
263
281
  captured = haml_buffer.buffer.slice!(position..-1).split(/^/)
@@ -273,6 +291,8 @@ module Haml
273
291
  line[min_tabs..-1]
274
292
  end.join
275
293
  end
294
+ ensure
295
+ haml_buffer.capture_position = nil
276
296
  end
277
297
 
278
298
  def puts(*args) # :nodoc:
@@ -342,6 +362,11 @@ END
342
362
  # </table>
343
363
  #
344
364
  def haml_tag(name, *rest, &block)
365
+ ret = ErrorReturn.new(<<MESSAGE)
366
+ haml_tag outputs directly to the Haml template.
367
+ Disregard its return value and use the - operator.
368
+ MESSAGE
369
+
345
370
  name = name.to_s
346
371
  text = rest.shift.to_s unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
347
372
  flags = []
@@ -352,7 +377,7 @@ END
352
377
 
353
378
  if text.nil? && block.nil? && (haml_buffer.options[:autoclose].include?(name) || flags.include?(:/))
354
379
  haml_concat "<#{name}#{attributes} />"
355
- return nil
380
+ return ret
356
381
  end
357
382
 
358
383
  if flags.include?(:/)
@@ -364,7 +389,7 @@ END
364
389
  if block.nil?
365
390
  tag << text.to_s << "</#{name}>"
366
391
  haml_concat tag
367
- return
392
+ return ret
368
393
  end
369
394
 
370
395
  if text
@@ -374,7 +399,7 @@ END
374
399
  if flags.include?(:<)
375
400
  tag << capture_haml(&block).strip << "</#{name}>"
376
401
  haml_concat tag
377
- return
402
+ return ret
378
403
  end
379
404
 
380
405
  haml_concat tag
@@ -382,7 +407,8 @@ END
382
407
  block.call
383
408
  tab_down
384
409
  haml_concat "</#{name}>"
385
- nil
410
+
411
+ ret
386
412
  end
387
413
 
388
414
  # Characters that need to be escaped to HTML entities from user input
@@ -78,9 +78,7 @@ module Haml
78
78
 
79
79
  class ::Hpricot::Doc
80
80
  def to_haml(tabs = 0)
81
- output = ''
82
- children.each { |child| output += child.to_haml(0) } if children
83
- output
81
+ (children || []).inject('') {|s, c| s << c.to_haml(0)}
84
82
  end
85
83
  end
86
84
 
@@ -90,6 +88,12 @@ module Haml
90
88
  end
91
89
  end
92
90
 
91
+ class ::Hpricot::CData
92
+ def to_haml(tabs = 0)
93
+ "#{tabulate(tabs)}:cdata\n#{parse_text(self.content, tabs + 1)}"
94
+ end
95
+ end
96
+
93
97
  class ::Hpricot::DocType
94
98
  def to_haml(tabs = 0)
95
99
  attrs = public_id.scan(/DTD\s+([^\s]+)\s*([^\s]*)\s*([^\s]*)\s*\/\//)[0]
@@ -83,7 +83,7 @@ module Haml
83
83
  DOCTYPE_REGEX = /(\d\.\d)?[\s]*([a-z]*)/i
84
84
 
85
85
  # The Regex that matches a literal string or symbol value
86
- LITERAL_VALUE_REGEX = /^\s*(:(\w*)|(('|")([^\\\#'"]*?)\4))\s*$/
86
+ LITERAL_VALUE_REGEX = /:(\w*)|(["'])([^\\#'"]|\\.)*\2/
87
87
 
88
88
  private
89
89
 
@@ -451,29 +451,18 @@ END
451
451
  attributes
452
452
  end
453
453
 
454
- def parse_literal_value(text)
455
- return nil unless text
456
- text.match(LITERAL_VALUE_REGEX)
457
-
458
- # $2 holds the value matched by a symbol, but is nil for a string match
459
- # $5 holds the value matched by a string
460
- $2 || $5
461
- end
462
-
463
454
  def parse_static_hash(text)
464
455
  return {} unless text
465
456
 
466
457
  attributes = {}
467
- text.split(',').each do |attrib|
468
- key, value, more = attrib.split('=>')
469
-
470
- # Make sure the key and value and only the key and value exist
471
- # Otherwise, it's too complicated or dynamic and we'll defer it to the actual Ruby parser
472
- key = parse_literal_value key
473
- value = parse_literal_value value
474
- return nil if more || key.nil? || value.nil?
475
-
476
- attributes[key] = value
458
+ scanner = StringScanner.new(text)
459
+ scanner.scan(/\s+/)
460
+ until scanner.eos?
461
+ return unless key = scanner.scan(LITERAL_VALUE_REGEX)
462
+ return unless scanner.scan(/\s*=>\s*/)
463
+ return unless value = scanner.scan(LITERAL_VALUE_REGEX)
464
+ attributes[eval(key).to_s] = eval(value).to_s
465
+ scanner.scan(/[,\s]*/)
477
466
  end
478
467
  attributes
479
468
  end
@@ -803,7 +792,7 @@ END
803
792
  unless @merged_text.empty?
804
793
  @merged_text.rstrip!
805
794
  else
806
- push_silent("_erbout.rstrip!", false)
795
+ push_silent("_hamlout.rstrip!", false)
807
796
  @dont_tab_up_next_text = true
808
797
  end
809
798
  end
@@ -113,7 +113,7 @@ module Sass
113
113
  index = 0
114
114
  while @lines[index]
115
115
  old_index = index
116
- child, index = build_tree(index)
116
+ child, index = build_tree(index, true)
117
117
 
118
118
  if child.is_a? Tree::Node
119
119
  child.line = old_index + 1
@@ -136,6 +136,7 @@ module Sass
136
136
  def split_lines
137
137
  @line = 0
138
138
  old_tabs = nil
139
+ last_line_was_comment = false
139
140
  @template.each_with_index do |line, index|
140
141
  @line += 1
141
142
 
@@ -143,6 +144,19 @@ module Sass
143
144
 
144
145
  if line[0] == COMMENT_CHAR && line[1] == SASS_COMMENT_CHAR && tabs == 0
145
146
  tabs = old_tabs
147
+ last_line_was_comment = true
148
+ elsif last_line_was_comment && tabs
149
+ if tabs > 0
150
+ context = "On line #{@line}"
151
+ context << " of #{@options[:filename]}" if @options[:filename]
152
+ warn <<ENDENDEND
153
+ DEPRECATION WARNING:
154
+ #{context}
155
+ Silent comments (//) in version 2.2 will comment out the lines indented beneath them.
156
+ Please indent line #{@line - 1} to match the surrounding indentation level
157
+ ENDENDEND
158
+ end
159
+ last_line_was_comment = false
146
160
  end
147
161
 
148
162
  if tabs # if line isn't blank
@@ -180,7 +194,8 @@ END
180
194
  spaces / 2
181
195
  end
182
196
 
183
- def build_tree(index)
197
+ def build_tree(index, root = false)
198
+ @root = root
184
199
  line, tabs = @lines[index]
185
200
  index += 1
186
201
  @line = index
@@ -251,12 +266,7 @@ END
251
266
  when :mixin
252
267
  raise SyntaxError.new("Mixins may only be defined at the root of a document.", @line)
253
268
  when Array
254
- child.each do |c|
255
- if c.is_a?(Tree::DirectiveNode)
256
- raise SyntaxError.new("Import directives may only be used at the root of a document.", @line)
257
- end
258
- parent << c
259
- end
269
+ child.each {|c| parent << c}
260
270
  when Tree::Node
261
271
  parent << child
262
272
  end
@@ -392,6 +402,10 @@ END
392
402
  end
393
403
 
394
404
  def import(files)
405
+ unless @root
406
+ raise SyntaxError.new("Import directives may only be used at the root of a document.", @line)
407
+ end
408
+
395
409
  nodes = []
396
410
 
397
411
  files.split(/,\s*/).each do |filename|
@@ -618,6 +618,17 @@ END
618
618
  assert_raise(Haml::Error, "Invalid output format :html1") { engine("%br", :format => :html1) }
619
619
  end
620
620
 
621
+ def test_static_hashes
622
+ assert_equal("<a b='a =&gt; b'></a>\n", render("%a{:b => 'a => b'}", :suppress_eval => true))
623
+ assert_equal("<a b='a, b'></a>\n", render("%a{:b => 'a, b'}", :suppress_eval => true))
624
+ assert_equal("<a b='a\tb'></a>\n", render('%a{:b => "a\tb"}', :suppress_eval => true))
625
+ assert_equal("<a b='a\#{foo}b'></a>\n", render('%a{:b => "a\\#{foo}b"}', :suppress_eval => true))
626
+ end
627
+
628
+ def test_dynamic_hashes_with_suppress_eval
629
+ assert_equal("<a></a>\n", render('%a{:b => "a #{1 + 1} b", :c => "d"}', :suppress_eval => true))
630
+ end
631
+
621
632
  # HTML 4.0
622
633
 
623
634
  def test_html_has_no_self_closing_tags
@@ -141,6 +141,10 @@ HAML
141
141
  assert_raise(Haml::Error) { render("- haml_tag :p, :/ do\n foo") }
142
142
  end
143
143
 
144
+ def test_haml_tag_error_return
145
+ assert_raise(Haml::Error) { render("= haml_tag :p") }
146
+ end
147
+
144
148
  def test_is_haml
145
149
  assert(!ActionView::Base.new.is_haml?)
146
150
  assert_equal("true\n", render("= is_haml?"))
@@ -220,5 +224,20 @@ HAML
220
224
  def test_random_class_includes_tag_helper
221
225
  assert_equal "<p>some tag content</p>", ActsLikeTag.new.to_s
222
226
  end
227
+
228
+ def test_capture_with_nuke_outer
229
+ assert_equal "<div></div>\n*<div>hi there!</div>\n", render(<<HAML)
230
+ %div
231
+ = precede("*") do
232
+ %div> hi there!
233
+ HAML
234
+
235
+ assert_equal "<div></div>\n*<div>hi there!</div>\n", render(<<HAML)
236
+ %div
237
+ = precede("*") do
238
+ = " "
239
+ %div> hi there!
240
+ HAML
241
+ end
223
242
  end
224
243
 
@@ -74,6 +74,22 @@ class Html2HamlTest < Test::Unit::TestCase
74
74
  render_rhtml(%Q{<%- form_for -%>})
75
75
  end
76
76
 
77
+ def test_cdata
78
+ assert_equal(<<HAML.strip, render(<<HTML))
79
+ %p
80
+ :cdata
81
+ <a foo="bar" baz="bang">
82
+ <div id="foo">flop</div>
83
+ </a>
84
+ HAML
85
+ <p><![CDATA[
86
+ <a foo="bar" baz="bang">
87
+ <div id="foo">flop</div>
88
+ </a>
89
+ ]]></p>
90
+ HTML
91
+ end
92
+
77
93
  protected
78
94
 
79
95
  def render(text, options = {})
@@ -13,6 +13,126 @@
13
13
  20
14
14
  </div>
15
15
  <div id='body'> Quotes should be loved! Just like people!</div>
16
+ 0
17
+ 1
18
+ 2
19
+ 3
20
+ 4
21
+ 5
22
+ 6
23
+ 7
24
+ 8
25
+ 9
26
+ 10
27
+ 11
28
+ 12
29
+ 13
30
+ 14
31
+ 15
32
+ 16
33
+ 17
34
+ 18
35
+ 19
36
+ 20
37
+ 21
38
+ 22
39
+ 23
40
+ 24
41
+ 25
42
+ 26
43
+ 27
44
+ 28
45
+ 29
46
+ 30
47
+ 31
48
+ 32
49
+ 33
50
+ 34
51
+ 35
52
+ 36
53
+ 37
54
+ 38
55
+ 39
56
+ 40
57
+ 41
58
+ 42
59
+ 43
60
+ 44
61
+ 45
62
+ 46
63
+ 47
64
+ 48
65
+ 49
66
+ 50
67
+ 51
68
+ 52
69
+ 53
70
+ 54
71
+ 55
72
+ 56
73
+ 57
74
+ 58
75
+ 59
76
+ 60
77
+ 61
78
+ 62
79
+ 63
80
+ 64
81
+ 65
82
+ 66
83
+ 67
84
+ 68
85
+ 69
86
+ 70
87
+ 71
88
+ 72
89
+ 73
90
+ 74
91
+ 75
92
+ 76
93
+ 77
94
+ 78
95
+ 79
96
+ 80
97
+ 81
98
+ 82
99
+ 83
100
+ 84
101
+ 85
102
+ 86
103
+ 87
104
+ 88
105
+ 89
106
+ 90
107
+ 91
108
+ 92
109
+ 93
110
+ 94
111
+ 95
112
+ 96
113
+ 97
114
+ 98
115
+ 99
116
+ 100
117
+ 101
118
+ 102
119
+ 103
120
+ 104
121
+ 105
122
+ 106
123
+ 107
124
+ 108
125
+ 109
126
+ 110
127
+ 111
128
+ 112
129
+ 113
130
+ 114
131
+ 115
132
+ 116
133
+ 117
134
+ 118
135
+ 119
16
136
  Wow.|
17
137
  <p>
18
138
  Holy cow multiline tags! A pipe (|) even!
@@ -1,5 +1,5 @@
1
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'>
2
+ <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US' lang='en-US'>
3
3
  <head>
4
4
  <title>Hampton Catlin Is Totally Awesome</title>
5
5
  <meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
@@ -38,7 +38,7 @@
38
38
  </div>
39
39
  <ul class='really cool'>
40
40
  <% ('a'..'f').each do |a|%>
41
- <li><%= a %>
41
+ <li><%= a %></li>
42
42
  <% end %>
43
43
  <div class='of_divs_with_underscore' id='combo'><%= @should_eval = "with this text" %></div>
44
44
  <%= "foo".each_line do |line|
@@ -1,5 +1,5 @@
1
1
  !!!
2
- %html{html_attrs}
2
+ %html{:xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en-US", "lang" => "en-US"}
3
3
  %head
4
4
  %title Hampton Catlin Is Totally Awesome
5
5
  %meta{"http-equiv" => "Content-Type", :content => "text/html; charset=utf-8"}
@@ -12,7 +12,7 @@
12
12
  = 1 + 9 + 8 + 2 #numbers should work and this should be ignored
13
13
  #body= " Quotes should be loved! Just like people!"
14
14
  - 120.times do |number|
15
- - number
15
+ = number
16
16
  Wow.|
17
17
  %p
18
18
  = "Holy cow " + |
@@ -1,5 +1,5 @@
1
1
  !!!
2
- %html{html_attrs}
2
+ %html{:xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en-US", "lang" => "en-US"}
3
3
  %head
4
4
  %title Hampton Catlin Is Totally Awesome
5
5
  %meta{"http-equiv" => "Content-Type", :content => "text/html; charset=utf-8"}
@@ -12,7 +12,7 @@
12
12
  = 1 + 9 + 8 + 2 #numbers should work and this should be ignored
13
13
  #body= " Quotes should be loved! Just like people!"
14
14
  - 120.times do |number|
15
- - number
15
+ = number
16
16
  Wow.|
17
17
  %p
18
18
  = "Holy cow " + |
@@ -51,6 +51,7 @@ END
51
51
  "@import foo.sass" => "File to import not found or unreadable: foo.sass.",
52
52
  "@import templates/basic\n foo" => "Illegal nesting: Nothing may be nested beneath import directives.",
53
53
  "foo\n @import templates/basic" => "Import directives may only be used at the root of a document.",
54
+ "foo\n @import #{File.dirname(__FILE__)}/templates/basic" => "Import directives may only be used at the root of a document.",
54
55
  "!foo = bar baz !" => "Unterminated constant.",
55
56
  "!foo = !(foo)" => "Invalid constant.",
56
57
  "=foo\n :color red\n.bar\n +bang" => "Undefined mixin 'bang'.",
@@ -98,7 +99,13 @@ END
98
99
  end
99
100
 
100
101
  def test_exception_line
101
- to_render = "rule\n :attr val\n// comment!\n\n :broken\n"
102
+ to_render = <<SASS
103
+ rule
104
+ :attr val
105
+ // comment!
106
+
107
+ :broken
108
+ SASS
102
109
  begin
103
110
  Sass::Engine.new(to_render).render
104
111
  rescue Sass::SyntaxError => err
@@ -79,7 +79,6 @@ body
79
79
  :margin 0 5px
80
80
  :padding 5px 5px 0 5px
81
81
  :display inline
82
- // This comment is in the middle of this rule
83
82
  :font-size 1.1em
84
83
  // This comment is properly indented
85
84
  :color #fff
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.0.9
4
+ version: 2.0.10
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-02-21 00:00:00 -08:00
13
+ date: 2009-07-06 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies: []
16
16