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
@@ -6,11 +6,6 @@ module Haml
6
6
  class Buffer
7
7
  include Haml::Helpers
8
8
 
9
- # Set the maximum length for a line to be considered a one-liner.
10
- # Lines <= the maximum will be rendered on one line,
11
- # i.e. <tt><p>Hello world</p></tt>
12
- ONE_LINER_LENGTH = 50
13
-
14
9
  # The string that holds the compiled XHTML. This is aliased as
15
10
  # _erbout for compatibility with ERB-specific code.
16
11
  attr_accessor :buffer
@@ -18,6 +13,47 @@ module Haml
18
13
  # The options hash passed in from Haml::Engine.
19
14
  attr_accessor :options
20
15
 
16
+ # The Buffer for the enclosing Haml document.
17
+ # This is set for partials and similar sorts of nested templates.
18
+ # It's nil at the top level (see #toplevel?).
19
+ attr_accessor :upper
20
+
21
+ # See #active?
22
+ attr_writer :active
23
+
24
+ # True if the format is XHTML
25
+ def xhtml?
26
+ not html?
27
+ end
28
+
29
+ # True if the format is any flavor of HTML
30
+ def html?
31
+ html4? or html5?
32
+ end
33
+
34
+ # True if the format is HTML4
35
+ def html4?
36
+ @options[:format] == :html4
37
+ end
38
+
39
+ # True if the format is HTML5
40
+ def html5?
41
+ @options[:format] == :html5
42
+ end
43
+
44
+ # True if this buffer is a top-level template,
45
+ # as opposed to a nested partial.
46
+ def toplevel?
47
+ upper.nil?
48
+ end
49
+
50
+ # True if this buffer is currently being used to render a Haml template.
51
+ # However, this returns false if a subtemplate is being rendered,
52
+ # even if it's a subtemplate of this buffer's template.
53
+ def active?
54
+ @active
55
+ end
56
+
21
57
  # Gets the current tabulation of the document.
22
58
  def tabulation
23
59
  @real_tabs + @tabulation
@@ -30,9 +66,13 @@ module Haml
30
66
  end
31
67
 
32
68
  # Creates a new buffer.
33
- def initialize(options = {})
69
+ def initialize(upper = nil, options = {})
70
+ @active = true
71
+ @upper = upper
34
72
  @options = {
35
- :attr_wrapper => "'"
73
+ :attr_wrapper => "'",
74
+ :ugly => false,
75
+ :format => :xhtml
36
76
  }.merge options
37
77
  @buffer = ""
38
78
  @tabulation = 0
@@ -44,56 +84,72 @@ module Haml
44
84
 
45
85
  # Renders +text+ with the proper tabulation. This also deals with
46
86
  # making a possible one-line tag one line or not.
47
- def push_text(text, tab_change = 0)
48
- if(@tabulation > 0)
49
- # Have to push every line in by the extra user set tabulation
50
- text.gsub!(/^/m, ' ' * @tabulation)
87
+ def push_text(text, dont_tab_up = false, tab_change = 0)
88
+ if @tabulation > 0 && !@options[:ugly]
89
+ # Have to push every line in by the extra user set tabulation.
90
+ # Don't push lines with just whitespace, though,
91
+ # because that screws up precompiled indentation.
92
+ text.gsub!(/^(?!\s+$)/m, tabs)
93
+ text.sub!(tabs, '') if dont_tab_up
51
94
  end
52
-
53
- @buffer << "#{text}"
95
+
96
+ @buffer << text
54
97
  @real_tabs += tab_change
98
+ @dont_tab_up_next_line = false
55
99
  end
56
100
 
57
101
  # Properly formats the output of a script that was run in the
58
102
  # instance_eval.
59
- def push_script(result, flattened, close_tag = nil)
103
+ def push_script(result, preserve_script, in_tag = false, preserve_tag = false,
104
+ escape_html = false, nuke_inner_whitespace = false)
60
105
  tabulation = @real_tabs
61
-
62
- if flattened
63
- result = Haml::Helpers.find_and_preserve(result)
64
- end
65
-
66
- result = result.to_s
67
- while result[-1] == ?\n
68
- # String#chomp is slow
69
- result = result[0...-1]
106
+
107
+ if preserve_tag
108
+ result = Haml::Helpers.preserve(result)
109
+ elsif preserve_script
110
+ result = Haml::Helpers.find_and_preserve(result, options[:preserve])
70
111
  end
71
-
72
- if close_tag && Buffer.one_liner?(result)
112
+
113
+ result = result.to_s.rstrip
114
+ result = html_escape(result) if escape_html
115
+
116
+ has_newline = result.include?("\n")
117
+ if in_tag && (@options[:ugly] || !has_newline || preserve_tag)
73
118
  @buffer << result
