haml 1.8.0 → 2.0.3

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. data/FAQ +138 -0
  2. data/MIT-LICENSE +1 -1
  3. data/{README → README.rdoc} +66 -3
  4. data/REVISION +1 -0
  5. data/Rakefile +115 -147
  6. data/VERSION +1 -1
  7. data/bin/css2sass +0 -0
  8. data/bin/haml +2 -1
  9. data/bin/html2haml +0 -0
  10. data/bin/sass +0 -0
  11. data/init.rb +6 -1
  12. data/lib/haml/buffer.rb +122 -64
  13. data/lib/haml/engine.rb +77 -46
  14. data/lib/haml/error.rb +15 -6
  15. data/lib/haml/exec.rb +61 -10
  16. data/lib/haml/filters.rb +229 -74
  17. data/lib/haml/helpers/action_view_extensions.rb +1 -1
  18. data/lib/haml/helpers/action_view_mods.rb +109 -24
  19. data/lib/haml/helpers.rb +137 -76
  20. data/lib/haml/html.rb +8 -8
  21. data/lib/haml/precompiler.rb +280 -153
  22. data/lib/haml/template/patch.rb +10 -3
  23. data/lib/haml/template/plugin.rb +61 -10
  24. data/lib/haml/template.rb +14 -9
  25. data/lib/haml.rb +483 -214
  26. data/lib/sass/constant/color.rb +13 -13
  27. data/lib/sass/constant/literal.rb +8 -7
  28. data/lib/sass/constant/nil.rb +9 -0
  29. data/lib/sass/constant/number.rb +10 -10
  30. data/lib/sass/constant/operation.rb +4 -4
  31. data/lib/sass/constant/string.rb +3 -3
  32. data/lib/sass/constant.rb +46 -77
  33. data/lib/sass/css.rb +130 -56
  34. data/lib/sass/engine.rb +131 -43
  35. data/lib/sass/plugin/merb.rb +48 -12
  36. data/lib/sass/plugin/rails.rb +10 -4
  37. data/lib/sass/plugin.rb +33 -10
  38. data/lib/sass/tree/attr_node.rb +5 -5
  39. data/lib/sass/tree/directive_node.rb +2 -7
  40. data/lib/sass/tree/node.rb +1 -12
  41. data/lib/sass/tree/rule_node.rb +39 -31
  42. data/lib/sass/tree/value_node.rb +1 -1
  43. data/lib/sass.rb +194 -19
  44. data/rails/init.rb +1 -0
  45. data/test/benchmark.rb +67 -80
  46. data/test/haml/engine_test.rb +368 -152
  47. data/test/haml/helper_test.rb +68 -16
  48. data/test/haml/html2haml_test.rb +3 -4
  49. data/test/haml/results/content_for_layout.xhtml +1 -2
  50. data/test/haml/results/eval_suppressed.xhtml +2 -4
  51. data/test/haml/results/filters.xhtml +38 -30
  52. data/test/haml/results/helpers.xhtml +4 -8
  53. data/test/haml/results/just_stuff.xhtml +8 -7
  54. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  55. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  56. data/test/haml/results/original_engine.xhtml +3 -7
  57. data/test/haml/results/partials.xhtml +1 -0
  58. data/test/haml/results/tag_parsing.xhtml +1 -6
  59. data/test/haml/results/very_basic.xhtml +2 -4
  60. data/test/haml/results/whitespace_handling.xhtml +13 -21
  61. data/test/haml/template_test.rb +42 -57
  62. data/test/haml/templates/_partial.haml +1 -0
  63. data/test/haml/templates/filters.haml +39 -21
  64. data/test/haml/templates/helpers.haml +10 -10
  65. data/test/haml/templates/just_stuff.haml +8 -3
  66. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  67. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  68. data/test/haml/templates/partials.haml +1 -1
  69. data/test/haml/templates/tag_parsing.haml +0 -3
  70. data/test/haml/templates/whitespace_handling.haml +10 -10
  71. data/test/sass/engine_test.rb +97 -39
  72. data/test/sass/plugin_test.rb +4 -7
  73. data/test/sass/results/constants.css +2 -0
  74. data/test/sass/results/import.css +2 -2
  75. data/test/sass/results/mixins.css +95 -0
  76. data/test/sass/results/multiline.css +24 -0
  77. data/test/sass/templates/constants.sass +3 -0
  78. data/test/sass/templates/import.sass +4 -1
  79. data/test/sass/templates/importee.sass +4 -0
  80. data/test/sass/templates/mixins.sass +76 -0
  81. data/test/sass/templates/multiline.sass +20 -0
  82. data/test/test_helper.rb +18 -0
  83. metadata +70 -53
  84. data/lib/haml/helpers/action_view_mods.rb.rej +0 -30
  85. data/lib/haml/util.rb +0 -18
  86. data/lib/sass/constant.rb.rej +0 -42
  87. data/test/haml/runner.rb +0 -16
  88. data/test/profile.rb +0 -65
  89. data/test/sass/engine_test.rb.rej +0 -18
