ruote 2.1.6 → 2.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/CHANGELOG.txt +14 -0
  2. data/CREDITS.txt +4 -3
  3. data/Rakefile +4 -3
  4. data/TODO.txt +9 -6
  5. data/lib/ruote/context.rb +29 -13
  6. data/lib/ruote/engine.rb +14 -0
  7. data/lib/ruote/exp/flowexpression.rb +7 -0
  8. data/lib/ruote/fei.rb +15 -7
  9. data/lib/ruote/id/wfid_generator.rb +3 -0
  10. data/lib/ruote/log/fs_history.rb +3 -3
  11. data/lib/ruote/log/storage_history.rb +13 -7
  12. data/lib/ruote/log/test_logger.rb +6 -0
  13. data/lib/ruote/parser.rb +12 -3
  14. data/lib/ruote/parser/ruby_dsl.rb +53 -0
  15. data/lib/ruote/part/participant_list.rb +10 -0
  16. data/lib/ruote/part/storage_participant.rb +74 -3
  17. data/lib/ruote/storage/base.rb +9 -6
  18. data/lib/ruote/storage/hash_storage.rb +7 -2
  19. data/lib/ruote/util/misc.rb +4 -0
  20. data/lib/ruote/version.rb +1 -1
  21. data/lib/ruote/workitem.rb +35 -48
  22. data/ruote.gemspec +12 -9
  23. data/test/README.rdoc +3 -3
  24. data/test/functional/eft_18_concurrent_iterator.rb +8 -11
  25. data/test/functional/eft_24_add_branches.rb +18 -6
  26. data/test/functional/eft_27_inc.rb +2 -1
  27. data/test/functional/eft_6_concurrence.rb +9 -4
  28. data/test/functional/ft_20_storage_participant.rb +63 -29
  29. data/test/functional/ft_24_block_participants.rb +6 -0
  30. data/test/functional/ft_30_smtp_participant.rb +2 -2
  31. data/test/functional/ft_32_fs_history.rb +15 -10
  32. data/test/functional/ft_36_storage_history.rb +3 -3
  33. data/test/functional/ft_3_participant_registration.rb +8 -0
  34. data/test/functional/storage_helper.rb +2 -0
  35. data/test/functional/test.rb +9 -2
  36. data/test/unit/storage.rb +2 -1
  37. data/test/unit/test.rb +18 -2
  38. data/test/unit/ut_0_ruby_parser.rb +7 -0
  39. data/test/unit/ut_16_parser.rb +1 -1
  40. data/test/unit/ut_1_fei.rb +60 -2
  41. data/test/unit/ut_3_wait_logger.rb +2 -0
  42. data/test/unit/ut_7_workitem.rb +29 -2
  43. metadata +15 -4
@@ -2,6 +2,20 @@
2
2
  = ruote - CHANGELOG.txt
3
3
 
4
4
 
5
+ == ruote - 2.1.7 released 2010/02/15
6
+
7
+ - now works on WinXP, Ruby 1.8.7
8
+ - reformed msgs ids and adapted Ruote::StorageHistory
9
+ - engine.register_participant(x, Ruote::StorageParticipant) now returning
10
+ a instance of the participant, for easy query
11
+ - StorageParticipant, added #query(criteria), thanks Torsten and Brett
12
+ - Ruote::Workitem #lookup('deep.field') and #set_field('deep.field') are back
13
+ - Ruote::Workitem added == and hash (list.uniq friendly), thanks Brett
14
+ - Engine#configure(key, value), thanks Torsten
15
+ - Ruote.to_tree (lightweight version of Ruote.process_definition)
16
+ - ParticipantList#names, thanks Kenneth
17
+
18
+
5
19
  == ruote - 2.1.6 released 2010/02/08
6
20
 
7
21
  - welcoming ruote-dm (datamapper persistency)
@@ -8,14 +8,15 @@ Ruote (OpenWFEru) is an open source Ruby workflow (and sometimes BPM) engine.
8
8
  The main project team
9
9
  ---------------------
10
10
 
11
- John Mettraux http://jmettraux.wordpress.com
12
- Kenneth Kalmer http://www.opensourcery.co.za
11
+ John Mettraux - http://jmettraux.wordpress.com
12
+ Kenneth Kalmer - http://www.opensourcery.co.za
13
+ Torsten Schoenebaum - http://github.com/tosch
13
14
 