74
- @buffer << "</#{close_tag}>\n"
75
119
  @real_tabs -= 1
76
- else
77
- if close_tag
78
- @buffer << "\n"
79
- end
80
-
81
- result = result.gsub(/^/m, tabs(tabulation))
82
- @buffer << "#{result}\n"
83
-
84
- if close_tag
85
- @buffer << "#{tabs(tabulation-1)}</#{close_tag}>\n"
86
- @real_tabs -= 1
87
- end
120
+ return
121
+ end
122
+
123
+ @buffer << "\n" if in_tag && !nuke_inner_whitespace
124
+
125
+ # Precompiled tabulation may be wrong
126
+ if @tabulation > 0 && !in_tag
127
+ result = tabs + result
128
+ end
129
+
130
+ if has_newline && !@options[:ugly]
131
+ result = result.gsub "\n", "\n" + tabs(tabulation)
132
+
133
+ # Add tabulation if it wasn't precompiled
134
+ result = tabs(tabulation) + result if in_tag && !nuke_inner_whitespace
135
+ end
136
+ @buffer << "#{result}"
137
+ @buffer << "\n" unless nuke_inner_whitespace
138
+
139
+ if in_tag && !nuke_inner_whitespace
140
+ # We never get here if @options[:ugly] is true
141
+ @buffer << tabs(tabulation-1)
142
+ @real_tabs -= 1
88
143
  end
89
144
  nil
90
145
  end
91
146
 
92
147
  # Takes the various information about the opening tag for an
93
148
  # element, formats it, and adds it to the buffer.
94
- def open_tag(name, atomic, try_one_line, class_id, obj_ref, content, *attributes_hashes)
149
+ def open_tag(name, self_closing, try_one_line, preserve_tag, escape_html, class_id,
150
+ nuke_outer_whitespace, nuke_inner_whitespace, obj_ref, content, *attributes_hashes)
95
151
  tabulation = @real_tabs
96
-
152
+
97
153
  attributes = class_id
98
154
  attributes_hashes.each do |attributes_hash|
99
155
  attributes_hash.keys.each { |key| attributes_hash[key.to_s] = attributes_hash.delete(key) }
@@ -101,23 +157,21 @@ module Haml
101
157
  end
102
158
  self.class.merge_attrs(attributes, parse_object_ref(obj_ref)) if obj_ref
103
159
 
104
- if atomic
105
- str = " />\n"
106
- elsif try_one_line
107
- str = ">"
160
+ if self_closing
161
+ str = " />" + (nuke_outer_whitespace ? "" : "\n")
108
162
  else
109
- str = ">\n"
163
+ str = ">" + (try_one_line || preserve_tag || nuke_inner_whitespace ? "" : "\n")
110
164
  end
111
- @buffer << "#{tabs(tabulation)}<#{name}#{Precompiler.build_attributes(@options[:attr_wrapper], attributes)}#{str}"
165
+
166
+ attributes = Precompiler.build_attributes(html?, @options[:attr_wrapper], attributes)
167
+ @buffer << "#{nuke_outer_whitespace || @options[:ugly] ? '' : tabs(tabulation)}<#{name}#{attributes}#{str}"
168
+
112
169
  if content
113
- if Buffer.one_liner?(content)
114
- @buffer << "#{content}</#{name}>\n"
115
- else
116
- @buffer << "\n#{tabs(@real_tabs+1)}#{content}\n#{tabs(@real_tabs)}</#{name}>\n"
117
- end
118
- elsif !atomic
119
- @real_tabs += 1
170
+ @buffer << "#{content}</#{name}>" << (nuke_outer_whitespace ? "" : "\n")
171
+ return
120
172
  end
173
+
174
+ @real_tabs += 1 unless self_closing || nuke_inner_whitespace
121
175
  end
122
176
 
123
177
  def self.merge_attrs(to, from)
@@ -137,33 +191,33 @@ module Haml
137
191
  to.merge!(from)
138
192
  end
139
193
 
194
+ private
195
+
140
196
  # Some of these methods are exposed as public class methods
141
197
  # so they can be re-used in helpers.
142
198
 
143
- # Returns whether or not the given value is short enough to be rendered
144
- # on one line.
145
- def self.one_liner?(value)
146
- value.length <= ONE_LINER_LENGTH && value.scan(/\n/).empty?
147
- end
148
-
149
- private
150
-
151
199
  @@tab_cache = {}
152
200
  # Gets <tt>count</tt> tabs. Mostly for internal use.
153
- def tabs(count)
201
+ def tabs(count = 0)
154
202
  tabs = count + @tabulation