data/lib/haml/helpers.rb CHANGED
@@ -34,25 +34,44 @@ module Haml
34
34
  # include Haml::Helpers
35
35
  # end
36
36
  # context.init_haml_helpers
37
- # context.open :p, "Stuff"
38
- #
37
+ # context.haml_tag :p, "Stuff"
38
+ #
39
39
  def init_haml_helpers
40
- @haml_is_haml = true
41
- @haml_stack = [Haml::Buffer.new]
40
+ @haml_buffer = Haml::Buffer.new(@haml_buffer, Haml::Engine.new('').send(:options_for_buffer))
42
41
  nil
43
42
  end
44
43
 
45
44
  # call-seq:
46
- # find_and_preserve(input)
45
+ # non_haml { ... }
46
+ #
47
+ # Runs a block of code in a non-Haml context
48
+ # (i.e. #is_haml? will return false).
49
+ #
50
+ # This is mainly useful for rendering sub-templates such as partials in a non-Haml language,
51
+ # particularly where helpers may behave differently when run from Haml.
52
+ #
53
+ # Note that this is automatically applied to Rails partials.
54
+ def non_haml
55
+ was_active = @haml_buffer.active?
56
+ @haml_buffer.active = false
57
+ res = yield
58
+ @haml_buffer.active = was_active
59
+ res
60
+ end
61
+
62
+ # call-seq:
63
+ # find_and_preserve(input, tags = haml_buffer.options[:preserve])
47
64
  # find_and_preserve {...}
48
- #
49
- # Isolates the whitespace-sensitive tags in the string and uses preserve
50
- # to convert any endlines inside them into HTML entities for endlines.
51
- def find_and_preserve(input = '', &block)
65
+ #
66
+ # Uses preserve to convert any newlines inside whitespace-sensitive tags
67
+ # into the HTML entities for endlines.
68
+ # @tags@ is an array of tags to preserve.
69
+ # It defaults to the value of the <tt>:preserve</tt> option.
70
+ def find_and_preserve(input = '', tags = haml_buffer.options[:preserve], &block)
52
71
  return find_and_preserve(capture_haml(&block)) if block
53
72
 
54
73
  input = input.to_s
