fabulator 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.rdoc
|
4
|
+
lib/fabulator.rb
|
5
|
+
lib/fabulator/action.rb
|
6
|
+
lib/fabulator/action_lib.rb
|
7
|
+
lib/fabulator/context.rb
|
8
|
+
lib/fabulator/core.rb
|
9
|
+
lib/fabulator/core/actions.rb
|
10
|
+
lib/fabulator/core/actions/choose.rb
|
11
|
+
lib/fabulator/core/actions/for_each.rb
|
12
|
+
lib/fabulator/core/actions/variables.rb
|
13
|
+
lib/fabulator/core/constraint.rb
|
14
|
+
lib/fabulator/core/filter.rb
|
15
|
+
lib/fabulator/core/group.rb
|
16
|
+
lib/fabulator/core/parameter.rb
|
17
|
+
lib/fabulator/core/state.rb
|
18
|
+
lib/fabulator/core/state_machine.rb
|
19
|
+
lib/fabulator/core/transition.rb
|
20
|
+
lib/fabulator/expr.rb
|
21
|
+
lib/fabulator/expr/axis.rb
|
22
|
+
lib/fabulator/expr/axis_descendent_or_self.rb
|
23
|
+
lib/fabulator/expr/bin_expr.rb
|
24
|
+
lib/fabulator/expr/context.rb
|
25
|
+
lib/fabulator/expr/for_expr.rb
|
26
|
+
lib/fabulator/expr/function.rb
|
27
|
+
lib/fabulator/expr/function.rb.old
|
28
|
+
lib/fabulator/expr/if_expr.rb
|
29
|
+
lib/fabulator/expr/let_expr.rb
|
30
|
+
lib/fabulator/expr/literal.rb
|
31
|
+
lib/fabulator/expr/node.rb
|
32
|
+
lib/fabulator/expr/node.rb.bak
|
33
|
+
lib/fabulator/expr/node_logic.rb
|
34
|
+
lib/fabulator/expr/parser.rb
|
35
|
+
lib/fabulator/expr/path_expr.rb
|
36
|
+
lib/fabulator/expr/predicates.rb
|
37
|
+
lib/fabulator/expr/statement_list.rb
|
38
|
+
lib/fabulator/expr/step.rb
|
39
|
+
lib/fabulator/expr/unary_expr.rb
|
40
|
+
lib/fabulator/expr/union_expr.rb
|
41
|
+
lib/fabulator/version.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
= fabulator
|
2
|
+
|
3
|
+
* http://github.com/jgsmith/ruby-fabulator
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
The fabulator library provides a state machine implementation of a core
|
8
|
+
set of semantics for building data-driven applications using a simple
|
9
|
+
XML language coupled with an XQuery-like expression language.
|
10
|
+
|
11
|
+
== FEATURES/PROBLEMS:
|
12
|
+
|
13
|
+
* XML compilation into Ruby objects
|
14
|
+
* XQuery-like expression language for accessing data
|
15
|
+
* Easily extensible with XML actions and expression functions
|
16
|
+
|
17
|
+
== SYNOPSIS:
|
18
|
+
|
19
|
+
require 'fabulator'
|
20
|
+
|
21
|
+
parser = Fabulator::Expr::Parser.new
|
22
|
+
context = Fabulator::Expr::Context.new_context_environment
|
23
|
+
|
24
|
+
context.set_value( '/a', 'some_values' )
|
25
|
+
expr = parser.parse( 'f:sum(/a[. > 0]' )
|
26
|
+
result = expr.run( context )
|
27
|
+
|
28
|
+
puts "sum: #{ result.value }"
|
29
|
+
|
30
|
+
== REQUIREMENTS:
|
31
|
+
|
32
|
+
The expression engine does not depend on any external libraries. The
|
33
|
+
XML framework depends on the Ruby libxml libraries at
|
34
|
+
<http://libxml.rubyforge.org/>.
|
35
|
+
|
36
|
+
== INSTALL:
|
37
|
+
|
38
|
+
* sudo gem install fabulator
|
39
|
+
|
40
|
+
== LICENSE:
|
41
|
+
|
42
|
+
Copyright (c) 2009-2010 Texas A&M University
|
43
|
+
|
44
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
45
|
+
a copy of this software and associated documentation files (the
|
46
|
+
'Software'), to deal in the Software without restriction, including
|
47
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
48
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
49
|
+
permit persons to whom the Software is furnished to do so, subject to
|
50
|
+
the following conditions:
|
51
|
+
|
52
|
+
The above copyright notice and this permission notice shall be
|
53
|
+
included in all copies or substantial portions of the Software.
|
54
|
+
|
55
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
56
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
57
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
58
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
59
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
60
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
61
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__))+'/lib'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
gem 'hoe', '>= 2.1.0'
|
5
|
+
require 'hoe'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'fabulator'
|
8
|
+
|
9
|
+
Hoe.plugin :newgem
|
10
|
+
# Hoe.plugin :website
|
11
|
+
Hoe.plugin :cucumberfeatures
|
12
|
+
|
13
|
+
# Generate all the Rake tasks
|
14
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
15
|
+
$hoe = Hoe.spec 'fabulator' do
|
16
|
+
self.version = Fabulator::VERSION::STRING
|
17
|
+
self.developer 'James Smith', 'jgsmith@tamu.edu'
|
18
|
+
self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
19
|
+
self.rubyforge_name = self.name # TODO this is default value
|
20
|
+
# self.extra_deps = [['activesupport','>= 2.0.2']]
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'newgem/tasks'
|
25
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
26
|
+
|
27
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
28
|
+
# remove_task :default
|
29
|
+
# task :default => [:spec, :features]
|
30
|
+
|
31
|
+
desc "Look for TODO and FIXME tags in the code"
|
32
|
+
task :todo do
|
33
|
+
egrep /(FIXME|TODO|TBD)/
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "verify_committed, verify_rcov, post_news, release"
|
37
|
+
task :complete_release => [:verify_committed, :release]
|
38
|
+
|
39
|
+
desc "Verifies that there is no uncommitted code"
|
40
|
+
task :verify_committed do
|
41
|
+
IO.popen('git status') do |io|
|
42
|
+
io.each_line do |line|
|
43
|
+
raise "\n!!! Do a git commit first !!!\n\n" if line =~ /^#\s*modified:/
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
namespace :update do
|
49
|
+
desc "update the manifest"
|
50
|
+
task :manifest do
|
51
|
+
system %q[touch Manifest.txt; rake check_manifest | grep -v "(in " | patch]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
data/lib/fabulator.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Fabulator
|
2
|
+
class Action
|
3
|
+
|
4
|
+
def compile_xml(xml, context)
|
5
|
+
@context = context.merge(xml)
|
6
|
+
klass = self.class.name
|
7
|
+
if @@attributes[klass]
|
8
|
+
@@attributes[klass].each_pair do |nom, opts|
|
9
|
+
as = "@" + (opts[:as] || nom).to_s
|
10
|
+
self.instance_variable_set(as.to_sym, @context.attribute(opts[:namespace] || @@namespace[klass], nom.to_s, opts))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
@select = @context.get_select(@@has_select[klass]) if @@has_select.has_key?(klass)
|
14
|
+
if @@has_actions[klass]
|
15
|
+
case @@has_actions[klass]
|
16
|
+
when :simple:
|
17
|
+
@actions = @context.compile_actions(xml)
|
18
|
+
when :super:
|
19
|
+
@actions = ActionLib.current_super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.namespace(href)
|
26
|
+
@@namespace ||= { }
|
27
|
+
@@namespace[self.name] = href
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.attribute(nom, opts = { })
|
31
|
+
@@attributes ||= { }
|
32
|
+
@@attributes[self.name] ||= { }
|
33
|
+
@@attributes[self.name][nom.to_s] = opts
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.has_actions(t = :simple)
|
37
|
+
@@has_actions ||= { }
|
38
|
+
@@has_actions[self.name] = t
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.has_select(default = '.')
|
42
|
+
@@has_select ||= { }
|
43
|
+
@@has_select[self.name] = default
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,438 @@
|
|
1
|
+
module Fabulator
|
2
|
+
module ActionLib
|
3
|
+
@@action_descriptions = {}
|
4
|
+
@@function_descriptions = {}
|
5
|
+
@@function_args = { }
|
6
|
+
@@namespaces = {}
|
7
|
+
@@attributes = [ ]
|
8
|
+
@@last_description = nil
|
9
|
+
@@types = { }
|
10
|
+
@@axes = { }
|
11
|
+
|
12
|
+
def self.last_description
|
13
|
+
@@last_description
|
14
|
+
end
|
15
|
+
def self.namespaces
|
16
|
+
@@namespaces
|
17
|
+
end
|
18
|
+
def self.action_descriptions
|
19
|
+
@@action_descriptions
|
20
|
+
end
|
21
|
+
def self.function_description
|
22
|
+
@@function_description
|
23
|
+
end
|
24
|
+
def self.function_args
|
25
|
+
@@function_args
|
26
|
+
end
|
27
|
+
def self.attributes
|
28
|
+
@@attributes
|
29
|
+
end
|
30
|
+
def self.types
|
31
|
+
@@types
|
32
|
+
end
|
33
|
+
def self.axes
|
34
|
+
@@axes
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.last_description=(x)
|
38
|
+
@@last_description = x
|
39
|
+
end
|
40
|
+
def self.namespaces=(x)
|
41
|
+
@@namespaces = x
|
42
|
+
end
|
43
|
+
def self.action_descriptions=(x)
|
44
|
+
@@action_descriptions = x
|
45
|
+
end
|
46
|
+
def self.function_description=(x)
|
47
|
+
@@function_description = x
|
48
|
+
end
|
49
|
+
def self.function_args=(x)
|
50
|
+
@@function_args = x
|
51
|
+
end
|
52
|
+
def self.attributes=(x)
|
53
|
+
@@attributes = x
|
54
|
+
end
|
55
|
+
def self.types=(x)
|
56
|
+
@@types = x
|
57
|
+
end
|
58
|
+
def self.axes=(x)
|
59
|
+
@@axes = x
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.included(base)
|
63
|
+
base.extend(ClassMethods)
|
64
|
+
base.module_eval do
|
65
|
+
def self.included(new_base)
|
66
|
+
super
|
67
|
+
new_base.action_descriptions.merge! self.action_descriptions
|
68
|
+
new_base.function_descriptions.merge! self.function_descriptions
|
69
|
+
new_base.function_args.merge! self.function_args
|
70
|
+
new_base.types.merge! self.types
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.find_op(t,o)
|
76
|
+
(@@types[t[0]][t[1]][:ops][o] rescue nil)
|
77
|
+
end
|
78
|
+
|
79
|
+
# returns nil if no common type can be found
|
80
|
+
def self.unify_types(ts)
|
81
|
+
# breadth-first search from all ts to find common type that
|
82
|
+
# we can convert to. We have to check all levels each time
|
83
|
+
# in case one of the initial types becomes a common type across
|
84
|
+
# all ts
|
85
|
+
|
86
|
+
return nil if ts.empty? || ts.include?(nil)
|
87
|
+
|
88
|
+
# now group by types since we only need one of each type for unification
|
89
|
+
grouped = { }
|
90
|
+
ts.each do |t|
|
91
|
+
grouped[t.join('')] = t
|
92
|
+
end
|
93
|
+
|
94
|
+
grouped = grouped.values
|
95
|
+
|
96
|
+
return grouped.first if grouped.size == 1
|
97
|
+
|
98
|
+
# now we unify based on the first two and then adding one each time
|
99
|
+
# until we unify all of them
|
100
|
+
t1 = grouped.pop
|
101
|
+
t2 = grouped.pop
|
102
|
+
ut = self._unify_types(t1, t2)
|
103
|
+
return nil if ut.nil?
|
104
|
+
self.unify_types([ ut[:t] ] + grouped)
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.type_path(from, to)
|
108
|
+
return [] if from.nil? || to.nil? || from.join('') == to.join('')
|
109
|
+
ut = self._unify_types(from, to, true)
|
110
|
+
return [] if ut.nil? || ut[:t].join('') != to.join('')
|
111
|
+
return ut[:convert]
|
112
|
+
end
|
113
|
+
|
114
|
+
## TODO: allow unification with values as well so we can have
|
115
|
+
# conversions dependent on the value
|
116
|
+
# for example: strings that look like integers can convert to integers
|
117
|
+
def self._unify_types(t1, t2, ordered = false)
|
118
|
+
return nil if t1.nil? || t2.nil?
|
119
|
+
d1 = { t1.join('') => { :t => t1, :w => 1.0, :path => [ t1 ], :convert => [ ] } }
|
120
|
+
d2 = { t2.join('') => { :t => t2, :w => 1.0, :path => [ t2 ], :convert => [ ] } }
|
121
|
+
|
122
|
+
added = true
|
123
|
+
while added
|
124
|
+
added = false
|
125
|
+
[d1, d2].each do |d|
|
126
|
+
d.keys.each do |t|
|
127
|
+
if (@@types[d[t][:t][0]][d[t][:t][1]].has_key?(:to) rescue false)
|
128
|
+
@@types[d[t][:t][0]][d[t][:t][1]][:to].each do |conv|
|
129
|
+
w = d[t][:w] * conv[:weight]
|
130
|
+
conv_key = conv[:type].join('')
|
131
|
+
if d.has_key?(conv_key)
|
132
|
+
if d[conv_key][:w] < w
|
133
|
+
d[conv_key][:w] = w
|
134
|
+
d[conv_key][:path] = d[t][:path] + [ conv[:type] ]
|
135
|
+
d[conv_key][:convert] = d[t][:convert] + [ conv[:convert] ] - [nil]
|
136
|
+
end
|
137
|
+
else
|
138
|
+
d[conv_key] = {
|
139
|
+
:t => conv[:type],
|
140
|
+
:w => w,
|
141
|
+
:path => d[t][:path] + [ conv[:type] ],
|
142
|
+
:convert => d[t][:convert] + [ conv[:convert] ] - [ nil ]
|
143
|
+
}
|
144
|
+
added = true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
# go through each type looking for :from
|
150
|
+
@@types.keys.each do |ns|
|
151
|
+
@@types[ns].each_pair do |ct, cd|
|
152
|
+
next if cd[:from].nil?
|
153
|
+
to_key = ns + ct
|
154
|
+
cd[:from].each do |conv|
|
155
|
+
next if conv[:type].nil?
|
156
|
+
from_key = conv[:type].join('')
|
157
|
+
next if !d.has_key?(from_key)
|
158
|
+
w = d[from_key][:w] * conv[:weight]
|
159
|
+
if d.has_key?(to_key)
|
160
|
+
if d[to_key][:w] < w
|
161
|
+
d[to_key][:w] = w
|
162
|
+
d[to_key][:path] = d[from_key][:path] + [ conv[:type] ]
|
163
|
+
d[to_key][:convert] = d[from_key][:convert] + [ conv[:convert] ] - [nil]
|
164
|
+
end
|
165
|
+
else
|
166
|
+
d[to_key] = {
|
167
|
+
:t => [ns, ct],
|
168
|
+
:w => w * 95 / 100, # favor to over from
|
169
|
+
:path => d[from_key][:path] + [ conv[:type] ],
|
170
|
+
:convert => d[from_key][:convert] + [ conv[:convert] ] - [ nil ]
|
171
|
+
}
|
172
|
+
added = true
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
common = d1.keys & d2.keys
|
179
|
+
if ordered && common.include?(t2.join(''))
|
180
|
+
return d1[t2.join('')]
|
181
|
+
elsif !common.empty?
|
182
|
+
return d1[common.sort_by{ |c| d1[c][:w] * d2[c][:w] / d1[c][:path].size / d2[c][:path].size }.reverse.first]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
common = d1.keys & d2.keys
|
186
|
+
if ordered && common.include?(t2.join(''))
|
187
|
+
return d1[t2.join('')]
|
188
|
+
elsif !common.empty?
|
189
|
+
return d1[common.sort_by{ |c| d1[c][:w] * d2[c][:w] / d1[c][:path].size / d2[c][:path].size }.reverse.first]
|
190
|
+
end
|
191
|
+
return nil
|
192
|
+
end
|
193
|
+
|
194
|
+
def self.with_super(s, &block)
|
195
|
+
@@super ||= [] # not thread safe :-/
|
196
|
+
@@super.unshift(s)
|
197
|
+
yield
|
198
|
+
@@super.shift
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.current_super
|
202
|
+
return nil if @@super.nil? || @@super.empty?
|
203
|
+
return @@super.first
|
204
|
+
end
|
205
|
+
|
206
|
+
def compile_action(e, c)
|
207
|
+
if self.class.method_defined? "action:#{e.name}"
|
208
|
+
send "action:#{e.name}", e, c.merge(e)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def run_function(context, nom, args, depth=0)
|
213
|
+
ret = []
|
214
|
+
|
215
|
+
#begin
|
216
|
+
case self.function_run_type(nom)
|
217
|
+
when :mapping
|
218
|
+
ret = args.flatten.collect { |a| send "fctn:#{nom}", context, a }
|
219
|
+
when :reduction
|
220
|
+
ret = send "fctn:#{nom}", context, args.flatten
|
221
|
+
else
|
222
|
+
ret = send "fctn:#{nom}", context, args
|
223
|
+
end
|
224
|
+
#rescue => e
|
225
|
+
# raise "function #{nom} raised #{e}"
|
226
|
+
#end
|
227
|
+
ret = [ ret ] unless ret.is_a?(Array)
|
228
|
+
ret = ret.flatten.collect{ |r|
|
229
|
+
if r.is_a?(Fabulator::Expr::Node)
|
230
|
+
r
|
231
|
+
elsif r.is_a?(Hash)
|
232
|
+
rr = context.root.anon_node(nil, nil)
|
233
|
+
r.each_pair do |k,v|
|
234
|
+
rrr = context.root.anon_node(v) #, self.function_return_type(nom))
|
235
|
+
rrr.name = k
|
236
|
+
rr.add_child(rrr)
|
237
|
+
end
|
238
|
+
rr
|
239
|
+
else
|
240
|
+
context.root.anon_node(r) #, self.function_return_type(nom))
|
241
|
+
end
|
242
|
+
}
|
243
|
+
ret.flatten
|
244
|
+
end
|
245
|
+
|
246
|
+
def function_return_type(name)
|
247
|
+
(self.function_descriptions[name][:returns] rescue nil)
|
248
|
+
end
|
249
|
+
|
250
|
+
def function_run_scaling(name)
|
251
|
+
(self.function_descriptions[name][:scaling] rescue nil)
|
252
|
+
end
|
253
|
+
|
254
|
+
def function_run_type(name)
|
255
|
+
(self.function_descriptions[name][:type] rescue nil)
|
256
|
+
end
|
257
|
+
|
258
|
+
def function_args
|
259
|
+
@function_args ||= { }
|
260
|
+
end
|
261
|
+
|
262
|
+
def run_filter(context, nom)
|
263
|
+
send "filter:#{nom}", context
|
264
|
+
end
|
265
|
+
|
266
|
+
def run_constraint(context, nom)
|
267
|
+
context = [ context ] unless context.is_a?(Array)
|
268
|
+
paths = [ [], [] ]
|
269
|
+
context.each do |c|
|
270
|
+
p = send("constraint:#{nom}", c)
|
271
|
+
paths[0] += p[0]
|
272
|
+
paths[1] += p[1]
|
273
|
+
end
|
274
|
+
return [ (paths[0] - paths[1]).uniq, paths[1].uniq ]
|
275
|
+
end
|
276
|
+
|
277
|
+
def action_descriptions(hash=nil)
|
278
|
+
self.class.action_descriptions hash
|
279
|
+
end
|
280
|
+
|
281
|
+
def function_descriptions(hash=nil)
|
282
|
+
self.class.function_descriptions hash
|
283
|
+
end
|
284
|
+
|
285
|
+
def function_args(hash=nil)
|
286
|
+
self.class.function_args hash
|
287
|
+
end
|
288
|
+
|
289
|
+
module ClassMethods
|
290
|
+
def inherited(subclass)
|
291
|
+
subclass.action_descriptions.reverse_merge! self.action_descriptions
|
292
|
+
subclass.function_descriptions.reverse_merge! self.function_descriptions
|
293
|
+
super
|
294
|
+
end
|
295
|
+
|
296
|
+
def action_descriptions(hash = nil)
|
297
|
+
Fabulator::ActionLib.action_descriptions[self.name] ||= (hash ||{})
|
298
|
+
end
|
299
|
+
|
300
|
+
def function_descriptions(hash = nil)
|
301
|
+
Fabulator::ActionLib.action_descriptions[self.name] ||= (hash ||{})
|
302
|
+
end
|
303
|
+
|
304
|
+
def register_namespace(ns)
|
305
|
+
Fabulator::ActionLib.namespaces[ns] = self.new
|
306
|
+
end
|
307
|
+
|
308
|
+
def register_attribute(a, options = {})
|
309
|
+
ns = nil
|
310
|
+
Fabulator::ActionLib.namespaces.each_pair do |k,v|
|
311
|
+
if v.is_a?(self)
|
312
|
+
ns = k
|
313
|
+
end
|
314
|
+
end
|
315
|
+
Fabulator::ActionLib.attributes << [ ns, a, options ]
|
316
|
+
end
|
317
|
+
|
318
|
+
def register_type(nom, options={})
|
319
|
+
ns = nil
|
320
|
+
Fabulator::ActionLib.namespaces.each_pair do |k,v|
|
321
|
+
if v.is_a?(self)
|
322
|
+
ns = k
|
323
|
+
end
|
324
|
+
end
|
325
|
+
Fabulator::ActionLib.types[ns] ||= {}
|
326
|
+
Fabulator::ActionLib.types[ns][nom] = options
|
327
|
+
|
328
|
+
function nom do |ctx, args|
|
329
|
+
args[0].collect { |i|
|
330
|
+
i.to([ ns, nom ])
|
331
|
+
}
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def axis(nom, &block)
|
336
|
+
Fabulator::ActionLib.axes[nom] = block
|
337
|
+
end
|
338
|
+
|
339
|
+
def namespaces
|
340
|
+
Fabulator::ActionLib.namespaces
|
341
|
+
end
|
342
|
+
|
343
|
+
def desc(text)
|
344
|
+
Fabulator::ActionLib.last_description = RedCloth.new(Util.strip_leading_whitespace(text)).to_html
|
345
|
+
end
|
346
|
+
|
347
|
+
def action(name, klass = nil, &block)
|
348
|
+
self.action_descriptions[name] = Fabulator::ActionLib.last_description if Fabulator::ActionLib.last_description
|
349
|
+
Fabulator::ActionLib.last_description = nil
|
350
|
+
if block
|
351
|
+
define_method("action:#{name}", &block)
|
352
|
+
elsif !klass.nil?
|
353
|
+
action(name) { |e,r|
|
354
|
+
return klass.new.compile_xml(e,r)
|
355
|
+
}
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def function(name, returns = nil, takes = nil, &block)
|
360
|
+
self.function_descriptions[name] = { :returns => returns, :takes => takes }
|
361
|
+
self.function_descriptions[name][:description] = Fabulator::ActionLib.last_description if Fabulator::ActionLib.last_description
|
362
|
+
#self.function_args[name] = { :return => returns, :takes => takes }
|
363
|
+
Fabulator::ActionLib.last_description = nil
|
364
|
+
define_method("fctn:#{name}", &block)
|
365
|
+
end
|
366
|
+
|
367
|
+
def reduction(name, opts = {}, &block)
|
368
|
+
self.function_descriptions[name] = { :type => :reduction }.merge(opts)
|
369
|
+
self.function_descriptions[name][:description] = Fabulator::ActionLib.last_description if Fabulator::ActionLib.last_description
|
370
|
+
Fabulator::ActionLib.last_description = nil
|
371
|
+
define_method("fctn:#{name}", &block)
|
372
|
+
end
|
373
|
+
|
374
|
+
def mapping(name, opts = {}, &block)
|
375
|
+
self.function_descriptions[name] = { :type => :mapping }.merge(opts)
|
376
|
+
self.function_descriptions[name][:description] = Fabulator::ActionLib.last_description if Fabulator::ActionLib.last_description
|
377
|
+
Fabulator::ActionLib.last_description = nil
|
378
|
+
define_method("fctn:#{name}", &block)
|
379
|
+
end
|
380
|
+
|
381
|
+
def function_decl(name, expr, ns)
|
382
|
+
parser = Fabulator::Expr::Parser.new
|
383
|
+
fctn_body = parser.parse(expr, ns)
|
384
|
+
|
385
|
+
function name do |ctx, args, ns|
|
386
|
+
res = nil
|
387
|
+
ctx.in_context do
|
388
|
+
args.size.times do |i|
|
389
|
+
ctx.set_var((i+1).to_s, args[i])
|
390
|
+
end
|
391
|
+
res = fctn_body.run(ctx)
|
392
|
+
end
|
393
|
+
res
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
def filter(name, &block)
|
398
|
+
define_method("filter:#{name}", &block)
|
399
|
+
end
|
400
|
+
|
401
|
+
def constraint(name, &block)
|
402
|
+
define_method("constraint:#{name}", &block)
|
403
|
+
end
|
404
|
+
|
405
|
+
def compile_actions(xml, rdf_model)
|
406
|
+
actions = [ ]
|
407
|
+
xml.each_element do |e|
|
408
|
+
ns = e.namespaces.namespace.href
|
409
|
+
#Rails.logger.info("Compiling <#{ns}><#{e.name}>")
|
410
|
+
next unless Fabulator::ActionLib.namespaces.include?(ns)
|
411
|
+
actions << (Fabulator::ActionLib.namespaces[ns].compile_action(e, rdf_model) rescue nil)
|
412
|
+
#Rails.logger.info("compile_actions: #{actions}")
|
413
|
+
end
|
414
|
+
#Rails.logger.info("compile_actions: #{actions}")
|
415
|
+
actions = actions - [ nil ]
|
416
|
+
#Rails.logger.info("compile_actions returning: #{actions}")
|
417
|
+
return actions
|
418
|
+
end
|
419
|
+
|
420
|
+
end
|
421
|
+
|
422
|
+
module Util
|
423
|
+
def self.strip_leading_whitespace(text)
|
424
|
+
text = text.dup
|
425
|
+
text.gsub!("\t", " ")
|
426
|
+
lines = text.split("\n")
|
427
|
+
leading = lines.map do |line|
|
428
|
+
unless line =~ /^\s*$/
|
429
|
+
line.match(/^(\s*)/)[0].length
|
430
|
+
else
|
431
|
+
nil
|
432
|
+
end
|
433
|
+
end.compact.min
|
434
|
+
lines.inject([]) {|ary, line| ary << line.sub(/^[ ]{#{leading}}/, "")}.join("\n")
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|