155
- ' ' * tabs
156
203
  @@tab_cache[tabs] ||= ' ' * tabs
157
204
  end
158
205
 
159
206
  # Takes an array of objects and uses the class and id of the first
160
207
  # one to create an attributes hash.
208
+ # The second object, if present, is used as a prefix,
209
+ # just like you can do with dom_id() and dom_class() in Rails
161
210
  def parse_object_ref(ref)
211
+ prefix = ref[1]
162
212
  ref = ref[0]
163
213
  # Let's make sure the value isn't nil. If it is, return the default Hash.
164
214
  return {} if ref.nil?
165
215
  class_name = underscore(ref.class)
166
216
  id = "#{class_name}_#{ref.id || 'new'}"
217
+ if prefix
218
+ class_name = "#{ prefix }_#{ class_name}"
219
+ id = "#{ prefix }_#{ id }"
220
+ end
167
221
 
168
222
  {'id' => id, 'class' => class_name}
169
223
  end
@@ -3,7 +3,6 @@ require 'haml/buffer'
3
3
  require 'haml/precompiler'
4
4
  require 'haml/filters'
5
5
  require 'haml/error'
6
- require 'haml/util'
7
6
 
8
7
  module Haml
9
8
  # This is the class where all the parsing and processing of the Haml
@@ -24,57 +23,75 @@ module Haml
24
23
  # to produce the Haml document.
25
24
  attr :precompiled, true
26
25
 
26
+ # True if the format is XHTML
27
+ def xhtml?
28
+ not html?
29
+ end
30
+
31
+ # True if the format is any flavor of HTML
32
+ def html?
33
+ html4? or html5?
34
+ end
35
+
36
+ # True if the format is HTML4
37
+ def html4?
38
+ @options[:format] == :html4
39
+ end
40
+
41
+ # True if the format is HTML5
42
+ def html5?
43
+ @options[:format] == :html5
44
+ end
45
+
27
46
  # Creates a new instace of Haml::Engine that will compile the given
28
47
  # template string when <tt>render</tt> is called.
29
- # See README for available options.
48
+ # See README.rdoc for available options.
30
49
  #
31
50
  #--
32
51
  # When adding options, remember to add information about them
33
- # to README!
52
+ # to README.rdoc!
34
53
  #++
35
54
  #
36
55
  def initialize(template, options = {})
37
56
  @options = {
38
57
  :suppress_eval => false,
39
58
  :attr_wrapper => "'",
40
- :autoclose => ['meta', 'img', 'link', 'br', 'hr', 'input', 'area'],
41
- :filters => {
42
- 'sass' => Sass::Engine,
43
- 'plain' => Haml::Filters::Plain,
44
- 'preserve' => Haml::Filters::Preserve,
45
- 'redcloth' => Haml::Filters::RedCloth,
46
- 'textile' => Haml::Filters::Textile,
47
- 'markdown' => Haml::Filters::Markdown },
48
- :filename => '(haml)'
49
- }
50
- @options.rec_merge! options
51
59
 
52
- unless @options[:suppress_eval]
53
- @options[:filters].merge!({
54
- 'erb' => ERB,
55
- 'ruby' => Haml::Filters::Ruby
56
- })
57
- end
58
- @options[:filters].rec_merge! options[:filters] if options[:filters]
60
+ # Don't forget to update the docs in lib/haml.rb if you update these
61
+ :autoclose => %w[meta img link br hr input area param col base],
62
+ :preserve => %w[textarea pre],
59
63
 
60
- if @options[:locals]
61
- warn <<END
62
- DEPRECATION WARNING:
63
- The Haml :locals option is deprecated and will be removed in version 2.0.
64
- Use the locals option for Haml::Engine#render instead.
65
- END
64
+ :filename => '(haml)',
65
+ :line => 1,
66
+ :ugly => false,
67
+ :format => :xhtml,
68
+ :escape_html => false
69
+ }
70
+ @options.merge! options
71
+
72
+ unless [:xhtml, :html4, :html5].include?(@options[:format])
73
+ raise Haml::Error, "Invalid format #{@options[:format].inspect}"
66
74
  end
67
75
 
68
- @template = template.strip #String
76
+ @template = template.rstrip #String
69
77
  @to_close_stack = []
70
78
  @output_tabs = 0
71
79
  @template_tabs = 0
72
80
  @index = 0
73
81
  @flat_spaces = -1
82
+ @newlines = 0
83
+
84
+ if @options[:filters]
85
+ warn <<END
86
+ DEPRECATION WARNING:
87
+ The Haml :filters option is deprecated and will be removed in version 2.1.
88
+ Filters are now automatically registered.
89
+ END
90
+ end
74
91
 
