ruote 2.1.10 → 2.1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/CHANGELOG.txt +51 -1
  2. data/CREDITS.txt +9 -0
  3. data/README.rdoc +13 -0
  4. data/Rakefile +50 -21
  5. data/TODO.txt +42 -4
  6. data/examples/pong.rb +37 -0
  7. data/lib/ruote/context.rb +19 -9
  8. data/lib/ruote/engine/process_error.rb +10 -0
  9. data/lib/ruote/engine/process_status.rb +140 -41
  10. data/lib/ruote/engine.rb +394 -27
  11. data/lib/ruote/exp/command.rb +2 -0
  12. data/lib/ruote/exp/fe_concurrence.rb +8 -0
  13. data/lib/ruote/exp/fe_concurrent_iterator.rb +3 -0
  14. data/lib/ruote/exp/fe_cursor.rb +48 -4
  15. data/lib/ruote/exp/fe_iterator.rb +40 -0
  16. data/lib/ruote/exp/fe_listen.rb +3 -3
  17. data/lib/ruote/exp/fe_participant.rb +30 -12
  18. data/lib/ruote/exp/fe_ref.rb +126 -0
  19. data/lib/ruote/exp/fe_subprocess.rb +20 -1
  20. data/lib/ruote/exp/fe_wait.rb +4 -1
  21. data/lib/ruote/exp/fe_when.rb +7 -10
  22. data/lib/ruote/exp/flowexpression.rb +23 -12
  23. data/lib/ruote/exp/ro_attributes.rb +5 -8
  24. data/lib/ruote/exp/ro_variables.rb +4 -2
  25. data/lib/ruote/fei.rb +2 -0
  26. data/lib/ruote/id/wfid_generator.rb +1 -1
  27. data/lib/ruote/log/pretty.rb +137 -0
  28. data/lib/ruote/log/storage_history.rb +1 -1
  29. data/lib/ruote/log/test_logger.rb +51 -126
  30. data/lib/ruote/log/wait_logger.rb +8 -13
  31. data/lib/ruote/parser/ruby_dsl.rb +4 -4
  32. data/lib/ruote/parser.rb +2 -2
  33. data/lib/ruote/part/block_participant.rb +1 -1
  34. data/lib/ruote/part/engine_participant.rb +1 -1
  35. data/lib/ruote/part/storage_participant.rb +27 -28
  36. data/lib/ruote/part/template.rb +8 -3
  37. data/lib/ruote/receiver/base.rb +24 -6
  38. data/lib/ruote/storage/base.rb +76 -11
  39. data/lib/ruote/storage/fs_storage.rb +10 -0
  40. data/lib/ruote/storage/hash_storage.rb +19 -8
  41. data/lib/ruote/{part → svc}/dispatch_pool.rb +3 -2
  42. data/lib/ruote/svc/dollar_sub.rb +265 -0
  43. data/lib/ruote/{error_handler.rb → svc/error_handler.rb} +6 -1
  44. data/lib/ruote/{exp → svc}/expression_map.rb +31 -37
  45. data/lib/ruote/{part → svc}/participant_list.rb +165 -25
  46. data/lib/ruote/{evt → svc}/tracker.rb +0 -0
  47. data/lib/ruote/{util → svc}/treechecker.rb +0 -0
  48. data/lib/ruote/util/look.rb +4 -1
  49. data/lib/ruote/util/ometa.rb +21 -5
  50. data/lib/ruote/{subprocess.rb → util/subprocess.rb} +0 -0
  51. data/lib/ruote/version.rb +1 -1
  52. data/lib/ruote/worker.rb +29 -69
  53. data/lib/ruote/workitem.rb +28 -1
  54. data/ruote.gemspec +26 -22
  55. data/test/functional/base.rb +3 -0
  56. data/test/functional/concurrent_base.rb +1 -0
  57. data/test/functional/crunner.sh +1 -1
  58. data/test/functional/ct_0_concurrence.rb +6 -0
  59. data/test/functional/ct_1_iterator.rb +3 -0
  60. data/test/functional/ct_2_cancel.rb +5 -0
  61. data/test/functional/eft_13_iterator.rb +39 -4
  62. data/test/functional/eft_14_cursor.rb +39 -0
  63. data/test/functional/eft_30_ref.rb +140 -0
  64. data/test/functional/eft_3_participant.rb +25 -23
  65. data/test/functional/ft_10_dollar.rb +17 -1
  66. data/test/functional/ft_14_re_apply.rb +76 -0
  67. data/test/functional/ft_1_process_status.rb +170 -29
  68. data/test/functional/ft_20_storage_participant.rb +14 -0
  69. data/test/functional/ft_24_block_participants.rb +1 -1
  70. data/test/functional/ft_26_participant_timeout.rb +93 -0
  71. data/test/functional/ft_2_errors.rb +24 -17
  72. data/test/functional/ft_30_smtp_participant.rb +7 -2
  73. data/test/functional/ft_38_participant_more.rb +15 -0
  74. data/test/functional/ft_39_wait_for.rb +34 -1
  75. data/test/functional/ft_3_participant_registration.rb +270 -2
  76. data/test/functional/ft_40_wait_logger.rb +61 -0
  77. data/test/functional/ft_42_storage_copy.rb +4 -0
  78. data/test/functional/{ft_40_participant_on_reply.rb → ft_43_participant_on_reply.rb} +17 -0
  79. data/test/functional/ft_44_var_participant.rb +35 -0
  80. data/test/functional/ft_45_participant_accept.rb +64 -0
  81. data/test/functional/ft_46_launch_single.rb +49 -0
  82. data/test/functional/ft_5_on_error.rb +39 -1
  83. data/test/functional/storage_helper.rb +7 -1
  84. data/test/test_helper.rb +1 -1
  85. data/test/unit/storage.rb +105 -32
  86. data/test/unit/ut_0_ruby_parser.rb +31 -1
  87. data/test/unit/ut_16_parser.rb +20 -0
  88. data/test/unit/ut_19_part_template.rb +11 -1
  89. data/test/unit/ut_20_composite_storage.rb +1 -1
  90. data/test/unit/ut_4_expmap.rb +1 -1
  91. data/test/unit/ut_6_condition.rb +2 -2
  92. metadata +112 -74
  93. data/lib/ruote/exp/raw.rb +0 -44
  94. data/lib/ruote/util/dollar.rb +0 -193
