haml 3.2.0.alpha.13 → 3.2.0.alpha.14

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.

@@ -31,6 +31,9 @@ module Haml
31
31
  # engine when there are more than one.
32
32
  # @option options [String,Array<String>] :alias Any aliases for the filter.
33
33
  # For example, :coffee is also available as :coffeescript.
34
+ # @option options [String] :extend The name of a module to extend when
35
+ # defining the filter. Defaults to "Plain". This allows filters such as
36
+ # Coffee to "inherit" from Javascript, wrapping its output in script tags.
34
37
  # @since 3.2.0
35
38
  def register_tilt_filter(name, options = {})
36
39
  if constants.map(&:to_s).include?(name.to_s)
@@ -38,8 +41,8 @@ module Haml
38
41
  end
39
42
 
40
43
  filter = const_set(name, Module.new)
44
+ filter.extend const_get(options[:extend] || "Plain")
41
45
  filter.extend TiltFilter
42
-
43
46
  filter.extend PrecompiledTiltFilter if options.has_key? :precompiled
44
47
 
45
48
  if options.has_key? :template_class
@@ -310,10 +313,10 @@ END
310
313
  base.options = {}
311
314
  base.instance_eval do
312
315
  include Base
313
- def render(text)
314
- Haml::Util.silence_warnings do
315
- template_class.new(nil, 1, options) {text}.render
316
- end
316
+
317
+ def render_with_options(text, compiler_options)
318
+ text = template_class.new(nil, 1, options) {text}.render
319
+ super(text, compiler_options)
317
320
  end
318
321
  end
319
322
  end
@@ -332,13 +335,13 @@ END
332
335
  end
333
336
 
334
337
  # @!parse module Sass; end
335
- register_tilt_filter "Sass"
338
+ register_tilt_filter "Sass", :extend => "Css"
336
339
 
337
340
  # @!parse module Scss; end
338
- register_tilt_filter "Scss"
341
+ register_tilt_filter "Scss", :extend => "Css"
339
342
 
340
343
  # @!parse module Less; end
341
- register_tilt_filter "Less"
344
+ register_tilt_filter "Less", :extend => "Css"
342
345
 
343
346
  # @!parse module Markdown; end
344
347
  register_tilt_filter "Markdown"
@@ -347,7 +350,7 @@ END
347
350
  register_tilt_filter "Erb", :precompiled => true
348
351
 
349
352
  # @!parse module Coffee; end
350
- register_tilt_filter "Coffee", :alias => "coffeescript"
353
+ register_tilt_filter "Coffee", :alias => "coffeescript", :extend => "Javascript"
351
354
 
352
355
  # Parses the filtered text with ERB.
353
356
  # Not available if the {file:REFERENCE.md#suppress_eval-option
@@ -70,7 +70,7 @@ MESSAGE
70
70
  # context.haml_tag :p, "Stuff"
71
71
  #
72
72
  def init_haml_helpers
73
- @haml_buffer = Haml::Buffer.new(haml_buffer, Haml::Engine.new('').send(:options_for_buffer))
73
+ @haml_buffer = Haml::Buffer.new(haml_buffer, Options.new.for_buffer)
74
74
  nil
75
75
  end
76
76
 
@@ -190,6 +190,7 @@ MESSAGE
190
190
  # @yield [item] A block which contains Haml code that goes within list items
191
191
  # @yieldparam item An element of `enum`
192
192
  def list_of(enum, opts={}, &block)
193
+ opts_attributes = opts.empty? ? "" : " ".<<(opts.map{|k,v| "#{k}='#{v}'" }.join(" "))
193
194
  to_return = enum.collect do |i|
194
195
  result = capture_haml(i, &block)
195
196
 
@@ -200,7 +201,7 @@ MESSAGE
200
201
  result = result.strip
201
202
  end
202
203
 
203
- %Q!<li#{opts.empty? ? "" : " ".<<(opts.map{|k,v| "#{k}='#{v}'" }.join(" "))}>#{result}</li>!
204
+ %Q!<li#{opts_attributes}>#{result}</li>!
204
205
  end
