openwferu 0.9.10.653 → 0.9.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 (87) hide show
  1. data/README.txt +65 -33
  2. data/examples/README.txt +4 -0
  3. data/examples/flowtracing.rb +2 -0
  4. data/examples/homeworkreview.rb +2 -0
  5. data/examples/mano_tracker.rb +2 -0
  6. data/examples/openwferu.rb +3 -1
  7. data/examples/quotereporter.rb +2 -0
  8. data/examples/scheduler_cron_usage.rb +3 -1
  9. data/examples/scheduler_usage.rb +3 -1
  10. data/lib/openwfe/engine/engine.rb +15 -5
  11. data/lib/openwfe/expool/expressionpool.rb +30 -4
  12. data/lib/openwfe/expool/journal.rb +16 -18
  13. data/lib/openwfe/expool/journal_replay.rb +56 -8
  14. data/lib/openwfe/expool/wfidgen.rb +17 -8
  15. data/lib/openwfe/expressions/condition.rb +41 -26
  16. data/lib/openwfe/expressions/expressionmap.rb +7 -0
  17. data/lib/openwfe/expressions/fe_cancel.rb +1 -1
  18. data/lib/openwfe/expressions/fe_concurrence.rb +14 -5
  19. data/lib/openwfe/expressions/fe_cron.rb +3 -1
  20. data/lib/openwfe/expressions/fe_cursor.rb +1 -1
  21. data/lib/openwfe/expressions/fe_fqv.rb +58 -0
  22. data/lib/openwfe/expressions/fe_if.rb +1 -1
  23. data/lib/openwfe/expressions/fe_iterator.rb +5 -0
  24. data/lib/openwfe/expressions/fe_listen.rb +224 -0
  25. data/lib/openwfe/expressions/fe_misc.rb +32 -9
  26. data/lib/openwfe/expressions/fe_participant.rb +26 -6
  27. data/lib/openwfe/expressions/fe_sleep.rb +5 -7
  28. data/lib/openwfe/expressions/fe_timeout.rb +127 -0
  29. data/lib/openwfe/expressions/fe_when.rb +13 -1
  30. data/lib/openwfe/expressions/flowexpression.rb +4 -1
  31. data/lib/openwfe/expressions/time.rb +10 -27
  32. data/lib/openwfe/expressions/timeout.rb +23 -6
  33. data/lib/openwfe/flowexpressionid.rb +7 -6
  34. data/lib/openwfe/listeners/socketlisteners.rb +1 -1
  35. data/lib/openwfe/participants/enoparticipants.rb +103 -47
  36. data/lib/openwfe/participants/participant.rb +29 -3
  37. data/lib/openwfe/participants/participantmap.rb +10 -2
  38. data/lib/openwfe/participants/participants.rb +31 -19
  39. data/lib/openwfe/participants/socketparticipants.rb +3 -1
  40. data/lib/openwfe/rest/controlclient.rb +5 -18
  41. data/lib/openwfe/rest/oldrestservlet.rb +279 -0
  42. data/lib/openwfe/rest/restclient.rb +55 -25
  43. data/lib/openwfe/rest/worklistclient.rb +35 -44
  44. data/lib/openwfe/rest/xmlcodec.rb +79 -69
  45. data/lib/openwfe/rudefinitions.rb +15 -7
  46. data/lib/openwfe/storage/yamlextras.rb +3 -3
  47. data/lib/openwfe/util/observable.rb +64 -7
  48. data/lib/openwfe/util/scheduler.rb +107 -77
  49. data/lib/openwfe/util/workqueue.rb +5 -11
  50. data/lib/openwfe/utils.rb +3 -3
  51. data/lib/openwfe/version.rb +1 -2
  52. data/lib/openwfe/workitem.rb +3 -4
  53. data/lib/openwfe/worklist/oldrest.rb +244 -0
  54. data/lib/openwfe/worklist/storelocks.rb +288 -0
  55. data/lib/openwfe/worklist/storeparticipant.rb +4 -2
  56. data/lib/openwfe/worklist/worklist.rb +297 -0
  57. data/test/cron_test.rb +8 -9
  58. data/test/eno_test.rb +10 -13
  59. data/test/flowtestbase.rb +26 -17
  60. data/test/ft_15_iterator.rb +19 -0
  61. data/test/ft_23c_wait.rb +2 -2
  62. data/test/ft_2b_concurrence.rb +2 -2
  63. data/test/ft_30_socketlistener.rb +5 -1
  64. data/test/ft_32_journal.rb +1 -1
  65. data/test/ft_32c_journal.rb +102 -0
  66. data/test/ft_32d_journal.rb +85 -0
  67. data/test/ft_45_citerator.rb +25 -0
  68. data/test/ft_49_condition.rb +60 -2
  69. data/test/ft_4_misc.rb +15 -0
  70. data/test/ft_50_xml_attribute.rb +4 -4
  71. data/test/ft_53_null_noop_participant.rb +66 -0
  72. data/test/ft_54_listen.rb +223 -0
  73. data/test/ft_55_ptimeout.rb +64 -0
  74. data/test/ft_56_timeout.rb +55 -0
  75. data/test/ft_57_a.rb +109 -0
  76. data/test/ft_tests.rb +7 -0
  77. data/test/hparticipant_test.rb +3 -3
  78. data/test/obs_test.rb +115 -0
  79. data/test/orest_test.rb +224 -0
  80. data/test/pending.rb +24 -0
  81. data/test/rake_qtest.rb +5 -1
  82. data/test/rake_test.rb +4 -0
  83. data/test/scheduler_test.rb +31 -2
  84. data/test/sec_test.rb +7 -3
  85. data/test/slock_test.rb +82 -0
  86. metadata +19 -3
  87. data/test/ft_32b_journal.rb +0 -76
