processi

about processes and engines

ruote 2.2.0 released

Just released version 2.2.0 of ruote, a Ruby workflow engine. It interprets workflow definitions, routing tasks / work among participants.

Ruote.process_definition do
  alice :task => 'prepare offer'
  bob :task => 'revise offer'
  concurrence do
    david :task => 'revise offer'
    fred :task => 'revise offer', :if => '${offer.total} > 1000'
    elie :notification => 'offer for ${customer.name} (${customer.city}) out'
  end
  charly :task => 'submit offer'
  accounting :task => 'emit invoice'
end

 

the changelog

2.2.x

Why a 2.2.x ? It’s not that ruote 2.2.0 is not backward compatible but it now flags all the expression with a sub_id (formerly called a sub_wfid). Previously only the expressions in a subprocess would have a subid, now all the expressions have one. It prevents some nasty issues with concurrent-iterator and forget.

The second justification for a 2.2.x are stateful participants being dropped.

stateless participants

Before 2.2.0, participants could be registered as classes or instances. From now on, only participant classes can be registered. Each time a ruote worker dispatches a workitem to a participant it uses a new instance. Such “stateless” participants cannot share info via instance variables.

Block participants like

engine.register_participant 'total' do |workitem|
  workitem.fields['total'] = workitem.fields['items'].inject(0) { |t, (i, c)|
    item = Item.find(i)
    t = t + c * item.price
  end
end

are a bit harder to make “stateless”. But thanks to the ingenious Sourcify, grabbing the source of the block is not a problem. Small reminder, it grabs the source code of the block, not its closure.

Simply put, stateful participants have been dropped.

Now for the rest of the changes.

composite conditions

Conditions in process definitions were constrained to things like “${customer.level} == gold”, which works OK if you want to keep concise process definitions. Adding a quick ‘and’ should not require too many workarounds. This is now possible :

Ruote.process_definition do
  participant 'alice', :if => "${customer.level} == 'gold'"
  participant 'bob', :if => '${customer.level} == gold and ${customer.country} == Brazil'
end

Other idioms like “${customer_list} is empty”, “${customer} in ${customer_list}”, “${x} is null” are accepted. The tests are probably a more exhaustive source of info about those idioms.

Speaking of the dollar notation, it was all about strings, whatever the values it would turn them into strings. There is a new literal way for dealing directly with non string values.

on_error, on_terminate

The Engine instance has two new setters, on_error= and on_terminate=. For example, this

engine.on_error = 'supervisor'

states that the ‘supervisor’ participant (whatever you registered under that name) will receive a notification (a workitem) each time a process instance emits an error in the engine.

on_error and on_terminate accept participant names, subprocess names or directly process definitions :

engine.on_error = Ruote.process_definition do
  concurrence do
    administrator :msg => 'something went wrong'
    supervisor :msg => 'houston, we have a problem'
  end
end

filters

Thanks to a collaboration with Raphael filters are [back] in ruote.

They come in two forms : the filter expression (one-way filtering of passing workitems) and the :filter attribute (placing a filter around a process region).

The filter attribute may also point to participants (registered like any other participants), focused on workitem filtering (whereas regular participants pass work to the underlying, real, participant).

history

Prior to 2.2.0, ruote had no history, the worker activity was not ‘archived’. A ruote engine will now have a default history keeping in memory the most recent worker operations.

If you really need such a history, you’d better use/customize the StorageHistory class.

(well, you probably don’t need to keep record of all these operations).

sequel

There is a new storage implementation, ruote-sequel. As the name implies, it’s based on the excellent Sequel.

A nice addition to the list of storage implementations.

participants (again)

You register participants, they are all stateless, why not get a copy out for certain interactions ? Engine#participant is the counterpoint to Engine#register_participant.

Participants may provide their own timeout value by implementing the rtimeout method (the timeout given in the process definition, if any, will take precedence though).

expressions

The listen expression now reacts to a process instance entering or leaving a tag (a process instance region). See ruote and tags for an explanation. The Workitem class now has a dedicated __tags__ field containing a list of tag names the process instance (that emitted the workitem) is currently in.

Ruote now has a “switch” statement, it’s a given (also covers the new let expression).

The cancel_process expression has got a new alias “terminate”, concurrent_iterator can be shortened to “citerator” while the when expression can be written “once” (or “as_soon_as”).

There is a new let expression. The main usage is to isolate a set of subprocess definitions in a new scope (case like).

The registerp and unregisterp expressions let you register participants from process definitions (granted, you could do that from the consume method of a participant too).

It was present in ruote 0.9.x but got lost in the way. The lose expression and the :lose attribute are back (thanks Claudio). ‘forget’ and ‘lose’ are ways to put aside certain execution branches of a process instance.

next steps

Next steps ? Workers registering in the storage, pausing individual processes, having process supervise other processes, improved ruote-redis, …

 

Many thanks to all who helped along the way.

 

website : http://ruote.rubyforge.org
source : https://github.com/jmettraux/ruote
mailing list : http://groups.google.com/group/openwferu-users
irc : freenode.net #ruote

 

Written by John Mettraux

March 2, 2011 at 10:46 am

Posted in ruby, ruote, workflow

Follow

Get every new post delivered to your Inbox.