@@ -95,7 +95,7 @@ module Ruote
95
95
 
96
96
  def empty? (type)
97
97
 
98
- (get_many(type) == [])
98
+ (get_many(type, nil, :count => true) == 0)
99
99
  end
100
100
 
101
101
  #--
@@ -104,13 +104,34 @@ module Ruote
104
104
 
105
105
  def find_root_expression (wfid)
106
106
 
107
- get_many('expressions', /!#{wfid}$/).sort { |a, b|
108
- a['fei']['expid'] <=> b['fei']['expid']
107
+ get_many('expressions', wfid).sort_by { |fexp|
108
+ fexp['fei']['expid']
109
109
  }.select { |e|
110
110
  e['parent_id'].nil?
111
111
  }.first
112
112
  end
113
113
 
114
+ # Given all the expressions stored here, returns a sorted list of unique
115
+ # wfids (this is used in Engine#processes(opts).
116
+ #
117
+ # Understands the :skip, :limit and :descending options.
118
+ #
119
+ # This is a base implementation, different storage implementations may
120
+ # come up with different implementations (think CouchDB, which could
121
+ # provide a view for it).
122
+ #
123
+ def expression_wfids (opts)
124
+
125
+ wfids = ids('expressions').collect { |fei| fei.split('!').last }.uniq.sort
126
+
127
+ wfids = wfids.reverse if opts[:descending]
128
+
129
+ skip = opts[:skip] || 0
130
+ limit = opts[:limit] || wfids.length
131
+
132
+ wfids[skip, limit]
133
+ end
134
+
114
135
  #--
115
136
  # trackers
116
137
  #++
@@ -145,14 +166,20 @@ module Ruote
145
166
  #end
146
167
  end
147
168
 
169
+ # Places schedule in storage. Returns the id of the 'schedule' document.
170
+ # If the schedule got triggered immediately, nil is returned.
171
+ #
148
172
  def put_schedule (flavour, owner_fei, s, msg)
149
173
 
150
- if doc = prepare_schedule_doc(flavour, owner_fei, s, msg)
151
- put(doc)
152
- return doc['_id']
153
- end
174
+ doc = prepare_schedule_doc(flavour, owner_fei, s, msg)
175
+
176
+ return nil unless doc
154
177
 
155
- nil
178
+ r = put(doc)
179
+
180
+ raise "put_schedule failed" if r != nil
181
+
182
+ doc['_id']
156
183
  end
157
184
 
158
185
  def delete_schedule (schedule_id)
@@ -194,7 +221,9 @@ module Ruote
194
221
  configurations errors expressions msgs schedules variables workitems
195
222
  ].each do |type|