14
15
 
15
16
  Contributors
16
17
  ------------
17
18
 
18
- Torsten Schoenebaum - ActiveResourceParticipant and much more
19
+ Brett Anthoine - http://github.com/anb
19
20
  Matt Nichols - http://github.com/mattnichols
20
21
  Nicholas Faiz - http://github.com/biv
21
22
  Chris Beer - http://github.com/cbeer
data/Rakefile CHANGED
@@ -19,12 +19,12 @@ ruote is an open source ruby workflow engine.
19
19
  }
20
20
  gem.email = 'jmettraux@gmail.com'
21
21
  gem.homepage = 'http://ruote.rubyforge.org'
22
- gem.authors = [ 'John Mettraux', 'Kenneth Kalmer' ]
22
+ gem.authors = [ 'John Mettraux', 'Kenneth Kalmer', 'Torsten Schoenebaum' ]
23
23
  gem.rubyforge_project = 'ruote'
24
24
  gem.test_file = 'test/test.rb'
25
25
 
26
- gem.add_dependency 'rufus-json'
27
- gem.add_dependency 'rufus-cloche', '>= 0.1.13'
26
+ gem.add_dependency 'rufus-json', '>= 0.2.0'
27
+ gem.add_dependency 'rufus-cloche', '>= 0.1.14'
28
28
  gem.add_dependency 'rufus-dollar'
29
29
  gem.add_dependency 'rufus-lru'
30
30
  gem.add_dependency 'rufus-mnemo', '>= 1.1.0'
@@ -35,6 +35,7 @@ ruote is an open source ruby workflow engine.
35
35
  gem.add_development_dependency 'yard'
36
36
  gem.add_development_dependency 'builder'
37
37
  gem.add_development_dependency 'mailtrap'
38
+ gem.add_development_dependency 'jeweler'
38
39
 
39
40
  # Gem::Specification http://www.rubygems.org/read/chapter/20
40
41
  end
data/TODO.txt CHANGED
@@ -184,6 +184,13 @@
184
184
  [o] process_status.to_dot
185
185
  [o] EngineParticipant : don't wait in case of forget (reply could NEVER come !)
186
186
  [x] align :forget behaviour on EngineParticipant forget... OK as it is
187
+ [o] engine.re_apply(fei, wi) (thanks Torsten)... :wi => x, :tree => y...
188
+ [o] ruote-dm 2.1
189
+ [o] :tree => Ruote.to_tree { participant 'alpha' }
190
+ [o] implement == eql? hash for workitem
191
+ [o] StorageParticipant#query(wfid, participant_name, {fields})
192
+ [x] break fs_history, prepare for dm_history
193
+ [o] part = engine.register_participant :alpha, StorageParticipant should work...
187
194
 
188
195
  [ ] exp : exp (restricted form of eval ?)
189
196
  [ ] exp : case (is it necessary ?)
@@ -264,8 +271,6 @@
264
271
 
265
272
  [ ] engine.force_reply_to_parent(fei) ?
266
273
 
267
- [ ] break fs_history, prepare for dm_history
268
-
269
274
  [ ] :on_timeout => :rewind (break, jump to x)...
270
275
  [ ] rewind 'x' where x is a tagname (command x)
271
276
 
@@ -293,14 +298,12 @@
293
298
  [ ] shell ? irb ? Shell.new(storage)
294
299
  [ ] focus on fulldup or json.dup (via fulldup ?)
295
300
 
296
- [ ] ruote-dm 2.1
297
-
298
301
  [ ] implement pause engine
299
302
  [ ] implement pause process
300
303
 
301
304
  [ ] engine.on_error = 'participant_name'
302
305
 
303
- [ ] "open days" plugin / service / lambda ? is_open_day?(d)
306
+ [ ] "business days" plugin
304
307
 
305
- [ ] engine.re_apply(fei, wi) (thanks Torsten)... :wi => x, :tree => y...
308
+ [ ] issue with ruote-kit and inpa participants...
306
309
 
@@ -34,6 +34,8 @@ module Ruote
34
34
  #
35
35
  class Context
36
36
 
37
+ SERVICE_PREFIX = /^s\_/
38
+
37
39
  attr_reader :storage
38
40
  attr_accessor :worker
39
41
  attr_accessor :engine
