ruote 2.1.10 → 2.1.11

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 (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