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.
- data/CHANGELOG.md +17 -1
- data/REFERENCE.md +229 -385
- data/Rakefile +1 -1
- data/lib/haml/buffer.rb +1 -1
- data/lib/haml/compiler.rb +63 -26
- data/lib/haml/engine.rb +27 -118
- data/lib/haml/exec.rb +0 -77
- data/lib/haml/filters.rb +12 -9
- data/lib/haml/helpers.rb +3 -2
- data/lib/haml/options.rb +252 -0
- data/lib/haml/parser.rb +58 -59
- data/lib/haml/template.rb +3 -3
- data/lib/haml/template/plugin.rb +1 -1
- data/lib/haml/util.rb +37 -21
- data/lib/haml/version.rb +1 -1
- data/test/engine_test.rb +20 -2
- data/test/filters_test.rb +2 -2
- data/test/gemfiles/Gemfile.rails-3.0.x.lock +101 -0
- data/test/gemfiles/Gemfile.rails-3.1.x.lock +111 -0
- data/test/gemfiles/Gemfile.rails-3.2.x.lock +109 -0
- data/test/haml-spec/README.md +4 -4
- data/test/haml-spec/ruby_haml_test.rb +2 -2
- data/test/helper_test.rb +1 -1
- data/test/template_test.rb +72 -104
- data/test/test_helper.rb +2 -8
- data/test/util_test.rb +0 -4
- metadata +141 -153
- data/bin/html2haml +0 -7
- data/lib/haml/html.rb +0 -425
- data/lib/haml/html/erb.rb +0 -141
- data/test/html2haml/erb_tests.rb +0 -440
- data/test/html2haml_test.rb +0 -342
data/bin/html2haml
DELETED
data/lib/haml/html.rb
DELETED
@@ -1,425 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/../haml'
|
2
|
-
|
3
|
-
require 'haml/engine'
|
4
|
-
require 'rubygems'
|
5
|
-
require 'cgi'
|
6
|
-
require 'hpricot'
|
7
|
-
|
8
|
-
# Haml monkeypatches various Hpricot classes
|
9
|
-
# to add methods for conversion to Haml.
|
10
|
-
# @private
|
11
|
-
module Hpricot
|
12
|
-
# @see Hpricot
|
13
|
-
module Node
|
14
|
-
# Whether this node has already been converted to Haml.
|
15
|
-
# Only used for text nodes and elements.
|
16
|
-
#
|
17
|
-
# @return [Boolean]
|
18
|
-
attr_accessor :converted_to_haml
|
19
|
-
|
20
|
-
# Returns the Haml representation of the given node.
|
21
|
-
#
|
22
|
-
# @param tabs [Fixnum] The indentation level of the resulting Haml.
|
23
|
-
# @option options (see Haml::HTML#initialize)
|
24
|
-
def to_haml(tabs, options)
|
25
|
-
return "" if converted_to_haml || to_s.strip.empty?
|
26
|
-
text = uninterp(self.to_s)
|
27
|
-
node = next_node
|
28
|
-
while node.is_a?(::Hpricot::Elem) && node.name == "haml:loud"
|
29
|
-
node.converted_to_haml = true
|
30
|
-
text << '#{' <<
|
31
|
-
CGI.unescapeHTML(node.inner_text).gsub(/\n\s*/, ' ').strip << '}'
|
32
|
-
|
33
|
-
if node.next_node.is_a?(::Hpricot::Text)
|
34
|
-
node = node.next_node
|
35
|
-
text << uninterp(node.to_s)
|
36
|
-
node.converted_to_haml = true
|
37
|
-
end
|
38
|
-
|
39
|
-
node = node.next_node
|
40
|
-
end
|
41
|
-
return parse_text_with_interpolation(text, tabs)
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def erb_to_interpolation(text, options)
|
47
|
-
return text unless options[:erb]
|
48
|
-
text = CGI.escapeHTML(uninterp(text))
|
49
|
-
%w[<haml:loud> </haml:loud>].each {|str| text.gsub!(CGI.escapeHTML(str), str)}
|
50
|
-
::Hpricot::XML(text).children.inject("") do |str, elem|
|
51
|
-
if elem.is_a?(::Hpricot::Text)
|
52
|
-
str + CGI.unescapeHTML(elem.to_s)
|
53
|
-
else # <haml:loud> element
|
54
|
-
str + '#{' + CGI.unescapeHTML(elem.innerText.strip) + '}'
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def tabulate(tabs)
|
60
|
-
' ' * tabs
|
61
|
-
end
|
62
|
-
|
63
|
-
def uninterp(text)
|
64
|
-
text.gsub('#{', '\#{') #'
|
65
|
-
end
|
66
|
-
|
67
|
-
def attr_hash
|
68
|
-
attributes.to_hash
|
69
|
-
end
|
70
|
-
|
71
|
-
def parse_text(text, tabs)
|
72
|
-
parse_text_with_interpolation(uninterp(text), tabs)
|
73
|
-
end
|
74
|
-
|
75
|
-
def parse_text_with_interpolation(text, tabs)
|
76
|
-
text.strip!
|
77
|
-
return "" if text.empty?
|
78
|
-
|
79
|
-
text.split("\n").map do |line|
|
80
|
-
line.strip!
|
81
|
-
"#{tabulate(tabs)}#{'\\' if Haml::Engine::SPECIAL_CHARACTERS.include?(line[0])}#{line}\n"
|
82
|
-
end.join
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
# @private
|
88
|
-
HAML_TAGS = %w[haml:block haml:loud haml:silent]
|
89
|
-
|
90
|
-
HAML_TAGS.each do |t|
|
91
|
-
Hpricot::ElementContent[t] = {}
|
92
|
-
Hpricot::ElementContent.keys.each do |key|
|
93
|
-
Hpricot::ElementContent[t][key.hash] = true
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
Hpricot::ElementContent.keys.each do |k|
|
98
|
-
HAML_TAGS.each do |el|
|
99
|
-
val = Hpricot::ElementContent[k]
|
100
|
-
val[el.hash] = true if val.is_a?(Hash)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
module Haml
|
105
|
-
# Converts HTML documents into Haml templates.
|
106
|
-
# Depends on [Hpricot](http://github.com/whymirror/hpricot) for HTML parsing.
|
107
|
-
# If ERB conversion is being used, also depends on
|
108
|
-
# [Erubis](http://www.kuwata-lab.com/erubis) to parse the ERB
|
109
|
-
# and [ruby_parser](http://parsetree.rubyforge.org/) to parse the Ruby code.
|
110
|
-
#
|
111
|
-
# Example usage:
|
112
|
-
#
|
113
|
-
# Haml::HTML.new("<a href='http://google.com'>Blat</a>").render
|
114
|
-
# #=> "%a{:href => 'http://google.com'} Blat"
|
115
|
-
class HTML
|
116
|
-
# @param template [String, Hpricot::Node] The HTML template to convert
|
117
|
-
# @option options :erb [Boolean] (false) Whether or not to parse
|
118
|
-
# ERB's `<%= %>` and `<% %>` into Haml's `=` and `-`
|
119
|
-
# @option options :xhtml [Boolean] (false) Whether or not to parse
|
120
|
-
# the HTML strictly as XHTML
|
121
|
-
def initialize(template, options = {})
|
122
|
-
@options = options
|
123
|
-
|
124
|
-
if template.is_a? Hpricot::Node
|
125
|
-
@template = template
|
126
|
-
else
|
127
|
-
if template.is_a? IO
|
128
|
-
template = template.read
|
129
|
-
end
|
130
|
-
|
131
|
-
template = Haml::Util.check_encoding(template) {|msg, line| raise Haml::Error.new(msg, line)}
|
132
|
-
|
133
|
-
if @options[:erb]
|
134
|
-
require 'haml/html/erb'
|
135
|
-
template = ERB.compile(template)
|
136
|
-
end
|
137
|
-
|
138
|
-
method = @options[:xhtml] ? Hpricot.method(:XML) : method(:Hpricot)
|
139
|
-
@template = method.call(template.gsub('&', '&'))
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
# Processes the document and returns the result as a string
|
144
|
-
# containing the Haml template.
|
145
|
-
def render
|
146
|
-
@template.to_haml(0, @options)
|
147
|
-
end
|
148
|
-
alias_method :to_haml, :render
|
149
|
-
|
150
|
-
TEXT_REGEXP = /^(\s*).*$/
|
151
|
-
|
152
|
-
# @see Hpricot
|
153
|
-
# @private
|
154
|
-
class ::Hpricot::Doc
|
155
|
-
# @see Haml::HTML::Node#to_haml
|
156
|
-
def to_haml(tabs, options)
|
157
|
-
(children || []).inject('') {|s, c| s << c.to_haml(0, options)}
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
# @see Hpricot
|
162
|
-
# @private
|
163
|
-
class ::Hpricot::XMLDecl
|
164
|
-
# @see Haml::HTML::Node#to_haml
|
165
|
-
def to_haml(tabs, options)
|
166
|
-
"#{tabulate(tabs)}!!! XML\n"
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
# @see Hpricot
|
171
|
-
# @private
|
172
|
-
class ::Hpricot::CData
|
173
|
-
# @see Haml::HTML::Node#to_haml
|
174
|
-
def to_haml(tabs, options)
|
175
|
-
content = parse_text_with_interpolation(
|
176
|
-
erb_to_interpolation(self.content, options), tabs + 1)
|
177
|
-
"#{tabulate(tabs)}:cdata\n#{content}"
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
# @see Hpricot
|
182
|
-
# @private
|
183
|
-
class ::Hpricot::DocType
|
184
|
-
# @see Haml::HTML::Node#to_haml
|
185
|
-
def to_haml(tabs, options)
|
186
|
-
attrs = public_id.nil? ? ["", "", ""] :
|
187
|
-
public_id.scan(/DTD\s+([^\s]+)\s*([^\s]*)\s*([^\s]*)\s*\/\//)[0]
|
188
|
-
raise Haml::SyntaxError.new("Invalid doctype") if attrs == nil
|
189
|
-
|
190
|
-
type, version, strictness = attrs.map { |a| a.downcase }
|
191
|
-
if type == "html"
|
192
|
-
version = ""
|
193
|
-
strictness = "strict" if strictness == ""
|
194
|
-
end
|
195
|
-
|
196
|
-
if version == "1.0" || version.empty?
|
197
|
-
version = nil
|
198
|
-
end
|
199
|
-
|
200
|
-
if strictness == 'transitional' || strictness.empty?
|
201
|
-
strictness = nil
|
202
|
-
end
|
203
|
-
|
204
|
-
version = " #{version.capitalize}" if version
|
205
|
-
strictness = " #{strictness.capitalize}" if strictness
|
206
|
-
|
207
|
-
"#{tabulate(tabs)}!!!#{version}#{strictness}\n"
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
# @see Hpricot
|
212
|
-
# @private
|
213
|
-
class ::Hpricot::Comment
|
214
|
-
# @see Haml::HTML::Node#to_haml
|
215
|
-
def to_haml(tabs, options)
|
216
|
-
content = self.content
|
217
|
-
if content =~ /\A(\[[^\]]+\])>(.*)<!\[endif\]\z/m
|
218
|
-
condition = $1
|
219
|
-
content = $2
|
220
|
-
end
|
221
|
-
|
222
|
-
if content.include?("\n")
|
223
|
-
"#{tabulate(tabs)}/#{condition}\n#{parse_text(content, tabs + 1)}"
|
224
|
-
else
|
225
|
-
"#{tabulate(tabs)}/#{condition} #{content.strip}\n"
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
# @see Hpricot
|
231
|
-
# @private
|
232
|
-
class ::Hpricot::Elem
|
233
|
-
# @see Haml::HTML::Node#to_haml
|
234
|
-
def to_haml(tabs, options)
|
235
|
-
return "" if converted_to_haml
|
236
|
-
if name == "script" &&
|
237
|
-
(attr_hash['type'].nil? || attr_hash['type'] == "text/javascript") &&
|
238
|
-
(attr_hash.keys - ['type']).empty?
|
239
|
-
return to_haml_filter(:javascript, tabs, options)
|
240
|
-
elsif name == "style" &&
|
241
|
-
(attr_hash['type'].nil? || attr_hash['type'] == "text/css") &&
|
242
|
-
(attr_hash.keys - ['type']).empty?
|
243
|
-
return to_haml_filter(:css, tabs, options)
|
244
|
-
end
|
245
|
-
|
246
|
-
output = tabulate(tabs)
|
247
|
-
if options[:erb] && name[0...5] == 'haml:'
|
248
|
-
case name[5..-1]
|
249
|
-
when "loud"
|
250
|
-
lines = CGI.unescapeHTML(inner_text).split("\n").
|
251
|
-
map {|s| s.rstrip}.reject {|s| s.strip.empty?}
|
252
|
-
lines.first.gsub!(/^[ \t]*/, "= ")
|
253
|
-
|
254
|
-
if lines.size > 1 # Multiline script block
|
255
|
-
# Normalize the indentation so that the last line is the base
|
256
|
-
indent_str = lines.last[/^[ \t]*/]
|
257
|
-
indent_re = /^[ \t]{0,#{indent_str.count(" ") + 8 * indent_str.count("\t")}}/
|
258
|
-
lines.map! {|s| s.gsub!(indent_re, '')}
|
259
|
-
|
260
|
-
# Add an extra " " to make it indented relative to "= "
|
261
|
-
lines[1..-1].each {|s| s.gsub!(/^/, " ")}
|
262
|
-
|
263
|
-
# Add | at the end, properly aligned
|
264
|
-
length = lines.map {|s| s.size}.max + 1
|
265
|
-
lines.map! {|s| "%#{-length}s|" % s}
|
266
|
-
|
267
|
-
if next_sibling && next_sibling.is_a?(Hpricot::Elem) && next_sibling.name == "haml:loud" &&
|
268
|
-
next_sibling.inner_text.split("\n").reject {|s| s.strip.empty?}.size > 1
|
269
|
-
lines << "-#"
|
270
|
-
end
|
271
|
-
end
|
272
|
-
return lines.map {|s| output + s + "\n"}.join
|
273
|
-
when "silent"
|
274
|
-
return CGI.unescapeHTML(inner_text).split("\n").map do |line|
|
275
|
-
next "" if line.strip.empty?
|
276
|
-
"#{output}- #{line.strip}\n"
|
277
|
-
end.join
|
278
|
-
when "block"
|
279
|
-
return render_children("", tabs, options)
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
if self.next && self.next.text? && self.next.content =~ /\A[^\s]/
|
284
|
-
if self.previous.nil? || self.previous.text? &&
|
285
|
-
(self.previous.content =~ /[^\s]\Z/ ||
|
286
|
-
self.previous.content =~ /\A\s*\Z/ && self.previous.previous.nil?)
|
287
|
-
nuke_outer_whitespace = true
|
288
|
-
else
|
289
|
-
output << "= succeed #{self.next.content.slice!(/\A[^\s]+/).dump} do\n"
|
290
|
-
tabs += 1
|
291
|
-
output << tabulate(tabs)
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
output << "%#{name}" unless name == 'div' &&
|
296
|
-
(static_id?(options) ||
|
297
|
-
static_classname?(options) &&
|
298
|
-
attr_hash['class'].split(' ').any?(&method(:haml_css_attr?)))
|
299
|
-
|
300
|
-
if attr_hash
|
301
|
-
if static_id?(options)
|
302
|
-
output << "##{attr_hash['id']}"
|
303
|
-
remove_attribute('id')
|
304
|
-
end
|
305
|
-
if static_classname?(options)
|
306
|
-
leftover = attr_hash['class'].split(' ').reject do |c|
|
307
|
-
next unless haml_css_attr?(c)
|
308
|
-
output << ".#{c}"
|
309
|
-
end
|
310
|
-
remove_attribute('class')
|
311
|
-
set_attribute('class', leftover.join(' ')) unless leftover.empty?
|
312
|
-
end
|
313
|
-
output << haml_attributes(options) if attr_hash.length > 0
|
314
|
-
end
|
315
|
-
|
316
|
-
output << ">" if nuke_outer_whitespace
|
317
|
-
output << "/" if empty? && !etag
|
318
|
-
|
319
|
-
if children && children.size == 1
|
320
|
-
child = children.first
|
321
|
-
if child.is_a?(::Hpricot::Text)
|
322
|
-
if !child.to_s.include?("\n")
|
323
|
-
text = child.to_haml(tabs + 1, options)
|
324
|
-
return output + " " + text.lstrip.gsub(/^\\/, '') unless text.chomp.include?("\n")
|
325
|
-
return output + "\n" + text
|
326
|
-
elsif ["pre", "textarea"].include?(name) ||
|
327
|
-
(name == "code" && parent.is_a?(::Hpricot::Elem) && parent.name == "pre")
|
328
|
-
return output + "\n#{tabulate(tabs + 1)}:preserve\n" +
|
329
|
-
innerText.gsub(/^/, tabulate(tabs + 2))
|
330
|
-
end
|
331
|
-
elsif child.is_a?(::Hpricot::Elem) && child.name == "haml:loud"
|
332
|
-
return output + child.to_haml(tabs + 1, options).lstrip
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
render_children(output + "\n", tabs, options)
|
337
|
-
end
|
338
|
-
|
339
|
-
private
|
340
|
-
|
341
|
-
def render_children(so_far, tabs, options)
|
342
|
-
(self.children || []).inject(so_far) do |output, child|
|
343
|
-
output + child.to_haml(tabs + 1, options)
|
344
|
-
end
|
345
|
-
end
|
346
|
-
|
347
|
-
def dynamic_attributes
|
348
|
-
@dynamic_attributes ||= begin
|
349
|
-
Hash[attr_hash.map do |name, value|
|
350
|
-
next if value.empty?
|
351
|
-
full_match = nil
|
352
|
-
ruby_value = value.gsub(%r{<haml:loud>\s*(.+?)\s*</haml:loud>}) do
|
353
|
-
full_match = $`.empty? && $'.empty?
|
354
|
-
CGI.unescapeHTML(full_match ? $1: "\#{#{$1}}")
|
355
|
-
end
|
356
|
-
next if ruby_value == value
|
357
|
-
[name, full_match ? ruby_value : %("#{ruby_value}")]
|
358
|
-
end]
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
def to_haml_filter(filter, tabs, options)
|
363
|
-
content =
|
364
|
-
if children.first.is_a?(::Hpricot::CData)
|
365
|
-
children.first.content
|
366
|
-
else
|
367
|
-
CGI.unescapeHTML(self.innerText)
|
368
|
-
end
|
369
|
-
|
370
|
-
content = erb_to_interpolation(content, options)
|
371
|
-
content.gsub!(/\A\s*\n(\s*)/, '\1')
|
372
|
-
original_indent = content[/\A(\s*)/, 1]
|
373
|
-
if content.split("\n").all? {|l| l.strip.empty? || l =~ /^#{original_indent}/}
|
374
|
-
content.gsub!(/^#{original_indent}/, tabulate(tabs + 1))
|
375
|
-
end
|
376
|
-
|
377
|
-
"#{tabulate(tabs)}:#{filter}\n#{content}"
|
378
|
-
end
|
379
|
-
|
380
|
-
def static_attribute?(name, options)
|
381
|
-
attr_hash[name] && !dynamic_attribute?(name, options)
|
382
|
-
end
|
383
|
-
|
384
|
-
def dynamic_attribute?(name, options)
|
385
|
-
options[:erb] and dynamic_attributes.key?(name)
|
386
|
-
end
|
387
|
-
|
388
|
-
def static_id?(options)
|
389
|
-
static_attribute?('id', options) && haml_css_attr?(attr_hash['id'])
|
390
|
-
end
|
391
|
-
|
392
|
-
def static_classname?(options)
|
393
|
-
static_attribute?('class', options)
|
394
|
-
end
|
395
|
-
|
396
|
-
def haml_css_attr?(attr)
|
397
|
-
attr =~ /^[-:\w]+$/
|
398
|
-
end
|
399
|
-
|
400
|
-
# Returns a string representation of an attributes hash
|
401
|
-
# that's prettier than that produced by Hash#inspect
|
402
|
-
def haml_attributes(options)
|
403
|
-
attrs = attr_hash.sort.map do |name, value|
|
404
|
-
haml_attribute_pair(name, value, options)
|
405
|
-
end
|
406
|
-
if options[:html_style_attributes]
|
407
|
-
"(#{attrs.join(' ')})"
|
408
|
-
else
|
409
|
-
"{#{attrs.join(', ')}}"
|
410
|
-
end
|
411
|
-
end
|
412
|
-
|
413
|
-
# Returns the string representation of a single attribute key value pair
|
414
|
-
def haml_attribute_pair(name, value, options)
|
415
|
-
value = dynamic_attribute?(name, options) ? dynamic_attributes[name] : value.inspect
|
416
|
-
if options[:html_style_attributes]
|
417
|
-
"#{name}=#{value}"
|
418
|
-
else
|
419
|
-
name = name.index(/\W/) ? name.inspect : ":#{name}"
|
420
|
-
"#{name} => #{value}"
|
421
|
-
end
|
422
|
-
end
|
423
|
-
end
|
424
|
-
end
|
425
|
-
end
|
data/lib/haml/html/erb.rb
DELETED
@@ -1,141 +0,0 @@
|
|
1
|
-
require 'cgi'
|
2
|
-
require 'erubis'
|
3
|
-
require 'ruby_parser'
|
4
|
-
|
5
|
-
module Haml
|
6
|
-
class HTML
|
7
|
-
# A class for converting ERB code into a format that's easier
|
8
|
-
# for the {Haml::HTML} Hpricot-based parser to understand.
|
9
|
-
#
|
10
|
-
# Uses [Erubis](http://www.kuwata-lab.com/erubis)'s extensible parsing powers
|
11
|
-
# to parse the ERB in a reliable way,
|
12
|
-
# and [ruby_parser](http://parsetree.rubyforge.org/)'s Ruby knowledge
|
13
|
-
# to figure out whether a given chunk of Ruby code starts a block or not.
|
14
|
-
#
|
15
|
-
# The ERB tags are converted to HTML tags in the following way.
|
16
|
-
# `<% ... %>` is converted into `<haml:silent> ... </haml:silent>`.
|
17
|
-
# `<%= ... %>` is converted into `<haml:loud> ... </haml:loud>`.
|
18
|
-
# Finally, if either of these opens a Ruby block,
|
19
|
-
# `<haml:block> ... </haml:block>` will wrap the entire contents of the block -
|
20
|
-
# that is, everything that should be indented beneath the previous silent or loud tag.
|
21
|
-
class ERB < Erubis::Basic::Engine
|
22
|
-
# Compiles an ERB template into a HTML document containing `haml:` tags.
|
23
|
-
#
|
24
|
-
# @param template [String] The ERB template
|
25
|
-
# @return [String] The output document
|
26
|
-
# @see Haml::HTML::ERB
|
27
|
-
def self.compile(template)
|
28
|
-
new(template).src
|
29
|
-
end
|
30
|
-
|
31
|
-
# `html2haml` doesn't support HTML-escaped expressions.
|
32
|
-
def escaped_expr(code)
|
33
|
-
raise Haml::Error.new("html2haml doesn't support escaped expressions.")
|
34
|
-
end
|
35
|
-
|
36
|
-
# The ERB-to-Hamlized-HTML conversion has no preamble.
|
37
|
-
def add_preamble(src); end
|
38
|
-
|
39
|
-
# The ERB-to-Hamlized-HTML conversion has no postamble.
|
40
|
-
def add_postamble(src); end
|
41
|
-
|
42
|
-
# Concatenates the text onto the source buffer.
|
43
|
-
#
|
44
|
-
# @param src [String] The source buffer
|
45
|
-
# @param text [String] The raw text to add to the buffer
|
46
|
-
def add_text(src, text)
|
47
|
-
src << text
|
48
|
-
end
|
49
|
-
|
50
|
-
# Concatenates a silent Ruby statement onto the source buffer.
|
51
|
-
# This uses the `<haml:silent>` tag,
|
52
|
-
# and may close and/or open a Ruby block with the `<haml:block>` tag.
|
53
|
-
#
|
54
|
-
# In particular, a block is closed if this statement is some form of `end`,
|
55
|
-
# opened if it's a block opener like `do`, `if`, or `begin`,
|
56
|
-
# and both closed and opened if it's a mid-block keyword
|
57
|
-
# like `else` or `when`.
|
58
|
-
#
|
59
|
-
# @param src [String] The source buffer
|
60
|
-
# @param code [String] The Ruby statement to add to the buffer
|
61
|
-
def add_stmt(src, code)
|
62
|
-
src << '</haml:block>' if block_closer?(code) || mid_block?(code)
|
63
|
-
src << '<haml:silent>' << h(code) << '</haml:silent>' unless code.strip == "end"
|
64
|
-
src << '<haml:block>' if block_opener?(code) || mid_block?(code)
|
65
|
-
end
|
66
|
-
|
67
|
-
# Concatenates a Ruby expression that's printed to the document
|
68
|
-
# onto the source buffer.
|
69
|
-
# This uses the `<haml:silent>` tag,
|
70
|
-
# and may open a Ruby block with the `<haml:block>` tag.
|
71
|
-
# An expression never closes a block.
|
72
|
-
#
|
73
|
-
# @param src [String] The source buffer
|
74
|
-
# @param code [String] The Ruby expression to add to the buffer
|
75
|
-
def add_expr_literal(src, code)
|
76
|
-
src << '<haml:loud>' << h(code) << '</haml:loud>'
|
77
|
-
src << '<haml:block>' if block_opener?(code)
|
78
|
-
end
|
79
|
-
|
80
|
-
# `html2haml` doesn't support debugging expressions.
|
81
|
-
def add_expr_debug(src, code)
|
82
|
-
raise Haml::Error.new("html2haml doesn't support debugging expressions.")
|
83
|
-
end
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
# HTML-escaped some text (in practice, always Ruby code).
|
88
|
-
# A utility method.
|
89
|
-
#
|
90
|
-
# @param text [String] The text to escape
|
91
|
-
# @return [String] The escaped text
|
92
|
-
def h(text)
|
93
|
-
CGI.escapeHTML(text)
|
94
|
-
end
|
95
|
-
|
96
|
-
# Returns whether the code is valid Ruby code on its own.
|
97
|
-
#
|
98
|
-
# @param code [String] Ruby code to check
|
99
|
-
# @return [Boolean]
|
100
|
-
def valid_ruby?(code)
|
101
|
-
RubyParser.new.parse(code)
|
102
|
-
rescue Racc::ParseError
|
103
|
-
false
|
104
|
-
end
|
105
|
-
|
106
|
-
# Checks if a string of Ruby code opens a block.
|
107
|
-
# This could either be something like `foo do |a|`
|
108
|
-
# or a keyword that requires a matching `end`
|
109
|
-
# like `if`, `begin`, or `case`.
|
110
|
-
#
|
111
|
-
# @param code [String] Ruby code to check
|
112
|
-
# @return [Boolean]
|
113
|
-
def block_opener?(code)
|
114
|
-
valid_ruby?(code + "\nend") ||
|
115
|
-
valid_ruby?(code + "\nwhen foo\nend")
|
116
|
-
end
|
117
|
-
|
118
|
-
# Checks if a string of Ruby code closes a block.
|
119
|
-
# This is always `end` followed optionally by some method calls.
|
120
|
-
#
|
121
|
-
# @param code [String] Ruby code to check
|
122
|
-
# @return [Boolean]
|
123
|
-
def block_closer?(code)
|
124
|
-
valid_ruby?("begin\n" + code)
|
125
|
-
end
|
126
|
-
|
127
|
-
# Checks if a string of Ruby code comes in the middle of a block.
|
128
|
-
# This could be a keyword like `else`, `rescue`, or `when`,
|
129
|
-
# or even `end` with a method call that takes a block.
|
130
|
-
#
|
131
|
-
# @param code [String] Ruby code to check
|
132
|
-
# @return [Boolean]
|
133
|
-
def mid_block?(code)
|
134
|
-
return if valid_ruby?(code)
|
135
|
-
valid_ruby?("if foo\n#{code}\nend") || # else, elsif
|
136
|
-
valid_ruby?("begin\n#{code}\nend") || # rescue, ensure
|
137
|
-
valid_ruby?("case foo\n#{code}\nend") # when
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|