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,167 @@
|
|
1
|
+
module Fabulator
|
2
|
+
module Core
|
3
|
+
module Actions
|
4
|
+
class Choose < Fabulator::Action
|
5
|
+
def compile_xml(xml, context)
|
6
|
+
super
|
7
|
+
|
8
|
+
@choices = [ ]
|
9
|
+
@default = nil
|
10
|
+
|
11
|
+
xml.each_element do |e|
|
12
|
+
next unless e.namespaces.namespace.href == FAB_NS
|
13
|
+
case e.name
|
14
|
+
when 'when':
|
15
|
+
@choices << When.new.compile_xml(e, @context)
|
16
|
+
when 'otherwise':
|
17
|
+
@default = When.new.compile_xml(e, @context)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def run(context, autovivify = false)
|
24
|
+
@context.with(context) do |ctx|
|
25
|
+
@choices.each do |c|
|
26
|
+
if c.run_test(ctx)
|
27
|
+
return c.run(ctx)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
return @default.run(ctx) unless @default.nil?
|
31
|
+
return []
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class When < Fabulator::Action
|
37
|
+
namespace Fabulator::FAB_NS
|
38
|
+
attribute :test, :eval => true, :static => false
|
39
|
+
|
40
|
+
has_actions
|
41
|
+
|
42
|
+
def run_test(context)
|
43
|
+
return true if @test.nil?
|
44
|
+
result = @test.run(@context.merge(context)).collect{ |a| !!a.value }
|
45
|
+
return false if result.nil? || result.empty? || !result.include?(true)
|
46
|
+
return true
|
47
|
+
end
|
48
|
+
|
49
|
+
def run(context, autovivify = false)
|
50
|
+
return @actions.run(@context.merge(context))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class If < Fabulator::Action
|
55
|
+
namespace Fabulator::FAB_NS
|
56
|
+
attribute :test, :eval => true, :static => false
|
57
|
+
|
58
|
+
has_actions
|
59
|
+
|
60
|
+
def run(context, autovivify = false)
|
61
|
+
return [ ] if @test.nil?
|
62
|
+
@context.with(context) do |ctx|
|
63
|
+
test_res = @test.run(ctx).collect{ |a| !!a.value }
|
64
|
+
return [ ] if test_res.nil? || test_res.empty? || !test_res.include?(true)
|
65
|
+
return @actions.run(ctx)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Block < Fabulator::Action
|
71
|
+
|
72
|
+
namespace Fabulator::FAB_NS
|
73
|
+
has_actions
|
74
|
+
|
75
|
+
def run(context, autovivify = false)
|
76
|
+
return @actions.run(@context.merge(context),autovivify)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Goto < Fabulator::Action
|
81
|
+
namespace Fabulator::FAB_NS
|
82
|
+
attribute :test, :eval => true, :static => false
|
83
|
+
attribute :state, :static => true
|
84
|
+
|
85
|
+
def run(context, autovivify = false)
|
86
|
+
raise Fabulator::StateChangeException, @state, caller if @test.nil?
|
87
|
+
test_res = @test.run(@context.merge(context)).collect{ |a| !!a.value }
|
88
|
+
return [ ] if test_res.nil? || test_res.empty? || !test_res.include?(true)
|
89
|
+
raise Fabulator::StateChangeException, @state, caller
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class Catch < Fabulator::Action
|
94
|
+
namespace Fabulator::FAB_NS
|
95
|
+
attribute :test, :eval => true, :static => false
|
96
|
+
attribute :as, :static => true
|
97
|
+
|
98
|
+
has_actions
|
99
|
+
|
100
|
+
def run_test(context)
|
101
|
+
return true if @test.nil?
|
102
|
+
@context.with(context) do |ctx|
|
103
|
+
ctx.set_var(@as, context) if @as
|
104
|
+
result = @test.run(context).collect{ |a| !!a.value }
|
105
|
+
return false if result.nil? || result.empty? || !result.include?(true)
|
106
|
+
return true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def run(context, autovivify = false)
|
111
|
+
@context.with(context) do |ctx|
|
112
|
+
ctx.set_var(@as, context) if @as
|
113
|
+
return @actions.run(context)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class Raise < Fabulator::Action
|
119
|
+
|
120
|
+
namespace Fabulator::FAB_NS
|
121
|
+
attribute :test, :eval => true, :static => false
|
122
|
+
has_actions
|
123
|
+
|
124
|
+
def run(context, autovivify = false)
|
125
|
+
@context.with(context) do |ctx|
|
126
|
+
select = ctx.get_select
|
127
|
+
if !@test.nil?
|
128
|
+
test_res = @test.run(ctx).collect{ |a| !!a.value }
|
129
|
+
return [ ] if test_res.nil? || test_res.empty? || !test_res.include?(true)
|
130
|
+
end
|
131
|
+
res = [ ]
|
132
|
+
if select.nil? && !@actions.nil?
|
133
|
+
res = @actions.run(ctx, autovivify)
|
134
|
+
elsif !select.nil?
|
135
|
+
res = select.run(ctx, autovivify)
|
136
|
+
else
|
137
|
+
raise ctx # default if <raise/> with no attributes
|
138
|
+
end
|
139
|
+
|
140
|
+
return [ ] if res.empty?
|
141
|
+
|
142
|
+
raise Fabulator::Expr::Exception.new(res.first)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class Super < Fabulator::Action
|
148
|
+
namespace Fabulator::FAB_NS
|
149
|
+
has_select
|
150
|
+
has_actions :super
|
151
|
+
|
152
|
+
def run(context, autovivify = false)
|
153
|
+
return [] if @actions.nil?
|
154
|
+
|
155
|
+
@context.with(context) do |ctx|
|
156
|
+
new_context = @select.run(ctx,autovivify)
|
157
|
+
|
158
|
+
new_context = [ new_context ] unless new_context.is_a?(Array)
|
159
|
+
|
160
|
+
return new_context.collect { |c| @actions.run(ctx.with_root(c), autovivify) }.flatten
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Fabulator
|
2
|
+
module Core
|
3
|
+
module Actions
|
4
|
+
class ForEach < Fabulator::Action
|
5
|
+
namespace Fabulator::FAB_NS
|
6
|
+
attribute :as, :static => true
|
7
|
+
has_select
|
8
|
+
has_actions
|
9
|
+
|
10
|
+
def compile_xml(xml, context)
|
11
|
+
super
|
12
|
+
@sort = [ ]
|
13
|
+
|
14
|
+
xml.each_element do |e|
|
15
|
+
next unless e.namespaces.namespace.href == FAB_NS
|
16
|
+
case e.name
|
17
|
+
when 'sort-by':
|
18
|
+
@sort << Sort.new.compile_xml(e, @context)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def run(context, autovivify = false)
|
25
|
+
@context.with(context) do |ctx|
|
26
|
+
items = @select.run(ctx)
|
27
|
+
res = nil
|
28
|
+
ctx.in_context do |c|
|
29
|
+
if !@sort.empty?
|
30
|
+
items = items.sort_by{ |i|
|
31
|
+
c.set_var(@as, i) unless @as.nil?
|
32
|
+
@sort.collect{|s| s.run(c.with_root(i)) }.join("\0")
|
33
|
+
}
|
34
|
+
end
|
35
|
+
res = [ ]
|
36
|
+
items.each do |i|
|
37
|
+
c.set_var(@as, i) unless @as.nil?
|
38
|
+
res = res + @actions.run(c.with_root(i))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
return res
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Sort < Fabulator::Action
|
47
|
+
namespace Fabulator::FAB_NS
|
48
|
+
has_select
|
49
|
+
|
50
|
+
def run(context, autovivify = false)
|
51
|
+
(@select.run(@context.merge(context)).first.to_s rescue '')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Considering < Fabulator::Action
|
56
|
+
namespace Fabulator::FAB_NS
|
57
|
+
attribute :as, :static => true
|
58
|
+
has_select
|
59
|
+
has_actions
|
60
|
+
|
61
|
+
def run(context, autovivify = false)
|
62
|
+
return [] if @select.nil?
|
63
|
+
res = [ ]
|
64
|
+
@context.with(context) do |ctx|
|
65
|
+
c = @select.run(ctx)
|
66
|
+
root = nil
|
67
|
+
if @as.nil?
|
68
|
+
if c.size == 1
|
69
|
+
root = c.first
|
70
|
+
else
|
71
|
+
root = Fabulator::Expr::Node.new('data', context.root.roots, nil, c)
|
72
|
+
end
|
73
|
+
res = @actions.run(ctx.with_root(root))
|
74
|
+
else
|
75
|
+
ctx.in_context do |ctx2|
|
76
|
+
ctx2.set_var(@as, c)
|
77
|
+
res = @actions.run(ctx2)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
res
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class While < Fabulator::Action
|
86
|
+
namespace Fabulator::FAB_NS
|
87
|
+
attribute :test, :eval => true, :static => false
|
88
|
+
attribute :limit, :default => 1000
|
89
|
+
has_actions
|
90
|
+
|
91
|
+
def run(context, autovivify = false)
|
92
|
+
res = [ ]
|
93
|
+
counter = 0
|
94
|
+
@context.with(context) do |ctx|
|
95
|
+
lim = @limit.nil? ? 1000 : @limit.run(ctx).first.value
|
96
|
+
while counter < @limit && (!!@test.run(ctx).first.value rescue false)
|
97
|
+
res = res + @actions.run(ctx)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
res
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Fabulator
|
2
|
+
module Core
|
3
|
+
module Actions
|
4
|
+
class ValueOf < Fabulator::Action
|
5
|
+
namespace Fabulator::FAB_NS
|
6
|
+
has_select
|
7
|
+
|
8
|
+
def run(context, autovivify = false)
|
9
|
+
@select.run(@context.merge(context), autovivify)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Value < Fabulator::Action
|
14
|
+
attr_accessor :select, :name
|
15
|
+
|
16
|
+
namespace Fabulator::FAB_NS
|
17
|
+
attribute :path, :static => true
|
18
|
+
has_select nil
|
19
|
+
has_actions
|
20
|
+
|
21
|
+
def run(context, autovivify = false)
|
22
|
+
@context.with(context) do |ctx|
|
23
|
+
ctx.set_value(@path, @select.nil? ? @actions : @select )
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Variable < Fabulator::Action
|
29
|
+
namespace Fabulator::FAB_NS
|
30
|
+
attribute :name, :eval => false, :static => true
|
31
|
+
has_select
|
32
|
+
has_actions
|
33
|
+
|
34
|
+
def run(context, autovivify = false)
|
35
|
+
return [] if @name.nil?
|
36
|
+
res = [ ]
|
37
|
+
@context.with(context) do |ctx|
|
38
|
+
if @select
|
39
|
+
res = @select.run(ctx, autovivify)
|
40
|
+
elsif !@actions.empty?
|
41
|
+
@actions.each do |a|
|
42
|
+
res = a.run(ctx, autovivify)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
context.set_var(@name, res)
|
47
|
+
res
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Fabulator
|
2
|
+
module Core
|
3
|
+
class Constraint < Fabulator::Action
|
4
|
+
|
5
|
+
namespace Fabulator::FAB_NS
|
6
|
+
attribute :invert, :static => true, :default => 'false'
|
7
|
+
attribute :name, :as => :c_type, :static => true
|
8
|
+
has_select
|
9
|
+
|
10
|
+
def compile_xml(xml, ctx)
|
11
|
+
super
|
12
|
+
@constraints = [ ]
|
13
|
+
@values = [ ]
|
14
|
+
@params = [ ]
|
15
|
+
@attributes = { }
|
16
|
+
|
17
|
+
if xml.name == 'value'
|
18
|
+
@c_type = 'any'
|
19
|
+
@values << xml.content
|
20
|
+
else
|
21
|
+
xml.each_attr do |attr|
|
22
|
+
next unless attr.ns.href == FAB_NS
|
23
|
+
next if attr.name == 'name' || attr.name == 'invert'
|
24
|
+
@attributes[attr.name] = attr.value
|
25
|
+
end
|
26
|
+
xml.each_element do |e|
|
27
|
+
next unless e.namespaces.namespace.href == FAB_NS
|
28
|
+
e_ctx = @context.merge(e)
|
29
|
+
case e.name
|
30
|
+
when 'param':
|
31
|
+
pname = e_ctx.attribute(FAB_NS, 'name') # (e.get_attribute_ns(FAB_NS, 'name').value rescue nil)
|
32
|
+
if !pname.nil?
|
33
|
+
v = e_ctx.attribute(FAB_NS, 'value', { :default => e_ctx.get_select })
|
34
|
+
@params[pname] = v unless v.nil?
|
35
|
+
end
|
36
|
+
when 'constraint':
|
37
|
+
@constraints << Constraint.new.compile_xml(e, @context)
|
38
|
+
when 'value':
|
39
|
+
v = e_ctx.get_select
|
40
|
+
if v.nil?
|
41
|
+
v = e.content
|
42
|
+
end
|
43
|
+
@values << v unless v.nil?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def error_message(context)
|
51
|
+
"#{context.path} does not pass the constraint"
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_constraint(context)
|
55
|
+
# do special ones first
|
56
|
+
@context.with(context) do |ctx|
|
57
|
+
paths = [ [ ctx.root.path ], [] ]
|
58
|
+
r = paths
|
59
|
+
|
60
|
+
inv = (@invert == 'true' || @invert == 'yes') ? true : false
|
61
|
+
sense = !inv ? Proc.new { |r| r } : Proc.new { |r| r.reverse }
|
62
|
+
not_sense = inv ? Proc.new { |r| r } : Proc.new { |r| r.reverse }
|
63
|
+
|
64
|
+
case @c_type
|
65
|
+
when nil, '':
|
66
|
+
return sense.call(paths) if select.nil?
|
67
|
+
opts = @select.run(ctx).collect { |o| o.to_s }
|
68
|
+
if !opts.include?(ctx.root.to_s)
|
69
|
+
paths[0] -= [ ctx.root.path ]
|
70
|
+
paths[1] += [ ctx.root.path ]
|
71
|
+
end
|
72
|
+
return sense.call(paths)
|
73
|
+
when 'all':
|
74
|
+
# we have enclosed constraints
|
75
|
+
@constraints.each do |c|
|
76
|
+
r = c.test_constraint(ctx)
|
77
|
+
return sense.call(r) unless r[1].empty?
|
78
|
+
end
|
79
|
+
return not_sense.call(r)
|
80
|
+
when 'any':
|
81
|
+
if @values.empty?
|
82
|
+
@constraints.each do |c|
|
83
|
+
r = c.test_constraint(ctx)
|
84
|
+
return not_sense.call(r) if r[1].empty?
|
85
|
+
end
|
86
|
+
return sense.call(r)
|
87
|
+
else
|
88
|
+
calc_values = [ ]
|
89
|
+
@values.each do |v|
|
90
|
+
if v.is_a?(String)
|
91
|
+
calc_values << v
|
92
|
+
else
|
93
|
+
calc_values = calc_values + v.run(ctx).collect{ |i| i.value }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
if !calc_values.include?(ctx.root.value)
|
97
|
+
paths[0] -= [ ctx.root.path ]
|
98
|
+
paths[1] += [ ctx.root.path ]
|
99
|
+
end
|
100
|
+
return sense.call(paths)
|
101
|
+
end
|
102
|
+
when 'range':
|
103
|
+
fl = (@params['floor'].run(ctx) rescue nil)
|
104
|
+
ce = (@params['ceiling'].run(ctx) rescue nil)
|
105
|
+
if !fl.nil? && fl > c.value || !ce.nil? && ce < c.value
|
106
|
+
paths[0] -= [ c.path ]
|
107
|
+
paths[1] += [ c.path ]
|
108
|
+
end
|
109
|
+
return sense.call(r)
|
110
|
+
else
|
111
|
+
return not_sense.call(r)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Fabulator
|
2
|
+
module Core
|
3
|
+
class Filter < Fabulator::Action
|
4
|
+
namespace Fabulator::FAB_NS
|
5
|
+
attribute :name, :static => true
|
6
|
+
|
7
|
+
# def compile_xml(xml, context)
|
8
|
+
# @context = context.merge(xml)
|
9
|
+
# filter_type = @context.attribute(FAB_NS, 'name', {:static => true}).split(/:/, 2)
|
10
|
+
# if filter_type.size == 2
|
11
|
+
# @ns = @context.get_ns(filter_type[0])
|
12
|
+
# @name = filter_type[1]
|
13
|
+
# else
|
14
|
+
# @ns = FAB_NS
|
15
|
+
# @name = filter_type[0]
|
16
|
+
# end
|
17
|
+
# self
|
18
|
+
# end
|
19
|
+
|
20
|
+
def run(context)
|
21
|
+
filtered = [ ]
|
22
|
+
@context.with(context) do |ctx|
|
23
|
+
filter_type = @name.split(/:/,2)
|
24
|
+
ns = nil
|
25
|
+
name = nil
|
26
|
+
if filter_type.size == 2
|
27
|
+
ns = @context.get_ns(filter_type[0])
|
28
|
+
name = filter_type[1]
|
29
|
+
else
|
30
|
+
ns = FAB_NS
|
31
|
+
name = filter_type[0]
|
32
|
+
end
|
33
|
+
|
34
|
+
ctx.run_filter(ns, name)
|
35
|
+
filtered << ctx.root.path
|
36
|
+
end
|
37
|
+
return filtered
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|