data/README.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  = OpenWFEru, Standard Library Documentation
2
2
 
3
3
  == Prerequisites
4
- Ruby 1.8.4 or later
4
+ Ruby 1.8.5 or later
5
5
 
6
6
  == Supported platforms
7
7
  TODO
@@ -32,27 +32,70 @@ These are mostly stolen from the unit tests
32
32
 
33
33
  Creating an workflow engine instance
34
34
 
35
- require 'pp'
36
- require 'openwferu'
35
+ require 'rubygems'
36
+ require 'openwfe/def'
37
+ require 'openwfe/workitem'
38
+ require 'openwfe/engine/engine'
39
+
40
+ #
41
+ # instantiating an engine
42
+
43
+ engine = OpenWFE::Engine.new
44
+
45
+ #
46
+ # adding some participants
47
+
48
+ engine.register_participant :alice do |workitem|
49
+ puts "alice got a workitem..."
50
+ workitem.alice_comment = "this thing looks interesting"
51
+ end
52
+
53
+ engine.register_participant :bob do |workitem|
54
+ puts "bob got a workitem..."
55
+ workitem.bob_comment = "not for me, I prefer VB"
56
+ workitem.bob_comment2 = "Bob rules"
57
+ end
58
+
59
+ engine.register_participant :summarize do |workitem|
60
+ puts
61
+ puts "summary of process #{workitem.fei.workflow_instance_id}"
62
+ workitem.attributes.each do |k, v|
63
+ next unless k.match ".*_comment$"
64
+ puts " - #{k} : '#{v}'"
65
+ end
66
+ end
67
+
68
+ #
69
+ # a process definition
70
+
71
+ class TheProcessDefinition0 < OpenWFE::ProcessDefinition
72
+ sequence do
73
+ concurrence do
74
+ participant :alice
75
+ participant :bob
76
+ end
77
+ participant :summarize
78
+ end
79
+ end
80
+
81
+ #
82
+ # launching the process
83
+
84
+ li = OpenWFE::LaunchItem.new(TheProcessDefinition0)
85
+
86
+ li.initial_comment = "please give your impressions about http://ruby-lang.org"
87
+
88
+ fei = engine.launch li
89
+ #
90
+ # 'fei' means FlowExpressionId, the fei returned here is the
91
+ # identifier for the root expression of the newly launched process
92
+
93
+ puts "started process '#{fei.workflow_instance_id}'"
94
+
95
+ engine.wait_for fei
96
+ #
97
+ # blocks until the process terminates
37
98
 
