fabulator 0.0.1
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/History.txt +4 -0
- data/Manifest.txt +41 -0
- data/README.rdoc +61 -0
- data/Rakefile +54 -0
- data/lib/fabulator.rb +5 -0
- data/lib/fabulator/action.rb +46 -0
- data/lib/fabulator/action_lib.rb +438 -0
- data/lib/fabulator/context.rb +39 -0
- data/lib/fabulator/core.rb +8 -0
- data/lib/fabulator/core/actions.rb +514 -0
- data/lib/fabulator/core/actions/choose.rb +167 -0
- data/lib/fabulator/core/actions/for_each.rb +105 -0
- data/lib/fabulator/core/actions/variables.rb +52 -0
- data/lib/fabulator/core/constraint.rb +117 -0
- data/lib/fabulator/core/filter.rb +41 -0
- data/lib/fabulator/core/group.rb +123 -0
- data/lib/fabulator/core/parameter.rb +128 -0
- data/lib/fabulator/core/state.rb +91 -0
- data/lib/fabulator/core/state_machine.rb +153 -0
- data/lib/fabulator/core/transition.rb +164 -0
- data/lib/fabulator/expr.rb +37 -0
- data/lib/fabulator/expr/axis.rb +133 -0
- data/lib/fabulator/expr/axis_descendent_or_self.rb +26 -0
- data/lib/fabulator/expr/bin_expr.rb +178 -0
- data/lib/fabulator/expr/context.rb +368 -0
- data/lib/fabulator/expr/for_expr.rb +74 -0
- data/lib/fabulator/expr/function.rb +52 -0
- data/lib/fabulator/expr/if_expr.rb +22 -0
- data/lib/fabulator/expr/let_expr.rb +17 -0
- data/lib/fabulator/expr/literal.rb +39 -0
- data/lib/fabulator/expr/node.rb +216 -0
- data/lib/fabulator/expr/node_logic.rb +99 -0
- data/lib/fabulator/expr/parser.rb +1470 -0
- data/lib/fabulator/expr/path_expr.rb +49 -0
- data/lib/fabulator/expr/predicates.rb +45 -0
- data/lib/fabulator/expr/statement_list.rb +96 -0
- data/lib/fabulator/expr/step.rb +43 -0
- data/lib/fabulator/expr/unary_expr.rb +30 -0
- data/lib/fabulator/expr/union_expr.rb +21 -0
- data/lib/fabulator/template.rb +9 -0
- data/lib/fabulator/template/context.rb +51 -0
- data/lib/fabulator/template/parse_result.rb +153 -0
- data/lib/fabulator/template/parser.rb +17 -0
- data/lib/fabulator/template/standard_tags.rb +95 -0
- data/lib/fabulator/template/taggable.rb +88 -0
- data/lib/fabulator/version.rb +14 -0
- data/test/test_fabulator.rb +17 -0
- data/test/test_helper.rb +24 -0
- data/xslt/form.xsl +2161 -0
- metadata +182 -0
@@ -0,0 +1,368 @@
|
|
1
|
+
module Fabulator
|
2
|
+
module Expr
|
3
|
+
class Context
|
4
|
+
|
5
|
+
def initialize(parent_c = nil, xml = nil)
|
6
|
+
@parent = parent_c
|
7
|
+
@run_time_parent = nil
|
8
|
+
@ns = { }
|
9
|
+
@attributes = { }
|
10
|
+
@variables = { }
|
11
|
+
@position = nil
|
12
|
+
@last = nil
|
13
|
+
|
14
|
+
if parent_c.nil?
|
15
|
+
if xml.nil? || (xml.root rescue nil).nil?
|
16
|
+
roots = { }
|
17
|
+
roots['data'] = Fabulator::Expr::Node.new('data', roots, nil, [])
|
18
|
+
@root = roots['data']
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
if !xml.nil?
|
23
|
+
if xml.is_a?(self.class)
|
24
|
+
@run_time_parent = xml
|
25
|
+
else
|
26
|
+
parser = Fabulator::Expr::Parser.new
|
27
|
+
|
28
|
+
xml.namespaces.each do |ns|
|
29
|
+
@ns[ns.prefix] = ns.href
|
30
|
+
end
|
31
|
+
begin
|
32
|
+
@ns[''] = xml.namespaces.default.href
|
33
|
+
rescue
|
34
|
+
end
|
35
|
+
|
36
|
+
xml.each_attr do |attr|
|
37
|
+
v = attr.value
|
38
|
+
if !v.nil?
|
39
|
+
@attributes[attr.ns.href.to_s] ||= {}
|
40
|
+
@attributes[attr.ns.href.to_s][attr.name.to_s] = v
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def last?
|
48
|
+
return @last unless @last.nil?
|
49
|
+
return @last if @run_time_parent.nil?
|
50
|
+
return @run_time_parent.last?
|
51
|
+
end
|
52
|
+
|
53
|
+
def last=(l)
|
54
|
+
@last = !!l
|
55
|
+
end
|
56
|
+
|
57
|
+
def position
|
58
|
+
return @position unless @position.nil?
|
59
|
+
return @position if @run_time_parent.nil?
|
60
|
+
return @run_time_parent.position
|
61
|
+
end
|
62
|
+
|
63
|
+
def position=(p)
|
64
|
+
@position = p
|
65
|
+
end
|
66
|
+
|
67
|
+
def merge(s = nil)
|
68
|
+
self.class.new(self, s)
|
69
|
+
end
|
70
|
+
|
71
|
+
def attribute(ns, attr, popts = { })
|
72
|
+
opts = { :static => !@run_time_parent.nil? && !self.root.nil? }.update(popts)
|
73
|
+
value = nil
|
74
|
+
if @attributes.nil? || @attributes[ns].nil? || @attributes[ns].empty? || @attributes[ns][attr].nil?
|
75
|
+
if opts[:inherited]
|
76
|
+
value = @parent.nil? ? nil : @parent.attribute(ns, attr, opts)
|
77
|
+
end
|
78
|
+
else
|
79
|
+
value = @attributes[ns][attr]
|
80
|
+
end
|
81
|
+
if value.nil? && !opts[:default].nil?
|
82
|
+
value = opts[:default]
|
83
|
+
end
|
84
|
+
|
85
|
+
if !value.nil? && value.is_a?(String)
|
86
|
+
e = nil
|
87
|
+
if !opts[:eval]
|
88
|
+
if value =~ /^\{(.*)\}$/
|
89
|
+
e = $1
|
90
|
+
end
|
91
|
+
else
|
92
|
+
e = value
|
93
|
+
end
|
94
|
+
if !e.nil?
|
95
|
+
p = Fabulator::Expr::Parser.new
|
96
|
+
value = p.parse(e, self)
|
97
|
+
else
|
98
|
+
value = Fabulator::Expr::Literal.new(value, [ FAB_NS, value =~ /^\d+$/ ? 'numeric' : value =~ /^\d*\.\d+$/ || value =~ /^\d+\.\d*$/ ? 'numeric' : 'string' ])
|
99
|
+
end
|
100
|
+
if opts[:static]
|
101
|
+
value = value.run(self).collect{ |v| v.value }
|
102
|
+
if value.empty?
|
103
|
+
value = nil
|
104
|
+
elsif value.size == 1
|
105
|
+
value = value.first
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
value
|
111
|
+
end
|
112
|
+
|
113
|
+
def get_select(default = nil)
|
114
|
+
self.attribute(FAB_NS, 'select', { :eval => true, :static => false, :default => default })
|
115
|
+
end
|
116
|
+
|
117
|
+
def with_root(r)
|
118
|
+
ctx = self.class.new(self)
|
119
|
+
ctx.root = r
|
120
|
+
ctx
|
121
|
+
end
|
122
|
+
|
123
|
+
def root
|
124
|
+
if @root.nil?
|
125
|
+
return @run_time_parent.nil? ?
|
126
|
+
( @parent.nil? ? nil : @parent.root ) : @run_time_parent.root
|
127
|
+
end
|
128
|
+
@root
|
129
|
+
end
|
130
|
+
|
131
|
+
def root=(r)
|
132
|
+
@root = r
|
133
|
+
end
|
134
|
+
|
135
|
+
def get_var(v)
|
136
|
+
if !@variables.has_key?(v)
|
137
|
+
if @run_time_parent.nil?
|
138
|
+
if @parent.nil?
|
139
|
+
nil
|
140
|
+
else
|
141
|
+
@parent.get_var(v)
|
142
|
+
end
|
143
|
+
else
|
144
|
+
@run_time_parent.get_var(v)
|
145
|
+
end
|
146
|
+
else
|
147
|
+
@variables[v]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def set_var(v,vv)
|
152
|
+
@variables[v] = vv
|
153
|
+
end
|
154
|
+
|
155
|
+
def get_ns(n)
|
156
|
+
return @ns[n] if @ns.has_key?(n)
|
157
|
+
return @parent.get_ns(n) unless @parent.nil?
|
158
|
+
return nil
|
159
|
+
end
|
160
|
+
|
161
|
+
def set_ns(n,h)
|
162
|
+
@ns[n] = h
|
163
|
+
end
|
164
|
+
|
165
|
+
def each_namespace(&block)
|
166
|
+
if !@parent.nil?
|
167
|
+
@parent.each_namespace do |k,v|
|
168
|
+
yield k, v
|
169
|
+
end
|
170
|
+
end
|
171
|
+
@ns.each_pair do |k,v|
|
172
|
+
yield k, v
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def eval_expression(selection)
|
177
|
+
if selection.is_a?(String)
|
178
|
+
p = Fabulator::Expr::Parser.new
|
179
|
+
selection = p.parse(selection, self)
|
180
|
+
end
|
181
|
+
|
182
|
+
if selection.nil?
|
183
|
+
res = [ ]
|
184
|
+
else
|
185
|
+
# run selection against current context
|
186
|
+
res = selection.run(self)
|
187
|
+
end
|
188
|
+
return res
|
189
|
+
end
|
190
|
+
|
191
|
+
def run(action, autovivify = false)
|
192
|
+
action.run(self, autovivify)
|
193
|
+
end
|
194
|
+
|
195
|
+
def traverse_path(path, autovivify = false)
|
196
|
+
return [ self.root ] if path.nil? || path.is_a?(Array) && path.empty?
|
197
|
+
|
198
|
+
path = [ path ] unless path.is_a?(Array)
|
199
|
+
|
200
|
+
current = [ self.root ]
|
201
|
+
|
202
|
+
path.each do |c|
|
203
|
+
set = [ ]
|
204
|
+
current.each do |cc|
|
205
|
+
if c.is_a?(String)
|
206
|
+
cset = cc.children(c)
|
207
|
+
else
|
208
|
+
cset = c.run(self.with_root(cc), autovivify)
|
209
|
+
end
|
210
|
+
if cset.nil? || cset.empty?
|
211
|
+
if autovivify
|
212
|
+
if c.is_a?(String)
|
213
|
+
cset = [ cc.create_child(c) ]
|
214
|
+
else
|
215
|
+
cset = [ c.create_node(cc) ]
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
set = set + cset
|
220
|
+
end
|
221
|
+
current = set
|
222
|
+
end
|
223
|
+
return current
|
224
|
+
end
|
225
|
+
|
226
|
+
def set_value(p, v)
|
227
|
+
if p.is_a?(String) || v.is_a?(String)
|
228
|
+
parser = Fabulator::Expr::Parser.new
|
229
|
+
p = parser.parse(p,self) if p.is_a?(String)
|
230
|
+
v = parser.parse(v,self) if v.is_a?(String)
|
231
|
+
end
|
232
|
+
|
233
|
+
return [] if p.nil?
|
234
|
+
|
235
|
+
p = [ p ] unless p.is_a?(Array)
|
236
|
+
|
237
|
+
ret = [ ]
|
238
|
+
|
239
|
+
p.each do |pp|
|
240
|
+
tgts = pp.run(self, true)
|
241
|
+
src = nil
|
242
|
+
if !v.nil?
|
243
|
+
src = v.run(self)
|
244
|
+
end
|
245
|
+
|
246
|
+
tgts.each do |tgt|
|
247
|
+
tgt.prune
|
248
|
+
if src.nil? || src.empty?
|
249
|
+
tgt.value = nil
|
250
|
+
ret << tgt
|
251
|
+
elsif src.size == 1
|
252
|
+
tgt.copy(src.first)
|
253
|
+
ret << tgt
|
254
|
+
else
|
255
|
+
pp = tgt.parent
|
256
|
+
nom = tgt.name
|
257
|
+
pp.prune(pp.children(nom))
|
258
|
+
src.each do |s|
|
259
|
+
tgt = pp.create_child(nom,nil)
|
260
|
+
tgt.copy(s)
|
261
|
+
ret << tgt
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
ret
|
267
|
+
end
|
268
|
+
|
269
|
+
def get_values(ln = nil)
|
270
|
+
return [] if ln.nil?
|
271
|
+
self.eval_expression(ln).collect{ |c| c.value} - [ nil ]
|
272
|
+
end
|
273
|
+
|
274
|
+
def merge_data(d,p = nil)
|
275
|
+
# we have a hash or array based on root (r)
|
276
|
+
if p.nil?
|
277
|
+
root_context = [ self.root ]
|
278
|
+
else
|
279
|
+
root_context = self.traverse_path(p,true)
|
280
|
+
end
|
281
|
+
if root_context.size > 1
|
282
|
+
# see if we need to prune
|
283
|
+
new_rc = [ ]
|
284
|
+
root_context.each do |c|
|
285
|
+
if c.children.size == 0 && c.value.nil?
|
286
|
+
c.parent.prune(c) if c.parent
|
287
|
+
else
|
288
|
+
new_rc << c
|
289
|
+
end
|
290
|
+
end
|
291
|
+
if new_rc.size > 0
|
292
|
+
raise "Unable to merge data into multiple places simultaneously"
|
293
|
+
else
|
294
|
+
root_context = new_rc
|
295
|
+
end
|
296
|
+
else
|
297
|
+
root_context = root_context.first
|
298
|
+
end
|
299
|
+
if d.is_a?(Array)
|
300
|
+
node_name = root_context.name
|
301
|
+
root_context = root_context.parent
|
302
|
+
# get rid of empty children so we don't have problems later
|
303
|
+
root_context.children.each do |c|
|
304
|
+
if c.children.size == 0 && c.name == node_name && c.value.nil?
|
305
|
+
c.parent.prune(c)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
d.each do |i|
|
309
|
+
c = root_context.create_child(node_name)
|
310
|
+
self.with_root(c).merge_data(i)
|
311
|
+
end
|
312
|
+
elsif d.is_a?(Hash)
|
313
|
+
d.each_pair do |k,v|
|
314
|
+
bits = k.split('.')
|
315
|
+
c = self.with_root(root_context).traverse_path(bits,true).first
|
316
|
+
if v.is_a?(Hash) || v.is_a?(Array)
|
317
|
+
self.with_root(c).merge_data(v)
|
318
|
+
else
|
319
|
+
c.value = v
|
320
|
+
end
|
321
|
+
end
|
322
|
+
else
|
323
|
+
c = root_context.parent.create_child(root_context.name, d)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def compile_actions(xml)
|
328
|
+
actions = Fabulator::Expr::StatementList.new
|
329
|
+
local_ctx = self.merge(xml)
|
330
|
+
xml.each_element do |e|
|
331
|
+
ns = e.namespaces.namespace.href
|
332
|
+
next unless Fabulator::ActionLib.namespaces.include?(ns)
|
333
|
+
if ns == FAB_NS && e.name == 'ensure'
|
334
|
+
actions.add_ensure(local_ctx.compile_actions(e))
|
335
|
+
elsif ns == FAB_NS && e.name == 'catch'
|
336
|
+
actions.add_catch(local_ctx.compile_action(e))
|
337
|
+
else
|
338
|
+
actions.add_statement(local_ctx.compile_action(e)) # rescue nil)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
return actions
|
342
|
+
end
|
343
|
+
|
344
|
+
def compile_action(e)
|
345
|
+
ns = e.namespaces.namespace.href
|
346
|
+
return unless Fabulator::ActionLib.namespaces.include?(ns)
|
347
|
+
Fabulator::ActionLib.namespaces[ns].compile_action(e, self)
|
348
|
+
end
|
349
|
+
|
350
|
+
def in_context(&block)
|
351
|
+
ctx = self.merge
|
352
|
+
yield ctx
|
353
|
+
end
|
354
|
+
|
355
|
+
def with(ctx2, &block)
|
356
|
+
ctx = self.merge(ctx2)
|
357
|
+
yield ctx
|
358
|
+
end
|
359
|
+
|
360
|
+
def run_filter(ns, name)
|
361
|
+
handler = Fabulator::ActionLib.namespaces[ns]
|
362
|
+
return [] if handler.nil?
|
363
|
+
handler.run_filter(self, name)
|
364
|
+
end
|
365
|
+
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Fabulator
|
2
|
+
module Expr
|
3
|
+
class ForExpr
|
4
|
+
def initialize(v, e)
|
5
|
+
if v.size > 1
|
6
|
+
@var = v.shift
|
7
|
+
@expr = Fabulator::Expr::ForExpr.new(v, e)
|
8
|
+
else
|
9
|
+
@var = v.first
|
10
|
+
@expr = e
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def expr_type(context)
|
15
|
+
@expr.expr_type(context)
|
16
|
+
end
|
17
|
+
|
18
|
+
def run(context, autovivify = false)
|
19
|
+
result = [ ]
|
20
|
+
return result if @var.nil? || @expr.nil?
|
21
|
+
|
22
|
+
@var.each_binding(context, autovivify) do |b|
|
23
|
+
result = result + @expr.run(b)
|
24
|
+
end
|
25
|
+
return result
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class EveryExpr < ForExpr
|
30
|
+
def expr_type(context)
|
31
|
+
[ FAB_NS, 'boolean' ]
|
32
|
+
end
|
33
|
+
|
34
|
+
def run(context, autovivify = false)
|
35
|
+
result = super
|
36
|
+
result.each do |r|
|
37
|
+
return [ context.root.anon_node(false) ] unless !!r.value
|
38
|
+
end
|
39
|
+
return [ context.root.anon_node(true) ]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class SomeExpr < ForExpr
|
44
|
+
def expr_type(context)
|
45
|
+
[ FAB_NS, 'boolean' ]
|
46
|
+
end
|
47
|
+
|
48
|
+
def run(context, autovivify = false)
|
49
|
+
result = super
|
50
|
+
result.each do |r|
|
51
|
+
return [ context.root.anon_node(true) ] if !!r.value
|
52
|
+
end
|
53
|
+
return [ context.root.anon_node(false) ]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class ForVar
|
58
|
+
def initialize(n, e)
|
59
|
+
n =~ /^\$?(.*)$/
|
60
|
+
@var_name = $1
|
61
|
+
@expr = e
|
62
|
+
end
|
63
|
+
|
64
|
+
def each_binding(context, autovivify = false, &block)
|
65
|
+
@expr.run(context, autovivify).each do |e|
|
66
|
+
context.in_context do |ctx|
|
67
|
+
ctx.set_var(@var_name, e)
|
68
|
+
yield ctx
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Fabulator
|
2
|
+
module Expr
|
3
|
+
class Function
|
4
|
+
def initialize(ctx, nom, args)
|
5
|
+
bits = nom.split(/:/, 2)
|
6
|
+
@ns = ctx.get_ns(bits[0])
|
7
|
+
@name = bits[1]
|
8
|
+
@args = args
|
9
|
+
@ctx = ctx
|
10
|
+
end
|
11
|
+
|
12
|
+
def expr_type(context)
|
13
|
+
klass = ActionLib.namespaces[@ns]
|
14
|
+
(klass.function_return_type(@name) rescue nil)
|
15
|
+
end
|
16
|
+
|
17
|
+
def run(context, autovivify = false)
|
18
|
+
klass = ActionLib.namespaces[@ns]
|
19
|
+
return [] if klass.nil?
|
20
|
+
ctx = @ctx.merge(context)
|
21
|
+
return klass.run_function(
|
22
|
+
ctx, @name, @args.run(ctx)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class List
|
28
|
+
def initialize(args)
|
29
|
+
@args = args
|
30
|
+
end
|
31
|
+
|
32
|
+
def run(context, autovivify = false)
|
33
|
+
@args.collect{ |arg| arg.run(context, autovivify).flatten }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Tuple
|
38
|
+
def initialize(args)
|
39
|
+
@args = args
|
40
|
+
end
|
41
|
+
|
42
|
+
def run(context, autovivify = false)
|
43
|
+
items = @args.collect{ |arg| arg.run(context, autovivify).flatten }.flatten
|
44
|
+
ret = context.root.anon_node(nil, [ FAB_NS, 'tuple' ])
|
45
|
+
ret.value = items
|
46
|
+
ret.vtype = [ FAB_NS, 'tuple' ]
|
47
|
+
ret.set_attribute('size', items.size)
|
48
|
+
[ ret ]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|