processi

about processes and engines

Archive for the ‘bpm’ Category

ruote, process state and tags

A : “What state is this process in ?”
B : “Well, it’s running”
A : “I know, that’s not what I meant. Where is it now ?”
B : “Let’s check which participants have workitems (tasks) about this process now”
A : “No, I mean, the document is it still being reviewed ? What’s its state ?”
B : “Ah you mean, the document state, not the process state ?”
A : “Is there a difference ?”
B : “Well, if a business process deals with the state of multiple resources, you can’t equate process state and resource state”

Initially, for ruote (a ruby workflow engine), the “state of a process” was limited to “the set of visible workitems for that process”.

Ruote.process_definition do
  cursor do
    production
    concurrence do
      qa1
      qa2
    end
    rewind :unless => '${qa_ok}'
    packaging
    delivery
  end
end

For this process, the states are “nil” (not running), “production”, “qa1” and/or “qa2”, “delivery”, and “packaging”.

(note that there is no “terminated” state, since, out of the box, ruote doesn’t do ‘process archiving’ for you).

Asking all the participants about the workitems could get expensive, especially if they’re remote participants. It’s easier to ask the engine :

p engine.process(wfid)
  # =>
  # == Ruote::ProcessStatus ==
  #    expressions : 3
  #      0!69176db85a0651e7a8d8a16426bd93df!20110107-betesuguto : define {}
  #      0_0!be7ac163b2c6ba47d6d4b24bbb83fd8c!20110107-betesuguto : cursor {}
  #      0_0_0!3b19bdf68a953597969bac507229bcf1!20110107-betesuguto : participant {"ref"=>"production"}
  #    schedules : 0
  #    stored workitems : 1
  #    variables :     {}
  #    all_variables : {"0!69176db85a0651e7a8d8a16426bd93df!20110107-betesuguto"=>{}}
  #    errors : 0

p engine.process(wfid).position
  # =>
  # [
  #   [ "0_0_0!3b19bdf68a953597969bac507229bcf1!20110107-betesuguto",
  #     "production",
  #     {} ] ]