38
- # Create an engine to launch the flow
39
- engine = OpenWFEru::Engine.new
40
- engine.register_participant('test-.*', OpenWFEru::PrintParticipant.new())
41
-
42
- # Create a flow definition and assign to a new instance
43
- flow = '''<process-definition name="equals_0" revision="0">
44
- <if>
45
- <equals value="a" other-value="a" />
46
- <print>${field:__result__}</print>
47
- </if>
48
- </process-definition>'''
49
- li = OpenWFE::LaunchItem.new(flow)
50
-
51
- # Launch the instance
52
- engine.launch(li)
53
-
54
- # Examine it
55
- pp engine.get_expression_storage
56
99
 
57
100
  == How to help
58
101
 
@@ -69,19 +112,11 @@ tedious by hand.
69
112
 
70
113
  === Prerequisites
71
114
 
72
- You'll need libxml2-dev in order to compile the native libraries
73
- for libxml. Please check your your packaging system for the appropriate
74
- name.
75
-
76
- On Ubuntu you can install libxml2 with:
77
- sudo apt-get install libxml2-dev
78
-
79
115
  === Ruby Libraries Install
80
116
 
81
117
  1. gem install -r rake
82
118
  2. gem install -r rote redcloth
83
119
  3. gem install -r tidy
84
- 4. gem install -r libxml-ruby
85
120
 
86
121
  == Documentation
87
122
 
@@ -90,8 +125,5 @@ http://rubyforge.org/projects/openwferu
90
125
  or
91
126
  http://openwferu.rubyforge.org
92
127
 
93
- In the meantime the old site hosted at openwfe.org is at:
94
- http://www.openwfe.org/openwfe-ruby.html
95
-
96
- If you're interested in helping : http://groups.google.com/group/openwferu-users/topics
128
+ The users mailing list is at : http://groups.google.com/group/openwferu-users
97
129
 
data/examples/README.txt CHANGED
@@ -6,3 +6,7 @@ you can run quotereporter.rb with
6
6
 
7
7
  It will fetch a few quotes for stocks and produce an atom feed file (atom_feed.xml)
8
8
 
9
+ If openwferu was installed via rubygems, you can simply do
10
+
11
+ ruby quotereporter.rb
12
+
@@ -3,6 +3,8 @@
3
3
  # a little script that traces the flow given as input
4
4
  #
5
5
 
6
+ require 'rubygems'
7
+
6
8
  require 'openwfe/expressions/raw_prog'
7
9
  require 'openwfe/tools/flowtracer'
8
10
 
@@ -5,6 +5,8 @@
5
5
  # an OpenWFEru example
6
6
  #
7
7
 
8
+ require 'rubygems'
9
+
8
10
  require 'openwfe/engine/engine'
9
11
  require 'openwfe/expressions/raw_prog'
10
12
  #require 'openwfe/participants/soapparticipants'
@@ -45,6 +45,8 @@
45
45
  # for the context of this example
46
46
  #
47
47
 
48
+ require 'rubygems'
49
+
48
50
  #require 'openwfe/engine/engine'
49
51
  require 'openwfe/engine/file_persisted_engine'
50
52
  require 'openwfe/expressions/raw_prog'
@@ -52,5 +52,7 @@ li = OpenWFE::LaunchItem.new(TheProcessDefinition0)
52
52
 
53
53
  li.initial_comment = "please give your impressions about http://ruby-lang.org"
54
54
 
55
- engine.launch(li)
55
+ fei = engine.launch(li)
56
+
57
+ engine.wait_for fei
56
58
 
@@ -3,6 +3,8 @@
3
3
  # an OpenWFEru example
4
4
  #
5
5
 
6
+ require 'rubygems'
7
+
6
8
  require 'openwfe/engine/engine'
7
9
  require 'openwfe/expressions/raw_prog'
8
10
  require 'openwfe/participants/soapparticipants'
@@ -3,6 +3,8 @@
3
3
  # showing how to use the scheduler
4
4
  #
