pivotal-erector 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/README.txt +81 -0
  2. data/VERSION.yml +4 -0
  3. data/bin/erect +7 -0
  4. data/lib/erector/erect.rb +148 -0
  5. data/lib/erector/erected.rb +63 -0
  6. data/lib/erector/extensions/object.rb +18 -0
  7. data/lib/erector/indenting.rb +36 -0
  8. data/lib/erector/rails/extensions/action_controller/1.2.5/action_controller.rb +17 -0
  9. data/lib/erector/rails/extensions/action_controller/2.2.0/action_controller.rb +26 -0
  10. data/lib/erector/rails/extensions/action_controller.rb +8 -0
  11. data/lib/erector/rails/extensions/action_view.rb +21 -0
  12. data/lib/erector/rails/extensions/widget/1.2.5/widget.rb +18 -0
  13. data/lib/erector/rails/extensions/widget/2.2.0/widget.rb +36 -0
  14. data/lib/erector/rails/extensions/widget.rb +117 -0
  15. data/lib/erector/rails/supported_rails_versions.rb +14 -0
  16. data/lib/erector/rails/template_handlers/1.2.5/action_view_template_handler.rb +32 -0
  17. data/lib/erector/rails/template_handlers/2.0.0/action_view_template_handler.rb +36 -0
  18. data/lib/erector/rails/template_handlers/2.1.0/action_view_template_handler.rb +31 -0
  19. data/lib/erector/rails/template_handlers/2.2.0/action_view_template_handler.rb +46 -0
  20. data/lib/erector/rails/template_handlers/action_view_template_handler.rb +14 -0
  21. data/lib/erector/rails.rb +6 -0
  22. data/lib/erector/raw_string.rb +8 -0
  23. data/lib/erector/rhtml.treetop +156 -0
  24. data/lib/erector/unicode.rb +18185 -0
  25. data/lib/erector/unicode_builder.rb +67 -0
  26. data/lib/erector/version.rb +10 -0
  27. data/lib/erector/widget.rb +510 -0
  28. data/lib/erector/widgets/table.rb +96 -0
  29. data/lib/erector/widgets.rb +2 -0
  30. data/lib/erector.rb +16 -0
  31. data/spec/core_spec_suite.rb +3 -0
  32. data/spec/erect/erect_spec.rb +145 -0
  33. data/spec/erect/erected_spec.rb +80 -0
  34. data/spec/erect/rhtml_parser_spec.rb +318 -0
  35. data/spec/erector/indentation_spec.rb +136 -0
  36. data/spec/erector/unicode_builder_spec.rb +75 -0
  37. data/spec/erector/widget_spec.rb +657 -0
  38. data/spec/erector/widgets/table_spec.rb +99 -0
  39. data/spec/rails_spec_suite.rb +3 -0
  40. data/spec/spec_helper.rb +54 -0
  41. data/spec/spec_suite.rb +45 -0
  42. metadata +118 -0