(full gist at https://gist.github.com/769362)

What if the “state”, business-wise, has a different granularity than the simple one we derive from the participants ? Something more like a “stage”.

The “tag” attribute could help :

Ruote.process_definition do
  cursor do
    sequence :tag => 'production-stage' do
      production
      concurrence do
        qa1
        qa2
      end
    end
    rewind :unless => '${qa_ok}'
    sequence :tag => 'delivery-stage' do
      packaging
      delivery
    end
  end
end

Our process [instance] can be in “production-stage” or “delivery-stage” (or nowhere).

We can then ask for the tags of a process status :

p engine.process(wfid).tags
  # =>
  #  { "production-stage" => {
  #      "engine_id" => "engine",
  #      "wfid" => "20110107-hopakeze",
  #      "subid" => "0f62bbb3a0a19411aaef1524ebde657c",
  #      "expid" => "0_0_0" } }

(full gist at https://gist.github.com/769371)

Tags were originally meant to be used in conjunction with the undo/cancel and the redo expressions.

They later were used within cursors for jumps (along with participant names) and when “piloting” a cursor from outside.

Asking the engine about the ‘process state’ / ‘stage’ is fine, but what if a [remote] participant wants to know about the stage is in ?

Let’s add a ‘qa_stage’ tag.

Ruote.define do
  cursor do
    sequence :tag => 'production-stage' do
      production
      concurrence :tag => 'qa_stage' do
        qa1
        qa2
      end
    end
    rewind :unless => '${qa_ok}'
    sequence :tag => 'delivery-stage' do
      packaging
      delivery
    end
  end
end

The upcoming ruote 2.1.12 adds a #tags method to its workitems (a shortcut for workitem.fields[‘__tags__’]).

With a participant implementation that looks like

class MyParticipant
  include Ruote::LocalParticipant

  def consume(workitem)
    p [ workitem.participant_name, :tags, workitem.tags ]
    workitem.fields['qa_ok'] = true
    reply_to_engine(workitem)
  end
end

a run of our process will output

["production", :tags, ["production-stage"]]
["qa1", :tags, ["production-stage", "qa_stage"]]
["qa2", :tags, ["production-stage", "qa_stage"]]
["packaging", :tags, ["delivery-stage"]]
["delivery", :tags, ["delivery-stage"]]

(full gist at https://gist.github.com/769406)

There is a difference between asking the engine for the tags of a process intance and the list of tags returned by workitem#tags. The former returns all the tags currently active for the process instance, while the latter returns the list of tags that were traversed to reach the participant expression that emitted the workitem.

Another novelty brought in by ruote 2.1.12 is the possibility to listen to tag events (entering and leaving tags).

Until now, the http://ruote.rubyforge.org/exp/listen.html expression could only listen to participants (workitems reaching and returning from participants). The upcoming ruote 2.1.12 lets us listen to process instances entering and leaving tags.

For example, we could have a monitoring process that listen to process instances entering the ‘final-stage’ tag. Each time it happens, the participant ‘supervisor’ receives a copy of the workitem :

Ruote.process_definition do
  listen :to => 'final-stage', :upon => 'entering' do
    participant 'supervisor'
  end
end

By setting the :wfid attribute of the listen expression to true, we can limit the listening to the process instance to which the listen expression belongs.

It can be useful to approximate the ‘milestone’ workflow pattern :

Ruote.define do

  concurrence :count => 1 do
    # will terminate as soon as 1 branch replies (and cancel the other)

    sequence do
      participant 'a'
      participant 'b', :tag => 'milestone'
      participant 'c'
    end

    listen :to => 'milestone', :upon => 'entering', :wfid => true do

      concurrence :count => 1 do
        # will terminate as soon as 1 branch replies (and cancel the other)

        listen :to => 'milestone', :upon => 'leaving', :wfid => true
          # as soon as the tag 'milestone' is left, this listen will
          # exit (reply to the parent concurrence) and d will get cancelled

        participant 'd'
      end
    end
  end
end

The participant ‘d’ will only receive a workitem when the milestone is reached. As soon as the milestone is left, the workitem of participant ‘d’ is cancelled (removed from him).

Note how this implementation relies on “concurrence :count => 1”, a concurrence that exits as soon as 1 of its branches replies (and cancels the remaining branches).

In conclusion, tags have multiple uses, designating process state / stages, letting undo / redo segments of processes. The next ruote (2.1.12) adds tag information in workitems and lets the listen expression observe tag events (entering or leaving a tag).

 

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

 

Written by John Mettraux

January 7, 2011 at 1:24 pm

Posted in bpm, openwferu, ruby, ruote, workflow

ruote and switch

A programming language usually has some kind of super “if”, a switch statement.

Ruote is nothing more that an interpreter, a very patient one, one that can get stopped and restarted. (it also runs multiple process instances concurrently). Since it interprets some kind of high level business process gibberish, a switch statement is nice to have.

This post explores switch statement implementations for ruote, it starts with solutions for ruote 2.1.x and then shows solutions that use two new expressions in the upcoming ruote 2.1.12.

 

before ruote 2.1.12

For ruote [2.1], I wasn’t sure if a switch statement was explicitely needed. With a workitem field or a process variable, it’s easy to switch to a given subprocess.

Suppose we have an item pickup process. The system is onboard, the drivers leaves the depot knowing his immediate pickup point, but not the next, as the system asks after each pickup what to do (via the ‘get_next_task’ subprocess).

Ruote.process_definition do

  define 'coast_pickup' do
    # ...
  end
  define 'mountain_pickup' do
    # ...
  end
  define 'get_back_to_depot' do
    # ...
  end

  sequence do # body of the process

    cursor do
      subprocess 'get_next_task'
      subprocess '${next_task}'
      rewind :unless => '${next_task} == get_back_to_depot'
        # cursor rewinds unless next task is getting back to depot
    end
  end
end

All is well, our switch occurs at “subprocess ‘${next_task}'”. Note that if we wished to, we could use ref instead of subprocess if we wanted to point to participants and/or subprocesses.

But wait… Those subprocesses are defined for the whole process, they are not limited to the switch.

We can go a bit further and isolate the switch and its cases into its own subprocess.

Ruote.process_definition do

  define 'perform_next_task' do

    define 'coast_pickup' do
      # ...
    end
    define 'mountain_pickup' do
      # ...
    end
    define 'get_back_to_depot' do
      # ...
    end

    subprocess '${next_task}'
  end

  sequence do # body of the process

    cursor do
      subprocess 'check_tasks'
      subprocess 'perform_next_task'
      rewind :unless => '${next_task} == get_back_to_depot'
    end
  end
end

This is nice, we have a ‘perform_next_task’ subprocess wrapping the cases, the body of that subprocess calls the right case given the value of the “next_task” workitem field. If there is another subprocess named ‘mountain_pickup’ in the same process definition, the one in the switch subprocess will only shadow it within the case, in other words, the scope of the cases is limited to the switch subprocess.

But, you’ll say, with a real programming language not some toy process definition language, the switch and its cases are all wrapped neatly inline, they are not set apart.

Placing the switch and its cases in the main flow is OK, but it binds subprocesses… It can override subprocesses with the same name.

    cursor do
      subprocess 'check_tasks'
      sequence do
        define 'coast_pickup' do
          # ...
        end
        define 'mountain_pickup' do
          # ...
        end
        define 'get_back_to_depot' do
          # ...
        end
        subprocess '${next_task}'
      end
      rewind :unless => '${next_task} == get_back_to_depot'
    end

We need to look at the next version of ruote to solve that issue.

 

with ruote 2.1.12

For the upcoming ruote 2.1.12, I introduced a let expression :

    cursor do
      subprocess 'check_tasks'
      let do # let's have a new scope, just for our cases
        define 'coast_pickup' do
          # ...
        end
        define 'mountain_pickup' do
          # ...
        end
        define 'get_back_to_depot' do
          # ...
        end
        subprocess '${next_task}'
      end
      rewind :unless => '${next_task} == get_back_to_depot'
    end

The ‘let’ introduces a new scope, where our case subprocesses can get defined without overrides ones with the same name outside of the let block.

Now, how about something that really looks like a switch statement ?

    cursor do
      subprocess 'check_tasks'
      given '${next_task}' do
        of 'coast_pickup' do
          # ...
        end
        of 'mountain_pickup' do
          # ...
        end
        of 'get_back_to_depot' do
          # ...
        end
      end
      rewind :unless => '${next_task} == get_back_to_depot'
    end

With some Ruby/Perl -like magic, all the pickups could get covered by the same case :

    cursor do
      subprocess 'check_tasks'
      given '${next_task}' do
        of /^.+_pickup$/ do
          # ...
        end
        of 'get_back_to_depot' do
          # ...
        end
      end
      rewind :unless => '${next_task} == get_back_to_depot'
    end

The upcoming ruote 2.1.12 has this given expression. Look at its description, it not only covers “given an x of” scenarii but also “given that” ones.

 

conclusion

I still like the first version, it’s vanilla ruote “trigger the subprocess whose name is found in workitem field x”, it’s nice to have subprocesses that can be used as cases or called from other parts of the process definition.

Calling subprocesses doesn’t limit us to processes bound within the same process, processes bound at the engine level (engine variables) or external processes (given by their URI) are callable as well (see the subprocess expression doc for more information).

The given expression is interesting because it covers “given an x of” and “given that” scenarii (see the doc). It also has a “default” part which the first version doesn’t have.

Try to write processes that are concise and that read like english. And test them.

 

Written by John Mettraux

January 3, 2011 at 7:08 am

Posted in bpm, ruby, ruote, workflow

Methodismus

“Method and Routine” is the title of the chapter 4 of the book 2 of Clausewitz On War. It was written between 1816 and 1830, the author was taken away by cholera before he could complete the final revision of this work. Only the first chapter of the first book was considered finished.

I found a few passages in “Method and Routine” that could be put in perspective with business process management / workflow. In our age, it’s common to draw parallels between the conduct of war and the conduct of business, so let me reproduce those passages here. I’m very interested in the rise of ACM and its relation to BPM (see Mastering the Unpredictable), those quotes bring forth valuable reflections (for me at least).

I took them from the excellent Howard and Paret translation, for those of you who read German, here is a link to an online version of the book/chapter.

“Method,” finally, or “mode of procedure”, is a constantly recurring procedure that has been selected from several possibilities. It becomes routine when action is prescribed by method rather than by general principles or individual regulation. It must necessarily be assumed that all cases to which such a routine is applied will be essentially alike. Since this will not be entirely so, it is important that it be true of at least ‘as many as possible’. In other words, methodical procedure should be designed to meet the most probable cases. Routine is not based on definite individual premises, but rather on the ‘average probability’ of analogous cases. Its aim is to postulate an average truth, which, when applied evenly and constantly, will soon acquire some of the nature of a mechanical skill, which eventually does the right thing automatically.

(Routines) As such they may well have a place in the theory of conduct of war, provided they are not falsely represented as absolute, binding frameworks for action (systems); rather they are the best of the general forms, short cuts, and options that may be substituted for individual decisions.

Finally we have to remember that as the number of officers increases steadily in the lower ranks, the less the trust that can be placed on their true insight and mature judgement. Officers whom one should not expect to have any greater understanding than regulations and experience can give them have to be helped along by routine methods tantamount to rules. These will steady their judgment, and also guard them against eccentric and mistaken schemes, which are the greatest menace in a field where experience is so dearly bought.
Routine, apart from its sheer inevitability, also contains one positive advantage. Constant practice leads to brisk, precise, and reliable leadership, reducing natural friction and easing the working of the machine.
In short, routine will be more frequent and indispensable, the lower the level of action. As the level rises, its use will decrease to the point where, at the summit, it disappears completely. Consequently, it is more appropriate to tactics than to strategy.

But any method by which strategic plans are turned out ready-made, as if from some machine, must be totally rejected.

The chapter ends with warning about poverty of imagination and examples where routines where re-used out of context and led to [military] disaster. (Could the re-use of those quotes out of their original context led to [some kind of] disaster ?).

When it comes to strategy and tactics, here is how Clausewitz put it in the same book :

Tactic teaches the use of armed forces in the engagements.
Strategy teaches the use of engagements for the object of war.

 

Written by John Mettraux

August 6, 2010 at 6:52 am

Posted in acm, books, bpm, workflow

old process, knowledge worker

I’ve started reading Mastering the Unpredictable by Keith Swenson et al. I’d like to weave two links from from the first chapter to two things I have been exposed to.

“Mastering the Unpredictable” is a collection of Essays by Keith Swenson, Max J. Pucher, Jacob P. Ukelson and others. Those essays are reflections on “Adaptive Case Management” (ACM) and its rise alongside “Business Process Management” (BPM) (or where the BPM dream falls short). The title of the book derives its name from the unpredictability of the flow of work ACM would like to support.

the old process

The first chapter of the book is entitled “The Nature of Knowledge Work”, it starts with an example of a night rescue operation. How unfitting a BPMS (Business Process Management System) is for supporting the planning of such an operation. That reminded of a process I’ve been taught in officers’ school. You might summarize it as “assess, plan, execute” (as the first chapter of ‘MtU’ does), but let’s take the time to re-read that process definition.

I’m translating from my native language to english, here is the transcript :

The military method of decision taking acknowledges that
– only a systematic and rational process allows the integration of several people in the decision process,
– most of the time, the commander and the staff are under time pressure, are faced with doubt, only get imprecise, wrong or outdated pieces of information.

The commanding activities gather all the activities of the commander and of the staff, from the arrival of a task or the occurrence of a situation requiring an action, until the achievement of the task.

By respecting always the same sequence of commanding activities, the commander and the staff acquire the confidence necessary to lead in extraordinary and crisis situations.

The flow of commanding activities, performed successively :

1. planning the action
1.1 grasping the problem
1.2 urgency measures triggering
1.3 setting the schedule
1.4 situation assessment
1.5 decision taking
1.6 engagement plan conception
2. leading the action
2.1 issuing orders
2.2 control and conduct of the engagement

The evolution of the situation, as followed by the commander and his control and intelligence teams are the basis for direction the engagement. A change in the situation triggers a new commanding activities process.

During the action planning phase, it might be wise or it is simply necessary to start planning ‘reserved decisions’. The result of the planning of those decisions can generally be used during the action leading phase.

Apart from the direction of the ongoing engagement, the staff has to take care of the planning of subsequent potential task, this is called subsequent planning. The result of this planning is a new engagement plan or new engagement orders.

The first paragraph is interesting : “only a systematic and rational process allows the integration of several people in the decision process”. This process is a convention, shared by the members of a military organization, meant to blossom between a commander and his staff.

When I got taught this process, it often got referred as “commanding rhythm”. We were meant, as young officers, to already breath it, follow its flow. When a new mission arrived or a new situation occurred, we had to quickly grasp the problem at hand, trigger urgency measures (in order to preserve [what was left of] our initiative), determine when the decision would be taken, and all sorts of “when”, then assess, decide, plan… With the staff in our heads.

We were taught to learn and apply this process on our own, so that a) our decisions would be produced efficiently b) we could later join staff and participate in the process collaboratively for a commander. With some luck and lots of skills we could c) command, leveraging the people + the process.