196
223
 
197
- get_many(type).each do |item|
224
+ ids(type).each do |id|
225
+
226
+ item = get(type, id)
198
227
 
199
228
  item.delete('_rev')
200
229
  target.put(item)
@@ -207,6 +236,18 @@ module Ruote
207
236
  counter
208
237
  end
209
238
 
239
+ # Used when doing integration tests, removes all
240
+ # msgs, schedules, errors, expressions and workitems.
241
+ #
242
+ # NOTE that it doesn't remove engine variables (danger)
243
+ #
244
+ def clear
245
+
246
+ %w[ msgs schedules errors expressions workitems ].each do |type|
247
+ purge_type!(type)
248
+ end
249
+ end
250
+
210
251
  protected
211
252
 
212
253
  # Used by put_msg
@@ -272,11 +313,35 @@ module Ruote
272
313
 
273
314
  # Returns all the ats whose due date arrived (now or earlier)
274
315
  #
275
- def filter_schedules (scheds, now)
316
+ def filter_schedules (schedules, now)
276
317
 
277
318
  now = Ruote.time_to_utc_s(now)
278
319
 
279
- scheds.select { |sched| sched['at'] <= now }
320
+ schedules.select { |sch| sch['at'] <= now }
321
+ end
322
+
323
+ ## Returns true if the doc wfid is included in the wfids passed.
324
+ ##
325
+ #def wfid_match? (doc, wfids)
326
+ # wfids.find { |wfid| doc['_id'].index(wfid) } != nil
327
+ #end
328
+
329
+ # Used by #get_many. Returns true whenever one of the keys matches the
330
+ # doc['_id']. Works with strings (_id ends with key) or regexes (_id matches
331
+ # key).
332
+ #
333
+ # It's a class method meant to be used by the various storage
334
+ # implementations.
335
+ #
336
+ def self.key_match? (keys, doc)
337
+
338
+ _id = doc.is_a?(Hash) ? doc['_id'] : doc
339
+
340
+ if keys.first.is_a?(String)
341
+ keys.find { |key| _id[-key.length..-1] == key }
342
+ else # Regexp
343
+ keys.find { |key| key.match(_id) }
344
+ end
280
345
  end
281
346
  end
282
347
  end
@@ -35,6 +35,8 @@ Rufus::Json.detect_backend
35
35
  require 'rufus/cloche'
36
36
  # gem install rufus-cloche
37
37
 
38
+ require 'ruote/storage/base'
39
+
38
40
 
39
41
  module Ruote
40
42
 
@@ -84,6 +86,12 @@ module Ruote
84
86
 