55
- input.gsub(/<(textarea|code|pre)([^>]*)>(.*?)(<\/\1>)/im) do
74
+ input.gsub(/<(#{tags.map(&Regexp.method(:escape)).join('|')})([^>]*)>(.*?)(<\/\1>)/im) do
56
75
  "<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
57
76
  end
58
77
  end
@@ -67,7 +86,7 @@ module Haml
67
86
  def preserve(input = '', &block)
68
87
  return preserve(capture_haml(&block)) if block
69
88
 
70
- input.gsub(/\n/, '&#x000A;').gsub(/\r/, '')
89
+ input.chomp("\n").gsub(/\n/, '&#x000A;').gsub(/\r/, '')
71
90
  end
72
91
 
73
92
  alias_method :flatten, :preserve
@@ -107,14 +126,14 @@ module Haml
107
126
  def list_of(array, &block) # :yields: item
108
127
  to_return = array.collect do |i|
109
128
  result = capture_haml(i, &block)
110
-
129
+
111
130
  if result.count("\n") > 1
112
131
  result.gsub!("\n", "\n ")
113
132
  result = "\n #{result.strip}\n"
114
133
  else
115
134
  result.strip!
116
135
  end
117
-
136
+
118
137
  "<li>#{result}</li>"
119
138
  end
120
139
  to_return.join("\n")
@@ -153,17 +172,17 @@ module Haml
153
172
  # <strong>baz</strong>
154
173
  #
155
174
  def tab_up(i = 1)
156
- buffer.tabulation += i
175
+ haml_buffer.tabulation += i
157
176
  end
158
177
 
159
- # Increments the number of tabs the buffer automatically adds
178
+ # Decrements the number of tabs the buffer automatically adds
160
179
  # to the lines of the template.
161
180
  #
162
- # See tab_up.
181
+ # See also tab_up.
163
182
  def tab_down(i = 1)
164
- buffer.tabulation -= i
183
+ haml_buffer.tabulation -= i
165
184
  end
166
-
185
+
167
186
  # Surrounds the given block of Haml code with the given characters,
168
187
  # with no whitespace in between.
169
188
  # For example:
@@ -187,10 +206,10 @@ module Haml
187
206
  def surround(front, back = nil, &block)
188
207
  back ||= front
189
208
  output = capture_haml(&block)
190
-
209
+
191
210
  "#{front}#{output.chomp}#{back}\n"
192
211
  end
193
-
212
+
194
213
  # Prepends the given character to the beginning of the Haml block,
195
214
  # with no whitespace between.
196
215
  # For example:
@@ -205,7 +224,7 @@ module Haml
205
224
  def precede(char, &block)
206
225
  "#{char}#{capture_haml(&block).chomp}\n"
207
226
  end
208
-
227
+
209
228
  # Appends the given character to the end of the Haml block,
210
229
  # with no whitespace between.
211
230
  # For example:
@@ -222,7 +241,7 @@ module Haml
222
241
  def succeed(char, &block)
223
242
  "#{capture_haml(&block).chomp}#{char}\n"
224
243
  end
225
-
244
+
226
245
  # Captures the result of the given block of Haml code,
227
246
  # gets rid of the excess indentation,
228
247
  # and returns it as a string.
@@ -235,35 +254,40 @@ module Haml
235
254
  # the local variable <tt>foo</tt> would be assigned to "<p>13</p>\n".
236
255
  #
237
256
  def capture_haml(*args, &block)
238
- capture_haml_with_buffer(buffer.buffer, *args, &block)
257
+ capture_haml_with_buffer(haml_buffer.buffer, *args, &block)
239
258
  end
240
259
 
241
260
  # Outputs text directly to the Haml buffer, with the proper tabulation
242
261
  def puts(text = "")
243
- buffer.buffer << (' ' * buffer.tabulation) << text.to_s << "\n"
262
+ haml_buffer.buffer << (' ' * haml_buffer.tabulation) << text.to_s << "\n"
244
263
  nil
245
264
  end
246
265
 
247
266
  #
248
267
  # call-seq:
249
- # open(name, attributes = {}) {...}
250
- # open(name, text, attributes = {}) {...}
268
+ # haml_tag(name, *flags, attributes = {}) {...}
269
+ # haml_tag(name, text, *flags, attributes = {}) {...}
251
270
  #
252
271
  # Creates an HTML tag with the given name and optionally text and attributes.
253
272
  # Can take a block that will be executed
254
273
  # between when the opening and closing tags are output.
255
274
  # If the block is a Haml block or outputs text using puts,
256
275
  # the text will be properly indented.
257
- #
276
+ #
277
+ # <tt>flags</tt> is a list of symbol flags
278
+ # like those that can be put at the end of a Haml tag
279
+ # (<tt>:/</tt>, <tt>:<</tt>, and <tt>:></tt>).
280
+ # Currently, only <tt>:/</tt> and <tt>:<</tt> are supported.
281
+ #
258
282
  # For example,
259
283
  #
260
- # open :table do
261
- # open :tr do
262
- # open :td, {:class => 'cell'} do
263
- # open :strong, "strong!"
284
+ # haml_tag :table do
285
+ # haml_tag :tr do
286
+ # haml_tag :td, {:class => 'cell'} do
287
+ # haml_tag :strong, "strong!"
264
288
  # puts "data"
265
289
  # end
266
- # open :td do
290
+ # haml_tag :td do
267
291
  # puts "more_data"
268
292
  # end
269
293
  # end
@@ -285,87 +309,124 @@ module Haml
285
309
  # </tr>
286
310
  # </table>
287
311
  #
288
- def open(name, attributes = {}, alt_atts = {}, &block)
289
- text = nil
290
- if attributes.is_a? String
291
- text = attributes
292
- attributes = alt_atts
293
- end
312
+ def haml_tag(name, *rest, &block)
313
+ name = name.to_s
314
+ text = rest.shift.to_s unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
315
+ flags = []
316
+ flags << rest.shift while rest.first.is_a? Symbol
317
+ attributes = Haml::Precompiler.build_attributes(haml_buffer.html?,
318
+ haml_buffer.options[:attr_wrapper],
319
+ rest.shift || {})
294
320
 
295
- if text.nil? && block.nil?
296
- puts "<#{name}#{Haml::Precompiler.build_attributes(buffer.options[:attr_wrapper], attributes)} />"
321
+ if text.nil? && block.nil? && (haml_buffer.options[:autoclose].include?(name) || flags.include?(:/))
322
+ puts "<#{name}#{attributes} />"
297
323
  return nil
298
324
  end
299
325
 
300
- puts "<#{name}#{Haml::Precompiler.build_attributes(buffer.options[:attr_wrapper], attributes)}>"
301
- unless text && text.empty?
302
- tab_up
303
- # Print out either the text (using push_text) or call the block and add an endline
304
- if text
305
- puts(text)
306
- elsif block
307
- block.call
308
- end
309
- tab_down
326
+ if flags.include?(:/)
327
+ raise Error.new("Self-closing tags can't have content.") if text
328
+ raise Error.new("Illegal nesting: nesting within a self-closing tag is illegal.") if block
329
+ end
330
+
331
+ tag = "<#{name}#{attributes}>"
332
+ if block.nil?
333
+ tag << text.to_s << "</#{name}>"
334
+ puts tag
335
+ return
310
336
  end
337
+
338
+ if text
339
+ raise Error.new("Illegal nesting: content can't be both given to haml_tag :#{name} and nested within it.")
340
+ end
341
+
342
+ if flags.include?(:<)
343
+ tag << capture_haml(&block).strip << "</#{name}>"
344
+ puts tag
345
+ return
346
+ end
347
+
348
+ puts tag
349
+ tab_up
350
+ block.call
351
+ tab_down
311
352
  puts "</#{name}>"
312
353
  nil
313
354
  end
314
-
355
+
356
+ # Characters that need to be escaped to HTML entities from user input
357
+ HTML_ESCAPE = { '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;', '"'=>'&quot;', "'"=>'&#039;', }
358
+
359
+ # Returns a copy of <tt>text</tt> with ampersands, angle brackets and quotes
360
+ # escaped into HTML entities.
361
+ def html_escape(text)
362
+ text.to_s.gsub(/[\"><&]/) { |s| HTML_ESCAPE[s] }
363
+ end
364
+
365
+ # Escapes HTML entities in <tt>text</tt>, but without escaping an ampersand
366
+ # that is already part of an escaped entity.
367
+ def escape_once(text)
368
+ text.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |s| HTML_ESCAPE[s] }
369
+ end
370
+
371
+ # Returns whether or not the current template is a Haml template.
372
+ #
373
+ # This function, unlike other Haml::Helpers functions,
374
+ # also works in other ActionView templates,
375
+ # where it will always return false.
376
+ def is_haml?
377
+ !@haml_buffer.nil? && @haml_buffer.active?
378
+ end
379
+
315
380
  private
316
381
 
317
382
  # Gets a reference to the current Haml::Buffer object.
318
- def buffer
319
- @haml_stack[-1]
383
+ def haml_buffer
384
+ @haml_buffer
320
385
  end
321
-
386
+
322
387
  # Gives a proc the same local "_hamlout" and "_erbout" variables
323
388
  # that the current template has.
324
- def bind_proc(&proc)
325
- _hamlout = buffer
389
+ def haml_bind_proc(&proc)
390
+ _hamlout = haml_buffer
326
391
  _erbout = _hamlout.buffer
327
392
  proc { |*args| proc.call(*args) }
328
393
  end
329
-
394
+
330
395
  # Performs the function of capture_haml, assuming <tt>local_buffer</tt>
331
396
  # is where the output of block goes.
332
397
  def capture_haml_with_buffer(local_buffer, *args, &block)
333
398
  position = local_buffer.length
334
-
335
- block.call *args
336
-
399
+
400
+ block.call(*args)
401
+
337
402
  captured = local_buffer.slice!(position..-1)
338
-
403
+
339
404
  min_tabs = nil
340
405
  captured.each do |line|
341
406
  tabs = line.index(/[^ ]/)
342
407
  min_tabs ||= tabs
343
408
  min_tabs = min_tabs > tabs ? tabs : min_tabs
344
409
  end
345
-
410
+
346
411
  result = captured.map do |line|
347
412
  line[min_tabs..-1]
348
413
  end
349
414
  result.to_s
350
415
  end
351
416
 
352
- # Returns whether or not the current template is a Haml template.
353
- #
354
- # This function, unlike other Haml::Helpers functions,
355
- # also works in other ActionView templates,
356
- # where it will always return false.
357
- def is_haml?
358
- @haml_is_haml
359
- end
360
-
361
417
  include ActionViewExtensions if self.const_defined? "ActionViewExtensions"
362
418
  end
363
419
  end
364
420
 
365
- module ActionView
366
- class Base # :nodoc:
367
- def is_haml?
368
- false
369
- end
421
+ class Object
422
+ # Haml overrides various ActionView helpers,
423
+ # which call an #is_haml? method
424
+ # to determine whether or not the current context object
425
+ # is a proper Haml context.
426
+ # Because ActionView helpers may be included in non-ActionView::Base classes,
427
+ # it's a good idea to define is_haml? for all objects.
428
+ def is_haml?
429
+ false
370
430
  end
371
431
  end
432
+
data/lib/haml/html.rb CHANGED
@@ -29,7 +29,7 @@ module Haml
29
29
  end
30
30
 
31
31
  method = @@options[:xhtml] ? Hpricot.method(:XML) : method(:Hpricot)
32
- @template = method.call(template)
32
+ @template = method.call(template.gsub('&', '&amp;'))
33
33
  end
34
34
  end
35
35
 
@@ -59,7 +59,7 @@ module Haml
59
59
  String.new
60
60
  else
61
61
  lines = text.split("\n")
62
-
62
+
63
63
  lines.map do |line|
64
64
  line.strip!
65
65
  "#{tabulate(tabs)}#{'\\' if Haml::Engine::SPECIAL_CHARACTERS.include?(line[0])}#{line}\n"
@@ -73,7 +73,7 @@ module Haml
73
73
  def self.options
74
74
  @@options
75
75
  end
76
-
76
+
77
77
  TEXT_REGEXP = /^(\s*).*$/
78
78
 
79
79
  class ::Hpricot::Doc
@@ -129,14 +129,14 @@ module Haml
129
129
 
130
130
  class ::Hpricot::Elem
131
131
  def to_haml(tabs = 0)
132
- output = "#{tabulate(tabs)}"
132
+ output = "#{tabulate(tabs)}"
133
133
  if HTML.options[:rhtml] && name[0...5] == 'haml:'
134
134
  return output + HTML.send("haml_tag_#{name[5..-1]}",
135
135
  CGI.unescapeHTML(self.innerHTML))
136
136
  end
137
137
 
138
138
  output += "%#{name}" unless name == 'div' && (attributes.include?('id') || attributes.include?('class'))
139
-
139
+
140
140
  if attributes
141
141
  output += "##{attributes['id']}" if attributes['id']
142
142
  attributes['class'].split(' ').each { |c| output += ".#{c}" } if attributes['class']
@@ -144,10 +144,10 @@ module Haml
144
144
  remove_attribute('class')
145
145
  output += haml_attributes if attributes.length > 0
146
146
  end
147
-
147
+
148
148
  output += "/" if children.length == 0
149
149
  output += "\n"
150
-
150
+
151
151
  self.children.each do |child|
152
152
  output += child.to_haml(tabs + 1)
153
153
  end
@@ -169,7 +169,7 @@ module Haml
169
169
  end
170
170
 
171
171
  def self.haml_tag_loud(text)
172
- "= #{text.gsub(/\n\s*/, '; ').strip}\n"
172
+ "= #{text.gsub(/\n\s*/, ' ').strip}\n"
173
173
  end
174
174
 
175
175
  def self.haml_tag_silent(text)