75
92
  precompile
76
93
  rescue Haml::Error
77
- $!.backtrace.unshift "#{@options[:filename]}:#{@index}"
94
+ $!.backtrace.unshift "#{@options[:filename]}:#{@index + $!.line_offset + @options[:line] - 1}" if @index
78
95
  raise
79
96
  end
80
97
 
@@ -84,7 +101,7 @@ END
84
101
  # If it's a Binding or Proc object,
85
102
  # Haml uses it as the second argument to Kernel#eval;
86
103
  # otherwise, Haml just uses its #instance_eval context.
87
- #
104
+ #
88
105
  # Note that Haml modifies the evaluation context
89
106
  # (either the scope object or the "self" object of the scope binding).
90
107
  # It extends Haml::Helpers, and various instance variables are set
@@ -114,8 +131,7 @@ END
114
131
  # but if you're relying on local variables defined in the context of scope,
115
132
  # they won't work.
116
133
  def render(scope = Object.new, locals = {}, &block)
117
- locals = (@options[:locals] || {}).merge(locals)
118
- buffer = Haml::Buffer.new(options_for_buffer)
134
+ buffer = Haml::Buffer.new(scope.instance_variable_get('@haml_buffer'), options_for_buffer)
119
135
 
120
136
  if scope.is_a?(Binding) || scope.is_a?(Proc)
121
137
  scope_object = eval("self", scope)
@@ -129,17 +145,14 @@ END
129
145
 
130
146
  scope_object.instance_eval do
131
147
  extend Haml::Helpers
132
- @haml_stack ||= Array.new
133
- @haml_stack.push(buffer)
134
- @haml_is_haml = true
148
+ @haml_buffer = buffer
135
149
  end
136
150
 
137
- eval(@precompiled, scope, @options[:filename], 0)
151
+ eval(@precompiled, scope, @options[:filename], @options[:line])
138
152
 
139
153
  # Get rid of the current buffer
140
154
  scope_object.instance_eval do
141
- @haml_stack.pop
142
- @haml_is_haml = false
155
+ @haml_buffer = buffer.upper
143
156
  end
144
157
 
145
158
  buffer.buffer
@@ -176,7 +189,7 @@ END
176
189
  end
177
190
 
178
191
  eval("Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {};" +
179
- precompiled_with_ambles(local_names) + "}\n", scope, @options[:filename], 0)
192
+ precompiled_with_ambles(local_names) + "}\n", scope, @options[:filename], @options[:line])
180
193
  end
181
194
 
182
195
  # Defines a method on +object+
@@ -193,7 +206,7 @@ END
193
206
  #
194
207
  # Haml::Engine.new(".upcased= upcase").def_method(String, :upcased_div)
195
208
  # "foobar".upcased_div #=> "<div class='upcased'>FOOBAR</div>\n"
196
- #
209
+ #
197
210
  # The first argument of the defined method is a hash of local variable names to values.
198
211
  # However, due to an unfortunate Ruby quirk,
199
212
  # the local variables which can be assigned must be pre-declared.
@@ -209,7 +222,7 @@ END
209
222
  # obj = Object.new
210
223
  # Haml::Engine.new("%p= foo").def_method(obj, :render)
211
224
  # obj.render(:foo => "Hello!") #=> NameError: undefined local variable or method `foo'
212
- #
225
+ #
213
226
  # Note that Haml modifies the evaluation context
214
227
  # (either the scope object or the "self" object of the scope binding).
215
228
  # It extends Haml::Helpers, and various instance variables are set
@@ -218,7 +231,7 @@ END
218
231
  method = object.is_a?(Module) ? :module_eval : :instance_eval
219
232
 
220
233
  object.send(method, "def #{name}(_haml_locals = {}); #{precompiled_with_ambles(local_names)}; end",
221
- @options[:filename], 0)
234
+ @options[:filename], @options[:line])
222
235
  end
223
236
 
224
237
  private
@@ -230,9 +243,15 @@ END
230
243
  end
231
244
 
232
245
  # Returns a hash of options that Haml::Buffer cares about.
233
- # This should remain loadable form #inspect.
246
+ # This should remain loadable from #inspect.
234
247
  def options_for_buffer
235
- {:attr_wrapper => @options[:attr_wrapper]}
248
+ {
249
+ :autoclose => @options[:autoclose],
250
+ :preserve => @options[:preserve],
251
+ :attr_wrapper => @options[:attr_wrapper],
252
+ :ugly => @options[:ugly],
253
+ :format => @options[:format]
254
+ }
236
255
  end
237
256
  end
238
257
  end