85
87
  def get_many (type, key=nil, opts={})
86
88
 
89
+ if key
90
+ key = Array(key)
91
+ key = key.map { |k| "!#{k}" } if key.first.is_a?(String)
92
+ end
93
+ # assuming /!#{wfid}$/...
94
+
87
95
  @cloche.get_many(type, key, opts)
88
96
  end
89
97
 
@@ -92,6 +100,8 @@ module Ruote
92
100
  @cloche.ids(type)
93
101
  end
94
102
 
103
+ # Purges the storage completely.
104
+ #
95
105
  def purge!
96
106
 
97
107
  FileUtils.rm_rf(@cloche.dir)
@@ -57,7 +57,6 @@ module Ruote
57
57
 
58
58
  pre = get(doc['type'], doc['_id'])
59
59
 
60
- #if pre && ( ! opts[:update_rev]) && pre['_rev'] != doc['_rev']
61
60
  if pre && pre['_rev'] != doc['_rev']
62
61
  return pre
63
62
  end
@@ -122,15 +121,25 @@ module Ruote
122
121
 
123
122
  synchronize do
124
123
 
125
- docs = key ?
126
- @h[type].values.select { |doc| doc['_id'].match(key) } :
124
+ keys = key ?
125
+ Array(key).map { |k| k.is_a?(String) ? "!#{k}" : k } : nil
126
+
127
+ docs = keys ?
128
+ @h[type].values.select { |doc|
129
+ Ruote::StorageBase.key_match?(keys, doc)
130
+ } :
127
131
  @h[type].values
128
132
 
129
- if l = opts[:limit]
130
- docs[0, l]
131
- else
132
- docs
133
- end
133
+ docs = docs.sort_by { |d| d['_id'] }
134
+
135
+ return docs.size if opts[:count]
136
+
137
+ docs = docs.reverse if opts[:descending]
138
+
139
+ skip = opts[:skip] || 0
140
+ limit = opts[:limit] || docs.size
141
+
142
+ docs[skip, limit]
134
143
  end
135
144
  end
136
145
 
@@ -141,6 +150,8 @@ module Ruote
141
150
  @h[type].keys.sort
142
151
  end
143
152
 
153
+ # Purges the storage completely.
154
+ #
144
155
  def purge!
145
156
 
