html2haml 1.0.0.beta.1

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.
@@ -0,0 +1,153 @@
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
+ # Returns whether the code has any content
107
+ # This is used to test whether lines have been removed by erubis, such as comments
108
+ #
109
+ # @param code [String] Ruby code to check
110
+ # @return [Boolean]
111
+ def has_code?(code)
112
+ code != "\n"
113
+ end
114
+
115
+ # Checks if a string of Ruby code opens a block.
116
+ # This could either be something like `foo do |a|`
117
+ # or a keyword that requires a matching `end`
118
+ # like `if`, `begin`, or `case`.
119
+ #
120
+ # @param code [String] Ruby code to check
121
+ # @return [Boolean]
122
+ def block_opener?(code)
123
+ return unless has_code?(code)
124
+ valid_ruby?(code + "\nend") ||
125
+ valid_ruby?(code + "\nwhen foo\nend")
126
+ end
127
+
128
+ # Checks if a string of Ruby code closes a block.
129
+ # This is always `end` followed optionally by some method calls.
130
+ #
131
+ # @param code [String] Ruby code to check
132
+ # @return [Boolean]
133
+ def block_closer?(code)
134
+ return unless has_code?(code)
135
+ valid_ruby?("begin\n" + code)
136
+ end
137
+
138
+ # Checks if a string of Ruby code comes in the middle of a block.
139
+ # This could be a keyword like `else`, `rescue`, or `when`,
140
+ # or even `end` with a method call that takes a block.
141
+ #
142
+ # @param code [String] Ruby code to check
143
+ # @return [Boolean]
144
+ def mid_block?(code)
145
+ return unless has_code?(code)
146
+ return if valid_ruby?(code)
147
+ valid_ruby?("if foo\n#{code}\nend") || # else, elsif
148
+ valid_ruby?("begin\n#{code}\nend") || # rescue, ensure
149
+ valid_ruby?("case foo\n#{code}\nend") # when
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,3 @@
1
+ module Html2haml
2
+ VERSION = "1.0.0.beta.1"
3
+ end
@@ -0,0 +1,477 @@
1
+ require 'test_helper'
2
+
3
+ class ErbTest < MiniTest::Unit::TestCase
4
+ def test_erb
5
+ assert_equal '- foo = bar', render_erb('<% foo = bar %>')
6
+ assert_equal '- foo = bar', render_erb('<% foo = bar -%>')
7
+ assert_equal '= h @item.title', render_erb('<%=h @item.title %>')
8
+ assert_equal '= h @item.title', render_erb('<%=h @item.title -%>')
9
+ end
10
+
11
+ def test_inline_erb
12
+ assert_equal("%p= foo", render_erb("<p><%= foo %></p>"))
13
+ end
14
+
15
+ def test_non_inline_erb
16
+ assert_equal(<<HAML.rstrip, render_erb(<<HTML))
17
+ %p
18
+ = foo
19
+ HAML
20
+ <p>
21
+ <%= foo %>
22
+ </p>
23
+ HTML
24
+ assert_equal(<<HAML.rstrip, render_erb(<<HTML))
25
+ %p
26
+ = foo
27
+ HAML
28
+ <p>
29
+ <%= foo %></p>
30
+ HTML
31
+ assert_equal(<<HAML.rstrip, render_erb(<<HTML))
32
+ %p
33
+ = foo
34
+ HAML
35
+ <p><%= foo %>
36
+ </p>
37
+ HTML
38
+ end
39
+
40
+ def test_erb_in_cdata
41
+ assert_equal(<<HAML.rstrip, render_erb(<<HTML))
42
+ :cdata
43
+ Foo \#{bar} baz
44
+ HAML
45
+ <![CDATA[Foo <%= bar %> baz]]>
46
+ HTML
47
+ end
48
+
49
+ def test_erb_in_script
50
+ assert_equal(<<HAML.rstrip, render_erb(<<HTML))
51
+ :javascript
52
+ function foo() {
53
+ return \#{foo.to_json};
54
+ }
55
+ HAML
56
+ <script type="text/javascript">
57
+ function foo() {
58
+ return <%= foo.to_json %>;
59
+ }
60
+ </script>
61
+ HTML
62
+ end
63
+
64
+ def test_erb_in_style
65
+ assert_equal(<<HAML.rstrip, render_erb(<<HTML))
66
+ :css
67
+ foo {
68
+ bar: \#{"baz"};
69
+ }
70
+ HAML
71
+ <style type="text/css">
72
+ foo {
73
+ bar: <%= "baz" %>;
74
+ }
75
+ </style>
76
+ HTML
77
+ end
78
+
79
+ def test_erb_in_line
80
+ assert_equal 'foo bar #{baz}', render_erb('foo bar <%= baz %>')
81
+ assert_equal 'foo bar #{baz}! Bang.', render_erb('foo bar <%= baz %>! Bang.')
82
+ end
83
+
84
+ def test_erb_multi_in_line
85
+ assert_equal('foo bar #{baz}! Bang #{bop}.',
86
+ render_erb('foo bar <%= baz %>! Bang <%= bop %>.'))
87
+ assert_equal('foo bar #{baz}#{bop}!',
88
+ render_erb('foo bar <%= baz %><%= bop %>!'))
89
+ end
90
+
91
+ def test_erb_with_html_special_chars
92
+ assert_equal '= 3 < 5 ? "OK" : "Your computer is b0rken"',
93
+ render_erb('<%= 3 < 5 ? "OK" : "Your computer is b0rken" %>')
94
+ end
95
+
96
+ def test_erb_in_class_attribute
97
+ assert_equal "%div{:class => dyna_class} I have a dynamic attribute",
98
+ render_erb('<div class="<%= dyna_class %>">I have a dynamic attribute</div>')
99
+ end
100
+
101
+ def test_erb_in_id_attribute
102
+ assert_equal "%div{:id => dyna_id} I have a dynamic attribute",
103
+ render_erb('<div id="<%= dyna_id %>">I have a dynamic attribute</div>')
104
+ end
105
+
106
+ def test_erb_in_attribute_results_in_string_interpolation
107
+ assert_equal('%div{:id => "item_#{i}"} Ruby string interpolation FTW',
108
+ render_erb('<div id="item_<%= i %>">Ruby string interpolation FTW</div>'))
109
+ end
110
+
111
+ def test_erb_in_attribute_with_trailing_content
112
+ assert_equal('%div{:class => "#{12}!"} Bang!',
113
+ render_erb('<div class="<%= 12 %>!">Bang!</div>'))
114
+ end
115
+
116
+ def test_erb_in_html_escaped_attribute
117
+ assert_equal '%div{:class => "foo"} Bang!',
118
+ render_erb('<div class="<%= "foo" %>">Bang!</div>')
119
+ end
120
+
121
+ def test_erb_in_attribute_to_multiple_interpolations
122
+ assert_equal('%div{:class => "#{12} + #{13}"} Math is super',
123
+ render_erb('<div class="<%= 12 %> + <%= 13 %>">Math is super</div>'))
124
+ end
125
+
126
+ def test_whitespace_eating_erb_tags
127
+ assert_equal '- form_for', render_erb('<%- form_for -%>')
128
+ end
129
+
130
+ def test_interpolation_in_erb
131
+ assert_equal('= "Foo #{bar} baz"', render_erb('<%= "Foo #{bar} baz" %>'))
132
+ end
133
+
134
+ def test_interpolation_in_erb_attrs
135
+ assert_equal('%p{:foo => "#{bar} baz"}',
136
+ render_erb('<p foo="<%= "#{bar} baz" %>"></p>'))
137
+ end
138
+
139
+ def test_multiline_erb_silent_script
140
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
141
+ .blah
142
+ - foo
143
+ - bar
144
+ - baz
145
+ %p foo
146
+ HAML
147
+ <div class="blah">
148
+ <%
149
+ foo
150
+ bar
151
+ baz
152
+ %>
153
+ <p>foo</p>
154
+ </div>
155
+ ERB
156
+ end
157
+
158
+ def test_multiline_erb_loud_script
159
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
160
+ .blah
161
+ = foo + |
162
+ bar.baz.bang + |
163
+ baz |
164
+ %p foo
165
+ HAML
166
+ <div class="blah">
167
+ <%=
168
+ foo +
169
+ bar.baz.bang +
170
+ baz
171
+ %>
172
+ <p>foo</p>
173
+ </div>
174
+ ERB
175
+ end
176
+
177
+ def test_weirdly_indented_multiline_erb_loud_script
178
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
179
+ .blah
180
+ = foo + |
181
+ bar.baz.bang + |
182
+ baz |
183
+ %p foo
184
+ HAML
185
+ <div class="blah">
186
+ <%=
187
+ foo +
188
+ bar.baz.bang +
189
+ baz
190
+ %>
191
+ <p>foo</p>
192
+ </div>
193
+ ERB
194
+ end
195
+
196
+ def test_two_multiline_erb_loud_scripts
197
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
198
+ .blah
199
+ = foo + |
200
+ bar.baz.bang + |
201
+ baz |
202
+ -#
203
+ = foo.bar do |
204
+ bang |
205
+ end |
206
+ %p foo
207
+ HAML
208
+ <div class="blah">
209
+ <%=
210
+ foo +
211
+ bar.baz.bang +
212
+ baz
213
+ %>
214
+ <%= foo.bar do
215
+ bang
216
+ end %>
217
+ <p>foo</p>
218
+ </div>
219
+ ERB
220
+ end
221
+
222
+ def test_multiline_then_single_line_erb_loud_scripts
223
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
224
+ .blah
225
+ = foo + |
226
+ bar.baz.bang + |
227
+ baz |
228
+ = foo.bar
229
+ %p foo
230
+ HAML
231
+ <div class="blah">
232
+ <%=
233
+ foo +
234
+ bar.baz.bang +
235
+ baz
236
+ %>
237
+ <%= foo.bar %>
238
+ <p>foo</p>
239
+ </div>
240
+ ERB
241
+ end
242
+
243
+ def test_multiline_erb_but_really_single_line
244
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
245
+ .blah
246
+ = foo
247
+ %p foo
248
+ HAML
249
+ <div class="blah">
250
+ <%=
251
+ foo
252
+ %>
253
+ <p>foo</p>
254
+ </div>
255
+ ERB
256
+ end
257
+
258
+ ### Block Parsing
259
+
260
+ def test_block_parsing
261
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
262
+ - foo do
263
+ %p bar
264
+ HAML
265
+ <% foo do %>
266
+ <p>bar</p>
267
+ <% end %>
268
+ ERB
269
+ end
270
+
271
+ def test_block_parsing_with_args
272
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
273
+ - foo do |a, b, c|
274
+ %p bar
275
+ HAML
276
+ <% foo do |a, b, c| %>
277
+ <p>bar</p>
278
+ <% end %>
279
+ ERB
280
+ end
281
+
282
+ def test_block_parsing_with_equals
283
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
284
+ = foo do
285
+ %p bar
286
+ HAML
287
+ <%= foo do %>
288
+ <p>bar</p>
289
+ <% end %>
290
+ ERB
291
+ end
292
+
293
+ def test_block_parsing_with_modified_end
294
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
295
+ - foo do
296
+ blah
297
+ - end.bip
298
+ HAML
299
+ <% foo do %>
300
+ blah
301
+ <% end.bip %>
302
+ ERB
303
+ end
304
+
305
+ def test_block_parsing_with_modified_end_with_block
306
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
307
+ - foo do
308
+ blah
309
+ - end.bip do
310
+ brang
311
+ HAML
312
+ <% foo do %>
313
+ blah
314
+ <% end.bip do %>
315
+ brang
316
+ <% end %>
317
+ ERB
318
+ end
319
+
320
+ def test_multiline_block_opener
321
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
322
+ - foo bar
323
+ - baz bang
324
+ - biddle do
325
+ foo
326
+ HAML
327
+ <% foo bar
328
+ baz bang
329
+ biddle do %>
330
+ foo
331
+ <% end %>
332
+ ERB
333
+ end
334
+
335
+ def test_if_elsif_else_parsing
336
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
337
+ - if foo
338
+ %p bar
339
+ - elsif bar.foo("zip")
340
+ #bang baz
341
+ - else
342
+ %strong bibble
343
+ HAML
344
+ <% if foo %>
345
+ <p>bar</p>
346
+ <% elsif bar.foo("zip") %>
347
+ <div id="bang">baz</div>
348
+ <% else %>
349
+ <strong>bibble</strong>
350
+ <% end %>
351
+ ERB
352
+ end
353
+
354
+ def test_case_when_parsing
355
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
356
+ - case foo.bar
357
+ - when "bip"
358
+ %p bip
359
+ - when "bop"
360
+ %p BOP
361
+ - when bizzle.bang.boop.blip
362
+ %em BIZZLE BANG BOOP BLIP
363
+ HAML
364
+ <% case foo.bar %>
365
+ <% when "bip" %>
366
+ <p>bip</p>
367
+ <% when "bop" %>
368
+ <p>BOP</p>
369
+ <% when bizzle.bang.boop.blip %>
370
+ <em>BIZZLE BANG BOOP BLIP</em>
371
+ <% end %>
372
+ ERB
373
+
374
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
375
+ - case foo.bar
376
+ - when "bip"
377
+ %p bip
378
+ - when "bop"
379
+ %p BOP
380
+ - when bizzle.bang.boop.blip
381
+ %em BIZZLE BANG BOOP BLIP
382
+ HAML
383
+ <% case foo.bar
384
+ when "bip" %>
385
+ <p>bip</p>
386
+ <% when "bop" %>
387
+ <p>BOP</p>
388
+ <% when bizzle.bang.boop.blip %>
389
+ <em>BIZZLE BANG BOOP BLIP</em>
390
+ <% end %>
391
+ ERB
392
+ end
393
+
394
+ def test_begin_rescue_ensure
395
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
396
+ - begin
397
+ %p a
398
+ - rescue FooException => e
399
+ %p b
400
+ - ensure
401
+ %p c
402
+ HAML
403
+ <% begin %>
404
+ <p>a</p>
405
+ <% rescue FooException => e %>
406
+ <p>b</p>
407
+ <% ensure %>
408
+ <p>c</p>
409
+ <% end %>
410
+ ERB
411
+ end
412
+
413
+ # Regression
414
+
415
+ def test_tag_inside_block
416
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
417
+ %table
418
+ - foo.each do
419
+ %tr
420
+ HAML
421
+ <table>
422
+ <% foo.each do %>
423
+ <tr></tr>
424
+ <% end %>
425
+ </table>
426
+ ERB
427
+ end
428
+
429
+ def test_silent_inside_block_inside_tag
430
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
431
+ %table
432
+ - foo.each do
433
+ - haml_puts "foo"
434
+ HAML
435
+ <table>
436
+ <% foo.each do %>
437
+ <% haml_puts "foo" %>
438
+ <% end %>
439
+ </table>
440
+ ERB
441
+ end
442
+
443
+ def test_commented_erb_should_not_cause_indentation
444
+ assert_equal(<<HAML.rstrip, render_erb(<<ERB))
445
+ %title
446
+ html2haml and multiline titles
447
+ = # stylesheet_link_tag :first
448
+ = stylesheet_link_tag 'another file'
449
+ HAML
450
+ <title>
451
+ html2haml and multiline titles
452
+ </title>
453
+ <%=# stylesheet_link_tag :first %>
454
+ <%#= stylesheet_link_tag :second %>
455
+ <%# stylesheet_link_tag :third %>
456
+ <%= stylesheet_link_tag 'another file' %>
457
+ ERB
458
+ end
459
+
460
+ def test_should_wrap_in_silent
461
+ assert_equal(<<HTML.rstrip, Haml::HTML::ERB.new(<<ERB).src)
462
+ <haml:silent> some_variable_or_function \n</haml:silent>
463
+ HTML
464
+ <% some_variable_or_function %>
465
+ ERB
466
+ end
467
+
468
+ #comment content is removed by erubis
469
+ def test_should_wrap_process_comments_as_empty_lines
470
+ assert_equal(<<HTML.rstrip, Haml::HTML::ERB.new(<<ERB).src)
471
+ <haml:silent>\n</haml:silent>
472
+ HTML
473
+ <%# some_variable_or_function %>
474
+ ERB
475
+ end
476
+
477
+ end