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.
Files changed (50) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +41 -0
  3. data/README.rdoc +61 -0
  4. data/Rakefile +54 -0
  5. data/lib/fabulator.rb +5 -0
  6. data/lib/fabulator/action.rb +46 -0
  7. data/lib/fabulator/action_lib.rb +438 -0
  8. data/lib/fabulator/context.rb +39 -0
  9. data/lib/fabulator/core.rb +8 -0
  10. data/lib/fabulator/core/actions.rb +514 -0
  11. data/lib/fabulator/core/actions/choose.rb +167 -0
  12. data/lib/fabulator/core/actions/for_each.rb +105 -0
  13. data/lib/fabulator/core/actions/variables.rb +52 -0
  14. data/lib/fabulator/core/constraint.rb +117 -0
  15. data/lib/fabulator/core/filter.rb +41 -0
  16. data/lib/fabulator/core/group.rb +123 -0
  17. data/lib/fabulator/core/parameter.rb +128 -0
  18. data/lib/fabulator/core/state.rb +91 -0
  19. data/lib/fabulator/core/state_machine.rb +153 -0
  20. data/lib/fabulator/core/transition.rb +164 -0
  21. data/lib/fabulator/expr.rb +37 -0
  22. data/lib/fabulator/expr/axis.rb +133 -0
  23. data/lib/fabulator/expr/axis_descendent_or_self.rb +26 -0
  24. data/lib/fabulator/expr/bin_expr.rb +178 -0
  25. data/lib/fabulator/expr/context.rb +368 -0
  26. data/lib/fabulator/expr/for_expr.rb +74 -0
  27. data/lib/fabulator/expr/function.rb +52 -0
  28. data/lib/fabulator/expr/if_expr.rb +22 -0
  29. data/lib/fabulator/expr/let_expr.rb +17 -0
  30. data/lib/fabulator/expr/literal.rb +39 -0
  31. data/lib/fabulator/expr/node.rb +216 -0
  32. data/lib/fabulator/expr/node_logic.rb +99 -0
  33. data/lib/fabulator/expr/parser.rb +1470 -0
  34. data/lib/fabulator/expr/path_expr.rb +49 -0
  35. data/lib/fabulator/expr/predicates.rb +45 -0
  36. data/lib/fabulator/expr/statement_list.rb +96 -0
  37. data/lib/fabulator/expr/step.rb +43 -0
  38. data/lib/fabulator/expr/unary_expr.rb +30 -0
  39. data/lib/fabulator/expr/union_expr.rb +21 -0
  40. data/lib/fabulator/template.rb +9 -0
  41. data/lib/fabulator/template/context.rb +51 -0
  42. data/lib/fabulator/template/parse_result.rb +153 -0
  43. data/lib/fabulator/template/parser.rb +17 -0
  44. data/lib/fabulator/template/standard_tags.rb +95 -0
  45. data/lib/fabulator/template/taggable.rb +88 -0
  46. data/lib/fabulator/version.rb +14 -0
  47. data/test/test_fabulator.rb +17 -0
  48. data/test/test_helper.rb +24 -0
  49. data/xslt/form.xsl +2161 -0
  50. metadata +182 -0
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2010-02-23
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
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,5 @@
1
+ require 'fabulator/expr'
2
+ require 'fabulator/action_lib'
3
+ require 'fabulator/action'
4
+ require 'fabulator/core'
5
+ require 'fabulator/version'
@@ -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