I had a conversation with Lucas from Howcast the other day:
http://ruote-irclogs.s3.amazonaws.com/log_2011-08-11.html
There is always this recurring pattern, I emit a workitem towards a participant and I want it to be reminded of it, like 1 week before the timeout and then a final time, 1 day before the task closes.
Up until now, it involved writing something like:
---8<---
concurrence :count => 1 do
participant 'toto', :task => 'clean the car', :timeout => '4w'
sequence :lose => true do
wait '2w'
participant 'toto', :notification => 'clean the car !'
end
sequence :lose => true do
wait '3w'
participant 'toto', :notification => 'DO CLEAN THE CAR !!'
end
end
--->8---
You can wrap that in a subprocess:
---8<---
define 'do_task' do
concurrence :count => 1 do
participant '${v:target}', :task => '${v:task}', :timeout => '${v:tout}'
sequence :lose => true do
wait '${v:first_reminder}'
participant '${v:target}', :notification => '${v:task} !'
end
sequence :lose => true do
wait '${v:second_reminder}'
participant '${v:target}', :notification => '${v:task} !!'
end
end
end
do_task :target => 'toto', :first_reminder => '2w', :second_reminder => '3w', :tout => '4w'
--->8---
But it becomes quickly rather ugly.
With Lucas, we discussed having something shorter.
---8<---
participant 'toto', :task => 'clean the car', :reminders => '2w, 3w', :timeout => '4w'
--->8---
or
---8<---
participant 'toto', :task => 'clean the car', :reminders => '2w: first_reminder, 3w: second_reminder, 4w: timeout'
define 'first_reminder' do
participant '${v:text}', :msg => '1st reminder'
end
define 'second_reminder' do
concurrence do
participant '${v:text}', :msg => '2nd reminder'
participant 'supervisor', :msg => '2nd reminder for ${v:text} / ${v:task}'
end
end
--->8---
These are not always reminders, and it could be beneficial to attach them to other things that only participant expression. Let's try with "timers":
---8<---
sequence :timers => '2w: notify, 3w: timeout' do
participant 'toto'
participant 'doug'
end
--->8---
In this short example, the sequence has a timeout of three weeks, and after two weeks, the subprocess (or participant) named 'notify' is triggered.
I'm working on this feature right now, here is a summary of it:
- they are 'timers'
- it's a list than can get attached to any expression
- they trigger participants, subprocesses or the 'timeout' special behaviour
- maybe 'error' is another special behaviour, 'cancel' as well ('redo' ?)
It requires a bit of rework of the timeout infra, fortunately it should be backward compatible.
Thoughts, comments ?
--
John Mettraux - http://lambda.io/processi
Dear list,
http://ruote-irclogs.s3.amazonaws.com/log_2011-08-11.html
or
Thoughts, comments ?
--
you received this message because you are subscribed to the "ruote
users" group.
to post : send email to openwfe...@googlegroups.com to unsubscribe
: send email to openwferu-use...@googlegroups.com
more options : http://groups.google.com/group/openwferu-users?hl=en
the timers feature has been implemented and merged in the ruote 2.2.1 master.
It behaves as previously described in this thread. It's backward compatible.
It lead to the inclusion of a new common attribute, :flank.
| replies to parent ? | cancellable ? |
-------+---------------------+---------------+
forget | immediately | no |
lose | never | yes |
flank | immediately | yes |
(sorry if the ascii table doesn't render properly in your email client).
Timers are "flanking" the expression to which they are tied.
---8<---
sequence :timers => '2d: first_reminder, 3d: final_reminder, 4d: timeout' do
# ...
end
--->8---
(where first_reminder and final_reminder are participants or subprocesses).
Since they replied immediately upon being triggered, the expression keeps track of its "flanks". When an expression is cancelled, its flanks are cancelled as well, they thus shouldn't outlive their expression.
Since :flank is a common attribute, you can write
---8<---
sequence do
sequence :flank => true do
bob :task => 'side work'
end
alice :task => 'main work'
end
--->8---
Where bob is given a task as long as alice is performing the "main work" task.
This could previously be achieved with something like
---8<---
concurrence :count => 1 do
sequence :lose => true do
bob :task => 'side work'
end
alice :task => 'main work'
end
--->8---
which I explained many times in this mailing list.
I'm not sure "flank" is the right word, I wanted to use "side" or "wing", but well, to flank / a flank, to side / a side, to wing, ... It's flank for now, many someone has a convincing suggestion.
I hope the :flank pattern is more readable than its concurrence :count => 1 original.
I will probably make an expression for flank, so that
---8<---
sequence do
flank do
bob :task => 'side work'
charly : task => 'side work, part 2'
end
alice :task => 'main work'
end
--->8---
(forget and lose also have their own expressions).
Now I have to document that.
I hope to release 2.2.1 soon, I have troubles with the CI setting for now.
Comments and suggestions are welcome, thanks to Lucas (Howcast) for triggering this work and sharing his ideas during the inception.
Best regards,
Hello Hartog,
well spotted. Nice domain for the example, I like it a lot.
What happens with the sequence is that, well, it executes in sequence, so you have to put the flanks first. I could force any expressions with flanking children to grab them and apply them first, but for now I prefer going the simplest way.
---8<---
repeat do
alpha_platoon :task => 'main push'
over :if => '${success}'
commit_reserve :if => '${reserve_available}'
commit_help_unit :if => '${help_available}', :flank => true
over :if => '${failure}'
end
--->8---
In this [contrived] example, help units could come in and "flank", one at a time, ending with more than one flank.
Let's try to solve your issue by using concurrence:
---8<---
concurrence do
1st_ork_platoon :task => "attack gates through valley"
archers :task => "setup positions in hills", :flank => true
2nd_ork_platoon :task => "lay low behind hills", :flank => true
warg_riders :task => "circle compound, attack rear", :flank => true
end
--->8---
This concurrence would be over as soon as the 1st platoon is over with its task. Flanks would be cancelled at that point.
It's close to the initial
---8<---
concurrence :count => 1 do
1st_ork_platoon :task => "attack gates through valley"
archers :task => "setup positions in hills", :lose => true
2nd_ork_platoon :task => "lay low behind hills", :lose => true
warg_riders :task => "circle compound, attack rear", :lose => true
end
--->8---
So I'm not that successful with the introduction of 'flank'...
---8<---
sequence do
# Flanks
archers :task => "setup positions in hills", :flank => true
2nd_ork_platoon :task => "lay low behind hills", :flank => true
warg_riders :task => "circle compound, attack rear", :flank => true
# Schwerpunkt
1st_ork_platoon :task => "attack gates through valley"
end
--->8---
You're right, I'm forcing a potentially non-natural ordering. I think concurrence + flank, doesn't look to bad (at least it removes the :count).
What do you think ?
Hello Eric,
I know this is not elegant, but I'd do it this way:
---8<---
alice :task => 'prep package', :timers => '${r:Time.at(wi.fields['contract_date']) - 5.days}: reminder'
--->8---
(note I've used ActiveSupport #days in this example).
> Not sure if its part of the same use case but you could also see:
> remind me 5 days before the end of the month,quarter year
That would require a good temporal language in ruote.
Others have implemented such languages in Ruby, I was thinking people would simply use the one they prefer in their participants (or in ${r:x} constructs).
https://github.com/mojombo/chronic
https://github.com/bokmann/business_time
https://github.com/dcparker/temporals
...
One thing I could do is checking whether the participant class responds to a #timers method and call it if present. This is currently only implemented for timeouts (via a #rtimeout method (since JRuby forces a #timeout method on every object)).
We currently only have
---8<---
class MyParticipant
include Ruote::LocalParticipant
def rtimeout(workitem)
(Time.at(workitem.fields['contract_date']) + 10.days).to_s
end
end
--->8---
we could thus have also
---8<---
class MyParticipant
include Ruote::LocalParticipant
def timers(workitem)
(Time.at(workitem.fields['contract_date']) - 5.days).to_s + ': ' +
'reminder'
end
end
--->8---
Not sure if my answer makes sense, kind regards,
OK, it's in
https://github.com/jmettraux/ruote/commit/edd797ed5078dc09a4f46a5d89a164c817d3cc41
Best regards,