the knowledge worker

The first chapter of “Mastering the Unpredictable” goes on with the distinction between routine work and knowledge work. The character of the “knowledge worker” is evoked, and that reminded me of this passage, from another book :

When an architect sits down with pen and paper to determine the strength of an abutment by a complicated calculation, the truth of the answer at which he arrives is not an expression of his own personality. First he selects the data with care, then he submits them to a mental process not of his own invention, of whose logic he is not at the moment fully conscious, but which he applies for the most part mechanically. It is never like that in war. Continual change and the need to respond to it compels the commander to carry the whole intellectual apparatus of his knowledge within him.

He must always be ready to bring forth the appropriate decision. By total assimilation with his mind and life, the commander’s knowledge must be transformed into a genuine capability.

This is taken from Carl von Clausewitz On War (Howard and Paret edition, p. 147). There is the opposition between routine work and knowledge work. Clausewitz goes on and speaks about “natural talent” for the crafty knowledge work… commander.

conclusion

These were my two links. An old process and an even older (~1830) quote about knowledge work.

The tool of the commander is his staff. What is the tool of the knowledge worker ?

For those of you lucky enough to be in Japan this friday, Keith Swenson will be speaking about Mastering the Unpredictable in Yokohama. Feel free to register at http://atnd.org/events/5954.

 