5
5
 
6
+ require 'rubygems'
7
+
6
8
  require 'time'
7
9
 
8
10
  require 'openwfe/util/scheduler'
@@ -34,7 +36,7 @@ end
34
36
 
35
37
  scheduler.schedule_in("2m10s") do
36
38
  p "after 2 minutes and 10 seconds stopping the scheduler and exiting..."
37
- scheduler.do_stop
39
+ scheduler.stop
38
40
  end
39
41
  #
40
42
  # using a regular "at" job to stop the scheduler after 4 minutes
@@ -3,6 +3,8 @@
3
3
  # showing how to use the scheduler
4
4
  #
5
5
 
6
+ require 'rubygems'
7
+
6
8
  require 'time'
7
9
 
8
10
  require 'openwfe/util/scheduler'
@@ -35,7 +37,7 @@ end
35
37
 
36
38
  scheduler.schedule_in("5500") do
37
39
  p "after 5500 ms stopping the scheduler and exiting..."
38
- scheduler.do_stop
40
+ scheduler.stop
39
41
  end
40
42
 
41
43
  #scheduler.schedule_at("x" do
@@ -41,6 +41,7 @@
41
41
  #
42
42
 
43
43
  require 'logger'
44
+ require 'fileutils'
44
45
 
45
46
  require 'openwfe/workitem'
46
47
  require 'openwfe/rudefinitions'
@@ -70,11 +71,20 @@ module OpenWFE
70
71
  # Accepts an optional initial application_context (containing
71
72
  # initialization params for services for example).
72
73
  #
74
+ # The engine itself uses one param :logger, used to define
75
+ # where all the log output for OpenWFEru should go.
76
+ # By default, this output goes to logs/openwferu.log
77
+ #
73
78
  def initialize (application_context={})
74
79
 
75
80
  super(S_ENGINE, application_context)
76
81
 
77
- $OWFE_LOG = Logger.new("engine.log") unless $OWFE_LOG
82
+ $OWFE_LOG = application_context[:logger]
83
+
84
+ unless $OWFE_LOG
85
+ FileUtils.mkdir("logs") unless File.exist?("logs")
86
+ $OWFE_LOG = Logger.new("logs/openwferu.log")
87
+ end
78
88
 
79
89
  # build order matters.
80
90
  #
@@ -91,8 +101,6 @@ module OpenWFE
91
101
 
92
102
  build_participant_map()
93
103
 
94
- #get_expression_pool.reschedule()
95
-
96
104
  linfo { "new() --- engine started --- #{self.object_id}" }
97
105
  end
98
106
 
@@ -255,10 +263,10 @@ module OpenWFE
255
263
 
256
264
  result = if Scheduler.is_cron_string(freq)
257
265
 
258
- get_scheduler.schedule(freq, nil, listener, nil)
266
+ get_scheduler.schedule(freq, listener)
259
267
  else
260
268
 
261
- get_scheduler.schedule_every(freq, listener, nil)
269
+ get_scheduler.schedule_every(freq, listener)
262
270
  end
263
271
  end
264
272
 
@@ -332,6 +340,8 @@ module OpenWFE
332
340
  end
333
341
  end
334
342
 
343
+ linfo { "stop() stopped engine '#{@service_name}'" }
344
+
335
345
  nil
336
346
  end
337
347
 
@@ -47,6 +47,7 @@ require 'rexml/document'
47
47
  require 'openwfe/utils'
48
48
  require 'openwfe/service'
49
49
  require 'openwfe/logging'
50
+ require 'openwfe/omixins'
50
51
  require 'openwfe/rudefinitions'
51
52
  require 'openwfe/flowexpressionid'
52
53
  require 'openwfe/util/lru'
@@ -132,7 +133,8 @@ module OpenWFE
132
133
  MonitorMixin,
133
134
  OwfeServiceLocator,
134
135
  Observable,
135
- WorkqueueMixin
136
+ WorkqueueMixin,
137
+ FeiMixin
136
138
 
137
139
  SAFETY_LEVEL = 2
138
140
  #
@@ -155,7 +157,7 @@ module OpenWFE
155
157
  end