146
157
  @h = %w[
@@ -57,7 +57,7 @@ module Ruote
57
57
 
58
58
  flavour = msg['flavour']
59
59
 
60
- participant = @context.plist.lookup(msg['participant_name'])
60
+ participant = @context.plist.instantiate(msg['participant'])
61
61
 
62
62
  begin
63
63
  participant.cancel(Ruote::FlowExpressionId.new(msg['fei']), flavour)
@@ -73,7 +73,8 @@ module Ruote
73
73
 
74
74
  def dispatch (msg)
75
75
 
76
- participant = @context.plist.lookup(msg['participant_name'])
76
+ participant = @context.plist.lookup(
77
+ msg['participant'] || msg['participant_name'], msg['workitem'])
77
78
 
78
79
  if participant.respond_to?(:do_not_thread) && participant.do_not_thread
79
80
  do_dispatch(participant, msg)
@@ -0,0 +1,265 @@
1
+ #--
2
+ # Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ # Made in Japan.
23
+ #++
24
+
25
+
26
+ require 'rufus/dollar' # gem 'rufus-dollar'
27
+ require 'ruote/svc/treechecker'
28
+ require 'ruote/util/lookup'
29
+
30
+
31
+ module Ruote
32
+
33
+ #
34
+ # This service is in charge of extrapolating strings like
35
+ # "${f:nada} == ${f:y}".
36
+ #
37
+ # It relies on the rufus-dollar gem.
38
+ #
39
+ # It's OK to override this service with your own.
40
+ #
41
+ class DollarSubstitution
42
+
43
+ def initialize (context)
44
+
45
+ @context = context
46
+ end
47
+
48
+ # Performs 'dollar substitution' on a piece of text with as input
49
+ # a flow expression and a workitem (fields and variables).
50
+ #
51
+ # With help from Nick Petrella (2008/03/20)
52
+ #
53
+ def s (text, flow_expression, workitem)
54
+
55
+ if text.is_a?(String)
56
+
57
+ Rufus.dsub(text, dict_class.new(flow_expression, workitem))
58
+
59
+ elsif text.is_a?(Array)
60
+
61
+ text.collect { |e| s(e, flow_expression, workitem) }
62
+
63
+ elsif text.is_a?(Hash)
64
+
65
+ text.inject({}) { |h, (k, v)|
66
+
67
+ h[s(k, flow_expression, workitem)] = s(v, flow_expression, workitem)
68
+ h
69
+ }
70
+
71
+ else
72
+
73
+ text
74
+ end
75
+ end
76
+
77
+ # This method is public, for easy overriding. This implementation returns
78
+ # Ruote::Dollar::Dict whose instances are used to extrapolate dollar
79
+ # strings like "${f:customer}" or "${r:Time.now.to_s}/${f:year_target}"
80
+ #
81
+ def dict_class
82
+
83
+ ::Ruote::Dollar::Dict
84
+ end
85
+ end
86
+
87
+
88
+ #
89
+ # A mini-namespace Ruote::Dollar for Dict and RubyContext, just to separate
90
+ # them from the rest of Ruote.
91
+ #
92
+ module Dollar
93
+
94
+ #
95
+ # Wrapping a flow expression and the current workitem as a
96
+ # Hash-like object ready for lookup at substitution time.
97
+ #
98
+ class Dict
99
+
100
+ attr_reader :fexp
101
+ attr_reader :workitem
102
+
103
+ def initialize (flowexpression, workitem)
104
+
105
+ @fexp = flowexpression
106
+ @workitem = workitem
107
+ end
108
+
109
+ def [] (key)
110
+
111
+ return @fexp.fei.to_storage_id if key == 'fei'
112
+ return @fexp.fei.wfid if key == 'wfid'
113
+ return @fexp.fei.sub_wfid if key == 'sub_wfid'
114
+ return @fexp.fei.expid if key == 'expid'
115
+
116
+ pr, k = extract_prefix(key)
117
+
118
+ # stage 0
119
+
120
+ v = lookup(pr[0, 1], k)
121
+
122
+ return v if v != nil
123
+
124
+ # stage 1
125
+
126
+ return '' if pr.size < 2
127
+
128
+ lookup(pr[1, 1], k)
129
+ end
130
+
131
+ def []= (key, value)
132
+
133
+ pr, k = extract_prefix(key)
134
+ pr = pr[0, 1]
135
+
136
+ if pr == 'f'
137
+
138
+ @workitem.set_attribute(k, value)
139
+
140
+ elsif @fexp
141
+
142
+ @fexp.set_variable(k, value)
143
+ end
144
+ end
145
+
146
+ def has_key? (key)
147
+
148
+ pr, k = extract_prefix(key)
149
+
150
+ return true if pr == 'r'
151
+
152
+ (self[key] != nil)
153
+ end
154
+
155
+ protected
156
+
157
+ def lookup (pr, key)
158
+
159
+ case pr
160
+ when 'v' then @fexp.lookup_variable(key)
161
+ when 'f' then Ruote.lookup(@workitem['fields'], key)
162
+ when 'r' then ruby_eval(key)
163
+ else nil
164
+ end
165
+ end
166
+
167
+ def extract_prefix (key)
168
+
169
+ i = key.index(':')
170
+
171
+ return [ 'f', key ] if not i
172
+ # 'f' is the default prefix (field, not variable)
173
+
174
+ pr = key[0..i-1] # until ':'
175
+ pr = pr[0, 2] # the first two chars
176
+
177
+ pr = pr[0, 1] unless (pr == 'vf') or (pr == 'fv')
178
+
179
+ [ pr, key[i+1..-1] ]
180
+ end
181
+
182
+ # TODO : rdoc me
183
+ #
184
+ def ruby_eval (ruby_code)
185
+
186
+ return '' if @fexp.context['ruby_eval_allowed'] != true
187
+
188
+ @fexp.context.treechecker.check(ruby_code)
189
+
190
+ RubyContext.new(self).instance_eval(ruby_code)
191
+ end
192
+ end
193
+
194
+ # Dict uses this RubyContext class to evaluate ruby code. The method
195
+ # of this instance are directly visible to "${r:ruby_code}" ruby code.
196
+ #
197
+ class RubyContext < Ruote::BlankSlate
198
+
199
+ attr_reader :workitem
200
+
201
+ def initialize (dict)
202
+
203
+ @dict = dict
204
+ @workitem = Ruote::Workitem.new(@dict.workitem)
205
+ end
206
+
207
+ # The FlowExpression for which the rendering/substitution is occurring.
208
+ #
209
+ def flow_expression
210
+ @dict.fexp
211
+ end
212
+ alias fe flow_expression
213
+ alias fexp flow_expression
214
+
215
+ # The FlowExpressionId of the expression for which the
216
+ # rendering/substitution is occurring.
217
+ #
218
+ def fei
219
+ @dict.fexp.fei
220
+ end
221
+
222
+ alias wi workitem
223
+
224
+ # The engine_id, if any.
225
+ #
226
+ def engine_id
227
+ @dict.fexp.context.engine_id
228
+ end
229
+
230
+ # This 'd' function can be called from inside ${r:...} notations.
231
+ #
232
+ # pdef = Ruote.process_definition do
233
+ # sequence do
234
+ # set 'f:toto' => 'person'
235
+ # echo "${r:d('f:toto')}"
236
+ # end
237
+ # end
238
+ #
239
+ # will yield "person".
240
+ #
241
+ def d (s)
242
+
243
+ Rufus.dsub("${#{s}}", @dict)
244
+ end
245
+
246
+ # Given a workitem with the field "newspaper" set to "NYT",
247
+ # "${r:newspaper}" will eval to "NYT".
248
+ #
249
+ # If the field "cars" hold the value [ "bmw", "volkswagen" ],
250
+ # "${r:cars[0]}" will eval to "bmw".
251
+ #
252
+ # Else the regular NoMethodError will be raised.
253
+ #
254
+ def method_missing (m, *args)
255
+
256
+ if args.length < 1 && v = @workitem.fields[m.to_s]
257
+ return v
258
+ end
259
+
260
+ super
261
+ end
262
+ end
263
+ end
264
+ end
265
+
@@ -45,7 +45,8 @@ module Ruote
45
45
  def msg_handle (msg, exception)
46
46
 
47
47
  fexp = Ruote::Exp::FlowExpression.fetch(
48
- @context, msg['fei'] || msg['workitem']['fei'])
48
+ @context, msg['fei'] || msg['workitem']['fei']
49
+ ) rescue nil
49
50
 