Written by John Mettraux

June 28, 2010 at 2:16 am

Posted in acm, bpm, workflow

workflow, bpm, selected resources

I have started to gather posts and blogs I think are worth a read in the workflow, BPM, Adaptive Case Management, rules, etc fields.

The list is at http://ruote.rubyforge.org/resources.html

I hope to list there resources that are sincere and passionate, and that challenge my point of view on workflow engines and enterprisey programming in general. I will try to avoid things that are press release like, that include words like “leading” or “fortune 500”, that are too closed, not leaving place for a slice of doubt.

 

Written by John Mettraux

March 31, 2010 at 4:35 am

Posted in acm, bpm, rules, ruote, workflow

ruote and decision tables

ruote 2.1.7 is out and it felt like it was time to update the CsvParticipant that could be found in ruote 0.9.

Ruote has been relying on rufus-decision for its decision table needs and ruote 0.9 was integrating a CsvParticipant directly. For ruote 2.1, the DecisionParticipant is left in the rufus-decision gem.

It’s not the first time I write something about decision tables. But I noticed that I hadn’t really written anything about how to mix ruote processes and decision tables.

Let’s consider the decision table in the adjacent figure.

Given two input values “type of visit” and “participant physician ?” the level of “reimbursement” is decided.

The CSV version of this decision table is available online.

