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.
- data/lib/amrita2/core.rb +1897 -0
- data/lib/amrita2/rails_bridge.rb +40 -0
- data/lib/amrita2/rd.rb +314 -0
- data/lib/amrita2/template.rb +304 -0
- data/lib/amrita2/version.rb +9 -0
- data/lib/amrita2.rb +1 -0
- metadata +44 -0
data/lib/amrita2/core.rb
ADDED
@@ -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, '&')
|
189
|
+
s.gsub!("<", '<')
|
190
|
+
s.gsub!(">", '>')
|
191
|
+
s
|
192
|
+
end
|
193
|
+
|
194
|
+
# escape &<>"'
|
195
|
+
def sanitize_attribute_value(text)
|
196
|
+
s = text.dup
|
197
|
+
s.gsub!(AMP_WITHOUT_REFRENCE, '&')
|
198
|
+
s.gsub!("<", '<')
|
199
|
+
s.gsub!(">", '>')
|
200
|
+
s.gsub!('"', '"')
|
201
|
+
s.gsub!("'", ''')
|
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!("&", '&')
|
245
|
+
s.gsub!("'", ''')
|
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
|