156
158
 
157
159
  #
158
- # Makes sure to call the do_stop() method of the Stoppable mixin
160
+ # Stops this expression pool (especially its workqueue).
159
161
  #
160
162
  def stop
161
163
  @stopped = true
@@ -186,6 +188,9 @@ module OpenWFE
186
188
 
187
189
  wfdurl = launchitem.workflow_definition_url
188
190
 
191
+ raise "launchitem.workflow_definition_url not set, cannot launch" \
192
+ unless wfdurl
193
+
189
194
  definition = if wfdurl.match "^field:"
190
195
  wfdfield = wfdurl[6..-1]
191
196
  launchitem.attributes.delete wfdfield
@@ -717,10 +722,31 @@ module OpenWFE
717
722
 
718
723
  protected
719
724
 
725
+ #
726
+ # This method is called by the workqueue when processing
727
+ # the atomic work operations.
728
+ #
720
729
  def do_process_workelement elt
721
730
 
722
- message, fei, workitem = elt
723
- send message, fei, workitem
731
+ begin
732
+
733
+ message, fei, workitem = elt
734
+ send message, fei, workitem
735
+
736
+ rescue Exception => e
737
+
738
+ se = OpenWFE::exception_to_s(e)
739
+
740
+ onotify :error, fei, message, workitem, se
741
+
742
+ fei = extract_fei fei
743
+
744
+ lwarn do
745
+ "#{self.service_name} " +
746
+ "operation :#{message.to_s} on #{fei.to_s} " +
747
+ "failed with\n" + se
748
+ end
749
+ end
724
750
  end
725
751
 
726
752
  #
@@ -107,11 +107,12 @@ module OpenWFE
107
107
  def queue_event (event, *args)
108
108
  synchronize do
109
109
 
110
+ #ldebug { "queue_event() :#{event}" }
111
+
110
112
  return if event == :stop
111
113
  return if event == :launch
112
114
  return if event == :reschedule
113
115
 
114
- #wfid = args[0].parent_wfid
115
116
  wfid = extract_fei(args[0]).parent_wfid
116
117
  #
117
118
  # maybe args[0] could be a FlowExpression instead
@@ -123,6 +124,8 @@ module OpenWFE
123
124
  bucket = get_bucket(wfid)
124
125
  bucket << e
125
126
 
127
+ #ldebug { "queue_event() bucket : #{bucket.object_id}" }
128
+
126
129
  if event == :terminate
127
130
 
128
131
  bucket.flush
@@ -133,11 +136,11 @@ module OpenWFE
133
136
  # 'move' journal to the done/ subdir of journal/
134
137
  #
135
138
  FileUtils.cp(
136
- bucket.get_path,
137
- @donedir + "/" + bucket.get_file_name)
139
+ bucket.path,
140
+ @donedir + "/" + File.basename(bucket.path))
138
141
  end
139
142
 
140
- FileUtils.rm bucket.get_path
143
+ FileUtils.rm bucket.path
141
144
  end
142
145
  end
143
146
  end
@@ -163,7 +166,7 @@ module OpenWFE
163
166
  end
164
167
 
165
168
  def get_bucket (wfid)
166
- @buckets[wfid] ||= Bucket.new(@workdir, wfid)
169
+ @buckets[wfid] ||= Bucket.new(get_path(wfid))
167
170
  end
168
171
 
169
172
  def serialize_event (event, *args)
@@ -172,18 +175,21 @@ module OpenWFE
172
175
  args.to_yaml
173
176
  end
174
177
 
178
+ def get_path (wfid)
179
+ @workdir + "/" + wfid.to_s + ".journal"
180
+ end
181
+
175
182
  #
176
183
  # for each process instance, there is one bucket holding the
177
184
  # events waiting to get written in the journal
178
185
  #
179
186
  class Bucket
180
187
 
181
- attr_reader :wfid, :events
188
+ attr_reader :path, :events
182
189
 
183
- def initialize (workdir, wfid)
190
+ def initialize (path)
184
191
  super()
185
- @workdir = workdir
186
- @wfid = wfid
192
+ @path = path
187
193
  @events = []
