pivotal-erector 0.5.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.
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