amrita2 1.9.6 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +112 -0
- data/init.rb +6 -0
- data/lib/amrita2/gettext.rb +116 -0
- data/lib/amrita2/macro.rb +153 -0
- data/lib/amrita2/rails_bridge.rb +172 -26
- data/lib/amrita2/template.rb +2634 -234
- data/lib/amrita2/testsupport.rb +171 -0
- data/lib/amrita2/version.rb +3 -3
- data/lib/amrita2.rb +1 -0
- data/sample/depot/app/controllers/admin_controller.rb +59 -0
- data/sample/depot/app/controllers/application.rb +20 -0
- data/sample/depot/app/controllers/info_controller.rb +19 -0
- data/sample/depot/app/controllers/login_controller.rb +85 -0
- data/sample/depot/app/controllers/store_controller.rb +68 -0
- data/sample/depot/app/helpers/admin_helper.rb +7 -0
- data/sample/depot/app/helpers/application_helper.rb +10 -0
- data/sample/depot/app/helpers/ar_form.rb +169 -0
- data/sample/depot/app/helpers/form_tag.rb +24 -0
- data/sample/depot/app/helpers/info_helper.rb +7 -0
- data/sample/depot/app/helpers/standard_form.rb +73 -0
- data/sample/depot/app/helpers/store_helper.rb +14 -0
- data/sample/depot/app/models/cart.rb +36 -0
- data/sample/depot/app/models/cart_item.rb +26 -0
- data/sample/depot/app/models/line_item.rb +34 -0
- data/sample/depot/app/models/order.rb +57 -0
- data/sample/depot/app/models/product.rb +41 -0
- data/sample/depot/app/models/user.rb +83 -0
- data/sample/depot/config/boot.rb +49 -0
- data/sample/depot/config/environment.rb +83 -0
- data/sample/depot/config/environments/development.rb +24 -0
- data/sample/depot/config/environments/production.rb +24 -0
- data/sample/depot/config/environments/test.rb +24 -0
- data/sample/depot/config/routes.rb +10 -0
- data/sample/depot/db/migrate/001_create_products.rb +18 -0
- data/sample/depot/db/migrate/002_add_price.rb +14 -0
- data/sample/depot/db/migrate/003_add_test_data.rb +68 -0
- data/sample/depot/db/migrate/004_add_sessions.rb +20 -0
- data/sample/depot/db/migrate/005_create_orders.rb +21 -0
- data/sample/depot/db/migrate/006_create_line_items.rb +27 -0
- data/sample/depot/db/migrate/007_create_users.rb +18 -0
- data/sample/depot/db/schema.rb +45 -0
- data/sample/depot/public/dispatch.rb +15 -0
- data/sample/depot/test/functional/admin_controller_test.rb +54 -0
- data/sample/depot/test/functional/info_controller_test.rb +23 -0
- data/sample/depot/test/functional/login_controller_test.rb +74 -0
- data/sample/depot/test/functional/store_controller_test.rb +57 -0
- data/sample/depot/test/integration/dsl_user_stories_test.rb +126 -0
- data/sample/depot/test/integration/user_stories_test.rb +70 -0
- data/sample/depot/test/performance/order_speed_test.rb +58 -0
- data/sample/depot/test/test_helper.rb +16 -0
- data/sample/depot/test/unit/cart_test.rb +39 -0
- data/sample/depot/test/unit/cart_test1.rb +31 -0
- data/sample/depot/test/unit/line_item_test.rb +15 -0
- data/sample/depot/test/unit/order_test.rb +15 -0
- data/sample/depot/test/unit/product_test.rb +98 -0
- data/sample/depot/vendor/plugins/amrita2/init.rb +6 -0
- data/sample/hello/hello.rb +22 -0
- data/sample/login_engine/app/controllers/application.rb +16 -0
- data/sample/login_engine/app/controllers/user_controller.rb +265 -0
- data/sample/login_engine/app/helpers/application_helper.rb +3 -0
- data/sample/login_engine/app/helpers/form_tag.rb +16 -0
- data/sample/login_engine/app/helpers/two_columns.rb +24 -0
- data/sample/login_engine/app/helpers/two_columns_form.rb +47 -0
- data/sample/login_engine/app/helpers/user_helper.rb +88 -0
- data/sample/login_engine/app/models/user.rb +7 -0
- data/sample/login_engine/app/models/user_notify.rb +75 -0
- data/sample/login_engine/config/boot.rb +45 -0
- data/sample/login_engine/config/environment.rb +140 -0
- data/sample/login_engine/config/environments/development.rb +21 -0
- data/sample/login_engine/config/environments/production.rb +18 -0
- data/sample/login_engine/config/environments/test.rb +19 -0
- data/sample/login_engine/config/routes.rb +23 -0
- data/sample/login_engine/db/migrate/001_create_users.rb +25 -0
- data/sample/login_engine/db/schema.rb +25 -0
- data/sample/login_engine/lib/config.rb +113 -0
- data/sample/login_engine/lib/hpricot_test_extension.rb +80 -0
- data/sample/login_engine/lib/login_engine/authenticated_system.rb +113 -0
- data/sample/login_engine/lib/login_engine/authenticated_user.rb +155 -0
- data/sample/login_engine/lib/login_engine.rb +62 -0
- data/sample/login_engine/public/dispatch.rb +10 -0
- data/sample/login_engine/test/functional/amrita2_test.rb +267 -0
- data/sample/login_engine/test/functional/user_controller_test.rb +544 -0
- data/sample/login_engine/test/mocks/mail.rb +14 -0
- data/sample/login_engine/test/mocks/time.rb +19 -0
- data/sample/login_engine/test/test_helper.rb +31 -0
- data/sample/login_engine/test/unit/user_test.rb +116 -0
- data/sample/login_engine/vendor/plugins/amrita2/init.rb +6 -0
- data/specs/attribute.rb +201 -0
- data/specs/datatypes.rb +231 -0
- data/specs/dictionary.rb +68 -0
- data/specs/erb_cdata.rb +187 -0
- data/specs/filters.rb +513 -0
- data/specs/gettext/erb_gettext.rb +42 -0
- data/specs/gettext/gettext_util.rb +65 -0
- data/specs/gettext/static_text.rb +103 -0
- data/specs/impl/code_generator.rb +87 -0
- data/specs/impl/dynamic_element.rb +92 -0
- data/specs/impl/hash_delegator.rb +57 -0
- data/specs/impl/parse_opt.rb +34 -0
- data/specs/impl/preprocess.rb +823 -0
- data/specs/impl/testsupport.rb +89 -0
- data/specs/inlineruby.rb +429 -0
- data/specs/intro.rb +654 -0
- data/specs/loop.rb +203 -0
- data/specs/macro.rb +532 -0
- data/specs/sample.rb +34 -0
- data/specs/sanitize.rb +110 -0
- data/specs/template.rb +189 -0
- data/specs/trace.rb +97 -0
- metadata +138 -19
- data/lib/amrita2/core.rb +0 -1897
- data/lib/amrita2/rd.rb +0 -314
data/lib/amrita2/template.rb
CHANGED
|
@@ -1,304 +1,2704 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'hpricot'
|
|
3
|
+
require 'erb'
|
|
4
|
+
#require 'rexml/document'
|
|
5
|
+
require 'enumerator'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module Amrita2
|
|
9
|
+
module ScalarData
|
|
10
|
+
def amrita_value
|
|
11
|
+
Amrita2::Util::sanitize_text(to_s)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module DictionaryData
|
|
16
|
+
def amrita_value(name)
|
|
17
|
+
self.__send__(name)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.===(other)
|
|
21
|
+
if other == nil
|
|
22
|
+
true
|
|
23
|
+
else
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module Enum
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
module NullObject
|
|
33
|
+
def amrita_value(key=nil)
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def each
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
module ElementHelper
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class NilClass
|
|
46
|
+
include Amrita2::NullObject
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class FalseClass
|
|
50
|
+
include Amrita2::NullObject
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class String
|
|
54
|
+
include Amrita2::ScalarData
|
|
55
|
+
def amrita_value
|
|
56
|
+
Amrita2::Util::sanitize_text(self)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class Integer
|
|
61
|
+
include Amrita2::ScalarData
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class BicDecimal
|
|
65
|
+
include Amrita2::ScalarData
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class Float
|
|
69
|
+
include Amrita2::ScalarData
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class Time
|
|
73
|
+
include Amrita2::ScalarData
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# class REXML::Element
|
|
77
|
+
# include Amrita2::ScalarData
|
|
78
|
+
# def amrita_value
|
|
79
|
+
# Amrita2::SanitizedString[to_s]
|
|
80
|
+
# end
|
|
81
|
+
|
|
82
|
+
# def as_amrita_dictionary(*args)
|
|
83
|
+
# Amrita2::Util::REXMLConverter.new(*args).convert(self)
|
|
84
|
+
# end
|
|
85
|
+
# end
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class Array
|
|
89
|
+
include Amrita2::Enum
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class Range
|
|
93
|
+
include Amrita2::Enum
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
class Hash
|
|
97
|
+
include Amrita2::DictionaryData
|
|
98
|
+
def amrita_value(name)
|
|
99
|
+
self[name]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class Struct
|
|
104
|
+
include Amrita2::DictionaryData
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
class Binding
|
|
108
|
+
include Amrita2::DictionaryData
|
|
109
|
+
|
|
110
|
+
def amrita_value(name)
|
|
111
|
+
eval "begin; #{name}; rescue(NameError); @#{name};end;", self
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
class Hpricot::Elem
|
|
116
|
+
include Amrita2::ElementHelper
|
|
117
|
+
end
|
|
2
118
|
|
|
3
119
|
module Amrita2
|
|
4
|
-
|
|
120
|
+
FilterMethods = [
|
|
121
|
+
:filter_element,
|
|
122
|
+
:parse_element,
|
|
123
|
+
:parse_node,
|
|
124
|
+
:setup_type_renderer,
|
|
125
|
+
:generate_dynamic_element,
|
|
126
|
+
:define_element_method,
|
|
127
|
+
:loop_check_code,
|
|
128
|
+
:method_body_code,
|
|
129
|
+
:value_filter_code,
|
|
130
|
+
:renderer_code,
|
|
131
|
+
:element_render_code
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
module Core
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
module Util
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
module Runtime
|
|
5
141
|
include Amrita2
|
|
142
|
+
include Util
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
module Filters
|
|
146
|
+
class Base
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
module Renderers #:nodoc: all
|
|
151
|
+
class Base
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
module Macro #:nodoc: all
|
|
156
|
+
module Standard
|
|
157
|
+
end
|
|
158
|
+
include Standard
|
|
159
|
+
|
|
160
|
+
class Base
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
module CompileTimeContext #:nodoc: all
|
|
165
|
+
include Filters
|
|
166
|
+
include Renderers
|
|
167
|
+
include Macro
|
|
168
|
+
include Standard
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
class TemplateError < RuntimeError
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
module Util # :nodoc: all
|
|
175
|
+
|
|
176
|
+
# Amrita2 sanitize anything except for SanitizedString
|
|
177
|
+
# If you want to sanitize yourself and don't want to Amrita2 sanitize your object,
|
|
178
|
+
# pass SanitizedString[x] as model data.
|
|
179
|
+
class SanitizedString < String
|
|
180
|
+
def SanitizedString::[](s)
|
|
181
|
+
new(s.to_s).freeze
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def amrita_value
|
|
185
|
+
self
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def to_s
|
|
189
|
+
self
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def *(n)
|
|
193
|
+
SanitizedString[super]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def inspect
|
|
197
|
+
%[SanitizedString[#{super}]]
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# This module provide methods for avoid XSS vulnerability
|
|
202
|
+
# taken from IPA home page(Japanese)
|
|
203
|
+
# http://www.ipa.go.jp/security/awareness/vendor/programming/a01_02.html
|
|
204
|
+
NAMECHAR = '[-\w\d\.:]'
|
|
205
|
+
NAME = "([\\w:]#{NAMECHAR}*)"
|
|
206
|
+
NOT_REFERENCE = "(?!#{NAME};|&#\\d+;|&#x[0-9a-fA-F]+;)" # borrowed from rexml
|
|
207
|
+
AMP_WITHOUT_REFRENCE = /&#{NOT_REFERENCE}/
|
|
208
|
+
# escape &<>
|
|
209
|
+
|
|
210
|
+
def self.sanitize_text(text)
|
|
211
|
+
return nil unless text
|
|
212
|
+
s = text.dup
|
|
213
|
+
s.gsub!(AMP_WITHOUT_REFRENCE, '&')
|
|
214
|
+
s.gsub!("<", '<')
|
|
215
|
+
s.gsub!(">", '>')
|
|
216
|
+
s
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# escape &<>"'
|
|
220
|
+
def self.sanitize_attribute_value(text)
|
|
221
|
+
return nil unless text
|
|
222
|
+
s = text.dup
|
|
223
|
+
s.gsub!(AMP_WITHOUT_REFRENCE, '&')
|
|
224
|
+
s.gsub!("<", '<')
|
|
225
|
+
s.gsub!(">", '>')
|
|
226
|
+
s.gsub!('"', '"')
|
|
227
|
+
#s.gsub!("'", ''')
|
|
228
|
+
s
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
DefaultAllowedScheme = {
|
|
232
|
+
'http' => true,
|
|
233
|
+
'https' => true,
|
|
234
|
+
'ftp' => true,
|
|
235
|
+
'mailto' => true,
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
#UrlInvalidChar = Regexp.new(%q|[^;/?:@&=+$,A-Za-z0-9\-_.!~*'()%]|)
|
|
239
|
+
UrlInvalidChar = Regexp.new(%q|[^;/?:@&=+$,A-Za-z0-9\-_.!~*'()%#]|) #'
|
|
240
|
+
|
|
241
|
+
# +sanitize_url+ accepts only these characters
|
|
242
|
+
# --- http://www.ietf.org/rfc/rfc2396.txt ---
|
|
243
|
+
# uric = reserved | unreserved | escaped
|
|
244
|
+
# reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
|
|
245
|
+
# unreserved = alphanum | mark
|
|
246
|
+
# mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
|
|
247
|
+
# escaped = "%" hex hex
|
|
248
|
+
#
|
|
249
|
+
# +sanitize_url+ accepts only schems specified by +allowd_scheme+
|
|
250
|
+
#
|
|
251
|
+
# The default is http: https: ftp: mailt:
|
|
252
|
+
|
|
253
|
+
def self.sanitize_url(text, allowd_scheme = DefaultAllowedScheme)
|
|
254
|
+
# return nil if text has characters not allowd for URL
|
|
255
|
+
|
|
256
|
+
return nil if text =~ UrlInvalidChar
|
|
257
|
+
|
|
258
|
+
# return '' if text has an unknown scheme
|
|
259
|
+
# --- http://www.ietf.org/rfc/rfc2396.txt ---
|
|
260
|
+
# scheme = alpha *( alpha | digit | "+" | "-" | "." )
|
|
261
|
+
|
|
262
|
+
if text =~ %r|^([A-Za-z][A-Za-z0-9+\-.]*):|
|
|
263
|
+
return nil unless allowd_scheme[$1]
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# escape HTML
|
|
267
|
+
# special = "&" | "<" | ">" | '"' | "'"
|
|
268
|
+
# But I checked "<" | ">" | '"' before.
|
|
269
|
+
s = text.dup
|
|
270
|
+
#s.gsub!("&", '&')
|
|
271
|
+
s.gsub!("'", ''')
|
|
272
|
+
|
|
273
|
+
s
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
module OptionSupport # :nodoc: all
|
|
277
|
+
attr_reader :opt
|
|
278
|
+
def parse_opt(*args)
|
|
279
|
+
@opt = {}
|
|
280
|
+
hash = args.last
|
|
281
|
+
if hash.kind_of?(Hash)
|
|
282
|
+
@opt.merge!(hash)
|
|
283
|
+
args.pop
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
args.each do |key|
|
|
287
|
+
@opt[key] = key
|
|
288
|
+
end
|
|
289
|
+
@opt
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def [](key)
|
|
293
|
+
@opt[key]
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def method_missing(sym, *args, &block)
|
|
297
|
+
if @opt and @opt.has_key?(sym)
|
|
298
|
+
@opt[sym]
|
|
299
|
+
else
|
|
300
|
+
super
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
class Option
|
|
305
|
+
include OptionSupport
|
|
306
|
+
def initialize(*args)
|
|
307
|
+
parse_opt(*args)
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
class Tuple < Array # :nodoc: all
|
|
311
|
+
include ScalarData
|
|
312
|
+
def self.[](*args)
|
|
313
|
+
self.new(args)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
class REXMLConverter # :nodoc: all
|
|
318
|
+
include Amrita2
|
|
319
|
+
include Util::OptionSupport
|
|
320
|
+
|
|
321
|
+
def initialize(*args)
|
|
322
|
+
parse_opt(*args)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def convert(e)
|
|
326
|
+
@noattrs = select_elements(e, :noattrs)
|
|
327
|
+
@use_child_contents = select_elements(e, :use_child_contents)
|
|
328
|
+
@use_child = select_elements(e, :use_child)
|
|
329
|
+
convert1(e)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def convert1(e)
|
|
333
|
+
h = {}
|
|
334
|
+
if use_attr?(e)
|
|
335
|
+
e.attributes.each do |key, value|
|
|
336
|
+
h[key.intern] = value
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
ctag = get_contents_tag(e)
|
|
341
|
+
if ctag
|
|
342
|
+
if e.elements.size > 0
|
|
343
|
+
s = e.collect { |cc| cc.to_s }
|
|
344
|
+
h[ctag] = SanitizedString[s]
|
|
345
|
+
else
|
|
346
|
+
h[ctag] = e.text
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
e.each_element do |c|
|
|
351
|
+
if use_child_contents?(c)
|
|
352
|
+
if c.elements.size > 0
|
|
353
|
+
s = c.collect { |cc| cc.to_s }
|
|
354
|
+
h[c.name.intern] = SanitizedString[s]
|
|
355
|
+
else
|
|
356
|
+
h[c.name.intern] = c.text
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
if use_child?(c)
|
|
361
|
+
ch = convert1(c)
|
|
362
|
+
case h[c.name.intern]
|
|
363
|
+
when nil
|
|
364
|
+
h.merge!(ch)
|
|
365
|
+
when Array
|
|
366
|
+
h[c.name.intern] << ch[c.name.intern]
|
|
367
|
+
else
|
|
368
|
+
h[c.name.intern] = [h[c.name.intern], ch[c.name.intern]]
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
if @opt[:use_tag]
|
|
373
|
+
{ e.name.intern => h }
|
|
374
|
+
else
|
|
375
|
+
h
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
private
|
|
380
|
+
|
|
381
|
+
def select_elements(e, key)
|
|
382
|
+
case xpath = opt[key]
|
|
383
|
+
when String
|
|
384
|
+
REXML::XPath.match(e, xpath)
|
|
385
|
+
when nil,false
|
|
386
|
+
return []
|
|
387
|
+
else
|
|
388
|
+
REXML::XPath.match(e, "//*")
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def get_contents_tag(e)
|
|
393
|
+
case o = opt[:use_contents]
|
|
394
|
+
when true, :use_contents
|
|
395
|
+
:contents
|
|
396
|
+
when Symbol
|
|
397
|
+
o
|
|
398
|
+
when nil
|
|
399
|
+
nil
|
|
400
|
+
else
|
|
401
|
+
raise "not implemented"
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def use_attr?(e)
|
|
406
|
+
not @noattrs.include?(e)
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def use_child_contents?(e)
|
|
410
|
+
@use_child_contents.include?(e)
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def use_child?(e)
|
|
414
|
+
@use_child.include?(e)
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
module Runtime # :nodoc: all
|
|
420
|
+
def amrita_set_context_value(v)
|
|
421
|
+
Thread::current[:amrita_context_value] = v
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def amrita_get_context_value
|
|
425
|
+
Thread::current[:amrita_context_value]
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def new_element(tag, attrs={}, &block)
|
|
429
|
+
a = {}
|
|
430
|
+
attrs.each { |k,v| a[k.to_s] = v }
|
|
431
|
+
ret = Hpricot::Elem.new(Hpricot::STag.new(tag.to_s, a))
|
|
432
|
+
ret.instance_eval(&block) if block_given?
|
|
433
|
+
ret
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def start_tag(e, out="")
|
|
437
|
+
out << "<#{e.stag.name}"
|
|
438
|
+
e.attributes.each do |k, v|
|
|
439
|
+
next unless v
|
|
440
|
+
out << " "
|
|
441
|
+
out << "#{k}=\"#{Util::sanitize_attribute_value(v.to_s)}\""
|
|
442
|
+
#attr.write(out)
|
|
443
|
+
end unless e.attributes.empty?
|
|
444
|
+
out << ">"
|
|
445
|
+
out
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def end_tag(e, out="")
|
|
449
|
+
out << "</#{e.stag.name}>"
|
|
450
|
+
out
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
module ElementHelper # :nodoc: all
|
|
455
|
+
def elements
|
|
456
|
+
children.select do |c|
|
|
457
|
+
c.kind_of?(Hpricot::Elem)
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def contents
|
|
462
|
+
if children.size > 0
|
|
463
|
+
SanitizedString[children.to_s]
|
|
464
|
+
else
|
|
465
|
+
nil
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def set_attribute(key, val)
|
|
470
|
+
case val
|
|
471
|
+
when nil, ""
|
|
472
|
+
delete_attribute(key)
|
|
473
|
+
else
|
|
474
|
+
super
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def delete_attribute(key)
|
|
479
|
+
raw_attributes.delete(key)
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def add(elem)
|
|
483
|
+
insert_after(elem, nil)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def add_text(text)
|
|
487
|
+
insert_after(Hpricot::Text.new(text), nil)
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
def as_amrita_dictionary(opt={})
|
|
491
|
+
ret = {}
|
|
492
|
+
attributes.each do |k, v|
|
|
493
|
+
ret[k.intern] = v
|
|
494
|
+
end
|
|
495
|
+
if opt[:use_contents]
|
|
496
|
+
ret[opt[:use_contents]] =
|
|
497
|
+
Amrita2::SanitizedString[
|
|
498
|
+
children.collect do |c|
|
|
499
|
+
c.to_s
|
|
500
|
+
end.join
|
|
501
|
+
]
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
if opt[:use_tag]
|
|
505
|
+
{ name.intern => ret }
|
|
506
|
+
else
|
|
507
|
+
ret
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
module Core
|
|
513
|
+
class CodeGenerator # :nodoc: all
|
|
514
|
+
def initialize
|
|
515
|
+
@iehack = true
|
|
516
|
+
@lines = []
|
|
517
|
+
@indent = 0
|
|
518
|
+
init_strbuf
|
|
519
|
+
@module_stack = [CGModule.new('')]
|
|
520
|
+
@current_stream = "__stream__"
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def init_strbuf
|
|
524
|
+
@strbuf = ""
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
def code(line)
|
|
528
|
+
flush
|
|
529
|
+
@lines << [@indent, line]
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
def comment(comments)
|
|
533
|
+
comments.each do |c|
|
|
534
|
+
@lines << [@indent, "# #{c}"]
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
def result
|
|
539
|
+
flush
|
|
540
|
+
m = @module_stack.shift
|
|
541
|
+
if m
|
|
542
|
+
m.constants.each do |name, defs|
|
|
543
|
+
code("#{name} = #{defs}")
|
|
544
|
+
end
|
|
545
|
+
end
|
|
546
|
+
@lines.collect do |indent, l|
|
|
547
|
+
" " * indent + l
|
|
548
|
+
end.join("\n")
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
def put_constant(c)
|
|
552
|
+
@strbuf.concat c.to_s
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def flush
|
|
556
|
+
if @strbuf.size > 0
|
|
557
|
+
@lines << [@indent, "#{@current_stream}.concat(#{@strbuf.inspect})"]
|
|
558
|
+
init_strbuf
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
def put_hpricot_node(node)
|
|
563
|
+
s = ""
|
|
564
|
+
node.output(s)
|
|
565
|
+
put_constant s
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def level_up
|
|
569
|
+
@indent += 1
|
|
570
|
+
yield
|
|
571
|
+
flush
|
|
572
|
+
@indent -= 1
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
def if_(cond, &block)
|
|
576
|
+
code("if #{cond}")
|
|
577
|
+
level_up do
|
|
578
|
+
yield
|
|
579
|
+
end
|
|
580
|
+
code("end")
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
def case_(obj, &block)
|
|
584
|
+
code("case #{obj}")
|
|
585
|
+
yield
|
|
586
|
+
code("end")
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
def when_(obj, &block)
|
|
590
|
+
code("when #{obj}")
|
|
591
|
+
level_up do
|
|
592
|
+
yield
|
|
593
|
+
end
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
def else_(&block)
|
|
597
|
+
code("else")
|
|
598
|
+
level_up do
|
|
599
|
+
yield
|
|
600
|
+
end
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
def with_stream(new_stream_name=nil)
|
|
604
|
+
if new_stream_name
|
|
605
|
+
old_stream = @current_stream
|
|
606
|
+
code("before_#{new_stream_name} = #@current_stream")
|
|
607
|
+
@current_stream = new_stream_name.to_s
|
|
608
|
+
end
|
|
609
|
+
code("#@current_stream = '' ")
|
|
610
|
+
yield
|
|
611
|
+
flush
|
|
612
|
+
ensure
|
|
613
|
+
if new_stream_name
|
|
614
|
+
@current_stream = old_stream
|
|
615
|
+
code %[Thread::current[:amrita_stream] ||= {} ]
|
|
616
|
+
code %[Thread::current[:amrita_stream][#{new_stream_name.inspect}] = #{new_stream_name}]
|
|
617
|
+
code("#@current_stream = before_#{new_stream_name}")
|
|
618
|
+
end
|
|
619
|
+
code("#@current_stream")
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
def put_stream(name)
|
|
623
|
+
code %[#@current_stream.concat(Thread::current[:amrita_stream][#{name.inspect}])]
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def def_method(name, *args)
|
|
627
|
+
if args.size > 0
|
|
628
|
+
code("def #{name}(#{args.join(',')})")
|
|
629
|
+
else
|
|
630
|
+
code("def #{name}")
|
|
631
|
+
end
|
|
632
|
+
level_up do
|
|
633
|
+
with_stream do
|
|
634
|
+
yield
|
|
635
|
+
flush
|
|
636
|
+
end
|
|
637
|
+
end
|
|
638
|
+
code("end")
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
def put_static_element(e)
|
|
642
|
+
attr = e.attributes.collect do |name, value|
|
|
643
|
+
"#{name} = #{value.inspect}"
|
|
644
|
+
end
|
|
645
|
+
if attr.size > 0
|
|
646
|
+
put_constant("<#{e.name} #{attr.join(' ')}>")
|
|
647
|
+
else
|
|
648
|
+
put_constant("<#{e.name}>")
|
|
649
|
+
end
|
|
650
|
+
yield
|
|
651
|
+
put_constant("</#{e.name}>")
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
def put_expression(exp)
|
|
655
|
+
code("#{@current_stream}.concat((#{exp}).to_s)")
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
def put_string_expression(exp)
|
|
659
|
+
code("#{@current_stream}.concat(#{exp})")
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
def define_class(name, &block)
|
|
663
|
+
define_module_or_class("class", name, &block)
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
def define_module_or_class(typ, name, &block)
|
|
667
|
+
@module_stack.unshift CGModule.new(name)
|
|
668
|
+
code("#{typ} #{name}")
|
|
669
|
+
level_up do
|
|
670
|
+
yield
|
|
671
|
+
m = @module_stack.shift
|
|
672
|
+
m.end_of_module(self)
|
|
673
|
+
end
|
|
674
|
+
code("end")
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
def define_constant(const_def)
|
|
678
|
+
@module_stack.first.define_constant(const_def)
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
def eval(src)
|
|
682
|
+
code "eval(#{src}, __binding__)"
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
def eval_and_print(src)
|
|
686
|
+
put_string_expression "eval(#{src}, __binding__)"
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
class CGModule # :nodoc:
|
|
690
|
+
attr_reader :name
|
|
691
|
+
attr_reader :constants
|
|
692
|
+
attr_reader :methods
|
|
693
|
+
|
|
694
|
+
def initialize(name)
|
|
695
|
+
@name = name
|
|
696
|
+
@constants = []
|
|
697
|
+
@methods = []
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
def define_constant(const_def)
|
|
701
|
+
name = nil
|
|
702
|
+
@constants.each do |n, cdef|
|
|
703
|
+
if cdef == const_def
|
|
704
|
+
name = n
|
|
705
|
+
break
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
unless name
|
|
709
|
+
name = "C#{sprintf("%03d", @constants.size)}"
|
|
710
|
+
@constants << [name, const_def]
|
|
711
|
+
end
|
|
712
|
+
name
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
def define_method(vars, &block)
|
|
716
|
+
name = "m#{sprintf("%03d", @methods.size)}"
|
|
717
|
+
@methods << [name, vars, block]
|
|
718
|
+
name
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
def end_of_module(cg)
|
|
722
|
+
@constants.each do |name, defs|
|
|
723
|
+
cg.code("#{name} = #{defs}")
|
|
724
|
+
end
|
|
725
|
+
@methods.each do |name, vars, body|
|
|
726
|
+
cg.define_method(name, vars) do
|
|
727
|
+
body.call
|
|
728
|
+
end
|
|
729
|
+
end
|
|
730
|
+
end
|
|
731
|
+
end
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
class BaseNode # :nodoc: all
|
|
735
|
+
attr_reader :parent
|
|
736
|
+
|
|
737
|
+
def initialize(parent)
|
|
738
|
+
@parent = parent
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
def render_me(cg)
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
def module_src(cg)
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
def root
|
|
748
|
+
parent.root
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
def parent_de
|
|
752
|
+
parent
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
def dynamic?
|
|
756
|
+
false
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
def has_ruby?
|
|
760
|
+
false
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
class StaticNode < BaseNode # :nodoc: all
|
|
765
|
+
def initialize(parent, node)
|
|
766
|
+
super(parent)
|
|
767
|
+
@node = node
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
def render_me(cg)
|
|
771
|
+
#cg.put_string_expression(cg.define_constant(@node.to_s.inspect))
|
|
772
|
+
|
|
773
|
+
# to keep
|
|
774
|
+
s = ""
|
|
775
|
+
@node.output(s, :preserve=>true)
|
|
776
|
+
cg.put_string_expression(cg.define_constant(s.inspect))
|
|
777
|
+
end
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
class CommentNode < BaseNode # :nodoc: all
|
|
781
|
+
def initialize(parent, node)
|
|
782
|
+
super(parent)
|
|
783
|
+
@node = node
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
def render_me(cg)
|
|
787
|
+
#cg.put_string_expression(cg.define_constant(@node.to_s.inspect))
|
|
788
|
+
# to keep
|
|
789
|
+
s = ""
|
|
790
|
+
@node.output(s, :preserve=>true)
|
|
791
|
+
cg.put_string_expression(cg.define_constant(s.inspect))
|
|
792
|
+
end
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
class ErbNode < BaseNode # :nodoc: all
|
|
796
|
+
include Util
|
|
797
|
+
attr_reader :node
|
|
798
|
+
def initialize(parent, node)
|
|
799
|
+
super(parent)
|
|
800
|
+
@node = node
|
|
801
|
+
@erb_trim_mode = nil
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
def dynamic?
|
|
805
|
+
true
|
|
806
|
+
end
|
|
807
|
+
|
|
808
|
+
def render_me_old(cg)
|
|
809
|
+
src = @node.inner_text
|
|
810
|
+
src.each do |s|
|
|
811
|
+
cg.code("# #{s.chomp}")
|
|
812
|
+
end
|
|
813
|
+
erb = cg.define_constant %[ERB.new(#{with_context_value_erb(src).inspect}, nil, #{@erb_trim_mode.inspect})]
|
|
814
|
+
cg.code("amrita_set_context_value($_)")
|
|
815
|
+
cg.put_string_expression %[#{erb}.result(__binding__)]
|
|
816
|
+
cg.code("$_ = amrita_get_context_value")
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
def render_me(cg)
|
|
820
|
+
src = @node.inner_text
|
|
821
|
+
src.each do |s|
|
|
822
|
+
cg.code("# #{s.chomp}")
|
|
823
|
+
end
|
|
824
|
+
erb = cg.define_constant %[ERB.new(#{with_context_value_erb(src).inspect}, nil, #{@erb_trim_mode.inspect}).src]
|
|
825
|
+
cg.code("amrita_set_context_value($_)")
|
|
826
|
+
cg.eval_and_print(erb)
|
|
827
|
+
cg.code("$_ = amrita_get_context_value")
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
def has_ruby?
|
|
831
|
+
true
|
|
832
|
+
end
|
|
833
|
+
|
|
834
|
+
private
|
|
835
|
+
def with_context_value_erb(src)
|
|
836
|
+
[
|
|
837
|
+
"<% $_ = amrita_get_context_value %>",
|
|
838
|
+
src,
|
|
839
|
+
"<% amrita_set_context_value($_) %>",
|
|
840
|
+
].join("")
|
|
841
|
+
end
|
|
842
|
+
end
|
|
843
|
+
|
|
844
|
+
class ParentNode < BaseNode # :nodoc: all
|
|
845
|
+
attr_reader :element, :parent, :children
|
|
846
|
+
def initialize(parent, element)
|
|
847
|
+
super(parent)
|
|
848
|
+
@element = element
|
|
849
|
+
@children = parent_de.parse_element(@element)
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
def dynamic?
|
|
853
|
+
children.any? { |c| c.dynamic? }
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
def has_dynamic?
|
|
857
|
+
children.any? { |c| c.dynamic? }
|
|
858
|
+
end
|
|
859
|
+
|
|
860
|
+
def has_ruby?
|
|
861
|
+
children.any? { |c| c.has_ruby? }
|
|
862
|
+
end
|
|
863
|
+
|
|
864
|
+
def each(&block)
|
|
865
|
+
children.each(&block)
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
def module_src(cg)
|
|
869
|
+
each do |c|
|
|
870
|
+
c.module_src(cg)
|
|
871
|
+
end
|
|
872
|
+
end
|
|
873
|
+
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
class CompoundElement < ParentNode # :nodoc: all
|
|
877
|
+
def render_me(cg)
|
|
878
|
+
if @element.empty?
|
|
879
|
+
cg.put_constant(@element.to_s)
|
|
880
|
+
else
|
|
881
|
+
@parent.element_render_code(cg, @element) do
|
|
882
|
+
each do |c|
|
|
883
|
+
c.render_me(cg)
|
|
884
|
+
end
|
|
885
|
+
end
|
|
886
|
+
end
|
|
887
|
+
end
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
class DynamicElement < ParentNode # :nodoc: all
|
|
891
|
+
include Util
|
|
892
|
+
include OptionSupport
|
|
893
|
+
|
|
894
|
+
attr_reader :parent, :name, :children, :filter, :renderers, :element, :attrs
|
|
895
|
+
def self.delegate_to_filter(name)
|
|
896
|
+
module_eval <<-END
|
|
897
|
+
def #{name}(*args,&block)
|
|
898
|
+
@filter.#{name}(self, *args,&block)
|
|
899
|
+
end
|
|
900
|
+
END
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
FilterMethods.each do |m|
|
|
904
|
+
delegate_to_filter(m)
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
def initialize(parent, name, element, filters=[])
|
|
908
|
+
@parent,@name,@element = parent, name, element
|
|
909
|
+
parse_opt(root.opt)
|
|
910
|
+
@attrs = {}
|
|
911
|
+
delete_am_attrs
|
|
912
|
+
setup_filter(filters)
|
|
913
|
+
@element = filter_element(@element)
|
|
914
|
+
@renderers = []
|
|
915
|
+
super(parent, element)
|
|
916
|
+
|
|
917
|
+
setup_type_renderer
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
def [](key)
|
|
921
|
+
@attrs[key]
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
def parent_de
|
|
925
|
+
self
|
|
926
|
+
end
|
|
927
|
+
|
|
928
|
+
def dynamic?
|
|
929
|
+
true
|
|
930
|
+
end
|
|
931
|
+
|
|
932
|
+
def has_ruby?
|
|
933
|
+
super or am_for_value or am_skipif_value or am_v_value
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
def setup_filter(filters)
|
|
937
|
+
#default_filter = @parent ? @parent.filter : DefaultFilter.new
|
|
938
|
+
default_filter = DefaultFilter.new
|
|
939
|
+
default_filter.parse_opt(opt)
|
|
940
|
+
if filters.size > 0
|
|
941
|
+
@filter = current = filters.first
|
|
942
|
+
for f in filters do
|
|
943
|
+
current.next_ = f
|
|
944
|
+
current = f
|
|
945
|
+
end
|
|
946
|
+
current.next_ = default_filter
|
|
947
|
+
else
|
|
948
|
+
@filter = default_filter
|
|
949
|
+
end
|
|
950
|
+
end
|
|
951
|
+
|
|
952
|
+
def class_name
|
|
953
|
+
if name
|
|
954
|
+
"XX" + name.capitalize
|
|
955
|
+
else
|
|
956
|
+
"XX%d" % [object_id.abs]
|
|
957
|
+
end
|
|
958
|
+
end
|
|
959
|
+
|
|
960
|
+
def instance_name
|
|
961
|
+
class_name + "Instance"
|
|
962
|
+
end
|
|
963
|
+
|
|
964
|
+
def compile(cg = CodeGenerator.new)
|
|
965
|
+
define_element_method(cg) do
|
|
966
|
+
cg.code("$_ = value")
|
|
967
|
+
loop_check_code(cg)
|
|
968
|
+
method_body_code(cg)
|
|
969
|
+
end
|
|
970
|
+
end
|
|
971
|
+
|
|
972
|
+
def render_me(cg, n = name)
|
|
973
|
+
if (n)
|
|
974
|
+
cg.put_string_expression("#{instance_name}.render_with($_.amrita_value(:#{n}), __binding__)")
|
|
975
|
+
else
|
|
976
|
+
cg.put_string_expression("#{instance_name}.render_with($_, __binding__)")
|
|
977
|
+
end
|
|
978
|
+
end
|
|
979
|
+
|
|
980
|
+
def module_src(cg)
|
|
981
|
+
cg.define_class(class_name) do
|
|
982
|
+
cg.code <<-END
|
|
983
|
+
include Amrita2
|
|
984
|
+
include Amrita2::Runtime
|
|
985
|
+
END
|
|
986
|
+
compile(cg)
|
|
987
|
+
super
|
|
988
|
+
end
|
|
989
|
+
cg.code("#{instance_name} = #{class_name}.new")
|
|
990
|
+
end
|
|
991
|
+
|
|
992
|
+
def am_for_value
|
|
993
|
+
@attrs[am_for.intern]
|
|
994
|
+
end
|
|
995
|
+
|
|
996
|
+
def am_skipif_value
|
|
997
|
+
@attrs[am_skipif.intern]
|
|
998
|
+
end
|
|
999
|
+
|
|
1000
|
+
def am_v_value
|
|
1001
|
+
@attrs[am_v.intern]
|
|
1002
|
+
end
|
|
1003
|
+
|
|
1004
|
+
private
|
|
1005
|
+
|
|
1006
|
+
def delete_am_attrs
|
|
1007
|
+
[
|
|
1008
|
+
am_src,
|
|
1009
|
+
am_filter,
|
|
1010
|
+
am_skipif,
|
|
1011
|
+
am_for,
|
|
1012
|
+
am_v,
|
|
1013
|
+
].each do |key_s|
|
|
1014
|
+
key = key_s.intern
|
|
1015
|
+
@attrs[key] = @element.attributes[key_s]
|
|
1016
|
+
@element.delete_attribute(key_s)
|
|
1017
|
+
end
|
|
1018
|
+
end
|
|
1019
|
+
end
|
|
1020
|
+
|
|
1021
|
+
class RootElement < DynamicElement # :nodoc: all
|
|
1022
|
+
include Filters
|
|
1023
|
+
|
|
1024
|
+
attr_reader :xml_decl, :doctype
|
|
1025
|
+
|
|
1026
|
+
def initialize(elements, opt={}, &block)
|
|
1027
|
+
@xml_decl = @doctype = nil
|
|
1028
|
+
parse_opt(opt)
|
|
1029
|
+
if block_given?
|
|
1030
|
+
@filter_proc = block
|
|
1031
|
+
else
|
|
1032
|
+
@filter_proc = proc do |e, src, filters|
|
|
1033
|
+
end
|
|
1034
|
+
end
|
|
1035
|
+
e = Hpricot.make("<root />", :xml=>true).first
|
|
1036
|
+
elements.each do |c|
|
|
1037
|
+
case c
|
|
1038
|
+
when Hpricot::Elem, Hpricot::Text, Hpricot::Comment, Hpricot::BogusETag
|
|
1039
|
+
e.insert_after(c, nil)
|
|
1040
|
+
when Hpricot::XMLDecl
|
|
1041
|
+
@xml_decl = c
|
|
1042
|
+
when Hpricot::DocType
|
|
1043
|
+
@doctype = c
|
|
1044
|
+
else
|
|
1045
|
+
raise "#{c.class}"
|
|
1046
|
+
end
|
|
1047
|
+
end
|
|
1048
|
+
filters = []
|
|
1049
|
+
@filter_proc.call(e,nil, filters)
|
|
1050
|
+
super(nil, nil, e, filters)
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
def root
|
|
1054
|
+
self
|
|
1055
|
+
end
|
|
1056
|
+
|
|
1057
|
+
def compile(cg)
|
|
1058
|
+
cg.code <<-END
|
|
1059
|
+
include Amrita2
|
|
1060
|
+
include Amrita2::Runtime
|
|
1061
|
+
END
|
|
1062
|
+
super(cg)
|
|
1063
|
+
@children.each do |c|
|
|
1064
|
+
c.module_src(cg)
|
|
1065
|
+
end
|
|
1066
|
+
end
|
|
1067
|
+
|
|
1068
|
+
def compile_filters(element, name, *args)
|
|
1069
|
+
src = args.compact.join('|')
|
|
1070
|
+
#filters = src.split('|').collect { |f| parse_filter(f) }
|
|
1071
|
+
filters = parse_filter(src)
|
|
1072
|
+
@filter_proc.call(element, name, filters)
|
|
1073
|
+
filters
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
def parse_filter(src)
|
|
1077
|
+
src.gsub!("\n", " ")
|
|
1078
|
+
case src
|
|
1079
|
+
when "",nil
|
|
1080
|
+
[]
|
|
1081
|
+
else
|
|
1082
|
+
case ret = eval(src, @opt[:compiletime_binding])
|
|
1083
|
+
when Class
|
|
1084
|
+
[ret.new]
|
|
1085
|
+
when Module
|
|
1086
|
+
[ModuleExtendFilter[ret]]
|
|
1087
|
+
when Filters::Base
|
|
1088
|
+
[ret]
|
|
1089
|
+
when Array
|
|
1090
|
+
case ret.first
|
|
1091
|
+
when Symbol
|
|
1092
|
+
[FunctionFilter[*ret]]
|
|
1093
|
+
else
|
|
1094
|
+
ret
|
|
1095
|
+
end
|
|
1096
|
+
when Symbol
|
|
1097
|
+
[FunctionFilter[ret]]
|
|
1098
|
+
when nil
|
|
1099
|
+
[]
|
|
1100
|
+
else
|
|
1101
|
+
raise TemplateError, "unknown Filter type #{ret.class}"
|
|
1102
|
+
end
|
|
1103
|
+
end
|
|
1104
|
+
rescue ScriptError, NameError
|
|
1105
|
+
raise TemplateError, "error in filters #{src.inspect} #$!"
|
|
1106
|
+
end
|
|
1107
|
+
end
|
|
1108
|
+
|
|
1109
|
+
class DefaultFilter < Filters::Base
|
|
1110
|
+
include Renderers
|
|
1111
|
+
include Filters
|
|
1112
|
+
include Util::OptionSupport
|
|
1113
|
+
|
|
1114
|
+
def filter_element(de, element)
|
|
1115
|
+
element
|
|
1116
|
+
end
|
|
1117
|
+
|
|
1118
|
+
def parse_element(de, element)
|
|
1119
|
+
element.enum_for(:each_child).collect do |node|
|
|
1120
|
+
de.parse_node(node)
|
|
1121
|
+
end
|
|
1122
|
+
end
|
|
1123
|
+
|
|
1124
|
+
def parse_node(de, node)
|
|
1125
|
+
case node
|
|
1126
|
+
when Hpricot::Elem
|
|
1127
|
+
de.generate_dynamic_element(node) || CompoundElement.new(de, node)
|
|
1128
|
+
when Hpricot::CData
|
|
1129
|
+
ErbNode.new(de, node)
|
|
1130
|
+
when Hpricot::Text
|
|
1131
|
+
StaticNode.new(de, node)
|
|
1132
|
+
when Hpricot::Comment
|
|
1133
|
+
CommentNode.new(de, node)
|
|
1134
|
+
when Hpricot::BogusETag
|
|
1135
|
+
StaticNode.new(de, node)
|
|
1136
|
+
else
|
|
1137
|
+
raise "not implemented #{node.class}"
|
|
1138
|
+
end
|
|
1139
|
+
end
|
|
1140
|
+
|
|
1141
|
+
def generate_dynamic_element(de, e)
|
|
1142
|
+
src = e.attributes[de.am_src]
|
|
1143
|
+
filters_src = e.attributes[de.am_filter]
|
|
1144
|
+
if src
|
|
1145
|
+
name, fs = src.split('|', 2)
|
|
1146
|
+
name.strip!
|
|
1147
|
+
filters = de.root.compile_filters(e, name, fs, filters_src)
|
|
1148
|
+
DynamicElement.new(de, name , e, filters)
|
|
1149
|
+
elsif filters_src
|
|
1150
|
+
filters = de.root.compile_filters(e, nil, filters_src)
|
|
1151
|
+
DynamicElement.new(de, nil , e, filters)
|
|
1152
|
+
else
|
|
1153
|
+
nil
|
|
1154
|
+
end
|
|
1155
|
+
end
|
|
1156
|
+
|
|
1157
|
+
def setup_type_renderer(de)
|
|
1158
|
+
if de.has_dynamic?
|
|
1159
|
+
de.renderers << HashRenderer.new unless de.renderers.find {|r| r.kind_of?(HashRenderer) }
|
|
1160
|
+
else
|
|
1161
|
+
de.renderers << ScalarRenderer.new
|
|
1162
|
+
end
|
|
1163
|
+
de.renderers << ElseRenderer.new
|
|
1164
|
+
end
|
|
1165
|
+
|
|
1166
|
+
def define_element_method(de, cg, &block)
|
|
1167
|
+
cg.def_method("render_with", "value", "__binding__", "__cnt__ = -1 ", &block)
|
|
1168
|
+
end
|
|
1169
|
+
|
|
1170
|
+
def loop_check_code(de, cg)
|
|
1171
|
+
cg.if_ "$_.kind_of?(Amrita2::Enum) and not $_.kind_of?(ScalarData)" do
|
|
1172
|
+
cg.code("$_.each_with_index do |v, i| ")
|
|
1173
|
+
cg.level_up do
|
|
1174
|
+
cg.put_string_expression("render_with(v, __binding__, i) ")
|
|
1175
|
+
end
|
|
1176
|
+
cg.code("end")
|
|
1177
|
+
cg.code("return __stream__")
|
|
1178
|
+
end
|
|
1179
|
+
end
|
|
1180
|
+
|
|
1181
|
+
def method_body_code(de, cg)
|
|
1182
|
+
de.value_filter_code(cg, "value")
|
|
1183
|
+
#cg.code("p #{de.element.name.inspect}, $_, stream")
|
|
1184
|
+
de.renderer_code(cg, de.element)
|
|
1185
|
+
#cg.code("p #{de.element.name.inspect}, $_, stream")
|
|
1186
|
+
end
|
|
1187
|
+
|
|
1188
|
+
def value_filter_code(de, cg, value_name)
|
|
1189
|
+
end
|
|
1190
|
+
|
|
1191
|
+
def renderer_code(de, cg, element)
|
|
1192
|
+
r = de.renderers
|
|
1193
|
+
case r.size
|
|
1194
|
+
when 0
|
|
1195
|
+
when 1
|
|
1196
|
+
r.first.generate_body(cg, de, element)
|
|
1197
|
+
else
|
|
1198
|
+
cg.case_("$_") do
|
|
1199
|
+
r.each do |r1|
|
|
1200
|
+
r1.generate_when(cg) do
|
|
1201
|
+
r1.generate_body(cg, de, element)
|
|
1202
|
+
end
|
|
1203
|
+
end
|
|
1204
|
+
end
|
|
1205
|
+
end
|
|
1206
|
+
end
|
|
1207
|
+
|
|
1208
|
+
def element_render_code(de, cg, element, &block)
|
|
1209
|
+
if ((element.name == 'span' or element.name == '_' or element.name == 'root') and element.attributes.size == 0)
|
|
1210
|
+
yield
|
|
1211
|
+
else
|
|
1212
|
+
cg.put_static_element(element, &block)
|
|
1213
|
+
end
|
|
1214
|
+
end
|
|
1215
|
+
end
|
|
1216
|
+
|
|
1217
|
+
#
|
|
1218
|
+
# = using Amrita2::Template
|
|
1219
|
+
#
|
|
1220
|
+
# require "amrita2/template"
|
|
1221
|
+
# include Amrita2
|
|
1222
|
+
# tmpl = Template.new <<-END
|
|
1223
|
+
# <<html<
|
|
1224
|
+
# <<body<
|
|
1225
|
+
# <<h1 :title>>
|
|
1226
|
+
# <<p :body<
|
|
1227
|
+
# <<:template>> is a html template libraly for <<:lang>>
|
|
1228
|
+
# END
|
|
1229
|
+
#
|
|
1230
|
+
# puts tmpl.render_with(:title=>"hello world", :body=>{ :template=>"Amrita2", :lang=>"Ruby" })
|
|
1231
|
+
|
|
1232
|
+
class Template
|
|
1233
|
+
include Amrita2
|
|
1234
|
+
include Util
|
|
1235
|
+
include OptionSupport
|
|
1236
|
+
include Filters
|
|
1237
|
+
include CompileTimeContext
|
|
1238
|
+
include Runtime
|
|
1239
|
+
|
|
1240
|
+
# Specify attribute prefix for dynamic element. Defaults to 'am:'.
|
|
1241
|
+
attr_accessor :amrita_prefix
|
|
1242
|
+
# Specify weather use inline ruby. Defaults to +true+
|
|
1243
|
+
attr_accessor :inline_ruby
|
|
1244
|
+
|
|
1245
|
+
attr_reader :tracer
|
|
1246
|
+
attr_reader :root
|
|
1247
|
+
|
|
1248
|
+
# Initialize Template object using +text+ and +opts+.
|
|
1249
|
+
# Optionaly specify block for setting filters.
|
|
1250
|
+
#
|
|
1251
|
+
# t = Amrita2::Template.new(text) do |e, src, filters|
|
|
1252
|
+
# if src == "aaa"
|
|
1253
|
+
# filters << Amrita2::Filters::Default[456]
|
|
1254
|
+
# end
|
|
1255
|
+
# end
|
|
1256
|
+
|
|
1257
|
+
def initialize(text, *opts, &block)
|
|
1258
|
+
parse_opt(*opts)
|
|
1259
|
+
@text = text.dup
|
|
1260
|
+
@setuped = false
|
|
1261
|
+
@filter_proc = block
|
|
1262
|
+
@text_domain = nil
|
|
1263
|
+
@inline_ruby = true
|
|
1264
|
+
@amrita_prefix = @opt[:amrita_prefix] || "am:"
|
|
1265
|
+
end
|
|
1266
|
+
|
|
1267
|
+
def amrita_src=(v)
|
|
1268
|
+
@opt[:amrita_src] = v
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
def amrita_filter=(v)
|
|
1272
|
+
@opt[:amrita_filter] = v
|
|
1273
|
+
end
|
|
1274
|
+
|
|
1275
|
+
def compiletime_binding=(b)
|
|
1276
|
+
@opt[:compiletime_binding] = b
|
|
1277
|
+
end
|
|
1278
|
+
|
|
1279
|
+
# Print compiled ruby code and runtime trace to
|
|
1280
|
+
# +io_or_type+
|
|
1281
|
+
#
|
|
1282
|
+
# tmpl = Template.new "...."
|
|
1283
|
+
# tmpl.set_trace(STDOUT)
|
|
1284
|
+
# puts tmpl.render_with(...)
|
|
1285
|
+
|
|
1286
|
+
def set_trace(io_or_type, &block)
|
|
1287
|
+
@tracer = Tracer.new(io_or_type, &block)
|
|
1288
|
+
end
|
|
1289
|
+
|
|
1290
|
+
def setup
|
|
1291
|
+
setup_opt
|
|
1292
|
+
@preprocessor = PreProcessor.new(@opt)
|
|
1293
|
+
cg = CodeGenerator.new
|
|
1294
|
+
preprocessed_text = @preprocessor.process(@text)
|
|
1295
|
+
|
|
1296
|
+
if bc = @preprocessor.sections[:BeforeCompile]
|
|
1297
|
+
self.instance_eval do
|
|
1298
|
+
eval bc
|
|
1299
|
+
end
|
|
1300
|
+
end
|
|
1301
|
+
|
|
1302
|
+
filter_setup do |e, src, filters|
|
|
1303
|
+
filters.unshift Trace.new if @tracer and @tracer.auto_trace?
|
|
1304
|
+
filters.unshift InlineRuby.new if @inline_ruby
|
|
1305
|
+
end
|
|
1306
|
+
|
|
1307
|
+
compile(cg, preprocessed_text)
|
|
1308
|
+
@setuped = true
|
|
1309
|
+
cg.result
|
|
1310
|
+
ensure
|
|
1311
|
+
@tracer.code(cg) if @tracer and @tracer.code_trace?
|
|
1312
|
+
end
|
|
1313
|
+
|
|
1314
|
+
# render template with +value+ and +binding+.
|
|
1315
|
+
# use TOPLEVEL_BINDING if +binding+ is nil.
|
|
1316
|
+
#
|
|
1317
|
+
def render_with(value, binding_=nil)
|
|
1318
|
+
setup unless @setuped
|
|
1319
|
+
unless binding_
|
|
1320
|
+
if value.kind_of?(Binding)
|
|
1321
|
+
binding_ = value
|
|
1322
|
+
else
|
|
1323
|
+
binding_ = binding
|
|
1324
|
+
end
|
|
1325
|
+
end
|
|
1326
|
+
|
|
1327
|
+
ret = nil
|
|
1328
|
+
|
|
1329
|
+
if @opt[:process_xhtml]
|
|
1330
|
+
ret = ""
|
|
1331
|
+
ret << @root.xml_decl.to_s << "\n" if @root.xml_decl
|
|
1332
|
+
ret << @root.doctype.to_s << "\n" if @root.doctype
|
|
1333
|
+
ret << @cls.new.render_with(value, binding_)
|
|
1334
|
+
else
|
|
1335
|
+
ret = @cls.new.render_with(value, binding_)
|
|
1336
|
+
end
|
|
1337
|
+
SanitizedString[ret]
|
|
1338
|
+
end
|
|
1339
|
+
|
|
1340
|
+
private
|
|
1341
|
+
def setup_opt
|
|
1342
|
+
@opt[:am_src] = @opt[:amrita_src] || @amrita_prefix + "src"
|
|
1343
|
+
@opt[:am_filter] = @opt[:amrita_filter] || @amrita_prefix + "filter"
|
|
1344
|
+
@opt[:am_skipif] = @opt[:amrita_skipif] || @amrita_prefix + "skipif"
|
|
1345
|
+
@opt[:am_for] = @opt[:amrita_for] || @amrita_prefix + "for"
|
|
1346
|
+
@opt[:am_v] = @opt[:amrita_v] || @amrita_prefix + "v"
|
|
1347
|
+
@opt[:tracer] = @tracer
|
|
1348
|
+
@opt[:compiletime_binding] = @opt[:compiletime_binding] || binding
|
|
1349
|
+
end
|
|
1350
|
+
|
|
1351
|
+
def compile(cg, text)
|
|
1352
|
+
@elements = Hpricot.make(text, :xml=>true)
|
|
1353
|
+
@root = RootElement.new @elements, opt, &@filter_proc
|
|
1354
|
+
@root.text_domain = @text_domain if @text_domain
|
|
1355
|
+
@root.compile(cg)
|
|
1356
|
+
|
|
1357
|
+
@cls = Class.new
|
|
1358
|
+
|
|
1359
|
+
##############################################################################
|
|
1360
|
+
# very dirty hack
|
|
1361
|
+
# ruby-gettext does not work well with class without a name
|
|
1362
|
+
#
|
|
1363
|
+
#const_name = "T%08x" % Thread.current.object_id
|
|
1364
|
+
const_name = "T%08x" % @cls.object_id
|
|
1365
|
+
Amrita2.module_eval { remove_const(const_name) } if Amrita2.const_defined?(const_name)
|
|
1366
|
+
Amrita2.const_set(const_name, @cls)
|
|
1367
|
+
# This should be modified later
|
|
1368
|
+
##############################################################################
|
|
1369
|
+
|
|
1370
|
+
@cls.module_eval cg.result
|
|
1371
|
+
@cls.const_set(:Tracer, @tracer) if @tracer
|
|
1372
|
+
rescue SyntaxError
|
|
1373
|
+
raise RuntimeError,$!
|
|
1374
|
+
end
|
|
1375
|
+
|
|
1376
|
+
def setup_filter_proc
|
|
1377
|
+
old_proc = @filter_proc
|
|
1378
|
+
@filter_proc = proc do |e, name ,filters|
|
|
1379
|
+
filters.unshift Trace.new if @tracer and @tracer.auto_trace?
|
|
1380
|
+
filters.unshift InlineRuby.new if @inline_ruby
|
|
1381
|
+
old_proc.call(e, name ,filters) if old_proc
|
|
1382
|
+
end
|
|
1383
|
+
end
|
|
1384
|
+
|
|
1385
|
+
# ment to be used in macro.rb and BeforeCompile section
|
|
1386
|
+
def filter_setup(&block)
|
|
1387
|
+
old_proc = @filter_proc
|
|
1388
|
+
@filter_proc = proc do |e, name ,filters|
|
|
1389
|
+
block.call(e, name, filters)
|
|
1390
|
+
old_proc.call(e, name, filters) if old_proc
|
|
1391
|
+
end
|
|
1392
|
+
end
|
|
1393
|
+
|
|
1394
|
+
class Tracer
|
|
1395
|
+
def initialize(io_or_type, &block)
|
|
1396
|
+
@auto_trace = false
|
|
1397
|
+
@trace_proc = block
|
|
1398
|
+
case io_or_type
|
|
1399
|
+
when :code, :element, :all
|
|
1400
|
+
@type = io_or_type
|
|
1401
|
+
raise "need block" unless @trace_proc
|
|
1402
|
+
when :all_element
|
|
1403
|
+
@type = :element
|
|
1404
|
+
@auto_trace = true
|
|
1405
|
+
raise "need block" unless @trace_proc
|
|
1406
|
+
else
|
|
1407
|
+
if io_or_type.respond_to?(:<<)
|
|
1408
|
+
@type = :all
|
|
1409
|
+
@auto_trace = true
|
|
1410
|
+
@trace_proc = proc do |msg|
|
|
1411
|
+
io_or_type << msg << "\n"
|
|
1412
|
+
end
|
|
1413
|
+
else
|
|
1414
|
+
raise "unknown type for trace #{io_or_type.inspect}"
|
|
1415
|
+
end
|
|
1416
|
+
end
|
|
1417
|
+
end
|
|
1418
|
+
|
|
1419
|
+
def auto_trace?
|
|
1420
|
+
@auto_trace
|
|
1421
|
+
end
|
|
1422
|
+
|
|
1423
|
+
def code_trace?
|
|
1424
|
+
@type == :code or @type == :all
|
|
1425
|
+
end
|
|
1426
|
+
|
|
1427
|
+
def code(cg)
|
|
1428
|
+
@trace_proc.call(cg.result) if code_trace?
|
|
1429
|
+
end
|
|
1430
|
+
|
|
1431
|
+
def <<(msg)
|
|
1432
|
+
@trace_proc.call(msg) if @type == :all or @type == :element
|
|
1433
|
+
end
|
|
1434
|
+
end
|
|
1435
|
+
end
|
|
1436
|
+
|
|
1437
|
+
class PreProcessor # :nodoc: all
|
|
1438
|
+
attr_reader :sections
|
|
1439
|
+
CDataStart = %r[(.*?)(<!\[CDATA\[)(.*)]m
|
|
1440
|
+
CDataEnd = %r[(.*?)(\]\]>)(.*)]m
|
|
1441
|
+
|
|
1442
|
+
def initialize(opt={})
|
|
1443
|
+
@opt = opt
|
|
1444
|
+
init()
|
|
1445
|
+
@trace = false
|
|
1446
|
+
#@trace = true
|
|
1447
|
+
end
|
|
1448
|
+
|
|
1449
|
+
def process(s)
|
|
1450
|
+
init
|
|
1451
|
+
ret = process_block(s)
|
|
1452
|
+
ret = process_erb_and_inline_amxml(ret)
|
|
1453
|
+
ret << pop_tag(0)
|
|
1454
|
+
ret
|
|
1455
|
+
end
|
|
1456
|
+
|
|
1457
|
+
def process_erb_and_inline_amxml(s)
|
|
1458
|
+
if s =~ CDataStart
|
|
1459
|
+
out_of_cdata, cdata, in_cdata = $1, $2, $3
|
|
1460
|
+
process_out_of_cdata(out_of_cdata) + cdata + parse_in_cdata(in_cdata)
|
|
1461
|
+
else
|
|
1462
|
+
process_out_of_cdata(s)
|
|
1463
|
+
end
|
|
1464
|
+
end
|
|
1465
|
+
|
|
1466
|
+
def process_block(s)
|
|
1467
|
+
ret = ""
|
|
1468
|
+
lines = s.to_a
|
|
1469
|
+
while line = lines.shift
|
|
1470
|
+
next if line =~ /^\s*$/
|
|
1471
|
+
current_indent = line[/^ */].size
|
|
1472
|
+
ret << pop_tag(current_indent)
|
|
1473
|
+
|
|
1474
|
+
case line
|
|
1475
|
+
when %r[^\s*#]
|
|
1476
|
+
# It's a comment. do nothing
|
|
1477
|
+
when AmXml::R_AMXML_BLOCK_START
|
|
1478
|
+
amxml = $1
|
|
1479
|
+
while true
|
|
1480
|
+
l = lines.shift
|
|
1481
|
+
case l
|
|
1482
|
+
when %r[(.*)<(?: |-)*$]
|
|
1483
|
+
amxml << $1
|
|
1484
|
+
break
|
|
1485
|
+
when nil
|
|
1486
|
+
raise "incomplete amxml block start #{amxml}"
|
|
1487
|
+
else
|
|
1488
|
+
amxml << l
|
|
1489
|
+
end
|
|
1490
|
+
end
|
|
1491
|
+
a = AmXml.parse(amxml, @opt)
|
|
1492
|
+
raise "illeagal amxml multi line #{amxml.inspect}" unless a
|
|
1493
|
+
push_tag(current_indent, a.tag)
|
|
1494
|
+
ret << a.result(false)
|
|
1495
|
+
when AmXml::R_AMXML_ERB_LINE
|
|
1496
|
+
mark, src = $1,$2
|
|
1497
|
+
ret << "<![CDATA[<% "
|
|
1498
|
+
while true
|
|
1499
|
+
case mark
|
|
1500
|
+
when '='
|
|
1501
|
+
ret << "\n _erbout.concat(eval(%{#{src}}).to_s) "
|
|
1502
|
+
when String
|
|
1503
|
+
ret << "\n#{src}"
|
|
1504
|
+
else
|
|
1505
|
+
break
|
|
1506
|
+
end
|
|
1507
|
+
case l = lines.shift
|
|
1508
|
+
when AmXml::R_AMXML_ERB_LINE
|
|
1509
|
+
mark, src = $1,$2
|
|
1510
|
+
else
|
|
1511
|
+
mark = nil
|
|
1512
|
+
lines.unshift l
|
|
1513
|
+
end
|
|
1514
|
+
end
|
|
1515
|
+
ret << "\n%>]]>\n"
|
|
1516
|
+
when AmXml::R_AMXML_BLOCK_LINE
|
|
1517
|
+
a = AmXml.parse($1, {:trline=>$2.include?("-")}.merge(@opt))
|
|
1518
|
+
raise "illeagal amxml block line #{line.inspect}" unless a
|
|
1519
|
+
ret << a.result(false)
|
|
1520
|
+
push_tag(current_indent, a.tag)
|
|
1521
|
+
when %r[(.*)^\s*>>>\s*$]m
|
|
1522
|
+
ret << $1
|
|
1523
|
+
raise "unmatched >>> " unless @stack.size > 0
|
|
1524
|
+
ret << pop_tag(current_indent)
|
|
1525
|
+
when %r[^\s*\|\s*([\w:]*)\s*\|\|]
|
|
1526
|
+
lp = LineProcessor.new do |cell_text|
|
|
1527
|
+
PreProcessor.new.process_block(cell_text)
|
|
1528
|
+
end
|
|
1529
|
+
while lp.parse_line(line)
|
|
1530
|
+
line = lines.shift
|
|
1531
|
+
end
|
|
1532
|
+
lines.unshift(line) unless line =~ /^( |-)*$/
|
|
1533
|
+
ret << lp.get_result_xml
|
|
1534
|
+
ret
|
|
1535
|
+
else
|
|
1536
|
+
ret << line
|
|
1537
|
+
end
|
|
1538
|
+
end
|
|
1539
|
+
ret << pop_tag(0)
|
|
1540
|
+
ret
|
|
1541
|
+
end
|
|
1542
|
+
|
|
1543
|
+
private
|
|
1544
|
+
|
|
1545
|
+
def init
|
|
1546
|
+
@sections = {}
|
|
1547
|
+
@stack = []
|
|
1548
|
+
end
|
|
1549
|
+
|
|
1550
|
+
def process_out_of_cdata(s)
|
|
1551
|
+
erb_nest=0
|
|
1552
|
+
ret = ""
|
|
1553
|
+
while s.size > 0
|
|
1554
|
+
puts "process_out_of_cdata:#{erb_nest}:#{@stack.join('/')}:#{s}" if @trace
|
|
1555
|
+
case s
|
|
1556
|
+
when %r[\A(.*?)(<%|%>)((?=.?))]m
|
|
1557
|
+
case $2
|
|
1558
|
+
when "<%" ; erb_nest +=1
|
|
1559
|
+
when "%>" ; erb_nest -= 1
|
|
1560
|
+
end
|
|
1561
|
+
if $2 == "<%" and erb_nest == 1
|
|
1562
|
+
if Regexp.last_match.post_match[0] == "("[0] and
|
|
1563
|
+
s =~ %r[\A(.*?)(<%\((\w+?)\))]m
|
|
1564
|
+
ret << process_inline_amxml($1)
|
|
1565
|
+
s = process_section($3.intern, Regexp.last_match.post_match, 1)
|
|
1566
|
+
erb_nest -= 1
|
|
1567
|
+
else
|
|
1568
|
+
ret << process_inline_amxml($1) << "<![CDATA[<%"
|
|
1569
|
+
s = Regexp.last_match.post_match
|
|
1570
|
+
end
|
|
1571
|
+
elsif $2 == "%>" and erb_nest == 0
|
|
1572
|
+
ret << $1 << $2 << "]]>"
|
|
1573
|
+
s = Regexp.last_match.post_match
|
|
1574
|
+
else
|
|
1575
|
+
ret << $1 << $2
|
|
1576
|
+
s = Regexp.last_match.post_match
|
|
1577
|
+
end
|
|
1578
|
+
else
|
|
1579
|
+
ret << process_inline_amxml(s)
|
|
1580
|
+
s = ""
|
|
1581
|
+
end
|
|
1582
|
+
end
|
|
1583
|
+
ret
|
|
1584
|
+
end
|
|
1585
|
+
|
|
1586
|
+
def parse_in_cdata(s)
|
|
1587
|
+
if s =~ CDataEnd
|
|
1588
|
+
in_cdata, cdata_end, out_of_cdata = $1, $2, $3
|
|
1589
|
+
in_cdata + cdata_end + process_erb_and_inline_amxml(out_of_cdata)
|
|
1590
|
+
else
|
|
1591
|
+
raise "end of cdata not found in #{s}"
|
|
1592
|
+
end
|
|
1593
|
+
end
|
|
1594
|
+
|
|
1595
|
+
def process_section(section_id, s, nest)
|
|
1596
|
+
puts "%s:%d:%s" % [section_id, nest, s] if @trace
|
|
1597
|
+
@sections[section_id] ||= ""
|
|
1598
|
+
case s
|
|
1599
|
+
when %r[\A(.*?)(<%|%>)(.*)\Z]m
|
|
1600
|
+
case $2
|
|
1601
|
+
when "<%"
|
|
1602
|
+
@sections[section_id] << $1 << $2
|
|
1603
|
+
s = process_section(section_id, $3, nest+1)
|
|
1604
|
+
when "%>"
|
|
1605
|
+
@sections[section_id] << $1
|
|
1606
|
+
if nest == 1
|
|
1607
|
+
$3
|
|
1608
|
+
else
|
|
1609
|
+
@sections[section_id] << $2
|
|
1610
|
+
s = process_section(section_id, $3, nest-1)
|
|
1611
|
+
end
|
|
1612
|
+
else
|
|
1613
|
+
raise "can't happen"
|
|
1614
|
+
end
|
|
1615
|
+
else
|
|
1616
|
+
raise "can't happen"
|
|
1617
|
+
end
|
|
1618
|
+
end
|
|
1619
|
+
|
|
1620
|
+
def process_inline_amxml(s)
|
|
1621
|
+
puts "process_inline_amxml:%s" % s if @trace
|
|
1622
|
+
ret = ""
|
|
1623
|
+
s.to_s.scan(%r[(.*?)<<(.*?)>>]m) do |pre, amxml|
|
|
1624
|
+
ret << $1 << parse_inline_amxml(amxml)
|
|
1625
|
+
end
|
|
1626
|
+
|
|
1627
|
+
if Regexp.last_match
|
|
1628
|
+
ret + Regexp.last_match.post_match
|
|
1629
|
+
else
|
|
1630
|
+
s
|
|
1631
|
+
end
|
|
1632
|
+
end
|
|
1633
|
+
|
|
1634
|
+
def parse_inline_amxml(s)
|
|
1635
|
+
a = AmXml.parse(s, @opt)
|
|
1636
|
+
raise TemplateError, "illeagal inline amxml #{s}" unless a
|
|
1637
|
+
a.result(true)
|
|
1638
|
+
end
|
|
1639
|
+
|
|
1640
|
+
|
|
1641
|
+
def push_tag(indent, tag)
|
|
1642
|
+
@stack.unshift([indent, tag])
|
|
1643
|
+
end
|
|
1644
|
+
|
|
1645
|
+
def pop_tag(indent)
|
|
1646
|
+
ret = ""
|
|
1647
|
+
while @stack.size > 0 and @stack.first[0] >= indent
|
|
1648
|
+
tag = @stack.shift[1]
|
|
1649
|
+
if tag == :cdata
|
|
1650
|
+
ret << "]]>"
|
|
1651
|
+
else
|
|
1652
|
+
ret << "</#{tag}>"
|
|
1653
|
+
end
|
|
1654
|
+
end
|
|
1655
|
+
ret
|
|
1656
|
+
end
|
|
1657
|
+
|
|
1658
|
+
class AmXml
|
|
1659
|
+
attr_reader :tag, :attr, :src_name, :filters
|
|
1660
|
+
attr_reader :skipif, :skipunless, :for, :value
|
|
1661
|
+
|
|
1662
|
+
# Regexps borrowed from REXML rexml/baseparser.rb
|
|
1663
|
+
NCNAME_STR= '[\w:][\-\w\d.#]*?'
|
|
1664
|
+
NAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}"
|
|
1665
|
+
|
|
1666
|
+
R_TAG_ATTR = %r[\s*(#{NAME_STR})((?:\s*#{NAME_STR}\s*=\s*(["'])(?:.*?)\3)*)]um #"
|
|
1667
|
+
S_NO_SRC = %[\s*]
|
|
1668
|
+
S_SRC_AND_FILTERS = %q[(:([\\w\\d]*))\\s*(\\|.*?)*]
|
|
1669
|
+
S_FOR = %q[( \\[(.+)\\] )]
|
|
1670
|
+
S_COND = %q[\\?(\\!)?\\[(.+)\\]]
|
|
1671
|
+
S_VALUE = %q[\\{(.+)\\}]
|
|
1672
|
+
S_TRAILER = %q[((?: |\\-)*)]
|
|
1673
|
+
S_AMVARS = %[(#{S_FOR}|#{S_COND}|#{S_VALUE})?]
|
|
1674
|
+
R_AMXML_WITHOUT_TAG = %r[\A\s*(?:\/)?\s*(?:#{S_NO_SRC}|#{S_SRC_AND_FILTERS})*\s*#{S_AMVARS}\s*\Z]um
|
|
1675
|
+
R_AMXML = %r[\A#{R_TAG_ATTR}\s*(?:\/)?\s*(?:#{S_NO_SRC}|#{S_SRC_AND_FILTERS})*\s*#{S_AMVARS}\s*\Z]um
|
|
1676
|
+
R_AMXML_BLOCK_LINE = %r[^\s*<<\s*?(.*)\s*?<#{S_TRAILER}$]
|
|
1677
|
+
R_AMXML_BLOCK_START = %r[^\s*<<\s*((#{R_TAG_ATTR})?[^<>]*)\s*$]
|
|
1678
|
+
R_AMXML_ERB_LINE = %r[^\s*\%([ =])(.*)$]
|
|
1679
|
+
|
|
1680
|
+
def self.parse(s, opt={})
|
|
1681
|
+
self.new(opt).parse(s, opt[:trline])
|
|
1682
|
+
end
|
|
1683
|
+
|
|
1684
|
+
def initialize(opt)
|
|
1685
|
+
@opt = opt
|
|
1686
|
+
@tag = "_"
|
|
1687
|
+
@attr = ""
|
|
1688
|
+
@src_name = @filters = nil
|
|
1689
|
+
@skipif = @skipunless = @for = @value = nil
|
|
1690
|
+
end
|
|
1691
|
+
|
|
1692
|
+
def has_amxml_attrs?
|
|
1693
|
+
@src_name or @filters or @skipif or @skipunless or @for or @value
|
|
1694
|
+
end
|
|
1695
|
+
|
|
1696
|
+
def parse(s, trline=false)
|
|
1697
|
+
case s
|
|
1698
|
+
when R_AMXML_WITHOUT_TAG
|
|
1699
|
+
if trline
|
|
1700
|
+
@tag = "tr"
|
|
1701
|
+
else
|
|
1702
|
+
@tag = "_"
|
|
1703
|
+
end
|
|
1704
|
+
@src_name = $2.strip if $2
|
|
1705
|
+
@filters = $3[1..-1].strip if $3
|
|
1706
|
+
parse_am_vars($4)
|
|
1707
|
+
when %r[\A\s*([\w]+):([\-\w\d.]*)\s*(/?)\s*\Z] # <<aaa:xxx>>
|
|
1708
|
+
if $3 == "/"
|
|
1709
|
+
@tag = "#$1:#$2"
|
|
1710
|
+
else
|
|
1711
|
+
@tag, @src_name = $1, $2
|
|
1712
|
+
end
|
|
1713
|
+
@tag = "_" if @tag == nil or @tag == ""
|
|
1714
|
+
@attr = ""
|
|
1715
|
+
when R_AMXML
|
|
1716
|
+
@tag, @attr = $1.to_s, $2.to_s
|
|
1717
|
+
src_start = $4
|
|
1718
|
+
@src_name = $5
|
|
1719
|
+
@filters = $6[1..-1].strip if $6
|
|
1720
|
+
parse_am_vars($7)
|
|
1721
|
+
when "%"
|
|
1722
|
+
@tag = :cdata
|
|
1723
|
+
when /=\s*(\w+)\s*/
|
|
1724
|
+
@tag = [:put_stream,$1.intern]
|
|
1725
|
+
when /\s*(\w+)\s*=/
|
|
1726
|
+
@tag = [:change_stream,$1.intern]
|
|
1727
|
+
else
|
|
1728
|
+
return nil
|
|
1729
|
+
end
|
|
1730
|
+
parse_css_selecter_like
|
|
1731
|
+
self
|
|
1732
|
+
end
|
|
1733
|
+
|
|
1734
|
+
def parse_css_selecter_like
|
|
1735
|
+
while true
|
|
1736
|
+
case @tag
|
|
1737
|
+
when /^(\w+)([#\.])([\w-]+)(.*)$/
|
|
1738
|
+
@tag, cls = $1 + $4, $3
|
|
1739
|
+
case $2
|
|
1740
|
+
when "."
|
|
1741
|
+
@attr += " class=\"#{$3}\""
|
|
1742
|
+
when "#"
|
|
1743
|
+
@attr += " id=\"#{$3}\""
|
|
1744
|
+
else
|
|
1745
|
+
break
|
|
1746
|
+
end
|
|
1747
|
+
else
|
|
1748
|
+
break
|
|
1749
|
+
end
|
|
1750
|
+
end
|
|
1751
|
+
end
|
|
1752
|
+
|
|
1753
|
+
def parse_am_vars(s)
|
|
1754
|
+
case s
|
|
1755
|
+
when nil
|
|
1756
|
+
# do nothing
|
|
1757
|
+
when %r[#{S_FOR}]m
|
|
1758
|
+
@for = $2.strip
|
|
1759
|
+
when %r[#{S_COND}]m
|
|
1760
|
+
if $1 == "!"
|
|
1761
|
+
@skipif = $2.strip
|
|
1762
|
+
else
|
|
1763
|
+
@skipunless = $2.strip
|
|
1764
|
+
end
|
|
1765
|
+
when %r[#{S_VALUE}]m
|
|
1766
|
+
@value = $1.strip
|
|
1767
|
+
else
|
|
1768
|
+
raise "can't happen #{s}"
|
|
1769
|
+
end
|
|
1770
|
+
end
|
|
1771
|
+
|
|
1772
|
+
def result(single_tag)
|
|
1773
|
+
case @tag
|
|
1774
|
+
when :cdata
|
|
1775
|
+
return "<![CDATA["
|
|
1776
|
+
when Array
|
|
1777
|
+
case @tag[0]
|
|
1778
|
+
when :change_stream
|
|
1779
|
+
ret = "<_ am:filter='ChangeStream[#{@tag[1].inspect}]'"
|
|
1780
|
+
when :put_stream
|
|
1781
|
+
ret = "<_ am:filter='PutStream[#{@tag[1].inspect}]'"
|
|
1782
|
+
else
|
|
1783
|
+
raise "can't happen #{@tag.inspect}"
|
|
1784
|
+
end
|
|
1785
|
+
if single_tag
|
|
1786
|
+
ret << " />"
|
|
1787
|
+
else
|
|
1788
|
+
ret << " >"
|
|
1789
|
+
@tag = "_"
|
|
1790
|
+
end
|
|
1791
|
+
return ret
|
|
1792
|
+
end
|
|
1793
|
+
ret = "<"
|
|
1794
|
+
if @tag
|
|
1795
|
+
ret << @tag
|
|
1796
|
+
else
|
|
1797
|
+
ret << "_"
|
|
1798
|
+
end
|
|
1799
|
+
if @src_name and @src_name.size > 0
|
|
1800
|
+
am_src = @opt[:am_src] || "am:src"
|
|
1801
|
+
if @filters
|
|
1802
|
+
ret << make_attr(am_src, "#@src_name|#@filters")
|
|
1803
|
+
else
|
|
1804
|
+
ret << make_attr(am_src, @src_name)
|
|
1805
|
+
end
|
|
1806
|
+
else
|
|
1807
|
+
if @filters
|
|
1808
|
+
am_filter = @opt[:am_filter] || "am:filter"
|
|
1809
|
+
ret << make_attr(am_filter, @filters)
|
|
1810
|
+
end
|
|
1811
|
+
end
|
|
1812
|
+
if @for
|
|
1813
|
+
am_for = @opt[:am_for] || "am:for"
|
|
1814
|
+
ret << make_attr(am_for, @for)
|
|
1815
|
+
end
|
|
1816
|
+
if @skipif
|
|
1817
|
+
am_skipif = @opt[:am_skipif] || "am:skipif"
|
|
1818
|
+
ret << make_attr(am_skipif, @skipif)
|
|
1819
|
+
end
|
|
1820
|
+
if @skipunless
|
|
1821
|
+
am_skipif = @opt[:am_skipif] || "am:skipif"
|
|
1822
|
+
ret << make_attr(am_skipif, "not(#@skipunless)")
|
|
1823
|
+
end
|
|
1824
|
+
if @value
|
|
1825
|
+
am_v = @opt[:am_v] || "am:v"
|
|
1826
|
+
ret << make_attr(am_v, "HashDelegator.new($_) { {#@value} }")
|
|
1827
|
+
end
|
|
1828
|
+
ret << @attr.to_s
|
|
1829
|
+
if single_tag
|
|
1830
|
+
ret << " />"
|
|
1831
|
+
else
|
|
1832
|
+
ret << " >"
|
|
1833
|
+
end
|
|
1834
|
+
ret
|
|
1835
|
+
end
|
|
1836
|
+
|
|
1837
|
+
def make_attr(key, val)
|
|
1838
|
+
if val.include?(?')
|
|
1839
|
+
" #{key}=\"#{val}\""
|
|
1840
|
+
else
|
|
1841
|
+
" #{key}='#{val}'"
|
|
1842
|
+
end
|
|
1843
|
+
end
|
|
1844
|
+
end
|
|
6
1845
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
:use_args => { :use_args => true},
|
|
10
|
-
:use_original_element=> { :use_original_element => true},
|
|
11
|
-
:expand_by_member => { :expand_by_member => true },
|
|
12
|
-
:erb => { :erb => true, :use_args=>true },
|
|
13
|
-
:link => { :link_attr => [:href, [/javascript:.*/]] },
|
|
14
|
-
:input => { :value_attr => :value },
|
|
15
|
-
:checkbox => { :value_attr => :checked },
|
|
16
|
-
:encoding_euc => { :encoding=>'EUC-JP' }
|
|
17
|
-
}
|
|
1846
|
+
class LineProcessor
|
|
1847
|
+
attr_reader :cells
|
|
18
1848
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
when "NONE"
|
|
89
|
-
else
|
|
90
|
-
raise "unkonw encoding #{@encoding}"
|
|
1849
|
+
def initialize(&block)
|
|
1850
|
+
@cells = []
|
|
1851
|
+
@cell_proc = block
|
|
1852
|
+
end
|
|
1853
|
+
|
|
1854
|
+
def parse_line(line)
|
|
1855
|
+
case line
|
|
1856
|
+
when %r[^\s*#]
|
|
1857
|
+
true
|
|
1858
|
+
when %r[^\s*\|\s*([\w:]*)\s*\|\|]
|
|
1859
|
+
parse_cells($1.strip, Regexp.last_match.post_match)
|
|
1860
|
+
true
|
|
1861
|
+
else
|
|
1862
|
+
false
|
|
1863
|
+
end
|
|
1864
|
+
end
|
|
1865
|
+
|
|
1866
|
+
def parse_cells(attr_key, cells)
|
|
1867
|
+
attr_key = :text if attr_key == ""
|
|
1868
|
+
cnt = 0
|
|
1869
|
+
splited = ""
|
|
1870
|
+
cells.chomp.scan(%r[([^\|]+?)((?!r\|)\|(\|?)|$)]) do |s, th_flag|
|
|
1871
|
+
if s[-1] == ?\\
|
|
1872
|
+
splited = s[0..-2] + "|"
|
|
1873
|
+
next
|
|
1874
|
+
else
|
|
1875
|
+
s = splited.to_s + s
|
|
1876
|
+
splited = ""
|
|
1877
|
+
end
|
|
1878
|
+
|
|
1879
|
+
@cells[cnt] ||= {}
|
|
1880
|
+
cell = @cells[cnt]
|
|
1881
|
+
if cell[attr_key]
|
|
1882
|
+
cell[attr_key] << "\n" << s
|
|
1883
|
+
else
|
|
1884
|
+
cell[attr_key] = s
|
|
1885
|
+
end
|
|
1886
|
+
|
|
1887
|
+
cell[:tag] = (th_flag == "||" ? :th : :td)
|
|
1888
|
+
cnt += 1
|
|
1889
|
+
end
|
|
1890
|
+
end
|
|
1891
|
+
|
|
1892
|
+
def get_result_xml
|
|
1893
|
+
@cells.collect do |c|
|
|
1894
|
+
tag, text = c[:tag], @cell_proc.call(c[:text])
|
|
1895
|
+
c.delete(:tag)
|
|
1896
|
+
c.delete(:text)
|
|
1897
|
+
if c.size == 0
|
|
1898
|
+
if text == nil or text.strip == ""
|
|
1899
|
+
""
|
|
1900
|
+
else
|
|
1901
|
+
"<#{tag}>#{text}</#{tag}>"
|
|
1902
|
+
end
|
|
1903
|
+
else
|
|
1904
|
+
a = attr = c.collect do |k, v|
|
|
1905
|
+
next unless v
|
|
1906
|
+
v.strip!
|
|
1907
|
+
next if v == ""
|
|
1908
|
+
if v.include?(?')
|
|
1909
|
+
"#{k}=\"#{v}\""
|
|
1910
|
+
else
|
|
1911
|
+
"#{k}='#{v}'"
|
|
1912
|
+
end
|
|
1913
|
+
end.join(" ")
|
|
1914
|
+
"<#{tag} #{a}>#{text}</#{tag}>"
|
|
1915
|
+
end
|
|
1916
|
+
end.join("")
|
|
1917
|
+
end
|
|
91
1918
|
end
|
|
92
|
-
ret
|
|
93
1919
|
end
|
|
94
1920
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
@
|
|
100
|
-
|
|
101
|
-
|
|
1921
|
+
class Hook
|
|
1922
|
+
attr_reader :stream, :cnt, :element_s
|
|
1923
|
+
|
|
1924
|
+
def initialize(&block)
|
|
1925
|
+
@hook_proc = block
|
|
1926
|
+
end
|
|
1927
|
+
|
|
1928
|
+
def call(stream, cnt, element_s,&block)
|
|
1929
|
+
@stream = stream
|
|
1930
|
+
@cnt = cnt
|
|
1931
|
+
@element_s = element_s
|
|
1932
|
+
@render_me_proc = block
|
|
1933
|
+
instance_eval(&@hook_proc)
|
|
1934
|
+
end
|
|
1935
|
+
|
|
1936
|
+
def render_me_with(it)
|
|
1937
|
+
stream << @render_me_proc.call(nil, it)
|
|
1938
|
+
end
|
|
1939
|
+
|
|
1940
|
+
def render_child(name, it)
|
|
1941
|
+
stream << @render_me_proc.call(name, it)
|
|
1942
|
+
end
|
|
1943
|
+
|
|
1944
|
+
class Renderer < Renderers::Base # :nodoc: all
|
|
1945
|
+
def initialize
|
|
1946
|
+
super("Hook")
|
|
1947
|
+
end
|
|
1948
|
+
|
|
1949
|
+
def generate_body(cg, de, element)
|
|
1950
|
+
cg.code("e = #{ cg.define_constant(element.to_s.inspect)}")
|
|
1951
|
+
cg.code("$_.call(__stream__, __cnt__, e) do |name, it| ")
|
|
1952
|
+
cg.level_up do
|
|
1953
|
+
cg.case_('name') do
|
|
1954
|
+
cg.when_("nil") do
|
|
1955
|
+
cg.code("render_with(it, __binding__)")
|
|
1956
|
+
end
|
|
1957
|
+
de.children.each do |c|
|
|
1958
|
+
next unless c.kind_of?(DynamicElement)
|
|
1959
|
+
cg.when_(c.name.intern.inspect) do
|
|
1960
|
+
cg.code("#{c.instance_name}.render_with(it, __binding__)")
|
|
1961
|
+
end
|
|
1962
|
+
end
|
|
1963
|
+
end
|
|
1964
|
+
end
|
|
1965
|
+
cg.code("end")
|
|
1966
|
+
end
|
|
102
1967
|
end
|
|
103
|
-
@spec = @spec.clone unless @spec.cloned
|
|
104
|
-
@spec
|
|
105
1968
|
end
|
|
1969
|
+
end
|
|
1970
|
+
|
|
1971
|
+
module Renderers # :nodoc: all
|
|
1972
|
+
class Base
|
|
1973
|
+
def initialize(type_name)
|
|
1974
|
+
@type_name = type_name
|
|
1975
|
+
end
|
|
1976
|
+
|
|
1977
|
+
def generate_when(cg)
|
|
1978
|
+
cg.when_(@type_name) do
|
|
1979
|
+
yield
|
|
1980
|
+
end
|
|
1981
|
+
end
|
|
1982
|
+
end
|
|
1983
|
+
|
|
1984
|
+
class ScalarRenderer < Renderers::Base
|
|
1985
|
+
def initialize
|
|
1986
|
+
super("ScalarData")
|
|
1987
|
+
end
|
|
106
1988
|
|
|
107
|
-
|
|
108
|
-
|
|
1989
|
+
def generate_body(cg, de, element)
|
|
1990
|
+
de.element_render_code(cg, element) do
|
|
1991
|
+
cg.put_string_expression("$_.amrita_value")
|
|
1992
|
+
end
|
|
1993
|
+
end
|
|
109
1994
|
end
|
|
110
1995
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
1996
|
+
class NilRenderer < Base
|
|
1997
|
+
def initialize
|
|
1998
|
+
super("nil")
|
|
1999
|
+
end
|
|
2000
|
+
|
|
2001
|
+
def generate_body(cg, de, element)
|
|
2002
|
+
end
|
|
114
2003
|
end
|
|
115
2004
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
2005
|
+
class TrueRenderer < Base
|
|
2006
|
+
def initialize
|
|
2007
|
+
super("true")
|
|
2008
|
+
end
|
|
2009
|
+
|
|
2010
|
+
def generate_body(cg, de, element)
|
|
2011
|
+
cg.put_hpricot_node(element)
|
|
2012
|
+
end
|
|
2013
|
+
end
|
|
2014
|
+
|
|
2015
|
+
class HashRenderer < Base
|
|
2016
|
+
def initialize
|
|
2017
|
+
super("DictionaryData")
|
|
2018
|
+
end
|
|
2019
|
+
|
|
2020
|
+
|
|
2021
|
+
def generate_body(cg, de, element)
|
|
2022
|
+
de.element_render_code(cg, element) do
|
|
2023
|
+
de.children.each do |c|
|
|
2024
|
+
c.render_me(cg)
|
|
2025
|
+
end
|
|
2026
|
+
end
|
|
2027
|
+
end
|
|
2028
|
+
end
|
|
2029
|
+
|
|
2030
|
+
class ElseRenderer < Base
|
|
2031
|
+
def initialize
|
|
2032
|
+
super("Object")
|
|
2033
|
+
end
|
|
2034
|
+
|
|
2035
|
+
def generate_when(cg)
|
|
2036
|
+
cg.else_ do
|
|
2037
|
+
yield
|
|
2038
|
+
end
|
|
2039
|
+
end
|
|
2040
|
+
|
|
2041
|
+
def generate_body_new(cg, de, element)
|
|
2042
|
+
de.element_render_code(cg, element) do
|
|
2043
|
+
de.children.each do |c|
|
|
2044
|
+
c.render_me(cg)
|
|
2045
|
+
end
|
|
2046
|
+
end
|
|
2047
|
+
end
|
|
2048
|
+
|
|
2049
|
+
def generate_body(cg, de, element)
|
|
2050
|
+
if de.has_ruby?
|
|
2051
|
+
de.element_render_code(cg, element) do
|
|
2052
|
+
de.children.each do |c|
|
|
2053
|
+
c.render_me(cg)
|
|
2054
|
+
end
|
|
2055
|
+
end
|
|
2056
|
+
else
|
|
2057
|
+
cg.put_expression('raise %[type mismatch, got (#{$_.inspect}) for ' + element.to_s.inspect + ' ]')
|
|
2058
|
+
end
|
|
2059
|
+
end
|
|
2060
|
+
end
|
|
2061
|
+
end
|
|
2062
|
+
|
|
2063
|
+
module Filters
|
|
2064
|
+
include Renderers
|
|
2065
|
+
|
|
2066
|
+
class FilterArray < Array # :nodoc: all
|
|
2067
|
+
def initialize(*args)
|
|
2068
|
+
args.each { |a| self << a }
|
|
2069
|
+
end
|
|
2070
|
+
|
|
2071
|
+
def |(other)
|
|
2072
|
+
case other
|
|
2073
|
+
when Class
|
|
2074
|
+
a = self + [other.new]
|
|
2075
|
+
FilterArray.new(*a)
|
|
2076
|
+
when Filters::Base
|
|
2077
|
+
a = self + [other]
|
|
2078
|
+
FilterArray.new(*a)
|
|
122
2079
|
when Symbol
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
2080
|
+
self + FilterArray.new(FunctionFilter.new(other))
|
|
2081
|
+
when Array
|
|
2082
|
+
case other.first
|
|
2083
|
+
when Symbol
|
|
2084
|
+
self + FilterArray.new(FunctionFilter.new(*other))
|
|
2085
|
+
else
|
|
2086
|
+
self + other
|
|
129
2087
|
end
|
|
130
2088
|
else
|
|
131
|
-
raise "#{
|
|
2089
|
+
raise "not filter #{other.inspect}"
|
|
132
2090
|
end
|
|
133
2091
|
end
|
|
134
2092
|
end
|
|
135
2093
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
2094
|
+
class Base # :nodoc: all
|
|
2095
|
+
attr_accessor :next_
|
|
2096
|
+
def self.filter_method(*m)
|
|
2097
|
+
m.each do |name|
|
|
2098
|
+
module_eval <<-END
|
|
2099
|
+
def #{name}(*args,&block)
|
|
2100
|
+
next_.#{name}(*args,&block)
|
|
2101
|
+
end
|
|
2102
|
+
END
|
|
2103
|
+
end
|
|
2104
|
+
end
|
|
2105
|
+
|
|
2106
|
+
FilterMethods.each do |m|
|
|
2107
|
+
filter_method(m)
|
|
2108
|
+
end
|
|
2109
|
+
|
|
2110
|
+
def self.inherited(cls)
|
|
2111
|
+
super
|
|
2112
|
+
def cls.[](*args, &block)
|
|
2113
|
+
new(*args, &block)
|
|
2114
|
+
end
|
|
2115
|
+
|
|
2116
|
+
def cls.|(other)
|
|
2117
|
+
FilterArray.new(self.new) | other
|
|
2118
|
+
end
|
|
2119
|
+
end
|
|
2120
|
+
|
|
2121
|
+
def |(other)
|
|
2122
|
+
FilterArray.new(self) | other
|
|
2123
|
+
end
|
|
2124
|
+
|
|
2125
|
+
def parse_filter_a(f)
|
|
2126
|
+
case f = eval(f)
|
|
2127
|
+
when Class
|
|
2128
|
+
f.new
|
|
145
2129
|
else
|
|
146
|
-
|
|
2130
|
+
f
|
|
147
2131
|
end
|
|
148
2132
|
end
|
|
149
2133
|
end
|
|
150
2134
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
2135
|
+
class Attr < Base
|
|
2136
|
+
class Renderer < Renderers::Base
|
|
2137
|
+
include Util
|
|
2138
|
+
include OptionSupport
|
|
2139
|
+
def initialize(*args)
|
|
2140
|
+
parse_opt(*args)
|
|
2141
|
+
end
|
|
2142
|
+
|
|
2143
|
+
def generate_body(cg, de, element)
|
|
2144
|
+
use_body = (not de.has_dynamic?) && @opt.has_key?(:body)
|
|
2145
|
+
body_key = nil
|
|
2146
|
+
if use_body
|
|
2147
|
+
body_key = @opt[:body]
|
|
2148
|
+
body_key = :body unless body_key.kind_of?(Symbol)
|
|
2149
|
+
cg.code("body = $_.amrita_value(:#{body_key})")
|
|
2150
|
+
end
|
|
2151
|
+
if @opt.size > 0
|
|
2152
|
+
cg.code("a = {}")
|
|
2153
|
+
@opt.each do |key, v|
|
|
2154
|
+
next if key == :body
|
|
2155
|
+
if element.get_attribute(key)
|
|
2156
|
+
cg.case_("v = $_.amrita_value(:#{v})") do
|
|
2157
|
+
cg.when_("true") do
|
|
2158
|
+
cg.code("a[#{key.inspect}] = #{element.get_attribute(key).inspect}")
|
|
2159
|
+
end
|
|
2160
|
+
cg.when_("false, nil") do
|
|
2161
|
+
cg.code("a.delete(#{key.inspect})")
|
|
2162
|
+
end
|
|
2163
|
+
cg.else_ do
|
|
2164
|
+
cg.code("a[#{key.inspect}] = $_.amrita_value(:#{v})")
|
|
2165
|
+
end
|
|
2166
|
+
end
|
|
2167
|
+
else
|
|
2168
|
+
cg.code("v = $_.amrita_value(:#{v})")
|
|
2169
|
+
cg.code("a[#{key.inspect}] = v if v")
|
|
2170
|
+
end
|
|
2171
|
+
end
|
|
2172
|
+
else
|
|
2173
|
+
cg.code("a = $_ ")
|
|
2174
|
+
end
|
|
2175
|
+
|
|
2176
|
+
a = { }
|
|
2177
|
+
element.attributes.each do |k, v|
|
|
2178
|
+
next if @opt[k.intern]
|
|
2179
|
+
a[k] = v
|
|
2180
|
+
end
|
|
2181
|
+
cg.code("e = new_element(#{element.name.inspect}, #{a.inspect}.merge(a))")
|
|
2182
|
+
if use_body
|
|
2183
|
+
body_for_static(cg)
|
|
2184
|
+
else
|
|
2185
|
+
body_for_dynamic(cg, de, element)
|
|
2186
|
+
end
|
|
2187
|
+
end
|
|
2188
|
+
|
|
2189
|
+
def body_for_static(cg)
|
|
2190
|
+
cg.if_("body") do
|
|
2191
|
+
cg.put_string_expression("start_tag(e)")
|
|
2192
|
+
cg.put_string_expression("body.amrita_value")
|
|
2193
|
+
cg.put_string_expression("end_tag(e)")
|
|
2194
|
+
cg.else_ do
|
|
2195
|
+
cg.put_string_expression("e.to_s")
|
|
2196
|
+
end
|
|
2197
|
+
end
|
|
2198
|
+
end
|
|
2199
|
+
|
|
2200
|
+
def body_for_dynamic(cg, de, element)
|
|
2201
|
+
cg.put_string_expression("start_tag(e)")
|
|
2202
|
+
de.children.each do |c|
|
|
2203
|
+
c.render_me(cg)
|
|
2204
|
+
end
|
|
2205
|
+
cg.put_string_expression("end_tag(e)")
|
|
2206
|
+
end
|
|
2207
|
+
end
|
|
2208
|
+
|
|
2209
|
+
def initialize(*args)
|
|
2210
|
+
@args = args
|
|
2211
|
+
end
|
|
2212
|
+
|
|
2213
|
+
def setup_type_renderer(de)
|
|
2214
|
+
de.renderers.clear
|
|
2215
|
+
de.renderers << Renderer.new(*@args)
|
|
159
2216
|
end
|
|
160
2217
|
end
|
|
161
2218
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
2219
|
+
|
|
2220
|
+
class Default < Base
|
|
2221
|
+
def initialize(default_value_src)
|
|
2222
|
+
@default_value_src = default_value_src
|
|
2223
|
+
end
|
|
2224
|
+
|
|
2225
|
+
def value_filter_code(de, cg, value)
|
|
2226
|
+
cg.if_("$_") do
|
|
2227
|
+
super
|
|
2228
|
+
cg.else_ do
|
|
2229
|
+
cg.code("$_ = (#{@default_value_src.inspect})")
|
|
2230
|
+
end
|
|
2231
|
+
end
|
|
2232
|
+
end
|
|
2233
|
+
|
|
2234
|
+
def value_filter_code_old(de, cg, value)
|
|
2235
|
+
cg.code("$_ = $_ || (#{@default_value_src.inspect})")
|
|
2236
|
+
super
|
|
2237
|
+
end
|
|
2238
|
+
end
|
|
2239
|
+
|
|
2240
|
+
class Repeat < Base
|
|
2241
|
+
def initialize(cnt)
|
|
2242
|
+
@cnt = cnt.to_i
|
|
2243
|
+
end
|
|
2244
|
+
|
|
2245
|
+
def value_filter_code(de, cg, value)
|
|
2246
|
+
cg.code("$_ = $_ * #@cnt ")
|
|
2247
|
+
super
|
|
2248
|
+
end
|
|
2249
|
+
end
|
|
2250
|
+
|
|
2251
|
+
class Format < Base
|
|
2252
|
+
def initialize(format)
|
|
2253
|
+
@format = format
|
|
2254
|
+
end
|
|
2255
|
+
def value_filter_code(de, cg, value)
|
|
2256
|
+
cg.code("$_ = #{@format.inspect} % $_")
|
|
2257
|
+
super
|
|
169
2258
|
end
|
|
170
2259
|
end
|
|
171
2260
|
|
|
172
|
-
|
|
173
|
-
|
|
2261
|
+
class AcceptData < Base
|
|
2262
|
+
include Renderers
|
|
2263
|
+
def initialize(*types)
|
|
2264
|
+
@types = types
|
|
2265
|
+
end
|
|
2266
|
+
|
|
2267
|
+
def setup_type_renderer(de)
|
|
2268
|
+
nil_processed = true_processed = hook_processed = false
|
|
2269
|
+
@types.each do |t|
|
|
2270
|
+
case t
|
|
2271
|
+
when nil, NilClass
|
|
2272
|
+
de.renderers.unshift NilRenderer.new unless nil_processed
|
|
2273
|
+
nil_processed = true
|
|
2274
|
+
when true, TrueClass
|
|
2275
|
+
de.renderers << TrueRenderer.new unless true_processed
|
|
2276
|
+
true_processed = true
|
|
2277
|
+
when :hook
|
|
2278
|
+
de.renderers << Core::Hook::Renderer.new unless hook_processed
|
|
2279
|
+
hook_processed = true
|
|
2280
|
+
else
|
|
2281
|
+
raise ArgumentError,"unknown type for AcceptData #{t.inspect}"
|
|
2282
|
+
end
|
|
2283
|
+
end
|
|
2284
|
+
super
|
|
2285
|
+
end
|
|
174
2286
|
end
|
|
175
2287
|
|
|
176
|
-
|
|
177
|
-
|
|
2288
|
+
class NoSanitize < Base
|
|
2289
|
+
def value_filter_code(de, cg, value)
|
|
2290
|
+
super
|
|
2291
|
+
cg.code("$_ = SanitizedString[$_]")
|
|
2292
|
+
end
|
|
178
2293
|
end
|
|
179
2294
|
|
|
180
|
-
|
|
181
|
-
|
|
2295
|
+
class Each < Base
|
|
2296
|
+
include Util::OptionSupport
|
|
2297
|
+
def initialize(*args)
|
|
2298
|
+
parse_opt(*args)
|
|
2299
|
+
end
|
|
2300
|
+
|
|
2301
|
+
def value_filter_code(de, cg, value)
|
|
2302
|
+
var = "__cnt__#{de.class_name}"
|
|
2303
|
+
@opt.each do |k,va|
|
|
2304
|
+
cg.code("#{var} = __cnt__%#{va.size}")
|
|
2305
|
+
va.each_with_index do |v, n|
|
|
2306
|
+
case v
|
|
2307
|
+
when Symbol
|
|
2308
|
+
cg.code("$_[#{k.inspect}] = $_[#{v.inspect}] if #{var} == #{n}")
|
|
2309
|
+
when String
|
|
2310
|
+
cg.code("$_[#{k.inspect}] = #{v.inspect} if #{var} == #{n}")
|
|
2311
|
+
else
|
|
2312
|
+
raise("unknown Each type #{v.inspect}")
|
|
2313
|
+
end
|
|
2314
|
+
end
|
|
2315
|
+
end
|
|
2316
|
+
super
|
|
2317
|
+
end
|
|
182
2318
|
end
|
|
183
2319
|
|
|
184
|
-
|
|
185
|
-
|
|
2320
|
+
class ToHash < Base
|
|
2321
|
+
include Util::OptionSupport
|
|
2322
|
+
def initialize(key)
|
|
2323
|
+
@key = key
|
|
2324
|
+
end
|
|
2325
|
+
|
|
2326
|
+
def value_filter_code(de, cg, value)
|
|
2327
|
+
case @key
|
|
2328
|
+
when Symbol
|
|
2329
|
+
cg.code("$_ = { #{@key.inspect} => $_ }")
|
|
2330
|
+
when Hash
|
|
2331
|
+
cg.code("$_ = { ")
|
|
2332
|
+
@key.each do |k,v|
|
|
2333
|
+
cg.code(" #{k.inspect} => $_.amrita_value(#{v.inspect}),")
|
|
2334
|
+
end
|
|
2335
|
+
cg.code("}")
|
|
2336
|
+
end
|
|
2337
|
+
|
|
2338
|
+
super
|
|
2339
|
+
end
|
|
186
2340
|
end
|
|
187
2341
|
|
|
188
|
-
|
|
189
|
-
|
|
2342
|
+
class InlineRuby < Base # :nodoc: all
|
|
2343
|
+
include Runtime
|
|
2344
|
+
include Renderers
|
|
2345
|
+
include Util
|
|
2346
|
+
|
|
2347
|
+
def setup_type_renderer(de)
|
|
2348
|
+
de.renderers << HashRenderer.new unless de.renderers.find {|r| r.kind_of?(HashRenderer) }
|
|
2349
|
+
super
|
|
2350
|
+
end
|
|
2351
|
+
|
|
2352
|
+
def generate_dynamic_element(de, e)
|
|
2353
|
+
a = e.attributes
|
|
2354
|
+
ret = super
|
|
2355
|
+
return ret if ret
|
|
2356
|
+
|
|
2357
|
+
# check if element has any of inlineruby attributes
|
|
2358
|
+
if %w(am_skipif am_for am_v).any? { |k| a[de.__send__(k)] }
|
|
2359
|
+
filters_src = e.attributes[de.am_filter]
|
|
2360
|
+
filters = de.root.compile_filters(e, nil, filters_src)
|
|
2361
|
+
Core::DynamicElement.new(de, nil , e, filters)
|
|
2362
|
+
else
|
|
2363
|
+
nil
|
|
2364
|
+
end
|
|
2365
|
+
end
|
|
2366
|
+
|
|
2367
|
+
def loop_check_code(de, cg)
|
|
2368
|
+
foreach_ = de.am_for_value
|
|
2369
|
+
if foreach_
|
|
2370
|
+
cg.if_("__cnt__ < 0") do
|
|
2371
|
+
#cg.code("$_ = eval(#{foreach_.inspect}, __binding__)")
|
|
2372
|
+
cg.code("$_ = ")
|
|
2373
|
+
cg.eval(foreach_.inspect)
|
|
2374
|
+
super
|
|
2375
|
+
end
|
|
2376
|
+
else
|
|
2377
|
+
super
|
|
2378
|
+
end
|
|
2379
|
+
end
|
|
2380
|
+
|
|
2381
|
+
def value_filter_code(de, cg, value)
|
|
2382
|
+
cond = de.am_skipif_value
|
|
2383
|
+
value = de.am_v_value
|
|
2384
|
+
|
|
2385
|
+
if cond
|
|
2386
|
+
cg.code("amrita_set_context_value($_)")
|
|
2387
|
+
#cg.code("return '' if eval(#{with_context_value_expression(cond).inspect},__binding__)")
|
|
2388
|
+
cg.code("return '' if \\")
|
|
2389
|
+
cg.eval(with_context_value_expression(cond).inspect)
|
|
2390
|
+
end
|
|
2391
|
+
|
|
2392
|
+
if value
|
|
2393
|
+
cg.code("amrita_set_context_value($_)")
|
|
2394
|
+
#cg.code("$_ = eval(#{with_context_value_expression(value).inspect},__binding__)")
|
|
2395
|
+
cg.code("$_ = ")
|
|
2396
|
+
cg.eval(with_context_value_expression(value).inspect)
|
|
2397
|
+
end
|
|
2398
|
+
super
|
|
2399
|
+
end
|
|
2400
|
+
|
|
2401
|
+
private
|
|
2402
|
+
def with_context_value_expression(src)
|
|
2403
|
+
[
|
|
2404
|
+
"$_ = amrita_get_context_value",
|
|
2405
|
+
"ret = (#{src})",
|
|
2406
|
+
"amrita_set_context_value($_)",
|
|
2407
|
+
"ret"
|
|
2408
|
+
].join(";")
|
|
2409
|
+
end
|
|
190
2410
|
end
|
|
191
2411
|
|
|
192
|
-
|
|
193
|
-
|
|
2412
|
+
module NVarMixin
|
|
2413
|
+
private
|
|
2414
|
+
|
|
2415
|
+
def make_tupple_code(cg)
|
|
2416
|
+
init_code = @names.collect do |n|
|
|
2417
|
+
"$_.amrita_value(:#{n})"
|
|
2418
|
+
end.join(",")
|
|
2419
|
+
cg.code("$_ = Tuple[#{init_code}]")
|
|
2420
|
+
end
|
|
2421
|
+
|
|
2422
|
+
def replace_args(element)
|
|
2423
|
+
element.attributes.each do|k,v|
|
|
2424
|
+
element.set_attribute(k, replace_attr_args(v))
|
|
2425
|
+
end
|
|
2426
|
+
children = element.children.collect do |c|
|
|
2427
|
+
case c
|
|
2428
|
+
when Hpricot::Elem
|
|
2429
|
+
replace_args(c)
|
|
2430
|
+
when Hpricot::Text
|
|
2431
|
+
Hpricot::Text.new(replace_text_args(c.to_s))
|
|
2432
|
+
else
|
|
2433
|
+
raise "not implemented #{c.class}"
|
|
2434
|
+
end
|
|
2435
|
+
end
|
|
2436
|
+
element.children.clear
|
|
2437
|
+
children.each do |c|
|
|
2438
|
+
element.children << c
|
|
2439
|
+
end
|
|
2440
|
+
element
|
|
2441
|
+
end
|
|
2442
|
+
|
|
2443
|
+
def replace_attr_args(s)
|
|
2444
|
+
s.gsub(/(.)?\$(\d)/) do |ss|
|
|
2445
|
+
if $1 == "$"
|
|
2446
|
+
"$#$2"
|
|
2447
|
+
else
|
|
2448
|
+
$1.to_s + '#{Util::sanitize_attribute_value($_[' + ($2.to_i-1).to_s + '].to_s)}'
|
|
2449
|
+
end
|
|
2450
|
+
end
|
|
2451
|
+
end
|
|
2452
|
+
|
|
2453
|
+
def replace_text_args(s)
|
|
2454
|
+
s.gsub(/(.)?\$(\d)/) do |ss|
|
|
2455
|
+
if $1 == "$"
|
|
2456
|
+
"$#$2"
|
|
2457
|
+
else
|
|
2458
|
+
$1.to_s + '#{$_[' + ($2.to_i-1).to_s + '].amrita_value}'
|
|
2459
|
+
end
|
|
2460
|
+
end
|
|
2461
|
+
end
|
|
194
2462
|
end
|
|
195
2463
|
|
|
196
|
-
|
|
197
|
-
|
|
2464
|
+
class NVar < Filters::Base
|
|
2465
|
+
include NVarMixin
|
|
2466
|
+
def initialize(*names)
|
|
2467
|
+
@names = names
|
|
2468
|
+
end
|
|
2469
|
+
|
|
2470
|
+
def renderer_code(de, cg, element)
|
|
2471
|
+
make_tupple_code(cg) if @names.size > 0
|
|
2472
|
+
s = replace_args(element).to_s
|
|
2473
|
+
cg.put_string_expression(s.inspect.gsub(/\\#/, "#"))
|
|
2474
|
+
end
|
|
198
2475
|
end
|
|
199
2476
|
|
|
200
|
-
|
|
201
|
-
|
|
2477
|
+
class Eval < NVar
|
|
2478
|
+
def initialize(*names)
|
|
2479
|
+
@names = names
|
|
2480
|
+
end
|
|
2481
|
+
|
|
2482
|
+
private
|
|
2483
|
+
|
|
2484
|
+
def make_tupple_code(cg)
|
|
2485
|
+
cg.code("amrita_set_context_value($_)")
|
|
2486
|
+
init_code = @names.collect do |n|
|
|
2487
|
+
case n
|
|
2488
|
+
when Symbol
|
|
2489
|
+
"$_.amrita_value(:#{n})"
|
|
2490
|
+
else
|
|
2491
|
+
"eval(#{with_context_value_expression(n).inspect},__binding__)"
|
|
2492
|
+
end
|
|
2493
|
+
end.join(",")
|
|
2494
|
+
cg.code("$_ = Tuple[#{init_code}]")
|
|
2495
|
+
end
|
|
2496
|
+
|
|
2497
|
+
def with_context_value_expression(src)
|
|
2498
|
+
[
|
|
2499
|
+
"$_ = amrita_get_context_value",
|
|
2500
|
+
"ret = (#{src})",
|
|
2501
|
+
"amrita_set_context_value($_)",
|
|
2502
|
+
"ret"
|
|
2503
|
+
].join(";")
|
|
2504
|
+
end
|
|
202
2505
|
end
|
|
203
2506
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
def make_default_spec1(e, option, spec_stack, type_map, element_option)
|
|
211
|
-
key_attr = option[:key_attr_name].to_s
|
|
212
|
-
id_ = e.attributes[key_attr]
|
|
213
|
-
if id_
|
|
214
|
-
sym = id_.intern
|
|
215
|
-
opt = get_option(e, option, type_map, element_option, sym)
|
|
216
|
-
s = Core::DynamicElementSpec.new(sym, nil, opt)
|
|
217
|
-
s.parent = spec_stack.first
|
|
218
|
-
spec_stack.first.children << s
|
|
219
|
-
spec_stack.unshift(s)
|
|
220
|
-
e.elements.each do |c|
|
|
221
|
-
make_default_spec1(c, option, spec_stack, type_map, element_option)
|
|
222
|
-
end
|
|
223
|
-
spec_stack.shift
|
|
224
|
-
spec_stack.first.check_double_id if option[:check_double_id]
|
|
225
|
-
else
|
|
226
|
-
e.elements.each do |c|
|
|
227
|
-
make_default_spec1(c, option, spec_stack, type_map, element_option)
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
def get_option(e, option, type_map, element_option, sym)
|
|
233
|
-
ret = option.clone
|
|
234
|
-
a = e.attributes
|
|
235
|
-
typ = (a['amrita:type'] || element_option[sym])
|
|
236
|
-
case typ
|
|
237
|
-
when Symbol, String
|
|
238
|
-
default_opt = type_map[typ.intern] || { typ.intern=>true }
|
|
239
|
-
ret.merge!(default_opt) if default_opt
|
|
240
|
-
a.delete('amrita:type')
|
|
241
|
-
when Hash
|
|
242
|
-
default_opt = typ
|
|
243
|
-
ret.merge!(default_opt) if default_opt
|
|
244
|
-
a.delete('amrita:type')
|
|
245
|
-
end
|
|
246
|
-
if opt = a['amrita:option']
|
|
247
|
-
ret.merge!(eval("{#{opt} }"))
|
|
248
|
-
a.delete('amrita:option')
|
|
249
|
-
end
|
|
250
|
-
ret
|
|
251
|
-
end
|
|
252
|
-
end
|
|
2507
|
+
class NVarForAttr < Filters::Base
|
|
2508
|
+
def initialize(*names)
|
|
2509
|
+
@names = names
|
|
2510
|
+
end
|
|
253
2511
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
2512
|
+
def renderer_code(de, cg, element)
|
|
2513
|
+
make_tupple_code(cg) if @names.size > 0
|
|
2514
|
+
super
|
|
2515
|
+
end
|
|
2516
|
+
|
|
2517
|
+
def element_render_code(de, cg, element, &block)
|
|
2518
|
+
if (element.name == 'span' && element.attributes.size == 0)
|
|
2519
|
+
yield
|
|
2520
|
+
else
|
|
2521
|
+
replace_args(element)
|
|
2522
|
+
if element.children.size > 0
|
|
2523
|
+
cg.put_string_expression(element.stag.output('').inspect.gsub(/\\#/, "#"))
|
|
2524
|
+
yield
|
|
2525
|
+
cg.put_constant(element.etag.output(''))
|
|
2526
|
+
else
|
|
2527
|
+
cg.put_string_expression(%["#{element.to_s}"])
|
|
2528
|
+
end
|
|
2529
|
+
end
|
|
2530
|
+
end
|
|
2531
|
+
|
|
2532
|
+
private
|
|
2533
|
+
def make_tupple_code(cg)
|
|
2534
|
+
init_code = @names.collect do |n|
|
|
2535
|
+
"$_.amrita_value(:#{n})"
|
|
2536
|
+
end.join(",")
|
|
2537
|
+
cg.code("a = [#{init_code}]")
|
|
2538
|
+
end
|
|
2539
|
+
|
|
2540
|
+
def replace_args(element)
|
|
2541
|
+
element.attributes.each do|k,v|
|
|
2542
|
+
element.set_attribute(k, replace_attr_args(v))
|
|
2543
|
+
end
|
|
2544
|
+
end
|
|
2545
|
+
def replace_attr_args(s)
|
|
2546
|
+
s.gsub(/\$(\d)/) do |ss|
|
|
2547
|
+
'#{Util::sanitize_attribute_value(a[' + ($1.to_i-1).to_s + '].to_s)}'
|
|
2548
|
+
end
|
|
2549
|
+
end
|
|
259
2550
|
end
|
|
260
2551
|
|
|
261
|
-
|
|
262
|
-
|
|
2552
|
+
class FunctionFilter < Filters::Base
|
|
2553
|
+
def initialize(sym, *args)
|
|
2554
|
+
@sym, @args = sym, args
|
|
2555
|
+
end
|
|
2556
|
+
|
|
2557
|
+
def value_filter_code(de, cg, element)
|
|
2558
|
+
cg.code("$_ = $_.send(#{@sym.inspect}, *#{@args.inspect})")
|
|
2559
|
+
super
|
|
2560
|
+
end
|
|
263
2561
|
end
|
|
264
2562
|
|
|
265
|
-
|
|
266
|
-
|
|
2563
|
+
class CommandFilter < Filters::Base
|
|
2564
|
+
def initialize(*args)
|
|
2565
|
+
@args = args.collect do |a|
|
|
2566
|
+
a.inspect
|
|
2567
|
+
end.join(' ')
|
|
2568
|
+
end
|
|
2569
|
+
|
|
2570
|
+
def define_element_method(de, cg, &block)
|
|
2571
|
+
super do
|
|
2572
|
+
block.call
|
|
2573
|
+
cg.code "pipe = IO.popen(#@args, 'r+')"
|
|
2574
|
+
cg.code "pipe.write __stream__; pipe.close_write"
|
|
2575
|
+
cg.code "__stream__ = pipe.read ; pipe.close "
|
|
2576
|
+
end
|
|
2577
|
+
end
|
|
267
2578
|
end
|
|
268
|
-
end
|
|
269
2579
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
2580
|
+
class Trace < Filters::Base
|
|
2581
|
+
SEP = "\n" + "-" * 80 + "\n"
|
|
2582
|
+
def filter_element(de, element)
|
|
2583
|
+
if de.tracer
|
|
2584
|
+
old_s = element.to_s
|
|
2585
|
+
new_element = super
|
|
2586
|
+
new_s = new_element.to_s
|
|
2587
|
+
if old_s != new_s
|
|
2588
|
+
de.tracer << SEP << "before filter:\n" <<
|
|
2589
|
+
old_s << SEP << "after filter :\n" << new_s << SEP
|
|
2590
|
+
end
|
|
2591
|
+
new_element
|
|
2592
|
+
else
|
|
2593
|
+
super
|
|
2594
|
+
end
|
|
2595
|
+
end
|
|
2596
|
+
|
|
2597
|
+
def value_filter_code(de, cg, value)
|
|
2598
|
+
s = de.element.stag.output("")
|
|
2599
|
+
cg.code(%[Tracer << "%s:%s:%s" % [#{s.inspect},"in", $_.inspect] ])
|
|
2600
|
+
super
|
|
2601
|
+
end
|
|
2602
|
+
|
|
2603
|
+
def method_body_code(de, cg)
|
|
2604
|
+
super
|
|
2605
|
+
s = de.element.stag.output("")
|
|
2606
|
+
cg.code(%[Tracer << "%s:%s:%s" % [#{s.inspect},"out", __stream__] ])
|
|
2607
|
+
end
|
|
2608
|
+
end
|
|
2609
|
+
|
|
2610
|
+
class Join < Base
|
|
2611
|
+
def initialize(sep)
|
|
2612
|
+
@sep = case sep
|
|
2613
|
+
when :br ; "<br />"
|
|
2614
|
+
when :nbsp ; " "
|
|
2615
|
+
else ; sep
|
|
2616
|
+
end
|
|
2617
|
+
end
|
|
2618
|
+
|
|
2619
|
+
def filter_element(de, e)
|
|
2620
|
+
a = e.children.dup
|
|
2621
|
+
a.each do |n|
|
|
2622
|
+
case n
|
|
2623
|
+
when Hpricot::CData, Hpricot::Elem
|
|
2624
|
+
e.insert_after(Hpricot::Text.new(@sep), n) unless n == a.last
|
|
2625
|
+
when Hpricot::Text
|
|
2626
|
+
e.insert_after(Hpricot::Text.new(@sep), n) unless n == a.last
|
|
2627
|
+
n.content.strip!
|
|
2628
|
+
n.content.gsub!(/\s*\n\s*/, @sep)
|
|
2629
|
+
end
|
|
2630
|
+
end
|
|
2631
|
+
e
|
|
2632
|
+
end
|
|
2633
|
+
end
|
|
2634
|
+
|
|
2635
|
+
class ChangeStream < Base
|
|
2636
|
+
def initialize(name)
|
|
2637
|
+
@name = name
|
|
2638
|
+
end
|
|
2639
|
+
|
|
2640
|
+
def method_body_code(de, cg)
|
|
2641
|
+
cg.code("#ChangeStream")
|
|
2642
|
+
cg.with_stream(@name) do
|
|
2643
|
+
super
|
|
2644
|
+
end
|
|
2645
|
+
end
|
|
275
2646
|
end
|
|
276
2647
|
|
|
277
|
-
|
|
278
|
-
|
|
2648
|
+
class PutStream < Base
|
|
2649
|
+
def initialize(name)
|
|
2650
|
+
@name = name
|
|
2651
|
+
end
|
|
2652
|
+
|
|
2653
|
+
def method_body_code(de, cg)
|
|
2654
|
+
cg.code("#PutStream")
|
|
2655
|
+
cg.put_stream(@name)
|
|
2656
|
+
end
|
|
279
2657
|
end
|
|
280
2658
|
|
|
281
|
-
|
|
282
|
-
|
|
2659
|
+
class ModuleExtendFilter< Base
|
|
2660
|
+
def initialize(mod)
|
|
2661
|
+
@mod = mod
|
|
2662
|
+
end
|
|
2663
|
+
|
|
2664
|
+
def value_filter_code(de, cg, element)
|
|
2665
|
+
cg.code("$_.extend(#@mod)")
|
|
2666
|
+
super
|
|
2667
|
+
end
|
|
283
2668
|
end
|
|
284
2669
|
end
|
|
285
2670
|
|
|
286
|
-
class
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
@
|
|
2671
|
+
class HashDelegator
|
|
2672
|
+
include DictionaryData
|
|
2673
|
+
def initialize(parent, &block)
|
|
2674
|
+
@parent = parent
|
|
2675
|
+
@added_data = block.call
|
|
2676
|
+
raise 'block did not return a Hash' unless @added_data.kind_of?(DictionaryData)
|
|
290
2677
|
end
|
|
291
2678
|
|
|
292
|
-
def
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
2679
|
+
def [](key)
|
|
2680
|
+
self.send(key)
|
|
2681
|
+
rescue NoMethodError
|
|
2682
|
+
nil
|
|
2683
|
+
end
|
|
2684
|
+
|
|
2685
|
+
def id
|
|
2686
|
+
@parent.id
|
|
296
2687
|
end
|
|
297
2688
|
|
|
298
|
-
def
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
2689
|
+
def method_missing(sym, *args, &block)
|
|
2690
|
+
if @added_data.has_key?(sym)
|
|
2691
|
+
@added_data[sym]
|
|
2692
|
+
else
|
|
2693
|
+
if @parent.kind_of?(DictionaryData)
|
|
2694
|
+
@parent.amrita_value(sym)
|
|
2695
|
+
else
|
|
2696
|
+
@parent.send(sym)
|
|
2697
|
+
end
|
|
2698
|
+
end
|
|
302
2699
|
end
|
|
303
2700
|
end
|
|
2701
|
+
|
|
2702
|
+
SanitizedString = Util::SanitizedString
|
|
2703
|
+
Template = Core::Template
|
|
304
2704
|
end
|