fabulator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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