@@ -41,7 +43,6 @@ module Ruote
41
43
  def initialize (storage, worker_or_engine)
42
44
 
43
45
  @storage = storage
44
- @conf = default_conf.merge(@storage.get_configuration('engine') || {})
45
46
 
46
47
  @worker, @engine = if worker_or_engine.kind_of?(Ruote::Engine)
47
48
  [ worker_or_engine.worker, worker_or_engine ]
@@ -54,29 +55,37 @@ module Ruote
54
55
 
55
56
  def engine_id
56
57
 
57
- @conf['engine_id'] || 'engine'
58
+ get_conf['engine_id'] || 'engine'
58
59
  end
59
60
 
60
61
  def [] (key)
61
62
 
62
- @conf[key]
63
+ SERVICE_PREFIX.match(key) ? @services[key] : get_conf[key]
63
64
  end
64
65
 
65
66
  def []= (key, value)
66
67
 
67
- @conf[key] = value
68
+ raise(
69
+ ArgumentError.new('use context#add_service to register services')
70
+ ) if SERVICE_PREFIX.match(key)
71
+
72
+ cf = get_conf
73
+ cf[key] = value
74
+ @storage.put(cf)
75
+
76
+ value
68
77
  end
69
78
 
70
79
  def keys
71
80
 
72
- @conf.keys
81
+ get_conf.keys
73
82
  end
74
83
 
75
84
  def add_service (key, *args)
76
85
 
77
86
  path, klass, opts = args
78
87
 
79
- key = "s_#{key}" unless key.match(/^s\_/)
88
+ key = "s_#{key}" unless SERVICE_PREFIX.match(key)
80
89
 
81
90
  service = if klass
82
91
 
@@ -85,13 +94,13 @@ module Ruote
85
94
  aa = [ self ]
86
95
  aa << opts if opts
87
96
 
88
- @conf[key] = Ruote.constantize(klass).new(*aa)
97
+ @services[key] = Ruote.constantize(klass).new(*aa)
89
98
  else
90
99
 
91
- @conf[key] = path
100
+ @services[key] = path
92
101
  end
93
102
 
