xx 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/lib/xx-0.1.0.rb +728 -0
  2. data/lib/xx.rb +728 -0
  3. data/test/xx.rb +52 -0
  4. metadata +46 -0
@@ -0,0 +1,728 @@
1
+ unless defined? $__xx_rb__
2
+
3
+ require "rexml/document"
4
+
5
+
6
+ module XX
7
+ #--{{{
8
+ VERSION = "0.1.0"
9
+
10
+ %w(
11
+ CRAZY_LIKE_A_HELL
12
+ PERMISSIVE
13
+ STRICT
14
+ ANY
15
+ ).each{|c| const_set c, c}
16
+
17
+ class Document
18
+ #--{{{
19
+ attr "doc"
20
+ attr "stack"
21
+ attr "size"
22
+
23
+ def initialize *a, &b
24
+ #--{{{
25
+ @doc = ::REXML::Document::new *a, &b
26
+ @stack = [@doc]
27
+ @size = 0
28
+ #--}}}
29
+ end
30
+ def top
31
+ #--{{{
32
+ @stack.last
33
+ #--}}}
34
+ end
35
+ def push element
36
+ #--{{{
37
+ @stack.push element
38
+ #--}}}
39
+ end
40
+ def pop
41
+ #--{{{
42
+ @stack.pop unless @stack.size == 1
43
+ #--}}}
44
+ end
45
+ def tracking_additions
46
+ #--{{{
47
+ n = @size
48
+ yield
49
+ return @size - n
50
+ #--}}}
51
+ end
52
+ def to_str port = ""
53
+ #--{{{
54
+ @doc.write port, indent=-1, transitive=false, ie_hack=true
55
+ port
56
+ #--}}}
57
+ end
58
+ alias_method "to_s", "to_str"
59
+ def pretty port = ''
60
+ #--{{{
61
+ @doc.write port, indent=2, transitive=false, ie_hack=true
62
+ port
63
+ #--}}}
64
+ end
65
+ def create element
66
+ #--{{{
67
+ push element
68
+ begin
69
+ object = nil
70
+ additions =
71
+ tracking_additions do
72
+ object = yield element if block_given?
73
+ end
74
+ if object and additions.zero?
75
+ self << object
76
+ end
77
+ ensure
78
+ pop
79
+ end
80
+ self << element
81
+ element
82
+ #--}}}
83
+ end
84
+ def << object
85
+ #--{{{
86
+ t, x = top, object
87
+
88
+ if x
89
+ case t
90
+ when ::REXML::Document
91
+
92
+ begin
93
+ t <<
94
+ case x
95
+ when ::REXML::Document
96
+ x.root || ::REXML::Text::new(x.to_s)
97
+ when ::REXML::Element
98
+ x
99
+ when ::REXML::CData
100
+ x
101
+ when ::REXML::Text
102
+ x
103
+ else # string
104
+ ::REXML::Text::new(x.to_s)
105
+ end
106
+ rescue
107
+ if t.respond_to? "root"
108
+ t = t.root
109
+ retry
110
+ else
111
+ raise
112
+ end
113
+ end
114
+
115
+ when ::REXML::Element
116
+ t <<
117
+ case x
118
+ when ::REXML::Document
119
+ x.root || ::REXML::Text::new(x.to_s)
120
+ when ::REXML::Element
121
+ x
122
+ when ::REXML::CData
123
+ #::REXML::Text::new(x.write(""))
124
+ x
125
+ when ::REXML::Text
126
+ x
127
+ else # string
128
+ ::REXML::Text::new(x.to_s)
129
+ end
130
+
131
+ when ::REXML::Text
132
+ t <<
133
+ case x
134
+ when ::REXML::Document
135
+ x.write ""
136
+ when ::REXML::Element
137
+ x.write ""
138
+ when ::REXML::CData
139
+ x.write ""
140
+ when ::REXML::Text
141
+ x.write ""
142
+ else # string
143
+ x.to_s
144
+ end
145
+
146
+ else # other - try anyhow
147
+ t <<
148
+ case x
149
+ when ::REXML::Document
150
+ x.write ""
151
+ when ::REXML::Element
152
+ x.write ""
153
+ when ::REXML::CData
154
+ x.write ""
155
+ when ::REXML::Text
156
+ x.write ""
157
+ else # string
158
+ x.to_s
159
+ end
160
+ end
161
+ end
162
+
163
+ @size += 1
164
+ self
165
+ #--}}}
166
+ end
167
+ #--}}}
168
+ end
169
+
170
+ module Markup
171
+ #--{{{
172
+ class Error < ::StandardError; end
173
+
174
+ module InstanceMethods
175
+ #--{{{
176
+ def method_missing m, *a, &b
177
+ #--{{{
178
+ m = m.to_s
179
+
180
+ tag_method, tag_name = xx_class::xx_tag_method_name m
181
+
182
+ c_method_missing = xx_class::xx_config_for "method_missing", xx_which
183
+ c_tags = xx_class::xx_config_for "tags", xx_which
184
+
185
+ pat =
186
+ case c_method_missing
187
+ when ::XX::CRAZY_LIKE_A_HELL
188
+ %r/.*/
189
+ when ::XX::PERMISSIVE
190
+ %r/_$/o
191
+ when ::XX::STRICT
192
+ %r/_$/o
193
+ else
194
+ super
195
+ end
196
+
197
+ super unless m =~ pat
198
+
199
+ if c_method_missing == ::XX::STRICT
200
+ super unless c_tags.include? tag_name
201
+ end
202
+
203
+ ret, defined = nil
204
+
205
+ begin
206
+ xx_class::xx_define_tmp_method tag_method
207
+ xx_class::xx_define_tag_method tag_method, tag_name
208
+ ret = send tag_method, *a, &b
209
+ defined = true
210
+ ensure
211
+ xx_class::xx_remove_tag_method tag_method unless defined
212
+ end
213
+
214
+ ret
215
+ #--}}}
216
+ end
217
+ def xx_tag_ tag_name, *a, &b
218
+ #--{{{
219
+ tag_method, tag_name = xx_class::xx_tag_method_name tag_name
220
+
221
+ ret, defined = nil
222
+
223
+ begin
224
+ xx_class::xx_define_tmp_method tag_method
225
+ xx_class::xx_define_tag_method tag_method, tag_name
226
+ ret = send tag_method, *a, &b
227
+ defined = true
228
+ ensure
229
+ xx_class::xx_remove_tag_method tag_method unless defined
230
+ end
231
+
232
+ ret
233
+ #--}}}
234
+ end
235
+ alias_method "g_", "xx_tag_"
236
+ def xx_which *argv
237
+ #--{{{
238
+ @xx_which = nil unless defined? @xx_which
239
+ if argv.empty?
240
+ @xx_which
241
+ else
242
+ xx_which = @xx_which
243
+ begin
244
+ @xx_which = argv.shift
245
+ return yield
246
+ ensure
247
+ @xx_which = xx_which
248
+ end
249
+ end
250
+ #--}}}
251
+ end
252
+ def xx_with_doc_in_effect *a, &b
253
+ #--{{{
254
+ @xx_docs ||= []
255
+ doc = ::XX::Document::new *a
256
+ ddoc = doc.doc
257
+ begin
258
+ @xx_docs.push doc
259
+ b.call doc if b
260
+
261
+ doctype = xx_config_for "doctype", xx_which
262
+ if doctype
263
+ unless ddoc.doctype
264
+ doctype = ::REXML::DocType::new doctype unless
265
+ ::REXML::DocType === doctype
266
+ ddoc << doctype
267
+ end
268
+ end
269
+
270
+ xmldecl = xx_config_for "xmldecl", xx_which
271
+ if xmldecl
272
+ if ddoc.xml_decl == ::REXML::XMLDecl::default
273
+ xmldecl = ::REXML::XMLDecl::new xmldecl unless
274
+ ::REXML::XMLDecl === xmldecl
275
+ ddoc << xmldecl
276
+ end
277
+ end
278
+
279
+ return doc
280
+ ensure
281
+ @xx_docs.pop
282
+ end
283
+ #--}}}
284
+ end
285
+ def xx_doc
286
+ #--{{{
287
+ @xx_docs.last rescue raise "no xx_doc in effect!"
288
+ #--}}}
289
+ end
290
+ def xx_text_ *objects, &b
291
+ #--{{{
292
+ doc = xx_doc
293
+
294
+ text =
295
+ ::REXML::Text::new("",
296
+ respect_whitespace=true, parent=nil
297
+ )
298
+
299
+ objects.each do |object|
300
+ text << object.to_s if object
301
+ end
302
+
303
+ doc.create text, &b
304
+ #--}}}
305
+ end
306
+ alias_method "text_", "xx_text_"
307
+ alias_method "t_", "xx_text_"
308
+ def xx_markup_ *objects, &b
309
+ #--{{{
310
+ doc = xx_doc
311
+
312
+ doc2 = ::REXML::Document::new ""
313
+
314
+ objects.each do |object|
315
+ (doc2.root ? doc2.root : doc2) << ::REXML::Document::new(object.to_s)
316
+ end
317
+
318
+
319
+ ret = doc.create doc2, &b
320
+ puts doc2.to_s
321
+ STDIN.gets
322
+ ret
323
+ #--}}}
324
+ end
325
+ alias_method "x_", "xx_markup_"
326
+ def xx_any_ *objects, &b
327
+ #--{{{
328
+ doc = xx_doc
329
+ nothing = %r/.^/m
330
+
331
+ text =
332
+ ::REXML::Text::new("",
333
+ respect_whitespace=true, parent=nil, raw=true, entity_filter=nil, illegal=nothing
334
+ )
335
+
336
+ objects.each do |object|
337
+ text << object.to_s if object
338
+ end
339
+
340
+ doc.create text, &b
341
+ #--}}}
342
+ end
343
+ alias_method "h_", "xx_any_"
344
+ alias_method "x_", "xx_any_" # supplant for now
345
+ def xx_cdata_ *objects, &b
346
+ #--{{{
347
+ doc = xx_doc
348
+
349
+ cdata = ::REXML::CData::new ""
350
+
351
+ objects.each do |object|
352
+ cdata << object.to_s if object
353
+ end
354
+
355
+ doc.create cdata, &b
356
+ #--}}}
357
+ end
358
+ alias_method "c_", "xx_cdata_"
359
+ def xx_parse_attributes string
360
+ #--{{{
361
+ string = string.to_s
362
+ tokens = string.split %r/,/o
363
+ tokens.map{|t| t.sub!(%r/[^=]+=/){|key_eq| key_eq.chop << " : "}}
364
+ xx_parse_yaml_attributes(tokens.join(','))
365
+ #--}}}
366
+ end
367
+ alias_method "att_", "xx_parse_attributes"
368
+ def xx_parse_yaml_attributes string
369
+ #--{{{
370
+ require "yaml"
371
+ string = string.to_s
372
+ string = "{" << string unless string =~ %r/^\s*[{]/o
373
+ string = string << "}" unless string =~ %r/[}]\s*$/o
374
+ obj = ::YAML::load string
375
+ raise ArgumentError, "<#{ obj.class }> not Hash!" unless Hash === obj
376
+ obj
377
+ #--}}}
378
+ end
379
+ alias_method "at_", "xx_parse_yaml_attributes"
380
+ alias_method "yat_", "xx_parse_yaml_attributes"
381
+ def xx_class
382
+ #--{{{
383
+ @xx_class ||= self.class
384
+ #--}}}
385
+ end
386
+ def xx_tag_method_name *a, &b
387
+ #--{{{
388
+ xx_class.xx_tag_method_name *a, &b
389
+ #--}}}
390
+ end
391
+ def xx_define_tmp_method *a, &b
392
+ #--{{{
393
+ xx_class.xx_define_tmp_method *a, &b
394
+ #--}}}
395
+ end
396
+ def xx_define_tag_method *a, &b
397
+ #--{{{
398
+ xx_class.xx_define_tag_method *a, &b
399
+ #--}}}
400
+ end
401
+ def xx_remove_tag_method *a, &b
402
+ #--{{{
403
+ xx_class.xx_tag_remove_method *a, &b
404
+ #--}}}
405
+ end
406
+ def xx_ancestors
407
+ #--{{{
408
+ raise Error, "no xx_which in effect" unless xx_which
409
+ xx_class.xx_ancestors xx_which
410
+ #--}}}
411
+ end
412
+ def xx_config
413
+ #--{{{
414
+ xx_class.xx_config
415
+ #--}}}
416
+ end
417
+ def xx_config_for *a, &b
418
+ #--{{{
419
+ xx_class.xx_config_for *a, &b
420
+ #--}}}
421
+ end
422
+ def xx_configure *a, &b
423
+ #--{{{
424
+ xx_class.xx_configure *a, &b
425
+ #--}}}
426
+ end
427
+ #--}}}
428
+ end
429
+
430
+ module ClassMethods
431
+ #--{{{
432
+ def xx_tag_method_name m
433
+ #--{{{
434
+ m = m.to_s
435
+ tag_method, tag_name = m, m.gsub(%r/_+$/, "")
436
+ [ tag_method, tag_name ]
437
+ #--}}}
438
+ end
439
+ def xx_define_tmp_method m
440
+ #--{{{
441
+ define_method(m){ raise NotImplementedError, m.to_s }
442
+ #--}}}
443
+ end
444
+ def xx_define_tag_method tag_method, tag_name = nil
445
+ #--{{{
446
+ tag_method = tag_method.to_s
447
+ tag_name ||= tag_method.gsub %r/_+$/, ""
448
+
449
+ module_eval <<-code
450
+ def #{ tag_method } *a, &b
451
+ hashes, nothashes = a.partition{|x| Hash === x}
452
+
453
+ doc = xx_doc
454
+ element = ::REXML::Element::new '#{ tag_name }'
455
+
456
+ hashes.each{|h| h.each{|k,v| element.add_attribute k.to_s, v}}
457
+ nothashes.each{|nh| element << ::REXML::Text::new(nh.to_s)}
458
+
459
+ doc.create element, &b
460
+ end
461
+ code
462
+ tag_method
463
+ #--}}}
464
+ end
465
+ def xx_remove_tag_method tag_method
466
+ #--{{{
467
+ remove_method tag_method rescue nil
468
+ #--}}}
469
+ end
470
+ def xx_ancestors xx_which = self
471
+ #--{{{
472
+ list = []
473
+ ancestors.each do |a|
474
+ list << a if a < xx_which
475
+ end
476
+ xx_which.ancestors.each do |a|
477
+ list << a if a <= Markup
478
+ end
479
+ list
480
+ #--}}}
481
+ end
482
+ def xx_config
483
+ #--{{{
484
+ @@xx_config ||= Hash::new{|h,k| h[k] = {}}
485
+ #--}}}
486
+ end
487
+ def xx_config_for key, xx_which = nil
488
+ #--{{{
489
+ key = key.to_s
490
+ xx_which ||= self
491
+ xx_ancestors(xx_which).each do |a|
492
+ if xx_config[a].has_key? key
493
+ return xx_config[a][key]
494
+ end
495
+ end
496
+ nil
497
+ #--}}}
498
+ end
499
+ def xx_configure key, value, xx_which = nil
500
+ #--{{{
501
+ key = key.to_s
502
+ xx_which ||= self
503
+ xx_config[xx_which][key] = value
504
+ #--}}}
505
+ end
506
+ #--}}}
507
+ end
508
+
509
+ extend ClassMethods
510
+ include InstanceMethods
511
+
512
+ def self::included other, *a, &b
513
+ #--{{{
514
+ ret = super
515
+ other.module_eval do
516
+ include Markup::InstanceMethods
517
+ extend Markup::ClassMethods
518
+ class << self
519
+ define_method("included", Markup::XX_MARKUP_RECURSIVE_INCLUSION_PROC)
520
+ end
521
+ end
522
+ ret
523
+ #--}}}
524
+ end
525
+ XX_MARKUP_RECURSIVE_INCLUSION_PROC = method("included").to_proc
526
+
527
+ xx_configure "method_missing", XX::PERMISSIVE
528
+ xx_configure "tags", []
529
+ xx_configure "doctype", nil
530
+ xx_configure "xmldecl", nil
531
+ #--}}}
532
+ end
533
+
534
+ module XHTML
535
+ #--{{{
536
+ include Markup
537
+ xx_configure "doctype", %(html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd")
538
+
539
+ def xhtml_ which = XHTML, *a, &b
540
+ #--{{{
541
+ xx_which(which) do
542
+ doc = xx_with_doc_in_effect *a, &b
543
+ ddoc = doc.doc
544
+ root = ddoc.root
545
+ if root and root.name and root.name =~ %r/^html$/i
546
+ if root.attribute("lang",nil).nil? or root.attribute("lang",nil).to_s.empty?
547
+ root.add_attribute "lang", "en"
548
+ end
549
+ if root.attribute("xml:lang").nil? or root.attribute("xml:lang").to_s.empty?
550
+ root.add_attribute "xml:lang", "en"
551
+ end
552
+ if root.namespace.nil? or root.namespace.to_s.empty?
553
+ root.add_namespace "http://www.w3.org/1999/xhtml"
554
+ end
555
+ end
556
+ doc
557
+ end
558
+ #--}}}
559
+ end
560
+
561
+ module Strict
562
+ #--{{{
563
+ include XHTML
564
+ xx_configure "doctype", %(html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd")
565
+ xx_configure "tags", %w(
566
+ html head body div span DOCTYPE title link meta style p
567
+ h1 h2 h3 h4 h5 h6 strong em abbr acronym address bdo blockquote cite q code
568
+ ins del dfn kbd pre samp var br a base img
569
+ area map object param ul ol li dl dt dd table
570
+ tr td th tbody thead tfoot col colgroup caption form input
571
+ textarea select option optgroup button label fieldset legend script noscript b
572
+ i tt sub sup big small hr
573
+ )
574
+ xx_configure "method_missing", ::XX::STRICT
575
+
576
+ def xhtml_ which = XHTML::Strict, *a, &b
577
+ #--{{{
578
+ super(which, *a, &b)
579
+ #--}}}
580
+ end
581
+ #--}}}
582
+ end
583
+
584
+ module Transitional
585
+ #--{{{
586
+ include XHTML
587
+ xx_configure "doctype", %(html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd")
588
+ def xhtml_ which = XHTML::Transitional, *a, &b
589
+ #--{{{
590
+ super(which, *a, &b)
591
+ #--}}}
592
+ end
593
+ #--}}}
594
+ end
595
+ #--}}}
596
+ end
597
+
598
+ module HTML4
599
+ #--{{{
600
+ include Markup
601
+ xx_configure "doctype", %(html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN")
602
+
603
+ def html4_ which = HTML4, *a, &b
604
+ #--{{{
605
+ xx_which(which){ xx_with_doc_in_effect *a, &b }
606
+ #--}}}
607
+ end
608
+
609
+ module Strict
610
+ #--{{{
611
+ include HTML4
612
+ xx_configure "doctype", %(html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN")
613
+ xx_configure "tags", %w(
614
+ html head body div span DOCTYPE title link meta style p
615
+ h1 h2 h3 h4 h5 h6 strong em abbr acronym address bdo blockquote cite q code
616
+ ins del dfn kbd pre samp var br a base img
617
+ area map object param ul ol li dl dt dd table
618
+ tr td th tbody thead tfoot col colgroup caption form input
619
+ textarea select option optgroup button label fieldset legend script noscript b
620
+ i tt sub sup big small hr
621
+ )
622
+ xx_configure "method_missing", ::XX::STRICT
623
+ def html4_ which = HTML4::Strict, *a, &b
624
+ #--{{{
625
+ super(which, *a, &b)
626
+ #--}}}
627
+ end
628
+ #--}}}
629
+ end
630
+
631
+ module Transitional
632
+ #--{{{
633
+ include HTML4
634
+ xx_configure "doctype", %(html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN")
635
+ def html4_ which = HTML4::Transitional, *a, &b
636
+ #--{{{
637
+ super(which, *a, &b)
638
+ #--}}}
639
+ end
640
+ #--}}}
641
+ end
642
+ #--}}}
643
+ end
644
+ HTML = HTML4
645
+
646
+ module XML
647
+ #--{{{
648
+ include Markup
649
+ xx_configure "xmldecl", ::REXML::XMLDecl::new
650
+
651
+ def xml_ *a, &b
652
+ #--{{{
653
+ xx_which(XML){ xx_with_doc_in_effect *a, &b }
654
+ #--}}}
655
+ end
656
+ #--}}}
657
+ end
658
+ #--}}}
659
+ end
660
+
661
+ $__xx_rb__ = __FILE__
662
+ end
663
+
664
+
665
+
666
+
667
+
668
+
669
+
670
+
671
+
672
+
673
+ #
674
+ # simple examples - see samples/ dir for more complete examples
675
+ #
676
+
677
+ if __FILE__ == $0
678
+
679
+ class Table < ::Array
680
+ include XX::XHTML::Strict
681
+ include XX::HTML4::Strict
682
+ include XX::XML
683
+
684
+ def doc
685
+ html_{
686
+ head_{ title_{ "xhtml/html4/xml demo" } }
687
+
688
+ div_{
689
+ h_{ "< malformed html & un-escaped symbols" }
690
+ }
691
+
692
+ t_{ "escaped & text > <" }
693
+
694
+ x_{ "<any_valid> xml </any_valid>" }
695
+
696
+ div_(:style => :sweet){
697
+ em_ "this is a table"
698
+
699
+ table_(:width => 42, :height => 42){
700
+ each{|row| tr_{ row.each{|cell| td_ cell } } }
701
+ }
702
+ }
703
+
704
+ script_(:type => :dangerous){ cdata_{ "javascript" } }
705
+ }
706
+ end
707
+ def to_xhtml
708
+ xhtml_{ doc }
709
+ end
710
+ def to_html4
711
+ html4_{ doc }
712
+ end
713
+ def to_xml
714
+ xml_{ doc }
715
+ end
716
+ end
717
+
718
+ table = Table[ %w( 0 1 2 ), %w( a b c ) ]
719
+
720
+ methods = %w( to_xhtml to_html4 to_xml )
721
+
722
+ methods.each do |method|
723
+ 2.times{ puts "-" * 42 }
724
+ puts(table.send(method).pretty)
725
+ puts
726
+ end
727
+
728
+ end