205
206
  to_return.join("\n")
206
207
  end
@@ -0,0 +1,252 @@
1
+ module Haml
2
+ # This class encapsulates all of the configuration options that Haml
3
+ # understands. Please see the {file:REFERENCE.md#options Haml Reference} to
4
+ # learn how to set the options.
5
+ class Options
6
+
7
+ @defaults = {
8
+ :attr_wrapper => "'",
9
+ :autoclose => %w(meta img link br hr input area param col base),
10
+ :encoding => "UTF-8",
11
+ :escape_attrs => true,
12
+ :escape_html => false,
13
+ :filename => '(haml)',
14
+ :format => :xhtml,
15
+ :hyphenate_data_attrs => true,
16
+ :line => 1,
17
+ :mime_type => 'text/html',
18
+ :preserve => %w(textarea pre code),
19
+ :remove_whitespace => false,
20
+ :suppress_eval => false,
21
+ :ugly => false
22
+ }
23
+
24
+ @valid_formats = [:xhtml, :html4, :html5]
25
+
26
+ @buffer_option_keys = [:autoclose, :preserve, :attr_wrapper, :ugly, :format,
27
+ :encoding, :escape_html, :escape_attrs, :hyphenate_data_attrs]
28
+
29
+ # The default option values.
30
+ # @return Hash
31
+ def self.defaults
32
+ @defaults
33
+ end
34
+
35
+ # An array of valid values for the `:format` option.
36
+ # @return Array
37
+ def self.valid_formats
38
+ @valid_formats
39
+ end
40
+
41
+ # An array of keys that will be used to provide a hash of options to
42
+ # {Haml::Buffer}.
43
+ # @return Hash
44
+ def self.buffer_option_keys
45
+ @buffer_option_keys
46
+ end
47
+
48
+ # The character that should wrap element attributes. This defaults to `'`
49
+ # (an apostrophe). Characters of this type within the attributes will be
50
+ # escaped (e.g. by replacing them with `&apos;`) if the character is an
51
+ # apostrophe or a quotation mark.
52
+ attr_reader :attr_wrapper
53
+
54
+ # A list of tag names that should be automatically self-closed if they have
55
+ # no content. This can also contain regular expressions that match tag names
56
+ # (or any object which responds to `#===`). Defaults to `['meta', 'img',
57
+ # 'link', 'br', 'hr', 'input', 'area', 'param', 'col', 'base']`.
58
+ attr_accessor :autoclose
59
+
60
+ # The encoding to use for the HTML output.
61
+ # Only available on Ruby 1.9 or higher.
62
+ # This can be a string or an `Encoding` Object. Note that Haml **does not**
63
+ # automatically re-encode Ruby values; any strings coming from outside the
64
+ # application should be converted before being passed into the Haml
65
+ # template. Defaults to `Encoding.default_internal`; if that's not set,
66
+ # defaults to the encoding of the Haml template; if that's `US-ASCII`,
67
+ # defaults to `"UTF-8"`.
68
+ attr_reader :encoding
69
+
70
+ # Sets whether or not to escape HTML-sensitive characters in attributes. If
71
+ # this is true, all HTML-sensitive characters in attributes are escaped. If
72
+ # it's set to false, no HTML-sensitive characters in attributes are escaped.
73
+ # If it's set to `:once`, existing HTML escape sequences are preserved, but
74
+ # other HTML-sensitive characters are escaped.
75
+ #
76
+ # Defaults to `true`.
77
+ attr_accessor :escape_attrs
78
+
79
+ # Sets whether or not to escape HTML-sensitive characters in script. If this
80
+ # is true, `=` behaves like {file:REFERENCE.md#escaping_html `&=`};
81
+ # otherwise, it behaves like {file:REFERENCE.md#unescaping_html `!=`}. Note
82
+ # that if this is set, `!=` should be used for yielding to subtemplates and
83
+ # rendering partials. See also {file:REFERENCE.md#escaping_html Escaping HTML} and
84
+ # {file:REFERENCE.md#unescaping_html Unescaping HTML}.
85
+ #
86
+ # Defaults to false.
87
+ attr_accessor :escape_html
88
+
89
+ # The name of the Haml file being parsed.
90
+ # This is only used as information when exceptions are raised. This is
91
+ # automatically assigned when working through ActionView, so it's really
92
+ # only useful for the user to assign when dealing with Haml programatically.
93
+ attr_accessor :filename
94
+
95
+ # If set to `true`, Haml will convert underscores to hyphens in all
96
+ # {file:REFERENCE.md#html5_custom_data_attributes Custom Data Attributes} As
97
+ # of Haml 3.2, this defaults to `true`.
98
+ attr_accessor :hyphenate_data_attrs
99
+
100
+ # The line offset of the Haml template being parsed. This is useful for
101
+ # inline templates, similar to the last argument to `Kernel#eval`.
102
+ attr_accessor :line
103
+
104
+ # Determines the output format. Normally the default is `:xhtml`, although
105
+ # under Rails 3 it's `:html5`, since that's the Rails 3's default format.
106
+ # Other options are `:html4` and `:html5`, which are identical to `:xhtml`
107
+ # except there are no self-closing tags, the XML prolog is ignored and
108
+ # correct DOCTYPEs are generated.
109
+ #
110
+ # If the mime_type of the template being rendered is `text/xml` then a
111
+ # format of `:xhtml` will be used even if the global output format is set to
112
+ # `:html4` or `:html5`.
113
+ attr :format
114
+
115
+ # The mime type that the rendered document will be served with. If this is
116
+ # set to `text/xml` then the format will be overridden to `:xhtml` even if
117
+ # it has set to `:html4` or `:html5`.
118
+ attr_accessor :mime_type
119
+
120
+ # A list of tag names that should automatically have their newlines
121
+ # preserved using the {Haml::Helpers#preserve} helper. This means that any
122
+ # content given on the same line as the tag will be preserved. For example,
123
+ # `%textarea= "Foo\nBar"` compiles to `<textarea>Foo&#x000A;Bar</textarea>`.
124
+ # Defaults to `['textarea', 'pre']`. See also
125
+ # {file:REFERENCE.md#whitespace_preservation Whitespace Preservation}.
126
+ attr_accessor :preserve
127
+
128
+ # If set to `true`, all tags are treated as if both
129
+ # {file:REFERENCE.md#whitespace_removal__and_ whitespace removal} options
130
+ # were present. Use with caution as this may cause whitespace-related
131
+ # formatting errors.
132
+ #
133
+ # Defaults to `false`.
134
+ attr_reader :remove_whitespace
135
+
136
+ # Whether or not attribute hashes and Ruby scripts designated by `=` or `~`
137
+ # should be evaluated. If this is `true`, said scripts are rendered as empty
138
+ # strings.
139
+ #
140
+ # Defaults to `false`.
141
+ attr_accessor :suppress_eval
142
+
143
+ # If set to `true`, Haml makes no attempt to properly indent or format the
144
+ # HTML output. This significantly improves rendering performance but makes
145
+ # viewing the source unpleasant.
146
+ #
147
+ # Defaults to `true` in Rails production mode, and `false` everywhere else.
148
+ attr_accessor :ugly
149
+
150
+ def initialize(values = {}, &block)
151
+ defaults.each {|k, v| instance_variable_set :"@#{k}", v}
152
+ values.reject {|k, v| !defaults.has_key?(k) || v.nil?}.each {|k, v| send("#{k}=", v)}
153
+ yield if block_given?
154
+ end
155
+
156
+ # Retrieve an option value.
157
+ # @param key The value to retrieve.
158
+ def [](key)
159
+ send key
160
+ end
161
+
162
+ # Set an option value.
163
+ # @param key The key to set.
164
+ # @param value The value to set for the key.
165
+ def []=(key, value)
166
+ send "#{key}=", value
167
+ end
168
+
169
+ [:escape_attrs, :hyphenate_data_attrs, :remove_whitespace, :suppress_eval,
170
+ :ugly].each do |method|
171
+ class_eval(<<-END)
172
+ def #{method}?
173
+ !! @#{method}
174
+ end
175
+ END
176
+ end
177
+
178
+ # @return [Boolean] Whether or not the format is XHTML.
179
+ def xhtml?
180
+ not html?
181
+ end
182
+
183
+ # @return [Boolean] Whether or not the format is any flavor of HTML.
184
+ def html?
185
+ html4? or html5?
186
+ end
187
+
188
+ # @return [Boolean] Whether or not the format is HTML4.
189
+ def html4?
190
+ format == :html4
191
+ end
192
+
193
+ # @return [Boolean] Whether or not the format is HTML5.
194
+ def html5?
195
+ format == :html5
196
+ end
197
+
198
+ def attr_wrapper=(value)
199
+ @attr_wrapper = value || self.class.defaults[:attr_wrapper]
200
+ end
201
+
202
+ # Undef :format to suppress warning. It's defined above with the `:attr`
203
+ # macro in order to make it appear in Yard's list of instance attributes.
204
+ undef :format
205
+ def format
206
+ mime_type == "text/xml" ? "xhtml" : @format
207
+ end
208
+
209
+ def format=(value)
210
+ unless self.class.valid_formats.include?(value)
211
+ raise Haml::Error, "Invalid output format #{value.inspect}"
212
+ end
213
+ @format = value
214
+ end
215
+
216
+ def remove_whitespace=(value)
217
+ @ugly = true if value
218
+ @remove_whitespace = value
219
+ end
220
+
221
+ if RUBY_VERSION < "1.9"
222
+ attr_writer :encoding
223
+ else
224
+ def encoding=(value)
225
+ return unless value
226
+ @encoding = value.is_a?(Encoding) ? value.name : value.to_s
227
+ @encoding = "UTF-8" if @encoding.upcase == "US-ASCII"
228
+ end
229
+ end
230
+
231
+ # Returns a subset of options: those that {Haml::Buffer} cares about.
232
+ # All of the values here are such that when `#inspect` is called on the hash,
233
+ # it can be `Kernel#eval`ed to get the same result back.
234
+ #
235
+ # See {file:REFERENCE.md#options the Haml options documentation}.
236
+ #
237
+ # @return [{Symbol => Object}] The options hash
238
+ def for_buffer
239
+ self.class.buffer_option_keys.inject({}) do |hash, key|
240
+ hash[key] = send(key)
241
+ hash
242
+ end
243
+ end
244
+
245
+ private
246
+
247
+ def defaults
248
+ self.class.defaults
249
+ end
250
+
251
+ end
252
+ end
@@ -1,7 +1,7 @@
1
1
  require 'strscan'