50
51
  handle(msg, fexp, exception)
51
52
  end
@@ -102,6 +103,10 @@ module Ruote
102
103
 
103
104
  @context.storage.put_msg(
104
105
  'error_intercepted',
106
+ 'error_class' => exception.class.name,
107
+ 'error_message' => exception.message,
108
+ 'error_backtrace' => exception.backtrace,
109
+ # for backward compatibility
105
110
  'message' => exception.inspect,
106
111
  'wfid' => wfid,
107
112
  'msg' => msg)
@@ -30,16 +30,13 @@ end
30
30
  end
31
31
 
32
32
  require 'ruote/exp/flowexpression'
33
- require 'ruote/exp/raw'
34
33
 
35
34
 
36
- exppath = File.dirname(__FILE__)
35
+ exppath = File.join(File.dirname(__FILE__), '..', 'exp')
37
36
 
38
- Dir.new(exppath).entries.select { |p|
39
- p.match(/^fe_.*\.rb$/)
40
- }.each { |p|
41
- require exppath + '/' + p
42
- }
37
+ Dir.new(exppath).entries.each do |pa|
38
+ require(File.join('ruote', 'exp', pa)) if pa.match(/^fe_.*\.rb$/)
39
+ end
43
40
 
44
41
 
45
42
  module Ruote