Clearly this decision has to take place after the customer filled his reimbursement claim and it got reviewed by a clerk in the organization.

Up until now, applying the reimbursement rules was part of the tasks of the clerk (with probably some downstream checks), and our goal is to remove some of that burden.

Our process definition would look like :

Ruote.process_definition :name => 'reimbursement', :revision => '1.0' do
  cursor do
    customer :task => 'fill form'
    clerk :task => 'control form'
    rewind :if => '${f:missing_info}'
    determine_reimbursement_level
    finance :task => 'reimburse customer'
    customer :task => 'reimbursement notification'
  end
end

There are four participants involved in that process definition. Customer, Clerk and Finance our implemented with classical inbox/worklist participants : workitems for them get placed in their workqueue. (With ruote 2.1 I tend to use StorageParticipant for that or I emit the workitem via a ruote-amqp participant towards the target worklist or task manager).

“rewind” isn’t a participant, it’s a ruote expression working with the cursor expression.

That leaves us with the participant “determine_reimbursement_level” to implement.

The straightforward Ruby implementation of the participant would leverage a BlockParticipant and look like :

engine.register_participant 'determine_reimbursement_level' do |workitem|
  workitem.fields['reimbursement'] =
    case workitem.fields['type of visit']
      when 'doctor office'
        workitem.fields['participating physician ?'] == 'yes' ? '90%' : '50%'
      when 'hospital visit'
        workitem.fields['participating physician ?'] == 'yes' ? '0%' : '80%'
      when 'lab visit'
        workitem.fields['participating physician ?'] == 'yes' ? '0%' : '70%'
      else
        '0%'
    end
end

Given the values in the fields ‘type of visit’ and ‘participating physician ?’ this participant sets the value of the field ‘reimbursement’.