2
2
 
3
3
  module Haml
4
- module Parser
4
+ class Parser
5
5
  include Haml::Util
6
6
 
7
7
  # Designates an XHTML/XML element.
@@ -75,6 +75,58 @@ module Haml
75
75
  # The Regex that matches a literal string or symbol value
76
76
  LITERAL_VALUE_REGEX = /:(\w*)|(["'])((?![\\#]|\2).|\\.)*\2/
77
77
 
78
+ def initialize(template, options)
79
+ # :eod is a special end-of-document marker
80
+ @template = (template.rstrip).split(/\r\n|\r|\n/) + [:eod, :eod]
81
+ @options = options
82
+ @flat = false
83
+ @index = 0
84
+ @template_index = 0
85
+ @template_tabs = 0
86
+ end
87
+
88
+ def parse
89
+ @root = @parent = ParseNode.new(:root)
90
+ @haml_comment = false
91
+ @indentation = nil
92
+ @line = next_line
93
+
94
+ raise SyntaxError.new("Indenting at the beginning of the document is illegal.", @line.index) if @line.tabs != 0
95
+
96
+ while next_line
97
+ process_indent(@line) unless @line.text.empty?
98
+
99
+ if flat?
100
+ text = @line.full.dup
101
+ text = "" unless text.gsub!(/^#{@flat_spaces}/, '')
102
+ @filter_buffer << "#{text}\n"
103
+ @line = @next_line
104
+ next
105
+ end
106
+
107
+ @tab_up = nil
108
+ process_line(@line.text, @line.index) unless @line.text.empty? || @haml_comment
109
+ if @parent.type != :haml_comment && (block_opened? || @tab_up)
110
+ @template_tabs += 1
111
+ @parent = @parent.children.last
112
+ end
113
+
114
+ if !@haml_comment && !flat? && @next_line.tabs - @line.tabs > 1
115
+ raise SyntaxError.new("The line was indented #{@next_line.tabs - @line.tabs} levels deeper than the previous line.", @next_line.index)
116
+ end
117
+
118
+ @line = @next_line
119
+ end
120
+
121
+ # Close all the open tags
122
+ close until @parent.type == :root
123
+ @root
124
+ rescue Haml::Error => e
125
+ e.backtrace.unshift "#{@options[:filename]}:#{(e.line ? e.line + 1 : @index) + @options[:line] - 1}"
126
+ raise
127
+ end
128
+
129
+
78
130
  private
79
131
 
80
132
  # @private
@@ -124,44 +176,6 @@ END
124
176
  end
125
177
  end
126
178
 
127
- def parse
128
- @root = @parent = ParseNode.new(:root)
129
- @haml_comment = false
130
- @indentation = nil
131
- @line = next_line
132
-
133
- raise SyntaxError.new("Indenting at the beginning of the document is illegal.", @line.index) if @line.tabs != 0
134
-
135
- while next_line
136
- process_indent(@line) unless @line.text.empty?
137
-
138
- if flat?
139
- text = @line.full.dup
140
- text = "" unless text.gsub!(/^#{@flat_spaces}/, '')
141
- @filter_buffer << "#{text}\n"
142
- @line = @next_line
143
- next
144
- end
145
-
146
- @tab_up = nil
147
- process_line(@line.text, @line.index) unless @line.text.empty? || @haml_comment
148
- if @parent.type != :haml_comment && (block_opened? || @tab_up)
149
- @template_tabs += 1
150
- @parent = @parent.children.last
151
- end
152
-
153
- if !@haml_comment && !flat? && @next_line.tabs - @line.tabs > 1
154
- raise SyntaxError.new("The line was indented #{@next_line.tabs - @line.tabs} levels deeper than the previous line.", @next_line.index)
155
- end
156
-
157
- @line = @next_line
158
- end
159
-
160
- # Close all the open tags
161
- close until @parent.type == :root
162
- @root
163
- end
164
-
165
179
  # Processes and deals with lowering indentation.
166
180
  def process_indent(line)
167
181
  return unless line.tabs <= @template_tabs && @template_tabs > 0
@@ -491,6 +505,11 @@ END
491
505
  nuke_inner_whitespace = nuke_whitespace.include? '<'
492
506
  end
493
507
 
508
+ if @options[:remove_whitespace]
509
+ nuke_outer_whitespace = true
510
+ nuke_inner_whitespace = true
511
+ end
512
+
494
513
  value = value.to_s.strip
495
514
  [tag_name, attributes, attributes_hashes, object_ref, nuke_outer_whitespace,
496
515
  nuke_inner_whitespace, action, value, last_line || @index]
@@ -675,26 +694,6 @@ END
675
694
  !((text[-3..-2] =~ /\W\?/) || text[-3..-2] == "?\\")
676
695
  end
677
696
 
678
- def contains_interpolation?(str)
679
- str.include?('#{')
680
- end
681
-
682
- def unescape_interpolation(str, escape_html = nil)
683
- res = ''
684
- rest = Haml::Util.handle_interpolation str.dump do |scan|
685
- escapes = (scan[2].size - 1) / 2
686
- res << scan.matched[0...-3 - escapes]
687
- if escapes % 2 == 1
688
- res << '#{'
689
- else
690
- content = eval('"' + balance(scan, ?{, ?}, 1)[0][0...-1] + '"')
691
- content = "Haml::Helpers.html_escape((#{content}))" if escape_html
692
- res << '#{' + content + "}"# Use eval to get rid of string escapes
693
- end
694
- end
695
- res + rest
696
- end
697
-
698
697
  def balance(*args)
699
698
  res = Haml::Util.balance(*args)
700
699
  return res if res