@@ -48,41 +45,38 @@ module Ruote
48
45
  # Mapping from expression names (sequence, concurrence, ...) to expression
49
46
  # classes (Ruote::SequenceExpression, Ruote::ConcurrenceExpression, ...)
50
47
  #
48
+ # Requiring this ruote/svc/expression_map.rb file will automatically load
49
+ # all the expressions in ruote/exp/fe_*.rb.
50
+ #
51
+ # When the ExpressionMap is
52
+ # instantiated by the engine, it will look at the Ruote::Exp namespace
53
+ # and register as expression any constant in there whose name ends with
54
+ # "Expression", like "SequenceExpression" or "ParticipantExpression".
55
+ #
56
+ # So adding expressions to ruote should be as simple as making sure the
57
+ # engine sees your classes under Ruote::Exp before it instantiates this
58
+ # expression map (so that the expression map will automatically register
59
+ # your expressions).
60
+ #
51
61
  class ExpressionMap
52
62
 
63
+ # Will load any expression in the Ruote::Exp:: namespace and map
64
+ # its names to its class.
65
+ #
53
66
  def initialize (worker)
54
67
 
55
68
  @map = {}
56
- add(Ruote::Exp::RawExpression)
57
- add(Ruote::Exp::DefineExpression)
58
- add(Ruote::Exp::SequenceExpression)
59
- add(Ruote::Exp::EchoExpression)
60
- add(Ruote::Exp::ParticipantExpression)
61
- add(Ruote::Exp::SetExpression)
62
- add(Ruote::Exp::SubprocessExpression)
63
- add(Ruote::Exp::ConcurrenceExpression)
64
- add(Ruote::Exp::ConcurrentIteratorExpression)
65
- add(Ruote::Exp::ForgetExpression)
66
- add(Ruote::Exp::UndoExpression)
67
- add(Ruote::Exp::RedoExpression)
68
- add(Ruote::Exp::CancelProcessExpression)
69
- add(Ruote::Exp::WaitExpression)
70
- add(Ruote::Exp::ListenExpression)
71
- add(Ruote::Exp::CommandExpression)
72
- add(Ruote::Exp::IteratorExpression)
73
- add(Ruote::Exp::CursorExpression)
74
- add(Ruote::Exp::IfExpression)
75
- add(Ruote::Exp::EqualsExpression)
76
- add(Ruote::Exp::ReserveExpression)
77
- add(Ruote::Exp::SaveExpression)
78
- add(Ruote::Exp::RestoreExpression)
79
- add(Ruote::Exp::NoOpExpression)
80
- add(Ruote::Exp::ApplyExpression)
81
- add(Ruote::Exp::AddBranchesExpression)
82
- add(Ruote::Exp::ErrorExpression)
83
- add(Ruote::Exp::IncExpression)
84
- add(Ruote::Exp::WhenExpression)
85
- add(Ruote::Exp::CronExpression)
69
+
70
+ Ruote::Exp.constants.each do |con|
71
+
72
+ con = con.to_s
73
+ next unless con.match(/Expression$/)
74
+
75
+ cla = Ruote::Exp.const_get(con)
76
+ next unless cla.respond_to?(:expression_names)
77
+
78
+ add(cla)
79
+ end
86
80
  end
87
81
 
88
82
  # Returns the expression class for the given expression name