amrita2 1.9.6

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,1897 @@
1
+ require 'rexml/document'
2
+ require 'enumerator'
3
+ require 'erb'
4
+
5
+ # Amrita2 adds methods for sanitize support to Object and String.
6
+ # Every added method name has prefix like amrita_xxxxx
7
+ class Object
8
+ # sanitize any object before insertion to HTML document text
9
+ def amrita_sanitize
10
+ Amrita2::Sanitizer::sanitize_text(to_s)
11
+ end
12
+
13
+ # sanitize any object before insertion to HTML tag attribute
14
+ def amrita_sanitize_as_attribute
15
+ Amrita2::Sanitizer::sanitize_attribute_value(to_s)
16
+ end
17
+
18
+ # sanitize any object before insertion to HTML tag attribute for URL
19
+ def amrita_sanitize_as_url
20
+ Amrita2::Sanitizer::sanitize_url(to_s)
21
+ end
22
+ end
23
+
24
+ class Symbol
25
+ # treat Symbol and String equally
26
+ def intern
27
+ self
28
+ end
29
+ end
30
+
31
+ # Amrita2 adds methods for sanitize support to Object and String.
32
+ # Every added method name has prefix like amrita_xxxxx
33
+ class String
34
+ # sanitize self before insertion to HTML document text
35
+ def amrita_sanitize
36
+ Amrita2::Sanitizer::sanitize_text(self)
37
+ end
38
+
39
+ # sanitize self before insertion to HTML tag attribute
40
+ def amrita_sanitize_as_attribute
41
+ Amrita2::Sanitizer::sanitize_attribute_value(self)
42
+ end
43
+
44
+ # sanitize self before insertion to HTML tag attribute for URL
45
+ def amrita_sanitize_as_url
46
+ Amrita2::Sanitizer::sanitize_url(self)
47
+ end
48
+ end
49
+
50
+
51
+ # Amrita2 adds methods for support of internal compilation XML to Ruby conde
52
+ # Every added method name has prefix like amrita_xxxxx.
53
+ # Don't call them.
54
+ module REXML
55
+ class Child
56
+ attr_accessor :amrita_no_spec
57
+ def amrita_compile_main_code(cg)
58
+ #cg.put_constant(to_s)
59
+ cg.put_rexml_node(self)
60
+ end
61
+
62
+ def amrita_has_cdata?
63
+ false
64
+ end
65
+ end
66
+
67
+
68
+ class Parent < Child
69
+ def amrita_compile_main_code(cg)
70
+ if amrita_no_spec and not amrita_has_cdata?
71
+ super
72
+ else
73
+ each_child do |c|
74
+ c.amrita_compile_main_code(cg)
75
+ end
76
+ end
77
+ end
78
+
79
+ def amrita_has_cdata?
80
+ each_child do |c|
81
+ case c
82
+ when CData
83
+ return true
84
+ when Parent
85
+ return true if c.amrita_has_cdata?
86
+ else
87
+ end
88
+ end
89
+ false
90
+ end
91
+ end
92
+
93
+ class XMLDecl < Child # :nodoc:
94
+ def amrita_compile_main_code(cg)
95
+ # don't output XMLDecl
96
+ end
97
+ end
98
+
99
+ class DocType < Parent # :nodoc:
100
+ def amrita_compile_main_code(cg)
101
+ cg.put_constant(to_s)
102
+ cg.put_constant("\n")
103
+ end
104
+ end
105
+
106
+ class Comment < Child
107
+ def amrita_compile_main_code(cg)
108
+ cg.put_constant("<!--#{ to_s}-->")
109
+ end
110
+ end
111
+
112
+ class Element < Parent
113
+ attr_accessor :amrita_spec
114
+ def amrita_compile_main_code(cg)
115
+ if amrita_no_spec and not amrita_has_cdata?
116
+ super
117
+ else
118
+ cg.compile_element_main_code(self) do
119
+ super
120
+ end
121
+ end
122
+ end
123
+
124
+ def amrita_compile_sub_code(cg)
125
+ cg.compile_element_sub_code(self) do
126
+ elements.each do |e|
127
+ e.amrita_compile_sub_code(cg)
128
+ end
129
+ end
130
+ end
131
+
132
+ alias to_s_org to_s
133
+ def to_s
134
+ Amrita2::SanitizedString[to_s_org]
135
+ end
136
+ end
137
+
138
+ class CData < Text
139
+ def amrita_compile_main_code(cg)
140
+ cg.put_cdata(self)
141
+ end
142
+ end
143
+ end
144
+
145
+ module Amrita2 # :nodoc:
146
+ module SanitizeSupport # :nodoc:
147
+
148
+ # Amrita2 sanitize anything except for SanitizedString
149
+ # If you want to sanitize yourself and don't want to Amrita2 sanitize your object,
150
+ # pass SanitizedString[x] as model data.
151
+ class SanitizedString < String
152
+ def SanitizedString::[](s)
153
+ new(s).freeze
154
+ end
155
+
156
+ def amrita_sanitize
157
+ self
158
+ end
159
+
160
+ def amrita_sanitize_as_attribute
161
+ self
162
+ end
163
+
164
+ def amrita_sanitize_as_url
165
+ self
166
+ end
167
+
168
+ def to_s
169
+ self
170
+ end
171
+
172
+ def inspect
173
+ %[Amrita2::SanitizedString[#{super}]]
174
+ end
175
+ end
176
+
177
+ # This module provide methods for avoid XSS vulnerability
178
+ # taken from IPA home page(Japanese)
179
+ # http://www.ipa.go.jp/security/awareness/vendor/programming/a01_02.html
180
+ module Sanitizer # :nodoc:
181
+ NAMECHAR = '[-\w\d\.:]'
182
+ NAME = "([\\w:]#{NAMECHAR}*)"
183
+ NOT_REFERENCE = "(?!#{NAME};|&#\\d+;|&#x[0-9a-fA-F]+;)" # borrowed from rexml
184
+ AMP_WITHOUT_REFRENCE = /&#{NOT_REFERENCE}/
185
+ # escape &<>
186
+ def sanitize_text(text)
187
+ s = text.dup
188
+ s.gsub!(AMP_WITHOUT_REFRENCE, '&amp;')
189
+ s.gsub!("<", '&lt;')
190
+ s.gsub!(">", '&gt;')
191
+ s
192
+ end
193
+
194
+ # escape &<>"'
195
+ def sanitize_attribute_value(text)
196
+ s = text.dup
197
+ s.gsub!(AMP_WITHOUT_REFRENCE, '&amp;')
198
+ s.gsub!("<", '&lt;')
199
+ s.gsub!(">", '&gt;')
200
+ s.gsub!('"', '&quot;')
201
+ s.gsub!("'", '&#39;')
202
+ s
203
+ end
204
+
205
+ DefaultAllowedScheme = {
206
+ 'http' => true,
207
+ 'https' => true,
208
+ 'ftp' => true,
209
+ 'mailto' => true,
210
+ }
211
+
212
+ #UrlInvalidChar = Regexp.new(%q|[^;/?:@&=+$,A-Za-z0-9\-_.!~*'()%]|)
213
+ UrlInvalidChar = Regexp.new(%q|[^;/?:@&=+$,A-Za-z0-9\-_.!~*'()%#]|) #'
214
+
215
+ # +sanitize_url+ accepts only these characters
216
+ # --- http://www.ietf.org/rfc/rfc2396.txt ---
217
+ # uric = reserved | unreserved | escaped
218
+ # reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
219
+ # unreserved = alphanum | mark
220
+ # mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
221
+ # escaped = "%" hex hex
222
+ #
223
+ # +sanitize_url+ accepts only schems specified by +allowd_scheme+
224
+ #
225
+ # The default is http: https: ftp: mailt:
226
+
227
+ def sanitize_url(text, allowd_scheme = DefaultAllowedScheme)
228
+ # return nil if text has characters not allowd for URL
229
+
230
+ return nil if text =~ UrlInvalidChar
231
+
232
+ # return '' if text has an unknown scheme
233
+ # --- http://www.ietf.org/rfc/rfc2396.txt ---
234
+ # scheme = alpha *( alpha | digit | "+" | "-" | "." )
235
+
236
+ if text =~ %r|^([A-Za-z][A-Za-z0-9+\-.]*):|
237
+ return nil unless allowd_scheme[$1]
238
+ end
239
+
240
+ # escape HTML
241
+ # special = "&" | "<" | ">" | '"' | "'"
242
+ # But I checked "<" | ">" | '"' before.
243
+ s = text.dup
244
+ #s.gsub!("&", '&amp;')
245
+ s.gsub!("'", '&#39;')
246
+
247
+ s
248
+ end
249
+
250
+ module_function :sanitize_text, :sanitize_attribute_value, :sanitize_url
251
+ end
252
+ end
253
+ include SanitizeSupport
254
+
255
+ module Runtime # :nodoc:
256
+ include SanitizeSupport
257
+ def new_element(tag, attrs={})
258
+ ret = REXML::Element.new(tag.to_s)
259
+ attrs.each do |k,v|
260
+ ret.attributes[k.to_s] = v.to_s
261
+ end
262
+ ret
263
+ end
264
+
265
+ def start_tag(e, out="")
266
+ out << "<#{e.expanded_name}"
267
+ e.attributes.each_attribute do |attr|
268
+ next if attr.value == ""
269
+ out << " "
270
+ attr.write(out)
271
+ end unless e.attributes.empty?
272
+ out << ">"
273
+ out
274
+ end
275
+
276
+ def end_tag(e, out="")
277
+ out << "</#{e.expanded_name}>"
278
+ out
279
+ end
280
+
281
+ class Context # :nodoc:
282
+ attr_reader :main_stream, :sub_streams, :mv_index
283
+ attr_accessor :current_data, :current_binding
284
+
285
+ def initialize(stream="", old_context=nil, opt={})
286
+ @sub_streams = {}
287
+ if old_context
288
+ @sub_streams = old_context.sub_streams if opt[:inherit_sub_streams]
289
+ @main_stream = (stream or old_context.main_stream)
290
+ @current_binding = (opt[:binding] or old_context.current_binding)
291
+ @current_data = (opt[:data] or old_context.current_data)
292
+ @mv_index = old_context.mv_index
293
+ else
294
+ @main_stream = (stream or "")
295
+ @current_binding = TOPLEVEL_BINDING
296
+ @current_data = nil
297
+ @mv_index = {}
298
+ end
299
+ end
300
+
301
+ def sub_stream(key)
302
+ @sub_streams[key] ||= ""
303
+ end
304
+
305
+ def clear_substream(key)
306
+ @sub_streams[key] = ""
307
+ end
308
+ end
309
+
310
+ def context_stack
311
+ Thread::current[:amrita_context_stack] ||= [ Context.new ]
312
+ end
313
+
314
+ def current_context
315
+ context_stack.first
316
+ end
317
+
318
+ def new_context(stream = "", opt = {}, &block)
319
+ context_stack.unshift Context.new(stream, context_stack[0], opt)
320
+ yield
321
+ if stream.kind_of?(String)
322
+ SanitizedString[stream]
323
+ else
324
+ stream
325
+ end
326
+ ensure
327
+ context_stack.shift
328
+ end
329
+
330
+ def new_binding(b, &block)
331
+ new_context(nil, :binding => b, :inherit_sub_streams => true, &block)
332
+ end
333
+
334
+ def new_context_data(d, &block)
335
+ new_context(nil, :data => d, :inherit_sub_streams => true, &block)
336
+ end
337
+
338
+ def get_mainstream
339
+ current_context.main_stream
340
+ end
341
+
342
+ def get_substream(key)
343
+ current_context.sub_stream(key)
344
+ end
345
+
346
+ def output_substream(key)
347
+ c = current_context
348
+ s = c.sub_stream(key)
349
+ c.main_stream << SanitizedString[s]
350
+ c.clear_substream(key)
351
+ end
352
+
353
+ def set_binding(b)
354
+ current_context.current_binding = b
355
+ end
356
+
357
+ def get_binding
358
+ current_context.current_binding
359
+ end
360
+
361
+ def set_context_data(d)
362
+ current_context.current_data = d
363
+ end
364
+
365
+ def get_context_data
366
+ current_context.current_data
367
+ end
368
+ end
369
+ include Runtime
370
+
371
+ module Core # :nodoc:
372
+ class CodeGenerator # :nodoc:
373
+ attr_accessor :current_stream, :ie_hack, :eval_cdata_as_erb
374
+
375
+ def initialize(stream = "get_mainstream")
376
+ @current_stream = stream
377
+ init_results
378
+ init_strbuf
379
+ @current_indent = 0
380
+ @varcnt=0
381
+ @const_cnt=0
382
+ @ie_hack = false
383
+ @eval_cdata_as_erb = false
384
+ @module_stack = [CGModule.new('X')]
385
+ end
386
+
387
+ def put_rexml_node(node)
388
+ s = ""
389
+ node.write(s, -1, false, @ie_hack)
390
+ put_constant(s)
391
+ end
392
+
393
+ def put_constant(msg)
394
+ @strbuf.concat msg
395
+ end
396
+
397
+ def flush
398
+ if @strbuf.size > 0
399
+ @results << [@current_indent, "#{current_stream} << " + @strbuf.inspect]
400
+ init_strbuf
401
+ end
402
+ end
403
+
404
+ def put_expression(exp)
405
+ code("#{current_stream} << #{exp}")
406
+ end
407
+
408
+ def code(l)
409
+ flush
410
+ @results << [@current_indent, l]
411
+ end
412
+
413
+ def new_var
414
+ @varcnt += 1
415
+ "v#{@varcnt}"
416
+ end
417
+
418
+ def results
419
+ flush
420
+ m = @module_stack.shift
421
+ if m
422
+ m.constants.each do |name, defs|
423
+ code("#{name} = #{defs}")
424
+ end
425
+ end
426
+ @results.collect do |level, line|
427
+ " " * level + line
428
+ end
429
+ end
430
+
431
+ def if_(cond)
432
+ code("if #{cond}")
433
+ level_up do
434
+ yield
435
+ end
436
+ code("end")
437
+ end
438
+
439
+ def else_
440
+ level_down do
441
+ code("else")
442
+ end
443
+ yield
444
+ end
445
+
446
+ def level_up
447
+ flush
448
+ @current_indent += 1
449
+ yield
450
+ ensure
451
+ flush
452
+ @current_indent -= 1
453
+ end
454
+
455
+ def level_down
456
+ flush
457
+ @current_indent -= 1
458
+ yield
459
+ ensure
460
+ flush
461
+ @current_indent += 1
462
+ end
463
+
464
+ def define_method(name, *params)
465
+ if params.size == 0
466
+ code("def self.#{name}")
467
+ else
468
+ code("def self.#{name}(#{params.join(',')})")
469
+ end
470
+ level_up do
471
+ yield
472
+ end
473
+ code("end")
474
+ end
475
+
476
+ def define_element_method(name, new_stream, &block)
477
+ define_method(name, "val=nil", "attrs={}", "&block") do
478
+ unless @eval_cdata_as_erb
479
+ code("attrs = val if val.kind_of?(Hash)")
480
+ end
481
+ new_context("get_substream(:#{new_stream})") do
482
+ yield
483
+ end
484
+ end
485
+ end
486
+
487
+ def define_element_method_old(name, new_stream, &block)
488
+ define_method(name, "val=nil", "attrs={}", "&block") do
489
+ if_("val.kind_of?(AttrArray)") do
490
+ code("attrs = val ; val = val.body ")
491
+ unless @eval_cdata_as_erb
492
+ else_ do
493
+ code("attrs = val if val.kind_of?(Hash)")
494
+ end
495
+ end
496
+ end
497
+ new_context("get_substream(:#{new_stream})") do
498
+ yield
499
+ end
500
+ end
501
+ end
502
+
503
+ def define_module(name)
504
+ @module_stack.unshift CGModule.new(name)
505
+ code("module #{name}")
506
+ level_up do
507
+ yield
508
+ m = @module_stack.shift
509
+ m.end_of_module(self)
510
+ end
511
+ code("end")
512
+ end
513
+
514
+ def define_method_and_call(*vars, &block)
515
+ m = @module_stack[0]
516
+ meth_name = m.define_method(vars, &block)
517
+ meth_call_code = "#{meth_name}(#{(vars).join(',')})"
518
+ code(meth_call_code)
519
+ meth_call_code
520
+ end
521
+
522
+ def convert(encto, encfrom, *args)
523
+ a = args.join(",")
524
+ code("#{a} = *Iconv::iconv(#{encto.inspect}, #{encfrom.inspect}, #{a})")
525
+ end
526
+
527
+ def convert_hash(encto, encfrom, hash)
528
+ code("#{hash}.each do |k, v|")
529
+ level_up do
530
+ code("v = *Iconv::iconv(#{encto.inspect}, #{encfrom.inspect}, v)")
531
+ code("#{hash}[k] = v")
532
+ end
533
+ code("end")
534
+ end
535
+
536
+ def compile_element_main_code(element, &block)
537
+ spec = element.amrita_spec
538
+ if spec
539
+ spec.compile_element_main_code(element, self, &block)
540
+ else
541
+ generate_static_element_code(element, &block)
542
+ end
543
+ end
544
+
545
+ def compile_element_sub_code(element, &block)
546
+ spec = element.amrita_spec
547
+ if spec
548
+ spec.compile_element_sub_code(element, self, &block)
549
+ else
550
+ yield
551
+ end
552
+ end
553
+
554
+ def init_results
555
+ @results = []
556
+ end
557
+
558
+ def init_strbuf
559
+ @strbuf = ""
560
+ end
561
+
562
+ def generate_static_element_code(e, option={}, &block)
563
+ if e.amrita_no_spec and not e.amrita_has_cdata?
564
+ put_rexml_node(e)
565
+ elsif option[:mv_attr]
566
+ put_constant("<#{e.name} ")
567
+ attr_to_output = e.attributes.to_a
568
+ key_attr_nm = option[:key_attr_name].to_s
569
+ if option[:delete_id]
570
+ attr_to_output.reject! do |attr|
571
+ attr.name == key_attr_nm
572
+ end
573
+ end
574
+ a = [e.name] + attr_to_output.collect do |attr|
575
+ put_constant(attr.to_string+" ")
576
+ end
577
+ option[:mv_attr].each do |k, v|
578
+ put_constant "#{k}='"
579
+ c = define_constant("Amrita2::MultiValue.new(*#{v.inspect})")
580
+ put_expression("#{c}.value")
581
+ end
582
+ put_constant("'>")
583
+ yield
584
+ put_constant("</#{e.name}>")
585
+ elsif e.name and e.name.size > 0
586
+ no_tag = option[:delete_tag]
587
+ attr_to_output = e.attributes.to_a
588
+ unless no_tag
589
+ key_attr_nm = option[:key_attr_name].to_s
590
+ if option[:delete_id]
591
+ attr_to_output.reject! do |attr|
592
+ attr.name == key_attr_nm
593
+ end
594
+ end
595
+ end
596
+ no_tag ||= (e.name == 'span' and attr_to_output.size == 0)
597
+ unless no_tag
598
+ a = [e.name] + attr_to_output.collect do |attr|
599
+ attr.to_string
600
+ end
601
+ put_constant("<#{a.join(' ')}>")
602
+ end
603
+ yield
604
+ put_constant("</#{e.name}>") unless no_tag
605
+ else
606
+ yield
607
+ end
608
+ end
609
+
610
+ def new_context(new_stream="nil", opt={}, &block)
611
+ code("new_context(#{new_stream}, #{opt.inspect}) do")
612
+ level_up do
613
+ yield
614
+ end
615
+ code("end")
616
+ end
617
+
618
+ def put_cdata(cdata)
619
+ if @eval_cdata_as_erb
620
+ src = "<% $_ = get_context_data; %>" + cdata.to_s
621
+ erb = define_constant %[ERB.new(#{src.inspect})]
622
+ put_expression %[#{erb}.result(get_binding, &block)]
623
+ else
624
+ put_rexml_node(cdata)
625
+ end
626
+ end
627
+
628
+ def define_constant(const_def)
629
+ m = @module_stack[0]
630
+ ret = m.define_constant(const_def, @const_cnt)
631
+ @const_cnt += 1
632
+ ret
633
+ end
634
+
635
+ class CGModule # :nodoc:
636
+ attr_reader :name
637
+ attr_reader :constants
638
+ attr_reader :methods
639
+
640
+ def initialize(name)
641
+ @name = name
642
+ @constants = []
643
+ @methods = []
644
+ end
645
+
646
+ def define_constant(const_def, cnt)
647
+ name = "C#{sprintf("%03d", cnt)}"
648
+ @constants << [name, const_def]
649
+ name
650
+ end
651
+
652
+ def define_method(vars, &block)
653
+ name = "m#{sprintf("%03d", @methods.size)}"
654
+ @methods << [name, vars, block]
655
+ name
656
+ end
657
+
658
+ def end_of_module(cg)
659
+ @constants.each do |name, defs|
660
+ cg.code("#{name} = #{defs}")
661
+ end
662
+ @methods.each do |name, vars, body|
663
+ cg.define_method(name, vars) do
664
+ body.call
665
+ end
666
+ end
667
+ end
668
+ end
669
+ end
670
+
671
+ class Matcher # :nodoc:
672
+ def find(e)
673
+ return e if match(e)
674
+ e.elements.each do |c|
675
+ x = find(c)
676
+ return x if x
677
+ end
678
+ nil
679
+ end
680
+ end
681
+
682
+ class AttrMatcher < Matcher # :nodoc:
683
+ attr_reader :sym
684
+ def initialize(sym, val)
685
+ @sym = sym
686
+ @sym_s = sym.to_s
687
+ @val = val
688
+ end
689
+
690
+ def match(e)
691
+ if @val != '*'
692
+ @val == e.attributes[@sym_s]
693
+ else
694
+ e.attributes[@sym_s]
695
+ end
696
+ end
697
+ end
698
+
699
+ class TagMatcher < Matcher # :nodoc:
700
+ def initialize(tag)
701
+ @tag = tag.to_s
702
+ end
703
+
704
+ def match(e)
705
+ e.name == @tag
706
+ end
707
+ end
708
+
709
+ class AnyMatcher < Matcher # :nodoc:
710
+ def match(e)
711
+ true
712
+ end
713
+ end
714
+
715
+
716
+ class SpecOptionMeta # :nodoc:
717
+ module MetaData # :nodoc:
718
+ def check_option(spec, k, v)
719
+ end
720
+ end
721
+
722
+ @@options = {}
723
+ def self.get_option_meta(name)
724
+ @@options[name]
725
+ end
726
+
727
+ def self.define_option(name, meta=nil, *args)
728
+ case meta
729
+ when nil
730
+ meta = Object.new
731
+ meta.extend MetaData
732
+ when MetaData
733
+ # do nothing
734
+ when Module
735
+ m = Object.new
736
+ m.extend meta
737
+ meta = m
738
+ end
739
+ args.each do |mod|
740
+ meta.extend mod
741
+ end
742
+ @@options[name] = meta
743
+ end
744
+
745
+ def self.check_options(spec)
746
+ spec.option.each do |k, v|
747
+ meta = @@options[k]
748
+ case meta
749
+ when nil
750
+ raise NameError, "option #{k} was not defined"
751
+ else
752
+ meta.check_option(spec, k, v)
753
+ end
754
+ end
755
+ end
756
+
757
+ def self.setup_compiler(spec, compiler)
758
+ spec.option.each do |k, v|
759
+ next unless v
760
+ meta = @@options[k]
761
+ case meta
762
+ when CompilerOption
763
+ compiler.extend meta
764
+ end
765
+ end
766
+ end
767
+
768
+ def self.inherit_option(spec, child_opt)
769
+ spec.option.each do |k, v|
770
+ meta = @@options[k]
771
+ case meta
772
+ when InheritableOption
773
+ meta.inherite_option(k, v, child_opt)
774
+ end
775
+ end
776
+ end
777
+
778
+ def self.define_element_ext(spec, name, sym, params, &block)
779
+ meta = @@options[name]
780
+ if meta
781
+ meta.define_element_ext(spec, name, sym, params || {}, &block)
782
+ end
783
+ end
784
+
785
+ module SymbolOption # :nodoc:
786
+ def check_option(spec, k, v)
787
+ case v
788
+ when Symbol, String
789
+ else
790
+ raise "option #{k} must be a Symbol"
791
+ end
792
+ end
793
+ end
794
+
795
+ module InheritableOption # :nodoc:
796
+ include MetaData
797
+ def inherite_option(k, v, child_opt)
798
+ return if child_opt.has_key?(k)
799
+ child_opt[k] = v if v
800
+ end
801
+ end
802
+
803
+ module CompilerOption # :nodoc:
804
+ include MetaData
805
+ end
806
+
807
+ module ElementExt # :nodoc:
808
+ include MetaData
809
+ def define_element_ext(spec, name, sym, params, &block)
810
+ pp = params.clone
811
+ pp[name] = true
812
+ spec.dynamic_element(sym, pp, &block)
813
+ end
814
+ end
815
+
816
+ define_option(:key_attr_name, SymbolOption, InheritableOption)
817
+ define_option(:delete_tag)
818
+ define_option(:matcher)
819
+ define_option(:output_stream, SymbolOption)
820
+ define_option(:delete_id, InheritableOption)
821
+ define_option(:place_holder)
822
+ define_option(:match_many)
823
+ define_option(:generate_template, InheritableOption)
824
+ define_option(:encoding, InheritableOption)
825
+ define_option(:dummy_element, ElementExt)
826
+ define_option(:ie_hack)
827
+ define_option(:mv_attr)
828
+ define_option(:debug_source)
829
+ end
830
+
831
+ class DynamicElementSpec # :nodoc:
832
+ attr_accessor :cloned, :parent
833
+ attr_reader :sym, :matcher, :children, :option, :assigned_elements
834
+
835
+ def initialize(sym, parent, option={}, &block)
836
+ case sym
837
+ when Symbol
838
+ @sym = sym
839
+ when nil
840
+ @sym = nil
841
+ else
842
+ @sym = sym.to_s.intern
843
+ end
844
+ @parent = parent
845
+ @option, @children = option, []
846
+
847
+ SpecOptionMeta::check_options(self)
848
+ @direct_define = @option[:direct_define]
849
+ setup_matcher
850
+ setup_compiler
851
+ if block_given?
852
+ @in_setup = true
853
+ self.instance_eval(&block)
854
+ @in_setup = false
855
+ end
856
+
857
+ check_double_id
858
+ end
859
+
860
+ def inspect(lvl=0)
861
+ spc = " " * lvl
862
+ if children.size > 0
863
+ "#{spc}dynamic_element(#{sym.inspect}, #{@option.inspect}, #{self.class.name}) do\n" +
864
+ children.collect {|c| c.inspect(lvl+1)} .join +
865
+ "#{spc}end\n"
866
+ else
867
+ "#{spc}dynamic_element(#{sym.inspect}, #{@option.inspect}, #{self.class.name})\n"
868
+ end
869
+ end
870
+
871
+ def name_for(element)
872
+ @sym
873
+ end
874
+
875
+ def dynamic_element(name, opt={}, cls=DynamicElementSpec, &block)
876
+ SpecOptionMeta::inherit_option(self, opt)
877
+ new_spec = cls.new(name, self, opt, &block)
878
+ @children << new_spec
879
+ new_spec
880
+ end
881
+
882
+
883
+ def compile_element_main_code(element, cg, &block)
884
+ return if @option[:dummy_element]
885
+ @compiler.compile_main(element, self, cg, &block)
886
+ end
887
+
888
+ def compile_element_sub_code(element, cg, &block)
889
+ return if @option[:dummy_element]
890
+ @compiler.compile_sub(element, self, cg, &block)
891
+ end
892
+
893
+ def method_missing(*args, &block)
894
+ name = nil
895
+ name = args.shift if args.first.kind_of?(Symbol)
896
+ return super unless @in_setup
897
+ if @direct_define
898
+ params = args[-1] || {}
899
+ dynamic_element(name, params, &block)
900
+ else
901
+ sym = nil
902
+ sym = args.shift if args.first.kind_of?(Symbol)
903
+ SpecOptionMeta::define_element_ext(self, name , sym, args.first, &block) or
904
+ super
905
+ end
906
+ end
907
+
908
+ def module_name
909
+ sym.to_s.capitalize
910
+ end
911
+
912
+ def output_stream
913
+ @option[:output_stream] or sym
914
+ end
915
+
916
+ def get_methods
917
+ h = {}
918
+ ret = children.find_all do |c|
919
+ ret = h[c.sym]
920
+ h[c.sym] = true
921
+ ret != true and c.sym
922
+ end.collect {|c| c.sym }
923
+ if ret.size == 0
924
+ children.each do |c|
925
+ ret.concat c.get_methods
926
+ end
927
+ end
928
+ ret
929
+ end
930
+
931
+ def make_default_template
932
+ tag = @option[:html_tag]
933
+ tag = 'div' if @option[:div]
934
+ unless sym or tag
935
+ ret = children.collect do |c|
936
+ c.make_default_template
937
+ end.join('')
938
+ return ret
939
+ end
940
+
941
+ attr = @option[:html_attr]
942
+ a = nil
943
+ if attr
944
+ a = attr.collect do |k,v|
945
+ "#{k}='#{v}'"
946
+ end.join(' ')
947
+ end
948
+ if @option[:static_data]
949
+ tag_and_attr = tag.to_s
950
+ tag_and_attr += ' ' + a if attr and attr.size > 0
951
+ "<#{tag_and_attr}>#{@option[:static_data]}</#{tag}>"
952
+ else
953
+ tag ||= 'span'
954
+ tag_and_attr = tag.to_s
955
+ tag_and_attr += ' ' + a if attr and attr.size > 0
956
+ if sym or tag
957
+ idparam = ""
958
+ idparam = " #{@option[:key_attr_name] }='#{sym}'" if sym
959
+ tag = 'span' unless tag
960
+ if children.size > 0
961
+ "<#{tag_and_attr}#{idparam}>" +
962
+ children.collect do |c|
963
+ c.make_default_template
964
+ end.join('') + "</#{tag}>"
965
+ else
966
+ "<#{tag_and_attr} #{idparam} />"
967
+ end
968
+ else
969
+ end
970
+ end
971
+ end
972
+
973
+ def generate_expander(mod, parent=nil)
974
+ @compiler.generate_expander(self, mod, parent)
975
+ end
976
+
977
+ def get_dynamic_specs
978
+ if sym
979
+ [self]
980
+ else
981
+ children.collect do |c|
982
+ c.get_dynamic_specs
983
+ end.flatten
984
+ end
985
+ end
986
+
987
+ def check_double_id
988
+ h = {}
989
+ children.each do |c|
990
+ next unless c.sym
991
+ raise "dobule ID #{c.sym} in #{sym || 'ROOT'}" if h[c.sym] and not c.option[:dummy_element]
992
+ h[c.sym] = true
993
+ end
994
+ end
995
+
996
+ private
997
+ def setup_matcher
998
+ if sym
999
+ @matcher = @option[:matcher] || AttrMatcher.new(@option[:key_attr_name], sym.to_s)
1000
+ else
1001
+ @matcher = AnyMatcher.new
1002
+ end
1003
+ end
1004
+
1005
+ def setup_compiler
1006
+ if @option[:compiler]
1007
+ @compiler = @option[:compiler]
1008
+ else
1009
+ @compiler = DefaultCompiler.new
1010
+ SpecOptionMeta::setup_compiler(self, @compiler)
1011
+ end
1012
+ end
1013
+
1014
+ end
1015
+
1016
+ class RootSpec < DynamicElementSpec # :nodoc:
1017
+ def initialize(option={}, &block)
1018
+ option[:key_attr_name] = :id unless option[:key_attr_name]
1019
+ super(nil, nil, option, &block)
1020
+ end
1021
+
1022
+ def module_name
1023
+ "MainContext"
1024
+ end
1025
+
1026
+ def create_template_module(e, doc=nil)
1027
+ m = Module.new
1028
+ result = create_template_module_text(e)
1029
+ @option[:debug_source].puts result if @option[:debug_source]
1030
+ m.module_eval result
1031
+ if doc
1032
+ m.const_set(:XmlDecl, doc.xml_decl.to_s)
1033
+ m.const_set(:DocType, doc.doctype.to_s)
1034
+ end
1035
+ m
1036
+ end
1037
+
1038
+ def create_template_module_text(e)
1039
+ specs = [self]
1040
+
1041
+ traverse_and_search_match(specs, e)
1042
+ # not match check
1043
+ raise "SPEC #{specs.first.sym} was not matched" if specs.size > 0
1044
+
1045
+ cg = CodeGenerator.new
1046
+ cg.ie_hack = option[:ie_hack]
1047
+ cg.eval_cdata_as_erb = option[:eval_cdata_as_erb]
1048
+ cg.code("include Amrita2")
1049
+ cg.code("extend Amrita2")
1050
+
1051
+ cg.define_method(:expand_template, :out, "&block") do
1052
+ define_expand_template(cg, e)
1053
+ end
1054
+
1055
+ cg.define_module(module_name) do
1056
+ cg.code("Methods = #{get_methods.inspect}")
1057
+ cg.code("include Amrita2")
1058
+ cg.code("extend Amrita2")
1059
+ define_sub_modules(cg, e)
1060
+ end
1061
+
1062
+ cg.define_method(:xml_decl) do
1063
+ cg.code("XmlDecl.dup")
1064
+ end
1065
+
1066
+ cg.define_method(:doctype) do
1067
+ cg.code("DocType.dup")
1068
+ end
1069
+
1070
+ cg.results.join("\n")
1071
+ end
1072
+
1073
+ def define_expand_template(cg, e)
1074
+ cg.code("new_context(out) do")
1075
+ cg.level_up do
1076
+ cg.code("block.call(#{module_name or 'nil'}) if block_given? ")
1077
+ e.amrita_compile_main_code(cg)
1078
+ end
1079
+ cg.code("current_context.mv_index.clear")
1080
+ cg.code("end")
1081
+ end
1082
+
1083
+ def define_sub_modules(cg, e)
1084
+ e.amrita_compile_sub_code(cg)
1085
+ end
1086
+
1087
+ def traverse_and_search_match(child_specs, element)
1088
+ spec = child_specs.first
1089
+ unless spec
1090
+ element.amrita_no_spec = true unless element.amrita_spec
1091
+ return false
1092
+ end
1093
+ if spec.matcher.match(element)
1094
+ element.amrita_no_spec = false
1095
+ element.amrita_spec = spec if spec.sym
1096
+ child_specs.shift unless spec.option[:match_many]
1097
+ specs = spec.children.collect do |s|
1098
+ s.get_dynamic_specs
1099
+ end.flatten
1100
+ if spec.option[:generate_template] and specs.size > 0 and element.elements.size == 0
1101
+ generate_template(spec, element)
1102
+ end
1103
+ traverse_and_search_match(specs, element)
1104
+
1105
+ # not match check
1106
+ specs.each do |s|
1107
+ next if s.option[:match_many]
1108
+ next if s.option[:static_data]
1109
+ raise "SPEC #{s.sym} was not matched"
1110
+ end
1111
+ else
1112
+ # check children has spec and set it to amrita_no_spec and return the result
1113
+ f = false
1114
+ element.elements.each do |ce|
1115
+ f |= traverse_and_search_match(child_specs, ce)
1116
+ end
1117
+ element.amrita_no_spec = (not f)
1118
+ f
1119
+ end
1120
+ end
1121
+
1122
+ def generate_template(spec, element)
1123
+ src = spec.make_default_template
1124
+ doc = REXML::Document.new src
1125
+ doc.root.elements.each do |c|
1126
+ element.add(c)
1127
+ end
1128
+ end
1129
+
1130
+ def generate_expander(mod, parent=nil)
1131
+ Expander::Root.new(self, mod)
1132
+ end
1133
+ end
1134
+
1135
+ class DefaultCompiler # :nodoc:
1136
+ def compile_main(element, dyn_spec, cg, &block)
1137
+ if dyn_spec.option[:place_holder]
1138
+ cg.generate_static_element_code(element) do
1139
+ end
1140
+ elsif dyn_spec.option[:static_element]
1141
+ cg.generate_static_element_code(element) do
1142
+ element.each_child do |c|
1143
+ c.amrita_compile_main_code(cg)
1144
+ end
1145
+ end
1146
+ else
1147
+ sym = element.amrita_spec.sym
1148
+ compile_main_code(element, dyn_spec, cg, sym.to_s, &block)
1149
+ end
1150
+ end
1151
+
1152
+ def compile_sub(element, dyn_spec, cg, &block)
1153
+ if dyn_spec.option[:static_element]
1154
+ element.elements.each do |c|
1155
+ c.compile_sub_code(cg)
1156
+ end
1157
+ else
1158
+ compile_dynamic_element_code(element, dyn_spec, cg, &block)
1159
+ end
1160
+ end
1161
+
1162
+ def generate_expander(spec, mod, parent=nil)
1163
+ if spec.children.size > 0
1164
+ Expander::Node.new(parent, spec, mod)
1165
+ else
1166
+ Expander::Leaf.new(parent, spec, mod)
1167
+ end
1168
+ end
1169
+
1170
+ private
1171
+ def compile_main_code(element, dyn_spec, cg, sym)
1172
+ cg.code("output_substream(:#{sym})")
1173
+ end
1174
+
1175
+ def compile_dynamic_element_code(element, dyn_spec, cg, &block)
1176
+ sym = dyn_spec.sym
1177
+ if dyn_spec.children.size == 0
1178
+ if dyn_spec.option[:eval_cdata_as_erb] and element.amrita_has_cdata?
1179
+ code_for_not_leaf_element(element, dyn_spec, cg, &block)
1180
+ else
1181
+ code_for_leaf_element(element, dyn_spec, cg, &block)
1182
+ end
1183
+ else
1184
+ code_for_not_leaf_element(element, dyn_spec, cg, &block)
1185
+ end
1186
+ end
1187
+
1188
+ def code_for_leaf_element(element, dyn_spec, cg, &block)
1189
+ sym = dyn_spec.sym
1190
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1191
+ enc = dyn_spec.option[:encoding]
1192
+ if enc
1193
+ cg.convert("utf-8", enc, "sym", "val")
1194
+ cg.convert_hash("utf-8", enc, "attrs")
1195
+ end
1196
+ code_for_element_and_scalar(element, dyn_spec, cg)
1197
+ end
1198
+ end
1199
+
1200
+ def code_for_not_leaf_element(element, dyn_spec, cg, &block)
1201
+ sym = dyn_spec.name_for(element)
1202
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1203
+ code_for_element_with_child(element, dyn_spec, cg, &block)
1204
+ end
1205
+ cg.define_module(dyn_spec.module_name) do
1206
+ cg.code("Methods = #{dyn_spec.get_methods.inspect}")
1207
+ cg.code("include Amrita2")
1208
+ cg.code("extend Amrita2")
1209
+ yield
1210
+ end
1211
+ end
1212
+
1213
+ def code_for_element_and_scalar(element, dyn_spec, cg)
1214
+ cg.if_("val == true") do
1215
+ t = element.enum_for(:each_child).collect do |c|
1216
+ c.to_s
1217
+ end.join
1218
+ cc = cg.define_constant("SanitizedString[#{t.inspect}]")
1219
+ cg.code("val = #{cc}")
1220
+ end
1221
+ cg.if_("attrs.size == 0") do
1222
+ cg.generate_static_element_code(element, dyn_spec.option) do
1223
+ cg.put_expression("val.amrita_sanitize")
1224
+ end
1225
+ cg.else_ do
1226
+ gen_element_with_attr(element, dyn_spec, cg)
1227
+ cg.code("e.delete_attribute(#{dyn_spec.option[:key_attr_name].to_s.inspect})") if dyn_spec.option[:delete_id]
1228
+ cg.put_expression("start_tag(e)")
1229
+ cg.put_expression("val.amrita_sanitize")
1230
+ cg.put_expression("end_tag(e)")
1231
+ end
1232
+ end
1233
+ end
1234
+
1235
+ def code_for_element_with_child(element, dyn_spec, cg)
1236
+ cg.if_("val == true") do
1237
+ cg.if_("attrs.size == 0") do
1238
+ cg.put_constant(element.to_s)
1239
+ cg.else_ do
1240
+ gen_element_with_attr(element, dyn_spec, cg)
1241
+ cg.put_expression("start_tag(e)")
1242
+ element.each_child do |c|
1243
+ cg.put_constant(c.to_s)
1244
+ end
1245
+ cg.put_expression("end_tag(e)")
1246
+ end
1247
+ end
1248
+ cg.else_ do
1249
+ cg.if_("attrs.size == 0") do
1250
+ sub_method = nil
1251
+ cg.generate_static_element_code(element, dyn_spec.option) do
1252
+ sub_method = cg.define_method_and_call(:val, :attrs, "&block") do
1253
+ yield_block(cg, dyn_spec)
1254
+ element.each_child do |e|
1255
+ e.amrita_compile_main_code(cg)
1256
+ end
1257
+ end
1258
+ end
1259
+ cg.else_ do
1260
+ gen_element_with_attr(element, dyn_spec, cg)
1261
+ cg.put_expression("start_tag(e)")
1262
+ cg.code(sub_method)
1263
+ cg.put_expression("end_tag(e)")
1264
+ end
1265
+ end
1266
+ end
1267
+ end
1268
+ end
1269
+
1270
+ def gen_element_with_attr(element,dyn_spec, cg, varname='e')
1271
+ a = element.attributes.collect do |k, v|
1272
+ next if dyn_spec.option[:delete_id] and k == dyn_spec.option[:key_attr_name].to_s
1273
+ "#{k.intern.inspect} => #{v.inspect}, "
1274
+ end
1275
+ default_value = "{ #{a.join(' ')}}"
1276
+ cg.code("#{varname} = new_element(#{element.name.inspect}, #{default_value}.merge(attrs))")
1277
+ end
1278
+
1279
+ def yield_block(cg, dyn_spec)
1280
+ cg.code("yield(#{dyn_spec.module_name}) if block_given?")
1281
+ end
1282
+ end
1283
+ end
1284
+
1285
+ def self.define_template_spec(option={}, &block)
1286
+ Core::RootSpec.new(option, &block)
1287
+ end
1288
+
1289
+
1290
+ module ModelData # :nodoc:
1291
+ class AttrArray < Hash
1292
+ attr_reader :body
1293
+ def initialize(h, &block)
1294
+ h.each do |k, v|
1295
+ self[k] = v
1296
+ end
1297
+ if block_given?
1298
+ @body = yield
1299
+ else
1300
+ @body = true
1301
+ end
1302
+ end
1303
+ end
1304
+
1305
+ class Tuple < Array # :nodoc:
1306
+ def self.[](*args)
1307
+ self.new(args)
1308
+ end
1309
+ end
1310
+
1311
+ class MultiValue # :nodoc:
1312
+ include Runtime
1313
+ def initialize(*v)
1314
+ @values = v
1315
+ end
1316
+
1317
+ def value
1318
+ current_context.mv_index[object_id] ||= 0
1319
+ cnt = current_context.mv_index[object_id]
1320
+ v = @values[cnt]
1321
+ current_context.mv_index[object_id] = ((cnt+1) % @values.size)
1322
+ v
1323
+ end
1324
+ end
1325
+
1326
+ def e(tag, hash={})
1327
+ ret = REXML::Element.new(tag.to_s)
1328
+ hash.each do |k, v|
1329
+ ret.attributes[k.to_s] = v
1330
+ end
1331
+ if block_given?
1332
+ case child = yield
1333
+ when REXML::Element
1334
+ ret.add child
1335
+ else
1336
+ ret.text = child
1337
+ end
1338
+ end
1339
+ ret
1340
+ end
1341
+
1342
+ def a(hash={}, &block)
1343
+ AttrArray.new(hash, &block)
1344
+ end
1345
+
1346
+ def t(*args)
1347
+ Tuple[*args]
1348
+ end
1349
+ end
1350
+ include ModelData
1351
+
1352
+ module Expander # :nodoc:
1353
+ include ModelData
1354
+ class Base # :nodoc:
1355
+ include Runtime
1356
+ attr_reader :children, :name, :mod, :spec, :parent
1357
+ def initialize(parent, spec, pmod)
1358
+ @parent = parent
1359
+ @spec = spec
1360
+ @children = []
1361
+ @name = @spec.sym
1362
+ @method = pmod.method(@spec.sym || :expand_template) unless spec.option[:dummy_element]
1363
+ end
1364
+
1365
+ def call_template_method(data=nil, context_data=data, attrs=nil, &block)
1366
+ args = [data]
1367
+ args << attrs if attrs
1368
+ if @spec.option[:eval_cdata_as_erb] and context_data
1369
+ new_context_data(context_data) do
1370
+ @method.call(*args, &block)
1371
+ end
1372
+ else
1373
+ @method.call(*args, &block)
1374
+ end
1375
+ end
1376
+ end
1377
+
1378
+ class Leaf < Base # :nodoc:
1379
+ include Amrita2
1380
+ def expand(data, i=nil)
1381
+ case data
1382
+ when nil
1383
+ when String, Integer, true, false, Struct
1384
+ call_template_method(data)
1385
+ when REXML::Element
1386
+ if data.name
1387
+ call_template_method(SanitizedString[data.to_s])
1388
+ else
1389
+ call_template_method(data.text, data, data.attributes)
1390
+ end
1391
+ when AttrArray
1392
+ call_template_method(data.body, data, data)
1393
+ when Hash
1394
+ call_template_method(data)
1395
+ when Tuple
1396
+ @method.call(*data)
1397
+ when Enumerable
1398
+ data.each_with_index do |d, i|
1399
+ expand(d, i)
1400
+ end
1401
+ when Proc
1402
+ data.call(@parent.mod)
1403
+ else
1404
+ call_template_method(data)
1405
+ end
1406
+ end
1407
+ end
1408
+
1409
+ class Node < Base # :nodoc:
1410
+ include Amrita2
1411
+ def initialize(parent, spec, pmod)
1412
+ super
1413
+ @mod = pmod::const_get(spec.module_name)
1414
+ traverse_children(spec)
1415
+ end
1416
+
1417
+ def traverse_children(spec)
1418
+ spec.children.each do |c|
1419
+ if c.sym and not c.option[:dummy_element]
1420
+ @children << c.generate_expander(@mod, self)
1421
+ else
1422
+ traverse_children(c)
1423
+ end
1424
+ end
1425
+ end
1426
+
1427
+ def each(&block)
1428
+ @children.each do |c|
1429
+ next unless c.spec.sym
1430
+ next if c.spec.option[:dummy_element]
1431
+ yield(c)
1432
+ end
1433
+ end
1434
+
1435
+ def expand(data)
1436
+ case data
1437
+ when nil
1438
+ when Proc
1439
+ data.call(@mod)
1440
+ when AttrArray
1441
+ call_template_method(data) do
1442
+ data = data.body
1443
+ case data
1444
+ when Hash
1445
+ each do |c|
1446
+ k = c.name
1447
+ d = data[k] || data[k.to_s]
1448
+ c.expand(d)
1449
+ end
1450
+ when Struct, Enumerable
1451
+ raise 'not implemented'
1452
+ else
1453
+ expand_po(data)
1454
+ end
1455
+ end
1456
+ when Hash
1457
+ call_template_method(nil, data) do
1458
+ each do |c|
1459
+ k = c.name
1460
+ d = data[k] || data[k.to_s]
1461
+ c.expand(d)
1462
+ end
1463
+ end
1464
+ when Struct
1465
+ call_template_method(nil, data) do
1466
+ expand_po(data)
1467
+ end
1468
+ when String
1469
+ raise "can not expand s String(#{data})"
1470
+ when Tuple, true
1471
+ call_template_method do
1472
+ each do |c|
1473
+ c.expand(data)
1474
+ end
1475
+ end
1476
+ when Enumerable
1477
+ data.each do |d|
1478
+ expand(d)
1479
+ end
1480
+ else
1481
+ call_template_method do
1482
+ expand_po(data)
1483
+ end
1484
+ end
1485
+ end
1486
+
1487
+ def expand_po(data)
1488
+ if data.kind_of?(Binding)
1489
+ new_binding(data) do
1490
+ each do |c|
1491
+ c.expand(eval(c.name.to_s, data))
1492
+ end
1493
+ end
1494
+ else
1495
+ each do |c|
1496
+ mid = c.name
1497
+ begin
1498
+ meth = data.method(mid)
1499
+ case meth.arity
1500
+ when 0
1501
+ c.expand(meth.call)
1502
+ else
1503
+ meth.call(@mod)
1504
+ end
1505
+ rescue NameError
1506
+ c.expand(data.send(mid))
1507
+ end
1508
+ end
1509
+ end
1510
+ end
1511
+ end
1512
+
1513
+ class Root < Node # :nodoc:
1514
+ def initialize(spec, pmod)
1515
+ super(nil, spec, pmod)
1516
+ end
1517
+
1518
+ def expand_template(out,data)
1519
+ new_context_data(data) do
1520
+ case data
1521
+ when Proc
1522
+ data.call(@mod)
1523
+ when Hash
1524
+ @method.call(out) do
1525
+ @children.each do |c|
1526
+ k = c.name
1527
+ d = (data[k] || data[k.to_s])
1528
+ c.expand(d)
1529
+ end
1530
+ end
1531
+ else
1532
+ @method.call(out) do
1533
+ expand_po(data)
1534
+ end
1535
+ end
1536
+ end
1537
+ end
1538
+ end
1539
+ end
1540
+
1541
+ module Extention # :nodoc:
1542
+
1543
+ module UseOriginalElement # :nodoc:
1544
+ extend Core::SpecOptionMeta::CompilerOption
1545
+ include Amrita2::Runtime
1546
+
1547
+ def self.check_option(spec, k, v)
1548
+ end
1549
+
1550
+ def code_for_leaf_element(element, dyn_spec, cg, &block)
1551
+ sym = dyn_spec.sym
1552
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1553
+ cg.if_("block_given?") do
1554
+ gen_element_with_attr(element, dyn_spec, cg)
1555
+ cg.code("e.text = #{element.text.inspect}")
1556
+ cg.code("get_mainstream << yield(e).to_s")
1557
+ cg.else_ do
1558
+ code_for_element_and_scalar(element, dyn_spec, cg)
1559
+ end
1560
+ end
1561
+ end
1562
+ end
1563
+
1564
+ def code_for_element_with_child(element, dyn_spec, cg)
1565
+ cg.code("yield(#{dyn_spec.module_name})")
1566
+ cg.if_("get_substream(:#{dyn_spec.output_stream}).size == 0") do
1567
+ cg.generate_static_element_code(element, dyn_spec.option) do
1568
+ element.each_child do |e|
1569
+ e.amrita_compile_main_code(cg)
1570
+ end
1571
+ end
1572
+ cg.else_ do
1573
+ cg.code("output_substream(:#{dyn_spec.name_for(element)})")
1574
+ element.each_child do |e|
1575
+ e.amrita_compile_main_code(cg)
1576
+ end
1577
+ cg.put_constant(end_tag(element))
1578
+ end
1579
+ end
1580
+ end
1581
+
1582
+ def code_for_not_leaf_element(element, dyn_spec, cg, &block)
1583
+ sym = dyn_spec.name_for(element)
1584
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1585
+ code_for_element_with_child(element, dyn_spec, cg, &block)
1586
+ end
1587
+
1588
+ cg.define_module(dyn_spec.module_name) do
1589
+ cg.code("Methods = #{dyn_spec.get_methods.inspect}")
1590
+ cg.code("include Amrita2")
1591
+ cg.code("extend Amrita2")
1592
+ cg.define_method(sym, "val=nil", "attrs={}") do
1593
+ cg.new_context("get_substream(:#{sym})") do
1594
+ cg.code("e = yield(new_element(#{element.name.inspect}))")
1595
+ cg.code("get_mainstream << start_tag(e)")
1596
+ end
1597
+ yield
1598
+ end
1599
+ #cg.code("module_function :#{sym}")
1600
+ end
1601
+ end
1602
+
1603
+ Core::SpecOptionMeta::define_option(:use_original_element, self)
1604
+ public
1605
+ class Expander < Expander::Leaf # :nodoc:
1606
+ def expand(data, i=nil)
1607
+ case data
1608
+ when Proc
1609
+ @method.call do |e|
1610
+ data.call(e)
1611
+ end
1612
+ else
1613
+ super
1614
+ end
1615
+ end
1616
+ end
1617
+ public
1618
+ def generate_expander(spec, mod, parent=nil)
1619
+ Expander.new(parent, spec, mod)
1620
+ end
1621
+ end
1622
+
1623
+ module ExpandByMember # :nodoc:
1624
+ extend Core::SpecOptionMeta::CompilerOption
1625
+ def self.check_option(spec, k, v)
1626
+ end
1627
+
1628
+ def code_for_not_leaf_element(element, dyn_spec, cg, &block)
1629
+ sym = dyn_spec.name_for(element)
1630
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1631
+ code_for_element_with_child(element, dyn_spec, cg, &block)
1632
+ end
1633
+ cg.define_module(dyn_spec.module_name) do
1634
+ cg.code("Methods = #{dyn_spec.get_methods.inspect}")
1635
+ cg.code("include Amrita2")
1636
+ cg.code("extend Amrita2")
1637
+ yield
1638
+ end
1639
+ end
1640
+
1641
+ def code_for_element_with_child(element, dyn_spec, cg)
1642
+ cg.generate_static_element_code(element, dyn_spec.option) do
1643
+ cg.if_("block_given?") do
1644
+ cg.code("yield(#{dyn_spec.module_name})")
1645
+ cg.else_ do
1646
+ dyn_spec.children.each do |c|
1647
+ cg.code("#{dyn_spec.module_name}::#{c.sym}(val.#{c.sym})")
1648
+ end
1649
+ end
1650
+ end
1651
+ element.each_child do |e|
1652
+ e.amrita_compile_main_code(cg)
1653
+ end
1654
+ end
1655
+ end
1656
+ Core::SpecOptionMeta::define_option(:expand_by_member, self)
1657
+
1658
+ class Expander < Expander::Node # :nodoc:
1659
+ def expand(data, i=0)
1660
+ if data.kind_of?(Enumerable)
1661
+ data.each_with_index do |d, i|
1662
+ expand(d, i)
1663
+ end
1664
+ else
1665
+ @method.call(i) do
1666
+ each do |c|
1667
+ dd = data.send(c.name)
1668
+ c.expand(dd)
1669
+ end
1670
+ end
1671
+ end
1672
+ end
1673
+ end
1674
+
1675
+ public
1676
+ def generate_expander(spec, mod, parent=nil)
1677
+ Expander.new(parent, spec, mod)
1678
+ end
1679
+ end
1680
+
1681
+ module EvalCDataAsErb # :nodoc:
1682
+ include Core::SpecOptionMeta::InheritableOption
1683
+ extend Core::SpecOptionMeta::CompilerOption
1684
+ def self.check_option(spec, k, v)
1685
+ end
1686
+ def code_for_not_leaf_element(element, dyn_spec, cg, &block)
1687
+ sym = dyn_spec.name_for(element)
1688
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1689
+ cg.code('new_context_data(val) do')
1690
+ cg.level_up do
1691
+ cg.eval_cdata_as_erb = dyn_spec.option[:eval_cdata_as_erb]
1692
+ code_for_element_with_child(element, dyn_spec, cg, &block)
1693
+ end
1694
+ cg.code('end')
1695
+ end
1696
+ cg.define_module(dyn_spec.module_name) do
1697
+ cg.code("Methods = #{dyn_spec.get_methods.inspect}")
1698
+ cg.code("include Amrita2")
1699
+ cg.code("extend Amrita2")
1700
+ yield
1701
+ end
1702
+ end
1703
+ Core::SpecOptionMeta::define_option(:eval_cdata_as_erb, self)
1704
+ end
1705
+
1706
+ module UseArgs # :nodoc:
1707
+ extend Core::SpecOptionMeta::CompilerOption
1708
+ def self.check_option(spec, k, v)
1709
+ end
1710
+
1711
+ def code_for_leaf_element(element, dyn_spec, cg, &block)
1712
+ cg.code('include Amrita2::Runtime')
1713
+ sym = dyn_spec.sym
1714
+ cg.define_method(sym, "*args", "&block") do
1715
+ cg.new_context("get_substream(:#{dyn_spec.output_stream})") do
1716
+ complie_for_args(element, cg, dyn_spec.option[:delete_id])
1717
+ end
1718
+ end
1719
+ #cg.code("module_function :#{sym}")
1720
+ end
1721
+
1722
+ private
1723
+
1724
+ def complie_for_args(element, cg, delete_id)
1725
+ replace_args(element)
1726
+ #cg.code("get_mainstream << \"#{element.to_s}\"")
1727
+ s = ""
1728
+ element.write(s, -1, false, cg.ie_hack)
1729
+ cg.code("get_mainstream << \"#{s}\"")
1730
+ end
1731
+
1732
+ def replace_args(element)
1733
+ element.delete_attribute('id')
1734
+ element.attributes.each do|k,v|
1735
+ replace_attr_args(v)
1736
+ end
1737
+ element.each_child do |c|
1738
+ case c
1739
+ when REXML::Text
1740
+ c.value = replace_text_args(c.value) if c.value
1741
+ when REXML::Element
1742
+ replace_args(c)
1743
+ end
1744
+ end
1745
+ end
1746
+
1747
+ def replace_attr_args(s)
1748
+ s.gsub!(/\$(\d)/) do |ss|
1749
+ case $1
1750
+ when "0"
1751
+ '#{block.call.amrita_sanitize_as_attribute if block}'
1752
+ else
1753
+ '#{args[' + ($1.to_i-1).to_s + '].amrita_sanitize_as_attribute}'
1754
+ end
1755
+ end
1756
+ end
1757
+
1758
+ def replace_text_args(s)
1759
+ s.gsub(/\$(\d)/) do |ss|
1760
+ case $1
1761
+ when "0"
1762
+ '#{block.call.amrita_sanitize if block}'
1763
+ else
1764
+ '#{args[' + ($1.to_i-1).to_s + '].amrita_sanitize}'
1765
+ end
1766
+ end
1767
+ end
1768
+
1769
+ Core::SpecOptionMeta::define_option(:use_args, self)
1770
+ public
1771
+ end
1772
+
1773
+ module EnumerateChildren # :nodoc:
1774
+ extend Core::SpecOptionMeta::CompilerOption
1775
+ def self.check_option(spec, k, v)
1776
+ end
1777
+
1778
+ def compile_main(element, dyn_spec, cg, &block)
1779
+ cg.code('#compile_main')
1780
+ sym = element.amrita_spec.sym
1781
+ cg.generate_static_element_code(element, dyn_spec.option) do
1782
+ compile_main_code(element, dyn_spec, cg, sym.to_s, &block)
1783
+ end
1784
+ end
1785
+
1786
+ def code_for_element_and_scalar(element, dyn_spec, cg)
1787
+ cg.put_expression("val.amrita_sanitize")
1788
+ end
1789
+
1790
+ def code_for_element_with_child(element, dyn_spec, cg)
1791
+ yield_block(cg, dyn_spec)
1792
+ element.each_child do |e|
1793
+ e.amrita_compile_main_code(cg)
1794
+ end
1795
+ end
1796
+
1797
+ Core::SpecOptionMeta::define_option(:enumerate_chidren, self)
1798
+ end
1799
+
1800
+
1801
+ module ValueAttr # :nodoc:
1802
+ extend Core::SpecOptionMeta::CompilerOption
1803
+ def self.check_option(spec, k, v)
1804
+ end
1805
+
1806
+ def code_for_leaf_element(element, dyn_spec, cg, &block)
1807
+ cg.code('include Amrita2::Runtime')
1808
+ sym = dyn_spec.sym
1809
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1810
+ complie_for_single(element, dyn_spec, cg, dyn_spec.option[:value_attr])
1811
+ end
1812
+ end
1813
+
1814
+ def generate_expander(spec, mod, parent=nil)
1815
+ ret = super
1816
+ def ret.expand(data)
1817
+ case data
1818
+ when false, nil, "false"
1819
+ @method.call(nil)
1820
+ else
1821
+ super
1822
+ end
1823
+ end
1824
+ ret
1825
+ end
1826
+
1827
+ private
1828
+
1829
+ def complie_for_single(element, dyn_spec, cg, value_attr)
1830
+ gen_element_with_attr(element, dyn_spec, cg)
1831
+ cg.code("e.attributes['#{value_attr}'] = val")
1832
+ cg.code("e.text = #{element.text.inspect}") if element.text
1833
+ cg.code("get_mainstream << e.to_s")
1834
+ end
1835
+
1836
+ Core::SpecOptionMeta::define_option(:value_attr, self)
1837
+ end
1838
+
1839
+ module LinkAttr # :nodoc:
1840
+ extend Core::SpecOptionMeta::CompilerOption
1841
+ def self.check_option(spec, k, v)
1842
+ end
1843
+
1844
+ def code_for_leaf_element(element, dyn_spec, cg, &block)
1845
+ cg.code('include Amrita2::Runtime')
1846
+ sym = dyn_spec.sym
1847
+ cg.define_method(sym, "href", "val=nil", "attrs={}") do
1848
+ cg.if_("val.kind_of?(Hash)") do
1849
+ cg.code("attrs = val")
1850
+ cg.code("val = nil")
1851
+ end
1852
+ cg.new_context("get_substream(:#{dyn_spec.output_stream})") do
1853
+ complie_for_link(element, dyn_spec, cg, dyn_spec.option[:link_attr])
1854
+ end
1855
+ end
1856
+ end
1857
+
1858
+ private
1859
+
1860
+ def complie_for_link(element, dyn_spec, cg, link_attr)
1861
+ href, checks = *link_attr
1862
+ gen_element_with_attr(element, dyn_spec, cg)
1863
+ checks.each do |r|
1864
+ cg.code("href = '' if #{r.inspect} =~ href")
1865
+ end
1866
+ cg.code("e.attributes['#{href}'] = href if href")
1867
+ cg.code("e.delete_attribute('#{dyn_spec.option[:key_attr_name]}')") if dyn_spec.option[:delete_id]
1868
+ cg.code("e.text = (val || #{element.text.inspect}) ")
1869
+ cg.code("get_mainstream << e.to_s")
1870
+ end
1871
+
1872
+ Core::SpecOptionMeta::define_option(:link_attr, self)
1873
+ class LinkExpander < Expander::Leaf
1874
+ def expand(data, i=nil)
1875
+ case data
1876
+ when Array
1877
+ case data.first
1878
+ when Array, AttrArray, Hash
1879
+ data.each do |d|
1880
+ expand(d)
1881
+ end
1882
+ else
1883
+ @method.call(*data)
1884
+ end
1885
+ else
1886
+ super
1887
+ end
1888
+ end
1889
+ end
1890
+
1891
+ public
1892
+ def generate_expander(spec, mod, parent=nil)
1893
+ LinkExpander.new(parent, spec, mod)
1894
+ end
1895
+ end
1896
+ end
1897
+ end