Hi John,
understood, "how ruote works" is very low-level.
> Anyway, I'm planning to use ruote to replace an aging Java-based document approval system we use here. It's a fairly basic system and currently only supports two document types. Capexes and PRs. The company plans to expand document types in the near future, which is one reason for rewriting it, aside from the fact that it's suffering from severe code rot.
>
> Today, the two flows are such:
>
> 1. CAPEX is attached and a manager is selected from a drop down box.
> 2. CAPEX is routed to that manager's work queue and he/she is notified via email.
> 3. Manager approves or disapproves.
> - Approve: CAPEX is moved to Role Capex Manager's work queue (4).
> - Reject: CAPEX is marked rejected and returned to initiator.
> 4. If Capex Manager approves, it's moved to CFO's queue. If not, rejection as above.
> 5. If CFO approves, it's moved to President's queue. If not, rejection.
> 6. If President approved, it's marked as completed and initiator is notified.
> 7. Complete.
Some kind of a draft : http://gist.github.com/73752
I will answer to the rest of your questions a bit later. Best regards,
--
John Mettraux - http://jmettraux.wordpress.com
Hello again,
I've uploaded something at http://gist.github.com/73813
It explores some variants (like using a subprocess definition, ...)
Questions are welcome.
"attaching" documents to process instances is IMHO a bad thing : we
live in a web/internet world, everything should have a URI (IRI), even
if it's just a path to a document in the forgotten Z: drive or an
identifier in some Lotus Notes drawer. Like, in programmation, we tend
not to copy each value, but we pass pointers, pass the URL to stuff /
documents, let workflow/BPM stuff be independent of the
content/record/document management stuff.
> As you can see, these are fairly simple workflows based on responsbilities
> assigned by role. We essentially have a many-to-many relationship between
> users and roles.
Ruote process definitions only know about participants (and
subprocesses), users and roles emerge later (sorry, no convention over
configuration here).
> I was hoping someone might give me a helpful push in terms of accomplishing
> these sorts of flows in ruote. I suspect an example of doing this will be
> just the push I need. I believe based on other threads regarding ruote and
> passenger being a no-go, I'm going to attempt this with ruote-rest (unless
> JRuby does away with this problem).
If you just need a small departmental workflow engine, MRI with a
single Mongrel might be sufficient.
> 2. PR is routed to that manager's work queue and he/she is notified via
On Wed, Mar 4, 2009 at 5:30 AM, J B <jbwe...@gmail.com> wrote:
>
> 1. PR is attached and a form field is completed with the PR's dollar value.
> Manager is selected from drop down box.
> email.
> 3. Manager approves or disapproves.
> - Approve: see step 4.> 5. If CFO approves, capex is routed to Accounting for approval. If not,
> - Reject: PR is marked rejected and returned to initiator.
> 4. If dollar value is greater than $10000, PR is routed to CFO for approval.
> If not, it goes directly to number 6.
> rejection as above.
> 6. If Accounting approves, PR is marked as complete and submitter isHello again,
> notified. If not, rejection.
> 7. Complete.
I've uploaded something at http://gist.github.com/73813
It explores some variants (like using a subprocess definition, ...)
Questions are welcome.
"attaching" documents to process instances is IMHO a bad thing : we
live in a web/internet world, everything should have a URI (IRI), even
if it's just a path to a document in the forgotten Z: drive or an
identifier in some Lotus Notes drawer. Like, in programmation, we tend
not to copy each value, but we pass pointers, pass the URL to stuff /
documents, let workflow/BPM stuff be independent of the
content/record/document management stuff.
Ruote process definitions only know about participants (and
subprocesses), users and roles emerge later (sorry, no convention over
configuration here).
Yes, I'm still getting my head around this concept. So, for example, in the capex example you provided, I believe I need to register these participants:
engine.register_participant(:initiator, OpenWFE::FileParticipant.new)
engine.register_participant(:manager, OpenWFE::FileParticipant.new)
engine.register_participant(:cfo, OpenWFE::FileParticipant.new)
Hi,
you're welcome. I like this exercise since it allows me to check if
the process definition language (and the engine) are relevant.
>> Ruote process definitions only know about participants (and
>> subprocesses), users and roles emerge later (sorry, no convention over
>> configuration here).
>
> Yes, I'm still getting my head around this concept. So, for example, in the capex example you provided, I believe I need to register these participants:
>
> engine.register_participant(:initiator, OpenWFE::FileParticipant.new)
> engine.register_participant(:manager, OpenWFE::FileParticipant.new)
> engine.register_participant(:cfo, OpenWFE::FileParticipant.new)
Well, initiator, as I understood your description of the processes is
a user. The two draft processes fetch his name in the field named
"initiator" (ruote-web2 sets by default the name of the launching user
in "launcher"). So no need to register a participant for "initiator"
(but you should have a participant (or participants) for the users).
Same thing for manager. His name is set in the workitem field named
"manager". Note that in the second process draft, there is a
subprocess named "manager" that wraps the logic of fetching the
participant real name ("ref") in the field "manager" and of sending a
notification email.
CFO might deserve its own participant since (just a guess) there's
only one in your organization.
A participant is usually 'invoked' via something like :
<participant ref="nemo" />
that can be shortened to :
<nemo />
This last 'nemo' [pseudo-]expression points to either a participant or
a subprocess.
If the real participant is chosen "at runtime", the form is :
<participant ref="${f:target}" />
or
<participant ref="${target}" />
> I'm a little confused on this:
>
> participant :ref => '${field:initiator}', :activity => 'notification'
>
> The rdoc would seem to indicate this should be ${f:initiator}, so I'm sure I'm missing something.
${field:initiator} and ${f:initiator} are equivalent. They get
replaced by the value in the workitem field named 'initiator'.
${v:my_variable} will be replaced by the value in the process variable
named 'my_variable'. It is equivalent to ${my_variable},
${var:my_variable} and ${variable:my_variable}
${fv:toto} will be replaced by the value in the workitem field named
'toto'. If the workitem has no such field, the value in the process
variable named 'toto' will be used. If there is no field nor variable,
the empty string will be used for the substitution.
${vf:xxx} looks for a variable, then for a field.
Note that those "dollar substitutions" results are always strings.
There is a last trick : ${r:xxx} will execute the ruby code in xxx,
turn it to a string and insert it in the target string. There is a
check for potentially dangerous code. By default, this "ruby
evaluation" feature is disabled, you have to enable it by passing
:ruby_eval_allowed => true when instantiating the ruote engine.
I will write a page of documentation about this "dollar notation".
> And, a bit confused on archival, although I'm wondering if that's a participant to be defined as well?
It was just a suggestion, you probably don't need it. It could be some
kind of final step. You'd have to define a participant for it. Ruote
has a History component, but it won't keep track of the terminated
processes, hence the 'archival' proposal.
> Would you mind explaining the difference between field usage with the dollar sign and without? Example:
> _break :unless => '{f:approved}'
> vs
> participant :ref => '${field:initiator}'
Sorry, it's my mistake. Without the $, no substitution gets performed.
> I looked through the documentation for participant definition, and it appears that :activity is not a named attribute it expect, so am I to assume this simply is put into the workitem's hash? How would this workflow be driven in this case?
You are right, this is just a convention. The participant expression
places its attributes in a field named "params". Those parameters are
thus visible to the participant implementation itself. When the
participant replies to the engine, the workitem field "params" is
cleaned.
"activity" is often used in the workflow domain, but you could use any
other field/attribute name.
> I'd be interested in hearing how others learned ruote....if anyone cares to share.
I'm not addressed by the question, but I'll state anyway that since
Ruote is developed on top of a scripting language, it's very easy to
write tests / prototypes and then throw them away. Small steps with
instant satisfaction (or quick failure). I feel that this is an
advantage for learning Ruote, but more importantly it's an advantage
for writing and maintaining process definitions. Ruby makes it very
easy to test/benchmark ruote process definitions. It's extremely cheap
to have a test/experimental ruote environment, a staging environment,
and so, apart from the production environment. Test and throw away
(even throw away ruote after a while if it doesn't suit you (but don't
throw away the experience and knowledge gathered)).
Hello,
oh really, then I will link to it as well from the quickstart page,
I'm sure you're not the only one who prefers the todo list.
(isn't the debugger a bit of an overkill ? simple "puts" or "p" calls
should do the trick and save you precious time... OK, I shut up)
> Now, let me see if I can describe the basics at a high level, and you tell
> me where I'm wrong.
>
> In ruote, you create a process definition. That definition can be a number
> of types of flows, but the most basic is a sequence.
A process definition is a tree of expressions. The expression at the
root is the "process-definition", the child to that root is usually a
'sequence', but it's OK to have processes like (using XML, as the <>
make it stand out in the text of the email) :
<process-definition name="x" revision="y">
<participant ref="toto" />
</process-definition>
or
<process-definition name="x" revision="y">
<if test="${f:flag} == green">
<!-- then --><participant ref="a" />
<!-- else --><participant ref="b" />
</if>
</process-definition>
> A sequence steps through a list of participants, handing each a "workitem",
> which is actually of the class InflowWorkItem.
A sequence can step through a list of sequences, concurrences,
participants, ... Each of them gets handed a workitem (of the class
InflowWorkItem). A process definition defines a tree of expressions.
The process instance instantiates branches of the tree as needed (it
won't create branches that are discarded (if/then/else)).
> To start a process definition running, you would create a launch item. This
> launch item will allow you set keys into it, like a hash, which will end up
> in the workitem hash when that hash is handed to the participants.
Yes, but you can also simply launch a process by passing the definition :
engine.launch(pdef0)
or
engine.launch(%{<process-definition name="x" revision="y">
<participant ref="toto" />
</process-definition>})
or the URL to the process definition
engine.launch("http://documents.example.org/pdefs/pdef0_20080405_capex.xml")
In those 3 examples, the initial 'hash' is empty.
Launchitems are useful for pre-filling workitems.
> A "workitem", both in Launch form and Inflow form, should be treated as a
> hash. It's up to you what you place in this hash, and how you use it to make
> decisions if you wish the flow to continue (assuming you're using some form
> of looping sequence) or not.
True. With an "if" expression, you can let the decisions 'happen' in
the process instance, but as you explained decisions taken inside of
participants (human or automated) are OK (they even make business
processes easier to read).
> When you speak of proceeding a workitem, you're simply stating that the
> current participant doesn't do anything to prevent that workitem from moving
> to the next participant. If your participants don't really do anything at
> all, the workitem will proceed through a sequence unimpeded.
Sorry for the vagueness of this "proceed" concept. Human participants
usually have to explicitely send back the workitem to the engine for
it to proceed (resume) in its process instance.
Note that there is a timeout attribute to participants to resume
automatically if the participant forgot the workitem.
<participant ref="toto" timeout="2d5h" /> <!-- toto has two days and
five hours to 'act' with the workitem -->
When the timeout is triggered, the flow resumes with the workitem has
it was when it reached the timeout block.
There is a timeout expression
(http://openwferu.rubyforge.org/expressions.html#exp_timeout) for
setting a timeout for whole segments of processes.
The idea behind 'proceed' is that the engine fired a workitem at the
[real] participant and is waiting for the answer. Proceed is about
replying to the engine [with an updated version of the workitem
[payload]].
> So at the most basic level, we're talking about an engine that hands a hash
> to different objects (participants) in a flow based on how the process
> definition was set up.
Yes.
> Is the above correct (or close to being)? If not, where is my understanding
> incorrect? If so, I'm starting to grasp things (I think).
You'll be ready to write documentation soon ;-) Out of curiosity,
which workflow engine are you currently using ?
> I believe that many more would grasp ruote earlier if you created a very
> basic quickstart, which outlines things in the most simple manner possible
> like I've tried to do above. Maybe it exists and I missed it. I've been
> looking at this too long ;-).
Thanks for the suggestion. Will mull over it.
> So this leads me to another question. It would seem that once the workitem
> has been handed to the participant, the participant needs to decide if it
> can continue. This works well in the task example where the participant can
> simply prompt the user within the block and wait on input...but in a web
> environment where the interaction is disconnected there has to be another
> mechanism. I will download the ruote-web2 project and look at how it
> accomplishes this, but any pointers are welcome.
Ruote-web2 and ruote-rest do that via an "inbox" mechanism.
The todo example is super-vanilla, ruote is thought for an
asynchronous world (with sync being a subset of async). Maybe the
danger of the todo-list quickstart lies in making people believe that
ruote is just about synchronous question/answer cycles...
Best regards, thanks for the feedback,
Hello,
On Fri, Mar 6, 2009 at 2:21 AM, J B <jbwe...@gmail.com> wrote:
>
> That, along with reading other threads here and playing with your todo list
> example (which to me is a much better quickstart) in a debugger...
oh really, then I will link to it as well from the quickstart page,
I'm sure you're not the only one who prefers the todo list.
(isn't the debugger a bit of an overkill ? simple "puts" or "p" calls
should do the trick and save you precious time... OK, I shut up)
Yes, but you can also simply launch a process by passing the definition :
engine.launch(pdef0)
or
engine.launch(%{<process-definition name="x" revision="y">
<participant ref="toto" />
</process-definition>})
or the URL to the process definition
engine.launch("http://documents.example.org/pdefs/pdef0_20080405_capex.xml")
In those 3 examples, the initial 'hash' is empty.
Launchitems are useful for pre-filling workitems.
Sorry for the vagueness of this "proceed" concept. Human participants
> When you speak of proceeding a workitem, you're simply stating that the
> current participant doesn't do anything to prevent that workitem from moving
> to the next participant. If your participants don't really do anything at
> all, the workitem will proceed through a sequence unimpeded.
usually have to explicitely send back the workitem to the engine for
it to proceed (resume) in its process instance.
You'll be ready to write documentation soon ;-) Out of curiosity,
which workflow engine are you currently using ?
Ruote-web2 and ruote-rest do that via an "inbox" mechanism.
The todo example is super-vanilla, ruote is thought for an
asynchronous world (with sync being a subset of async). Maybe the
danger of the todo-list quickstart lies in making people believe that
ruote is just about synchronous question/answer cycles...
Best regards, thanks for the feedback,
Hello,
fetching the workitems (in the inbox) :
proceeding a workitem :
it's simply calling the reply method of the engine with the workitem.
The engine looks at the FlowExpressionId of the workitem to determine
to which expression / process instance the workitem belongs.
OK for the document based workflows, but sometimes it's way easier for
a Rails (or Ruby) person to simply use act_as_a_state_machine (or
'workflow') and attach states to the document "resource". Else it's
back to commercial [open source] vendor gaming with things like
Alfresco : http://stackoverflow.com/questions/593123/open-source-document-approval-system-or-framework
OK, back to ruote-web2. ActiveRecord >= 2.2 is no friend of mine :(
Best regards,
Hello,
On Fri, Mar 6, 2009 at 11:45 AM, J B <jbwe...@gmail.com> wrote:
>
>> Ruote-web2 and ruote-rest do that via an "inbox" mechanism.
>
> So maybe that documentation I ask for above is in fact the code our
> ruote-web2. I was unable to dig into that today, but will tomorrow.
fetching the workitems (in the inbox) :
http://github.com/jmettraux/ruote-web2/blob/2821ab2a002d35a915a126a7d7146c228201d68c/app/controllers/workitems_controller.rb#L39
proceeding a workitem :
http://github.com/jmettraux/ruote-web2/blob/2821ab2a002d35a915a126a7d7146c228201d68c/app/controllers/workitems_controller.rb#L145-154
OK for the document based workflows, but sometimes it's way easier for
a Rails (or Ruby) person to simply use act_as_a_state_machine (or
'workflow') and attach states to the document "resource". Else it's
back to commercial [open source] vendor gaming with things like
Alfresco : http://stackoverflow.com/questions/593123/open-source-document-approval-system-or-framework
OK, back to ruote-web2. ActiveRecord >= 2.2 is no friend of mine :(
Kenneth explored aasm / ruote :
http://www.opensourcery.co.za/2009/03/04/ruote-in-20-minutes/
State machines are great for resource lifecycle. If you can stick with
1 document 1 state then go for it. If you want to deal with [business]
processes and not only resources, change the processes often
(sometimes "in fly"), have resources taking part in multiple processes
(of different versions...), then ruote is worth exploring.
>> OK, back to ruote-web2. ActiveRecord >= 2.2 is no friend of mine :(
>
> Ah, so you've hit problems with rails 2.3 now as well?
Rails 2.3 is fine, ActiveRecord, since 2.2 is very finicky with
multithreaded apps like Ruote. Seriously thinking about using
DataMapper for all the database things occurring outside of the HTTP
request/response thing web frameworks are thought for. I already have
a DataMapper process runtime persistence implemented :
http://github.com/jmettraux/ruote/blob/dd5c9d3e1ae50a78daabd1e3f85939c1b82dd014/lib/openwfe/extras/expool/dm_expstorage.rb
(I'm clueless when it comes to Rails).
Hi List,
I was a bit unhappy with the length of that PR gist. There is a new,
shorter version :
It uses the new rewind-if / break-if attributes of the 'cursor' and
'loop' expressions. The conditions expressed in those attributes are
evaluated each time a 'transition' occurs in the cursor. I'm updating
the documentation right now (it was only mentioned in the rdoc as of
now).