All is well, but you may have noticed that the decision table is rather simplistic. And how does it cope with change ? What will it look like when the rules get hairy ? Will the Ruby developer have to intervene for each change ?

(it might not be a bad thing to rely on the Ruby developer, they tend to test carefully their creations)

Those rules are produced by ‘business users’ and one of their favourite tools is the spreadsheet. Rufus-decision can read CSV output of decision tables. The decision participant would look like :

require 'rufus/decision/participant'
  # gem install rufus-decision

engine.register_participant(
  'determine_reimbursement_level',
  Rufus::Decision::Participant,
  :table => {
in:type of visit,in:participating physician ?,out:reimbursement
doctor office,yes,90%
doctor office,no,50%
hospital visit,yes,0%
hospital visit,no,80%
lab visit,yes,0%
lab visit,no,70%
  })

Well, OK, that’s nice, but… We still have to restart the application at each change to the decision table or at least re-register the participant with the updated table. Tedious.

If our business people publish the decision table as CSV somewhere over the intranet (yes, I know, it’s an old-fashioned word), the decision participant can be simplified to :

require 'rufus/decision/participant'
  # gem install rufus-decision

engine.register_participant(
  'determine_reimbursement_level',
  Rufus::Decision::Participant,
  :table => 'http://somewhere.example.com/decision_tables/reimbursement.csv')

Each time a decision will have to be taken, the table will be read. This lets business users modify the rules at will.

The whole example is packaged as reimbursement.rb. It runs the process from the command line and grabs the decision table online. It shouldn’t be too difficult to take inspiration from it and integrate decision tables in web applications (like ruote-kit or barley).

There are many open possibilities when combining a workflow engine and a decision mechanism. One immediate advantage is that they can evolve with different rhythms. They can also be used in isolation : the clerk in our process could talk the claim over the phone with the customer and run blank decisions to help the customer with incomplete forms.

Of course, you can use rufus-decision without ruote, it’s a plain ruby gem.

 

http://github.com/jmettraux/rufus-decision – the source code of rufus-decision
http://github.com/jmettraux/ruote – the source code of ruote
http://ruote.rubyforge.org – ruote’s documentation
http://groups.google.com/group/openwferu-users – the mailing list for ruote and rufus-decision

 

Written by John Mettraux

February 17, 2010 at 4:32 am

Posted in bpm, bpms, decision, ruby, rufus, rules, ruote

Ruedas y Cervezas, first instance

“ruote” is the Italian for “wheels”. It’s the name of our ruby workflow engine. The name was proposed by Tomaso Tosolini, it stuck and we liked how people pronounced it “route” which is a good fit for a tool that routes work among participants.

If you translate “ruote” to Spanish you get “ruedas”. Add a thirsty community and you get “ruedas y cervezas”.

This event was organized and hosted by abstra.cc, a madrilenian company doing wonders with Java, JRuby, Scala and their blend of agile magic.

The meeting was divided in three parts. First, abstra.cc presented their work with ruote. They’ve bound it into their “Blue Mountain ERP” to drive the necessary business processes. Workitems are communicated to participants over XMPP. The platform is Java and the languages are JRuby and Scala. As you might have guessed, they run ruote on JRuby.

Then, Iván Martínez, student in the Universidad Politécnica de Madrid presented his work on a graphical process editor for ruote. It’s based on Flex and looks very promising. The abstra.cc guys encouraged him to share his code on github.com, I hope he will follow this suggestion.

For the part when people really get thirsty, I cooked up a bunch of slides about ruote 2.0. They were presented by Nando Sola. I have embedded them here. The first half of them is about why ruote 2.0, while the latter half is about the enhancements to the ruote process definition language.

I felt it was necessary to communicate a bit about ruote 2.0, especially since, in Madrid, people have tremendously contributed to ruote 0.9 (many thanks guys !). Ruote 2.0 is a small jump from 0.9, literally a jump, not a step, explanations are required.

Then beer ensued. A ruedas y cervezas (rc2) is planned for next month, I wish I could attend. So if you’re in Madrid and you’re interested in ruote or, less narrowly, in Ruby / JRuby / Scala / Java, contact Nando Sola and join the abstra.cc and UPM guys for the rc2.

 

 

Written by John Mettraux

October 15, 2009 at 10:35 am

Posted in bpm, ruby, ruote, workflow