On Mon, Apr 19, 2010 at 9:09 PM, Olle <
foe...@gmail.com> wrote:
>
> 1. Ruote-kit works under “_ruote” prefix, but ruote-kit-client
> connects to the root of web-server. I tried to set connect address in
> several forms:
http://localhost:8001/_ruote,
http://localhost:8001
> with no success. It looks like I missed something obvious…
Hello,
not sure if I understood the question / issue, but I have notified
Torsten and Kenneth about your email.
> 2. I’ve implemented an external participant class, which creates task
> in external task system. For some reason, I may not store the whole
> workitem in that task system, so I just send wfid an expid to the
> external system. To get information about completion of the task, I
> adjusted ruote-kit and expose “engine.reply” method under “_ruote/
> engine/reply”. Implementation is quite naïve(code quality may be poor,
> sorry: I’m not experienced in Ruby yet):
http://gist.github.com/370868
> . This web-service method is called from outside. Also I modified
> ruote-kit-client respectively. It works good, but I’m just wondering
> is such approach OK and would It works without side-effects ?
In those cases I would have gone for an extension of the
StorageParticipant class. It could look like this :
---8<---
class SemiExternalParticipant < Ruote::StorageParticipant
def consume (workitem)
notify_external_participant(workitem.fei)
super
end
end
--->8---
Then for the "reply", a PUT on /workitems/20100419-babaraca!!0_0 could
do the trick. And the workitem is waiting for you in the storage
participant, no need to call engine.process(wfid).
But you're the final judge. I don't see anything wrong with your approach.
> 3. Related to 2) suggestion. In my case, results of human tasks arrive
> raw and unstructured and should be transformed before using in the
> process. This could be done by additional step in the process like
> this:
> -----
> sequence do
> external_human_participant
> process_results
> end
> ------
> But using this way I need to “process_results” after every external
> human task and it looks artificial. What do you think about such an
> idea: external participant implements “on_reply” method and engine
> calls this method upon receiving a “reply” message? This let to
> compact process definition and move proceeding of results into the
> participant class, which knows how to proceed the results in context
> of process.
In order to simplify the process definition, you could leverage a
subprocess. Something like :
---8<---
Ruote.process_definition 'main' do
sequence do
external_call
end
# ...
define 'external_call'
sequence do
external_human_participant
process_results
end
end
end
--->8---
even hiding the subprocess definition in an engine variable (easy
re-use by all process definitions) :
---8<---
sequence do
external_call
end
# ...
engine.variables['external_call'] = Ruote.process_definition do
sequence do
external_human_participant
process_results
end
end
--->8---
You can also call subprocesses with subprocess :ref =>
'
http://pdefs.example.com/pdefs/external.rb'
But I understand the pain.
I will think about your on_reply idea. It makes lots of sense, but I'd
like to sleep on it for a while.
http://github.com/jmettraux/ruote/blob/ruote2.1/TODO.txt#L345-346
There is one thing. Since we now favour participants instantiated for
each dispatch over participants instantiated at register time, it
may/will not be the the 'same' participant doing the on_reply as the
one that did the dispatch.
If I get back to your question #2, you could do the "on_reply" in
there. But granted, this feels not right.
> 4. This question/suggestion also related to external participants.
> Please consider two slightly different scenarios:
>
> a. Participant creates task in the external task system.
> Also, It should annoy task performer with periodical (let’s say, every
> day) e-mails like this: “Please read and complete the task…”. If user
> doesn’t response for 3 days, frequency becames 2x: two times per day
> user receives a message “Oh, please, please read and complete the
> task…”. And so on. After a week, task gets cancelled. This behavior
> may be implemented in the process definition by some combination of
> “:timeout” participants and maybe “every” expression. But may be
> it’s possible for participant to use internal scheduler to schedule
> it’s own re-applying from “consume”? Better, participant may return
> some “schedule” or "next run time/interval" object from “consume”
> method which is processed by the engine/worker.
It's possible.
Here is a Rails example : (
http://gist.github.com/370998 )
---8<---
#
# config/initializers/ruote.rb
RuoteKit.configure do |c|
c.run_worker = true unless $RAKE_TASK
#c.set_storage(...
c.register do
participant 'daily' do |workitem|
Toto::Operations.trigger_inbox_reminders
end
catchall Ruote::StorageParticipant
end
end
if $RAKE_TASK != true && Rails.env != 'test'
path = Rails.root.join(*%w[ tmp pids ruote_cron_process.wfid ]).to_s
wfid = File.read(path).strip rescue nil
RuoteKit.engine.cancel_process(wfid) if wfid
wfid = RuoteKit.engine.launch(Ruote.define(:name => 'cron_process') do
cron '0 6 * * *' do
daily
end
end)
File.open(path, 'wb') { |f| f.puts(wfid) }
end
#
# lib/toto/operations.rb
module Toto
# Fires reminders for the workitems of the inbox participant.
#
def self.trigger_inbox_reminders
workitems = RuoteKit.storage_participant.by_field('remind')
users = workitems.inject({}) do |h, workitem|
if (Time.now - workitem.last_reminder) > workitem.remind_frequency
(h[workitem.user] || []) << workitem
end
h
end
users.each do |u, workitems|
::NotificationMailer.reminder(workitems)
end
end
end
--->8---
There is a dedicated cron process that runs every day at 6 in the
morning and sends reminder to users if necessary.
I think it could be adapted to your case. I use a block participant
(mea culpa), you could use a custom participant and place all the
reminder logic in it (instead of my Toto::Operations helper module).
You could place reminder frequency data in the workitem itself (since
the user hasn't yet touched).
The global "gets cancelled after 1 week" thing can be left to the
participant timeout attribute. The cron simply won't send reminders
for workitems that are not present (it doesn't keep track of them).
On the other hand, the hardcore "ruote way" could look like :
---8<---
concurrence :count => 1 do
the_real_participant :timeout => '1w'
repeat do
sleep '${time_before_next_reminder}'
send_reminder_to_real_participant
end
end
--->8---
As soon as "the_real_participant" exits the reminding loop gets
cancelled (thanks to :count => 1 which tells the concurrence to expect
only one branch to reply (and then cancel)).
As you would say "it's watered down". It could be wrapped in a subprocess.
> b. Participant works with external resource. If resource is
> locked, participant should try every 10 minutes for 2 hours and raise
> error if the resource still locked.
> Above scenarios could be defined in the process definition,
> but process becomes watered down and less readable. I'm almost sure
> that it is should be the responsibility (at least in my particular
> case) of the participant, not the process definition.
Those retry techniques belong to the participant IMHO.
Thanks for the excellent discussion ! I am learning a lot.
I'm trying to show you various techniques, I know you'll pick the
right one for your case or come up with some innovation.
Best regards,
--
John Mettraux -
http://jmettraux.wordpress.com