188
194
  end
189
195
 
@@ -197,21 +203,13 @@ module OpenWFE
197
203
  alias :length :size
198
204
 
199
205
  def flush
200
- File.open(get_path, "w+") do |f|
206
+ File.open(@path, "a+") do |f|
201
207
  @events.each do |e|
202
208
  f.puts e
203
209
  end
204
210
  end
205
211
  @events.clear
206
212
  end
207
-
208
- def get_path
209
- @workdir + "/" + get_file_name
210
- end
211
-
212
- def get_file_name
213
- @wfid.to_s + ".journal"
214
- end
215
213
  end
216
214
 
217
215
  end
@@ -136,6 +136,60 @@ module OpenWFE
136
136
  do_decompose(load_events(file_path), [], nil, 0)
137
137
  end
138
138
 
139
+ #
140
+ # Loads a journal file and return the content as a list of
141
+ # events. This method is made available for unit tests, as
142
+ # a public method it has not much interest.
143
+ #
144
+ def load_events (file_path)
145
+
146
+ File.open(file_path) do |f|
147
+ s = YAML.load_stream f
148
+ s.documents
149
+ end
150
+ end
151
+
152
+ #
153
+ # Takes an error event (as stored in the journal) and replays it
154
+ # (usually you'd have to fix the engine conf before replaying
155
+ # the error trigger)
156
+ #
157
+ # (Make sure to fix the cause of the error before triggering this
158
+ # method)
159
+ #
160
+ def replay_at_error (error_source_event)
161
+
162
+ get_expression_pool.queue_work \
163
+ error_source_event[3], # message (:do_apply for example)
164
+ error_source_event[2], # fei or exp
165
+ error_source_event[4] # workitem
166
+
167
+ # 0 is :error and 1 is the date and time of the error
168
+
169
+ linfo do
170
+ fei = extract_fei(error_source_event[2])
171
+ "replay_at_error() #{error_source_event[3]} #{fei}"
172
+ end
173
+ end
174
+
175
+ #
176
+ # Detects the last error that ocurred for a workflow instance
177
+ # and replays at that point (see replay_at_error).
178
+ #
179
+ # (Make sure to fix the cause of the error before triggering this
180
+ # method)
181
+ #
182
+ def replay_at_last_error (wfid)
183
+
184
+ events = load_events(get_path(wfid))
185
+
186
+ error_event = events.reverse.find do |evt|
187
+ evt[0] == :error
188
+ end
189
+
190
+ replay_at_error(error_event)
191
+ end
192
+
139
193
  protected
140
194
 
141
195
  def do_decompose (events, result, previous_state, offset)
@@ -149,14 +203,6 @@ module OpenWFE
149
203
  do_decompose(events, result, current_state, offset + 1)
150
204
  end
151
205
 
152
- def load_events (file_path)
153
-
154
- File.open(file_path) do |f|
155
- s = YAML.load_stream f
156
- s.documents
157
- end
158
- end
159
-
160
206
  def extract_state (file, offset)
161
207
 
162
208
  events = if file.is_a?(String)
@@ -189,6 +235,7 @@ module OpenWFE
189
235
  next if etype == :apply
190
236
  next if etype == :reply
191
237
  next if etype == :reply_to_parent
238
+ next if etype == :error
192
239
  next if seen[fei]
193
240
 
194
241
  seen[fei] = true
@@ -208,6 +255,7 @@ module OpenWFE
208
255
  fei = extract_fei e[2]
209
256
  next if etype == :update
210
257
  next if etype == :remove
258
+ next if etype == :error
211
259
  #next if etype == :reply_to_parent
212
260
  next if seen[fei]
213
261
  next unless participants[fei]
@@ -65,14 +65,7 @@ module OpenWFE
65
65
 
66
66
  load_last()
67
67
 
68
- begin
69
- @last_f = File.open(@last_fn, "w+")
70
- rescue Exception => e
71
- lwarn do
72
- "new() failed to open #{@last_fn}, continuing anyway...\n"+
73
- OpenWFE::exception_to_s(e)
74
- end
75
- end
68
+ ensure_last_f
76
69
  end