94
- self.class.class_eval %{ def #{key[2..-1]}; @conf['#{key}']; end }
103
+ self.class.class_eval %{ def #{key[2..-1]}; @services['#{key}']; end }
95
104
 
96
105
  service
97
106
  end
@@ -101,7 +110,7 @@ module Ruote
101
110
  @storage.shutdown if @storage.respond_to?(:shutdown)
102
111
  @worker.shutdown if @worker
103
112
 
104
- @conf.values.each do |s|
113
+ @services.values.each do |s|
105
114
 
106
115
  s.shutdown if s.respond_to?(:shutdown)
107
116
  end
@@ -109,13 +118,20 @@ module Ruote
109
118
 
110
119
  protected
111
120
 
121
+ def get_conf
122
+
123
+ @storage.get_configuration('engine') || {}
124
+ end
125
+
112
126
  def initialize_services
113
127
 
114
- @conf.keys.each do |key|
128
+ @services = {}
129
+
130
+ default_conf.merge(get_conf).each do |key, value|
115
131
 
116
- next unless key.match(/^s\_/)
132
+ next unless SERVICE_PREFIX.match(key)
117
133
 
118
- add_service(key, *@conf[key])
134
+ add_service(key, *value)
119
135
  end
120
136
  end
121
137
 
@@ -319,6 +319,20 @@ module Ruote
319
319
 
320
320
  @context.add_service(name, path_or_instance, classname, opts)
321
321
  end
322
+
323
+ # Sets a configuration option. Examples:
324
+ #
325
+ # # allow remote workflow definitions (for subprocesses or when launching
326
+ # # processes)
327
+ # @engine.configure('remote_definition_allowed', true)
328
+ #
329
+ # # allow ruby_eval
330
+ # @engine.configure('ruby_eval_allowed', true)
331
+ #
332
+ def configure (config_key, value)
333
+
334
+ @context[config_key] = value
335
+ end
322
336
  end
323
337
 
324
338
  #
@@ -507,6 +507,10 @@ module Ruote::Exp
507
507
  tree[2]
508
508
  end
509
509
 
510
+ # A tiny class-bound counter used when generating subprocesses ids.
511
+ #
512
+ @@sub_wfid_counter = -1
513
+
510
514
  # Generates a sub_wfid, without hitting storage.
511
515
  #
512
516
  # There's a better implementation for sure...
@@ -517,6 +521,9 @@ module Ruote::Exp
517
521
  $$, Time.now.to_f.to_s, self.hash.to_s, @h['fei'].inspect
518
522
  ].join('-').hash
519
523
 
524
+ @@sub_wfid_counter = (@@sub_wfid_counter + 1) % 1000
525
+ i = i * 1000 + (@@sub_wfid_counter)
526
+
520
527
  (i < 0 ? "1#{i * -1}" : "0#{i}").to_s
521
528
  end
522
529
 
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2009, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -39,7 +39,20 @@ module Ruote
39
39
  end
40
40
 
41
41
  #
42
- # TODO : document me
42
+ # The FlowExpressionId (fei for short) is an process expression identifier.
43
+ # Each expression when instantiated gets a unique fei.
44
+ #
45
+ # Feis are also used in workitems, where the fei is the fei of the
46
+ # [participant] expression that emitted the workitem.
47
+ #
48
+ # Feis can thus indicate the position of a workitem in a process tree.
49
+ #
50
+ # Feis contain four pieces of information :
51
+ #
52
+ # * wfid : workflow instance id, the identifier for the process instance
53
+ # * sub_wfid : the identifier for the sub process within the main instance
54
+ # * expid : the expression id, where in the process tree
55
+ # * engine_id : only relevant in multi engine scenarii (defaults to 'engine')
43
56
  #
44
57
  class FlowExpressionId
45
58
 
@@ -66,12 +79,10 @@ module Ruote
66
79
  end
67
80
 
68
81
  def to_storage_id
69
-
70
82
  "#{@h['expid']}!#{@h['sub_wfid']}!#{@h['wfid']}"
71
83
  end
72
84
 
73
85
  def self.to_storage_id (hfei)
74
-
75
86
  "#{hfei['expid']}!#{hfei['sub_wfid']}!#{hfei['wfid']}"
76
87
  end
77
88
 
@@ -88,12 +99,10 @@ module Ruote
88
99
  # '0_5_7', the child_id will be '7'.
89
100
  #
90
101
  def child_id
91
-
92
102
  h.expid.split(CHILD_SEP).last.to_i
93
103
  end
94
104
 
95
105
  def hash
96
-
97
106
  to_storage_id.hash
98
107
  end
99
108
 
@@ -117,7 +126,6 @@ module Ruote
117
126
  # Returns child_id... For an expid of '0_1_4', this will be 4.
118
127
  #
119
128
  def self.child_id (h)
120
-
121
129
  h['expid'].split(CHILD_SEP).last.to_i
122
130
  end
123
131
 
@@ -47,7 +47,10 @@ module Ruote
47
47
 
48
48
  def get_raw
49
49
 
50
+ lraw = @last['raw'] + 0.01
51
+
50
52
  raw = Time.now.utc
53
+ while raw.to_f <= lraw; raw = raw + 0.01; end
51
54
 
52
55
  @last['raw'] = raw.to_f
53
56
 
@@ -122,7 +122,7 @@ module Ruote
122
122
  #
123
123
  def by_date (date)
124
124
 
125
- date = Time.parse(date.to_s).strftime('%F')
125
+ date = Time.parse(date.to_s).strftime('%Y-%m-%d')
126
126
 
127
127
  lines = File.readlines(File.join(@path, "history_#{date}.json")) rescue []
128
128
 
@@ -158,7 +158,7 @@ module Ruote
158
158
  rotate_if_necessary
159
159
 
160
160
  @file.puts(Rufus::Json.encode(
161
- [ "#{@last.strftime('%F %T')}.#{"%06d" % @last.usec}", msg ]))
161
+ [ "#{@last.strftime('%Y-%m-%d %T')}.#{"%06d" % @last.usec}", msg ]))
162
162
  @file.flush
163
163
  end
164
164
 
@@ -173,7 +173,7 @@ module Ruote
173
173
 
174
174
  @file.close rescue nil
175
175
 
176
- fn = [ 'history', @last.strftime('%F') ].join('_') + '.json'
176
+ fn = [ 'history', @last.strftime('%Y-%m-%d') ].join('_') + '.json'
177
177
 
178
178
  @file = File.open(File.join(@path, fn), 'a')
