xx 0.1.0

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 (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