@@ -0,0 +1,657 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+
3
+ module WidgetSpec
4
+ describe Erector::Widget do
5
+ describe ".all_tags" do
6
+ it "returns set of full and empty tags" do
7
+ Erector::Widget.all_tags.class.should == Array
8
+ Erector::Widget.all_tags.should == Erector::Widget.full_tags + Erector::Widget.empty_tags
9
+ end
10
+ end
11
+
12
+ describe "#to_s" do
13
+ class << self
14
+ define_method("invokes #render and returns the string representation of the rendered widget") do
15
+ it "invokes #render and returns the string representation of the rendered widget" do
16
+ widget = Erector::Widget.new do
17
+ div "Hello"
18
+ end
19
+ mock.proxy(widget).render
20
+ widget.to_s.should == "<div>Hello</div>"
21
+ end
22
+ end
23
+ end
24
+
25
+ context "when passed no arguments" do
26
+ send "invokes #render and returns the string representation of the rendered widget"
27
+ end
28
+
29
+ context "when passed an argument that is #render" do
30
+ send "invokes #render and returns the string representation of the rendered widget"
31
+ end
32
+
33
+ context "when passed an argument that is not #render" do
34
+ attr_reader :widget
35
+ before do
36
+ @widget = Erector::Widget.new
37
+ def widget.alternate_render
38
+ div "Hello from Alternate Render"
39
+ end
40
+ mock.proxy(widget).alternate_render
41
+ end
42
+
43
+ it "invokes the passed in method name and returns the string representation of the rendered widget" do
44
+ widget.to_s(:alternate_render).should == "<div>Hello from Alternate Render</div>"
45
+ end
46
+
47
+ it "does not invoke #render" do
48
+ dont_allow(widget).render
49
+ widget.to_s(:alternate_render)
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "#instruct" do
55
+ it "when passed no arguments; returns an XML declaration with version 1 and utf-8" do
56
+ html = Erector::Widget.new do
57
+ instruct
58
+ # version must precede encoding, per XML 1.0 4th edition (section 2.8)
59
+ end.to_s.should == "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
60
+ end
61
+ end
62
+
63
+ describe "#widget" do
64
+ context "when nested" do
65
+ it "renders the tag around the rest of the block" do
66
+ parent_widget = Class.new(Erector::Widget) do
67
+ def render
68
+ div :id => "parent_widget" do
69
+ super
70
+ end
71
+ end
72
+ end
73
+ child_widget = Class.new(Erector::Widget) do
74
+ def render
75
+ div :id => "child_widget" do
76
+ super
77
+ end
78
+ end
79
+ end
80
+
81
+ widget = Class.new(Erector::Widget) do
82
+ def render
83
+ widget(parent_widget) do
84
+ widget(child_widget) do
85
+ super
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ widget.new(nil, :parent_widget => parent_widget, :child_widget => child_widget) do
92
+ div :id => "widget"
93
+ end.to_s.should == '<div id="parent_widget"><div id="child_widget"><div id="widget"></div></div></div>'
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "#element" do
99
+ context "when receiving one argument" do
100
+ it "returns an empty element" do
101
+ Erector::Widget.new do
102
+ element('div')
103
+ end.to_s.should == "<div></div>"
104
+ end
105
+ end
106
+
107
+ context "with a attribute hash" do
108
+ it "returns an empty element with the attributes" do
109
+ html = Erector::Widget.new do
110
+ element(
111
+ 'div',
112
+ :class => "foo bar",
113
+ :style => "display: none; color: white; float: left;",
114
+ :nil_attribute => nil
115
+ )
116
+ end.to_s
117
+ doc = Hpricot(html)
118
+ div = doc.at('div')
119
+ div[:class].should == "foo bar"
120
+ div[:style].should == "display: none; color: white; float: left;"
121
+ div[:nil_attribute].should be_nil
122
+ end
123
+ end
124
+
125
+ context "with an array of CSS classes" do
126
+ it "returns a tag with the classes separated" do
127
+ Erector::Widget.new do
128
+ element('div', :class => [:foo, :bar])
129
+ end.to_s.should == "<div class=\"foo bar\"></div>";
130
+ end
131
+ end
132
+
133
+ context "with an array of CSS classes as strings" do
134
+ it "returns a tag with the classes separated" do
135
+ Erector::Widget.new do
136
+ element('div', :class => ['foo', 'bar'])
137
+ end.to_s.should == "<div class=\"foo bar\"></div>";
138
+ end
139
+ end
140
+
141
+
142
+ context "with a CSS class which is a string" do
143
+ it "just use that as the attribute value" do
144
+ Erector::Widget.new do
145
+ element('div', :class => "foo bar")
146
+ end.to_s.should == "<div class=\"foo bar\"></div>";
147
+ end
148
+ end
149
+
150
+ context "with many attributes" do
151
+ it "alphabetize them" do
152
+ Erector::Widget.new do
153
+ empty_element('foo', :alpha => "", :betty => "5", :aardvark => "tough",
154
+ :carol => "", :demon => "", :erector => "", :pi => "3.14", :omicron => "", :zebra => "", :brain => "")
155
+ end.to_s.should == "<foo aardvark=\"tough\" alpha=\"\" betty=\"5\" brain=\"\" carol=\"\" demon=\"\" " \
156
+ "erector=\"\" omicron=\"\" pi=\"3.14\" zebra=\"\" />";
157
+ end
158
+ end
159
+
160
+ context "with inner tags" do
161
+ it "returns nested tags" do
162
+ widget = Erector::Widget.new do
163
+ element 'div' do
164
+ element 'div'
165
+ end
166
+ end
167
+ widget.to_s.should == '<div><div></div></div>'
168
+ end
169
+ end
170
+
171
+ context "with text" do
172
+ it "returns element with inner text" do
173
+ Erector::Widget.new do
174
+ element 'div', 'test text'
175
+ end.to_s.should == "<div>test text</div>"
176
+ end
177
+ end
178
+
179
+ context "with object other than hash" do
180
+ it "returns element with inner text == object.to_s" do
181
+ object = ['a', 'b']
182
+ Erector::Widget.new do
183
+ element 'div', object
184
+ end.to_s.should == "<div>#{object.to_s}</div>"
185
+ end
186
+ end
187
+
188
+ context "with parameters and block" do
189
+ it "returns element with inner html and attributes" do
190
+ Erector::Widget.new do
191
+ element 'div', 'class' => "foobar" do
192
+ element 'span', 'style' => 'display: none;'
193
+ end
194
+ end.to_s.should == '<div class="foobar"><span style="display: none;"></span></div>'
195
+ end
196
+ end
197
+
198
+ context "with content and parameters" do
199
+ it "returns element with content as inner html and attributes" do
200
+ Erector::Widget.new do
201
+ element 'div', 'test text', :style => "display: none;"
202
+ end.to_s.should == '<div style="display: none;">test text</div>'
203
+ end
204
+ end
205
+
206
+ context "with more than three arguments" do
207
+ it "raises ArgumentError" do
208
+ proc do
209
+ Erector::Widget.new do
210
+ element 'div', 'foobar', {}, 'fourth'
211
+ end.to_s
212
+ end.should raise_error(ArgumentError)
213
+ end
214
+ end
215
+
216
+ it "renders the proper full tags" do
217
+ Erector::Widget.full_tags.each do |tag_name|
218
+ expected = "<#{tag_name}></#{tag_name}>"
219
+ actual = Erector::Widget.new do
220
+ send(tag_name)
221
+ end.to_s
222
+ begin
223
+ actual.should == expected
224
+ rescue Spec::Expectations::ExpectationNotMetError => e
225
+ puts "Expected #{tag_name} to be a full element. Expected #{expected}, got #{actual}"
226
+ raise e
227
+ end
228
+ end
229
+ end
230
+
231
+ describe "quoting" do
232
+ context "when outputting text" do
233
+ it "quotes it" do
234
+ Erector::Widget.new do
235
+ element 'div', 'test &<>text'
236
+ end.to_s.should == "<div>test &amp;&lt;&gt;text</div>"
237
+ end
238
+ end
239
+
240
+ context "when outputting text via text" do
241
+ it "quotes it" do
242
+ Erector::Widget.new do
243
+ element 'div' do
244
+ text "test &<>text"
245
+ end
246
+ end.to_s.should == "<div>test &amp;&lt;&gt;text</div>"
247
+ end
248
+ end
249
+
250
+ context "when outputting attribute value" do
251
+ it "quotes it" do
252
+ Erector::Widget.new do
253
+ element 'a', :href => "foo.cgi?a&b"
254
+ end.to_s.should == "<a href=\"foo.cgi?a&amp;b\"></a>"
255
+ end
256
+ end
257
+
258
+ context "with raw text" do
259
+ it "does not quote it" do
260
+ Erector::Widget.new do
261
+ element 'div' do
262
+ text raw("<b>bold</b>")
263
+ end
264
+ end.to_s.should == "<div><b>bold</b></div>"
265
+ end
266
+ end
267
+
268
+ context "with raw text and no block" do
269
+ it "does not quote it" do
270
+ Erector::Widget.new do
271
+ element 'div', raw("<b>bold</b>")
272
+ end.to_s.should == "<div><b>bold</b></div>"
273
+ end
274
+ end
275
+
276
+ context "with raw attribute" do
277
+ it "does not quote it" do
278
+ Erector::Widget.new do
279
+ element 'a', :href => raw("foo?x=&nbsp;")
280
+ end.to_s.should == "<a href=\"foo?x=&nbsp;\"></a>"
281
+ end
282
+ end
283
+
284
+ context "with quote in attribute" do
285
+ it "quotes it" do
286
+ Erector::Widget.new do
287
+ element 'a', :onload => "alert(\"foo\")"
288
+ end.to_s.should == "<a onload=\"alert(&quot;foo&quot;)\"></a>"
289
+ end
290
+ end
291
+ end
292
+
293
+ context "with a non-string, non-raw" do
294
+ it "calls to_s and quotes" do
295
+ Erector::Widget.new do
296
+ element 'a' do
297
+ text [7, "foo&bar"]
298
+ end
299
+ end.to_s.should == "<a>7foo&amp;bar</a>"
300
+ end
301
+ end
302
+ end
303
+
304
+ describe "#empty_element" do
305
+ context "when receiving attributes" do
306
+ it "renders an empty element with the attributes" do
307
+ Erector::Widget.new do
308
+ empty_element 'input', :name => 'foo[bar]'
309
+ end.to_s.should == '<input name="foo[bar]" />'
310
+ end
311
+ end
312
+
313
+ context "when not receiving attributes" do
314
+ it "renders an empty element without attributes" do
315
+ Erector::Widget.new do
316
+ empty_element 'br'
317
+ end.to_s.should == '<br />'
318
+ end
319
+ end
320
+
321
+ it "renders the proper empty-element tags" do
322
+ Erector::Widget.empty_tags.each do |tag_name|
323
+ expected = "<#{tag_name} />"
324
+ actual = Erector::Widget.new do
325
+ send(tag_name)
326
+ end.to_s
327
+ begin
328
+ actual.should == expected
329
+ rescue Spec::Expectations::ExpectationNotMetError => e
330
+ puts "Expected #{tag_name} to be an empty-element tag. Expected #{expected}, got #{actual}"
331
+ raise e
332
+ end
333
+ end
334
+ end
335
+ end
336
+
337
+ describe "#nbsp" do
338
+ it "turns consecutive spaces into consecutive non-breaking spaces" do
339
+ Erector::Widget.new do
340
+ text nbsp("a b")
341
+ end.to_s.should == "a&#160;&#160;b"
342
+ end
343
+
344
+ it "works in text context" do
345
+ Erector::Widget.new do
346
+ element 'a' do
347
+ text nbsp("&<> foo")
348
+ end
349
+ end.to_s.should == "<a>&amp;&lt;&gt;&#160;foo</a>"
350
+ end
351
+
352
+ it "works in attribute value context" do
353
+ Erector::Widget.new do
354
+ element 'a', :href => nbsp("&<> foo")
355
+ end.to_s.should == "<a href=\"&amp;&lt;&gt;&#160;foo\"></a>"
356
+ end
357
+
358
+ it "defaults to a single non-breaking space if given no argument" do
359
+ Erector::Widget.new do
360
+ text nbsp
361
+ end.to_s.should == "&#160;"
362
+ end
363
+
364
+ end
365
+
366
+ describe "#character" do
367
+ it "renders a character given the codepoint number" do
368
+ Erector::Widget.new do
369
+ text character(160)
370
+ end.to_s.should == "&#xa0;"
371
+ end
372
+
373
+ it "renders a character given the unicode name" do
374
+ Erector::Widget.new do
375
+ text character(:right_arrow)
376
+ end.to_s.should == "&#x2192;"
377
+ end
378
+
379
+ it "renders a character above 0xffff" do
380
+ Erector::Widget.new do
381
+ text character(:old_persian_sign_ka)
382
+ end.to_s.should == "&#x103a3;"
383
+ end
384
+
385
+ it "throws an exception if a name is not recognized" do
386
+ lambda {
387
+ Erector::Widget.new do
388
+ text character(:no_such_character_name)
389
+ end.to_s
390
+ }.should raise_error("Unrecognized character no_such_character_name")
391
+ end
392
+
393
+ it "throws an exception if passed something besides a symbol or integer" do
394
+ # Perhaps calling to_s would be more ruby-esque, but that seems like it might
395
+ # be pretty confusing when this method can already take either a name or number
396
+ lambda {
397
+ Erector::Widget.new do
398
+ text character([])
399
+ end.to_s
400
+ }.should raise_error("Unrecognized argument to character: ")
401
+ end
402
+ end
403
+
404
+ describe "#join" do
405
+
406
+ it "empty array means nothing to join" do
407
+ Erector::Widget.new do
408
+ join [], Erector::Widget.new { text "x" }
409
+ end.to_s.should == ""
410
+ end
411
+
412
+ it "larger example with two tabs" do
413
+ Erector::Widget.new do
414
+ tab1 =
415
+ Erector::Widget.new do
416
+ a "Upload document", :href => "/upload"
417
+ end
418
+ tab2 =
419
+ Erector::Widget.new do
420
+ a "Logout", :href => "/logout"
421
+ end
422
+ join [tab1, tab2],
423
+ Erector::Widget.new { text nbsp(" |"); text " " }
424
+ end.to_s.should ==
425
+ '<a href="/upload">Upload document</a>&#160;| <a href="/logout">Logout</a>'
426
+ end
427
+
428
+ it "plain string as join separator means pass it to text" do
429
+ Erector::Widget.new do
430
+ join [
431
+ Erector::Widget.new { text "x" },
432
+ Erector::Widget.new { text "y" }
433
+ ], "<>"
434
+ end.to_s.should == "x&lt;&gt;y"
435
+ end
436
+
437
+ it "plain string as item to join means pass it to text" do
438
+ Erector::Widget.new do
439
+ join [
440
+ "<",
441
+ "&"
442
+ ], Erector::Widget.new { text " + " }
443
+ end.to_s.should == "&lt; + &amp;"
444
+ end
445
+
446
+ end
447
+
448
+ describe '#h' do
449
+ before do
450
+ @widget = Erector::Widget.new
451
+ end
452
+
453
+ it "escapes regular strings" do
454
+ @widget.h("&").should == "&amp;"
455
+ end
456
+
457
+ it "does not escape raw strings" do
458
+ @widget.h(@widget.raw("&")).should == "&"
459
+ end
460
+ end
461
+
462
+ describe "#javascript" do
463
+ context "when receiving a block" do
464
+ it "renders the content inside of script text/javascript tags" do
465
+ expected = <<-EXPECTED
466
+ <script type="text/javascript">
467
+ // <![CDATA[
468
+ if (x < y && x > z) alert("don't stop");
469
+ // ]]>
470
+ </script>
471
+ EXPECTED
472
+ expected.gsub!(/^ /, '')
473
+ Erector::Widget.new do
474
+ javascript do
475
+ rawtext 'if (x < y && x > z) alert("don\'t stop");'
476
+ end
477
+ end.to_s.should == expected
478
+ end
479
+ end
480
+
481
+ it "renders the raw content inside script tags when given text" do
482
+ expected = <<-EXPECTED
483
+ <script type="text/javascript">
484
+ // <![CDATA[
485
+ alert("&<>'hello");
486
+ // ]]>
487
+ </script>
488
+ EXPECTED
489
+ expected.gsub!(/^ /, '')
490
+ Erector::Widget.new do
491
+ javascript('alert("&<>\'hello");')
492
+ end.to_s.should == expected
493
+ end
494
+
495
+ context "when receiving a params hash" do
496
+ it "renders a source file" do
497
+ html = Erector::Widget.new do
498
+ javascript(:src => "/my/js/file.js")
499
+ end.to_s
500
+ doc = Hpricot(html)
501
+ doc.at('/')[:src].should == "/my/js/file.js"
502
+ end
503
+ end
504
+
505
+ context "when receiving text and a params hash" do
506
+ it "renders a source file" do
507
+ html = Erector::Widget.new do
508
+ javascript('alert("&<>\'hello");', :src => "/my/js/file.js")
509
+ end.to_s
510
+ doc = Hpricot(html)
511
+ script_tag = doc.at('script')
512
+ script_tag[:src].should == "/my/js/file.js"
513
+ script_tag.inner_html.should include('alert("&<>\'hello");')
514
+ end
515
+ end
516
+
517
+ context "with too many arguments" do
518
+ it "raises ArgumentError" do
519
+ proc do
520
+ Erector::Widget.new do
521
+ javascript 'foobar', {}, 'fourth'
522
+ end.to_s
523
+ end.should raise_error(ArgumentError)
524
+ end
525
+ end
526
+ end
527
+
528
+ describe "#css" do
529
+ it "makes a link when passed a string" do
530
+ Erector::Widget.new do
531
+ css "erector.css"
532
+ end.to_s.should == "<link href=\"erector.css\" rel=\"stylesheet\" type=\"text/css\" />"
533
+ end
534
+ end
535
+
536
+ describe "#url" do
537
+ it "renders an anchor tag with the same href and text" do
538
+ Erector::Widget.new do
539
+ url "http://example.com"
540
+ end.to_s.should == "<a href=\"http://example.com\">http://example.com</a>"
541
+ end
542
+ end
543
+
544
+ describe '#capture' do
545
+ it "should return content rather than write it to the buffer" do
546
+ widget = Erector::Widget.new do
547
+ captured = capture do
548
+ p 'Captured Content'
549
+ end
550
+ div do
551
+ text captured
552
+ end
553
+ end
554
+ widget.to_s.should == '<div><p>Captured Content</p></div>'
555
+ end
556
+
557
+ it "works with nested captures" do
558
+ widget = Erector::Widget.new do
559
+ captured = capture do
560
+ captured = capture do
561
+ p 'Nested Capture'
562
+ end
563
+ p 'Captured Content'
564
+ text captured
565
+ end
566
+ div do
567
+ text captured
568
+ end
569
+ end
570
+ widget.to_s.should == '<div><p>Captured Content</p><p>Nested Capture</p></div>'
571
+ end
572
+ end
573
+
574
+ describe 'nested' do
575
+ it "can insert another widget without raw" do
576
+ inner = Erector::Widget.new do
577
+ p "foo"
578
+ end
579
+
580
+ outer = Erector::Widget.new do
581
+ div inner
582
+ end.to_s.should == '<div><p>foo</p></div>'
583
+ end
584
+ end
585
+
586
+ describe '#widget' do
587
+ before do
588
+ class Parent < Erector::Widget
589
+ def render
590
+ text 1
591
+ widget Child do
592
+ text 2
593
+ third
594
+ end
595
+ end
596
+
597
+ def third
598
+ text 3
599
+ end
600
+ end
601
+
602
+ class Child < Erector::Widget
603
+ def render
604
+ super
605
+ end
606
+ end
607
+ end
608
+
609
+ it "renders nested widgets in the correct order" do
610
+ Parent.new.to_s.should == '123'
611
+ end
612
+ end
613
+
614
+ describe '#render_to' do
615
+ class A < Erector::Widget
616
+ def render
617
+ p "A"
618
+ end
619
+ end
620
+
621
+ it "renders to a doc" do
622
+ class B < Erector::Widget
623
+ def render
624
+ text "B"
625
+ A.new.render_to(@output)
626
+ text "B"
627
+ end
628
+ end
629
+ b = B.new
630
+ b.to_s.should == "B<p>A</p>B"
631
+ b.output.size.should == 10 # B, <p>, A, </p>, B
632
+ end
633
+
634
+ it "renders to a widget's doc" do
635
+ class B < Erector::Widget
636
+ def render
637
+ text "B"
638
+ A.new.render_to(self)
639
+ text "B"
640
+ end
641
+ end
642
+ b = B.new
643
+ b.to_s.should == "B<p>A</p>B"
644
+ b.output.size.should == 10 # B, <p>, A, </p>, B
645
+ end
646
+
647
+ it "passing a widget to text method renders it" do
648
+ Erector::Widget.new() do
649
+ text "B"
650
+ text A.new()
651
+ text "B"
652
+ end.to_s.should == "B<p>A</p>B"
653
+ end
654
+
655
+ end
656
+ end
657
+ end