179
179
  end
@@ -34,6 +34,8 @@ module Ruote
34
34
  #
35
35
  class StorageHistory
36
36
 
37
+ DATE_REGEX = /!(\d{4}-\d{2}-\d{2})!/
38
+
37
39
  def initialize (context, options={})
38
40
 
39
41
  @context = context
@@ -60,17 +62,20 @@ module Ruote
60
62
 
61
63
  ids = @context.storage.ids('history')
62
64
 
63
- first = Time.parse("#{ids.first.split('!')[0]} 00:00:00 UTC")
64
- last = Time.parse("#{ids.last.split('!')[0]} 00:00:00 UTC") + 24 * 3600
65
+ fm = DATE_REGEX.match(ids.first)[1]
66
+ lm = DATE_REGEX.match(ids.last)[1]
67
+
68
+ first = Time.parse("#{fm} 00:00:00 UTC")
69
+ last = Time.parse("#{lm} 00:00:00 UTC") + 24 * 3600
65
70
 
66
71
  [ first, last ]
67
72
  end
68
73
 
69
74
  def by_date (date)
70
75
 
71
- date = Time.parse(date.to_s).strftime('%F')
76
+ date = Time.parse(date.to_s).strftime('%Y-%m-%d')
72
77
 
73
- @context.storage.get_many('history', /^#{date}!/)
78
+ @context.storage.get_many('history', /!#{date}!/)
74
79
  end
75
80
 
76
81
  #def history_to_tree (wfid)
@@ -101,12 +106,13 @@ module Ruote
101
106
  msg['wfid'] || 'no_wfid'
102
107
  end
103
108
 
104
- t = Time.parse(msg['put_at'])
109
+ _id = msg['_id']
110
+ msg['original_id'] = _id
111
+ msg['_id'] = "#{_id}!#{si}"
105
112
 
106
- msg['original_id'] = msg['_id']
107
- msg['_id'] = "#{t.strftime('%F')}!#{t.to_i}_#{"%06d" % t.usec}!#{si}"
108
113
  msg['type'] = 'history'
109
114
  msg['original_put_at'] = msg['put_at']
115
+
110
116
  msg.delete('_rev')
111
117
 
112
118
  @context.storage.put(msg)
@@ -148,6 +148,11 @@ module Ruote
148
148
  interest = interest - 1
149
149
  @waiting = [ @waiting.first, interest ]
150
150
 
151
+ #@debug ||= {}
152
+ #c = @debug[action] ||= 0
153
+ #@debug[action] = c+1
154
+ #p [ interest, @debug ]
155
+
151
156
  (interest < 1)
152
157
 
153
158
  else # wfid
@@ -194,6 +199,7 @@ module Ruote
194
199
 
195
200
  def color (mod, s, clear=false)
196
201
 
202
+ return s if Ruote::WIN
197
203
  return s unless STDOUT.tty?
198
204
 
199
205
  "[#{mod}m#{s}#{clear ? '' : "[#{@color}m"}"
@@ -35,6 +35,8 @@ module Ruote
35
35
  #
36
36
  # A process definition parser.
37
37
  #
38
+ # Can parse XML, JSON, Ruby (and more) process definition representations.
39
+ #
38
40
  class Parser
39
41
 
40
42
  def initialize (context)
@@ -55,11 +57,9 @@ module Ruote
55
57
 
56
58
  if definition.index("\n") == nil
57
59
 
58
- u = URI.parse(definition)
59
-
60
60
  raise ArgumentError.new(
61
61
  "remote process definitions are not allowed"
62
- ) if u.scheme != nil && @context['remote_definition_allowed'] != true
62
+ ) if Ruote::Parser.remote?(definition) && @context['remote_definition_allowed'] != true
63
63
 
64
64
  return parse(open(definition).read)
65
65
  end
@@ -147,6 +147,15 @@ module Ruote
147
147
  tree.to_json
148
148
  end
149
149
 
150
+ # Returns true if the defintion is a remote URI
151
+ #
152
+ def self.remote? (definition)
153
+
154
+ u = URI.parse(definition)
155
+
156
+ (u.scheme != nil) && ( ! ('A'..'Z').include?(u.scheme))
157
+ end
158
+
150
159
  protected
151
160
 
152
161
  # Evaluates the ruby string in the code, but at fist, thanks to the