77
70
 
78
71
  #
@@ -124,11 +117,26 @@ module OpenWFE
124
117
  # file is closed.
125
118
  #
126
119
  def stop
120
+ #linfo { "stop() stopping '#{@service_name}'" }
127
121
  @last_f.close if @last_f
128
122
  end
129
123
 
130
124
  protected
131
125
 
126
+ def ensure_last_f
127
+ if (not @last_f) or @last_f.closed?
128
+ begin
129
+ @last_f = File.open(@last_fn, "w+")
130
+ rescue Exception => e
131
+ lwarn do
132
+ "new() failed to open #{@last_fn}, "+
133
+ "continuing anyway...\n"+
134
+ OpenWFE::exception_to_s(e)
135
+ end
136
+ end
137
+ end
138
+ end
139
+
132
140
  def now
133
141
  wfid = Time.now.to_f * 1000 * 10
134
142
  wfid.to_i
@@ -136,6 +144,7 @@ module OpenWFE
136
144
 
137
145
  def save_last
138
146
  return unless @last_f
147
+ ensure_last_f()
139
148
  @last_f.pos = 0
140
149
  @last_f.puts @last
141
150
  end
@@ -65,37 +65,19 @@ module OpenWFE
65
65
  # This is the method brought to expression classes including this
66
66
  # mixin. Easy evaluation of a conditon expressed in an attribute.
67
67
  #
68
- def eval_condition (attname, workitem)
68
+ def eval_condition (attname, workitem, nattname=nil)
69
69
 
70
- #attname = pick_attribute(attname) \
71
- # if attname.is_a?(Array)
70
+ positive = nil
71
+ negative = nil
72
72
 
73
- conditional = lookup_attribute(attname, workitem)
74
- rconditional = lookup_attribute("r"+attname.to_s, workitem)
73
+ positive = do_eval_condition(attname, workitem)
74
+ negative = do_eval_condition(nattname, workitem) if nattname
75
75
 
76
- return do_eval(rconditional) \
77
- if rconditional and not conditional
76
+ negative = (not negative) if negative != nil
78
77
 
79
- return nil \
80
- unless conditional
78
+ return positive if positive != nil
81
79
 
82
- ldebug { "eval_condition() 0 for '#{conditional}'" }
83
-
84
- conditional = from_xml conditional
85
-
86
- ldebug { "eval_condition() 1 for '#{conditional}'" }
87
-
88
- begin
89
- return to_boolean(do_eval(conditional))
90
- rescue Exception => e
91
- # probably needs some quoting...
92
- end
93
-
94
- conditional = do_quote(conditional)
95
-
96
- ldebug { "eval_condition() 2 for '#{conditional}'" }
97
-
98
- to_boolean(do_eval(conditional))
80
+ negative
99
81
  end
100
82
 
101
83
  #
@@ -122,6 +104,39 @@ module OpenWFE
122
104
 
123
105
  protected
124
106
 
107
+ def do_eval_condition (attname, workitem)
108
+
109
+ #attname = pick_attribute(attname) \
110
+ # if attname.is_a?(Array)
111
+
112
+ conditional = lookup_attribute(attname, workitem)
113
+ rconditional = lookup_attribute("r"+attname.to_s, workitem)
114
+
115
+ return do_eval(rconditional) \
116
+ if rconditional and not conditional
117
+
118
+ return nil \
119
+ unless conditional
120
+
121
+ ldebug { "do_eval_condition() 0 for '#{conditional}'" }
122
+
123
+ conditional = from_xml conditional
124
+
125
+ ldebug { "do_eval_condition() 1 for '#{conditional}'" }
126
+
127
+ begin
128
+ return to_boolean(do_eval(conditional))
129
+ rescue Exception => e
130
+ # probably needs some quoting...
131
+ end
132
+
133
+ conditional = do_quote(conditional)
134
+
135
+ ldebug { "do_eval_condition() 2 for '#{conditional}'" }
136
+
137
+ to_boolean(do_eval(conditional))
138
+ end
139
+
125
140
  private
126
141
 
127
142
  #