amrita2 1.9.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. data/README +112 -0
  2. data/init.rb +6 -0
  3. data/lib/amrita2/gettext.rb +116 -0
  4. data/lib/amrita2/macro.rb +153 -0
  5. data/lib/amrita2/rails_bridge.rb +172 -26
  6. data/lib/amrita2/template.rb +2634 -234
  7. data/lib/amrita2/testsupport.rb +171 -0
  8. data/lib/amrita2/version.rb +3 -3
  9. data/lib/amrita2.rb +1 -0
  10. data/sample/depot/app/controllers/admin_controller.rb +59 -0
  11. data/sample/depot/app/controllers/application.rb +20 -0
  12. data/sample/depot/app/controllers/info_controller.rb +19 -0
  13. data/sample/depot/app/controllers/login_controller.rb +85 -0
  14. data/sample/depot/app/controllers/store_controller.rb +68 -0
  15. data/sample/depot/app/helpers/admin_helper.rb +7 -0
  16. data/sample/depot/app/helpers/application_helper.rb +10 -0
  17. data/sample/depot/app/helpers/ar_form.rb +169 -0
  18. data/sample/depot/app/helpers/form_tag.rb +24 -0
  19. data/sample/depot/app/helpers/info_helper.rb +7 -0
  20. data/sample/depot/app/helpers/standard_form.rb +73 -0
  21. data/sample/depot/app/helpers/store_helper.rb +14 -0
  22. data/sample/depot/app/models/cart.rb +36 -0
  23. data/sample/depot/app/models/cart_item.rb +26 -0
  24. data/sample/depot/app/models/line_item.rb +34 -0
  25. data/sample/depot/app/models/order.rb +57 -0
  26. data/sample/depot/app/models/product.rb +41 -0
  27. data/sample/depot/app/models/user.rb +83 -0
  28. data/sample/depot/config/boot.rb +49 -0
  29. data/sample/depot/config/environment.rb +83 -0
  30. data/sample/depot/config/environments/development.rb +24 -0
  31. data/sample/depot/config/environments/production.rb +24 -0
  32. data/sample/depot/config/environments/test.rb +24 -0
  33. data/sample/depot/config/routes.rb +10 -0
  34. data/sample/depot/db/migrate/001_create_products.rb +18 -0
  35. data/sample/depot/db/migrate/002_add_price.rb +14 -0
  36. data/sample/depot/db/migrate/003_add_test_data.rb +68 -0
  37. data/sample/depot/db/migrate/004_add_sessions.rb +20 -0
  38. data/sample/depot/db/migrate/005_create_orders.rb +21 -0
  39. data/sample/depot/db/migrate/006_create_line_items.rb +27 -0
  40. data/sample/depot/db/migrate/007_create_users.rb +18 -0
  41. data/sample/depot/db/schema.rb +45 -0
  42. data/sample/depot/public/dispatch.rb +15 -0
  43. data/sample/depot/test/functional/admin_controller_test.rb +54 -0
  44. data/sample/depot/test/functional/info_controller_test.rb +23 -0
  45. data/sample/depot/test/functional/login_controller_test.rb +74 -0
  46. data/sample/depot/test/functional/store_controller_test.rb +57 -0
  47. data/sample/depot/test/integration/dsl_user_stories_test.rb +126 -0
  48. data/sample/depot/test/integration/user_stories_test.rb +70 -0
  49. data/sample/depot/test/performance/order_speed_test.rb +58 -0
  50. data/sample/depot/test/test_helper.rb +16 -0
  51. data/sample/depot/test/unit/cart_test.rb +39 -0
  52. data/sample/depot/test/unit/cart_test1.rb +31 -0
  53. data/sample/depot/test/unit/line_item_test.rb +15 -0
  54. data/sample/depot/test/unit/order_test.rb +15 -0
  55. data/sample/depot/test/unit/product_test.rb +98 -0
  56. data/sample/depot/vendor/plugins/amrita2/init.rb +6 -0
  57. data/sample/hello/hello.rb +22 -0
  58. data/sample/login_engine/app/controllers/application.rb +16 -0
  59. data/sample/login_engine/app/controllers/user_controller.rb +265 -0
  60. data/sample/login_engine/app/helpers/application_helper.rb +3 -0
  61. data/sample/login_engine/app/helpers/form_tag.rb +16 -0
  62. data/sample/login_engine/app/helpers/two_columns.rb +24 -0
  63. data/sample/login_engine/app/helpers/two_columns_form.rb +47 -0
  64. data/sample/login_engine/app/helpers/user_helper.rb +88 -0
  65. data/sample/login_engine/app/models/user.rb +7 -0
  66. data/sample/login_engine/app/models/user_notify.rb +75 -0
  67. data/sample/login_engine/config/boot.rb +45 -0
  68. data/sample/login_engine/config/environment.rb +140 -0
  69. data/sample/login_engine/config/environments/development.rb +21 -0
  70. data/sample/login_engine/config/environments/production.rb +18 -0
  71. data/sample/login_engine/config/environments/test.rb +19 -0
  72. data/sample/login_engine/config/routes.rb +23 -0
  73. data/sample/login_engine/db/migrate/001_create_users.rb +25 -0
  74. data/sample/login_engine/db/schema.rb +25 -0
  75. data/sample/login_engine/lib/config.rb +113 -0
  76. data/sample/login_engine/lib/hpricot_test_extension.rb +80 -0
  77. data/sample/login_engine/lib/login_engine/authenticated_system.rb +113 -0
  78. data/sample/login_engine/lib/login_engine/authenticated_user.rb +155 -0
  79. data/sample/login_engine/lib/login_engine.rb +62 -0
  80. data/sample/login_engine/public/dispatch.rb +10 -0
  81. data/sample/login_engine/test/functional/amrita2_test.rb +267 -0
  82. data/sample/login_engine/test/functional/user_controller_test.rb +544 -0
  83. data/sample/login_engine/test/mocks/mail.rb +14 -0
  84. data/sample/login_engine/test/mocks/time.rb +19 -0
  85. data/sample/login_engine/test/test_helper.rb +31 -0
  86. data/sample/login_engine/test/unit/user_test.rb +116 -0
  87. data/sample/login_engine/vendor/plugins/amrita2/init.rb +6 -0
  88. data/specs/attribute.rb +201 -0
  89. data/specs/datatypes.rb +231 -0
  90. data/specs/dictionary.rb +68 -0
  91. data/specs/erb_cdata.rb +187 -0
  92. data/specs/filters.rb +513 -0
  93. data/specs/gettext/erb_gettext.rb +42 -0
  94. data/specs/gettext/gettext_util.rb +65 -0
  95. data/specs/gettext/static_text.rb +103 -0
  96. data/specs/impl/code_generator.rb +87 -0
  97. data/specs/impl/dynamic_element.rb +92 -0
  98. data/specs/impl/hash_delegator.rb +57 -0
  99. data/specs/impl/parse_opt.rb +34 -0
  100. data/specs/impl/preprocess.rb +823 -0
  101. data/specs/impl/testsupport.rb +89 -0
  102. data/specs/inlineruby.rb +429 -0
  103. data/specs/intro.rb +654 -0
  104. data/specs/loop.rb +203 -0
  105. data/specs/macro.rb +532 -0
  106. data/specs/sample.rb +34 -0
  107. data/specs/sanitize.rb +110 -0
  108. data/specs/template.rb +189 -0
  109. data/specs/trace.rb +97 -0
  110. metadata +138 -19
  111. data/lib/amrita2/core.rb +0 -1897
  112. data/lib/amrita2/rd.rb +0 -314
data/lib/amrita2/core.rb DELETED
@@ -1,1897 +0,0 @@
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