h2. Decision Tables

<i>(work in progress)</i>

Decision tables are not part of the core OpenWFEru, they belong to the <tt>openwferu-extras</tt> gem.

Decision tables are mainly CSV tables (Comma Separated Value). OpenWFEru uses the <tt>csv</tt> standard Ruby library for parsing text comma separated values.

Decision tables are implemented as a "CsvTable":rdoc/classes/OpenWFE/Extras/CsvTable.html which can be used outside of the OpenWFEru context, standalone.

When used within a business process, csv tables are wrapped within "CsvParticipant":rdoc/classes/OpenWFE/Extras/CsvParticipant.html instances.

Decision tables were first explained in the "ruby decision tables":http://jmettraux.wordpress.com/2007/02/11/ruby-decision-tables/ blog post.


h3. Decision Tables outside of Business Processes (standalone)

<b>update</b> : The decision table implementation found in OpenWFEru got moved to its own project / gem named "rufus-decision":http://rufus.rubyforge.org/rufus-decision/ You'll find the latest information about those Ruby decision tables there.

At first, let's have a look at decision tables outside of the business process context, in a 'standalone' way.

<img src="images/decision_table.png" align="left" style="margin:7px;" />In this example, depending on the origin, the type of tea and a max_price, the decision table determines how many units should be ordered.

This Google Spreadsheets table features 3 columns for the input value and one for the output value (the number of units to order).

Input column labels are prefixed with "in:", output with "out:".

There is one rule per [data] row. They are evaluated in order. Input column labels are considered.

In order to use the table, it has to be saved as a CSV (comma separated values) file.

<img src="images/decision_csv.png" align="right" style="margin:7px; border: 1px solid;" />Blank lines will be ignored.

A CsvTable transformation process takes as input a hash (a map of values), the output is the [transformed] hash itself.
Input and output labels are referencing keys in that hash. For example, <tt>in:country</tt> references the hash key 'country' (in input).

As soon as all the input columns of a row do match, the ouptut column values are applied.


h4. Decision table quickstart

("original blog post":ihttp://jmettraux.wordpress.com/2007/08/06/ruby-decision-table-quickstart/)

Since OpenWFEru 0.9.14, a decision table can be referenced via an URL. This is very handy since Google Spreadsheets allows you to publish "sheets":http://spreadsheets.google.com/pub?key=pCkopoeZwCNsMWOVeDjR1TQ as "CSV":http://spreadsheets.google.com/pub?key=pCkopoeZwCNsMWOVeDjR1TQ&output=csv&gid=0.

The csv looks like :

<pre>
in:weather,in:month,out:take_umbrella?

raining,,yes
sunny,,no
cloudy,june,yes
cloudy,may,yes
cloudy,,no
</pre>

You can thus directly instantiate a CsvTable with the URL of the CSV view of the spreadsheet :

#:code#ruby#
require 'rubygems'
require 'openwfe/extras/util/csvtable'

table = OpenWFE::Extras::CsvTable.new(
    "http://spreadsheets.google.com/pub?key=pCkopoeZwCNsMWOVeDjR1TQ&output=csv&gid=0")

h = { "weather" => "cloudy", "month" => "may" }
table.transform h

puts h['take_umbrella?'] 
    #
    # will yield "yes"
#:code#


h4. Ruby code within decision tables

It's perfectly OK to have ruby code inside of a decision table. It may be useful to link the decision table to other software tools via Ruby.

Here is a simple example :

<pre>
in:weather,in:month,out:take_umbrella?

raining,,yes
sunny,,no
cloudy,june,${r: (rand * 100).to_i > 60 ? "yes" : "no"  }
cloudy,may,yes
cloudy,,no
</pre>

In a cloudy June day, there are 60% chances that an umbrella might be needed, Ruby will toss the [leaded] coin.

Note that the Ruby code is wrapped within a ${r: } envelope, the more explicit ${ruby: } may be used as well.

When using Ruby code within CSV used via a CsvParticipant, if the application_context parameter <tt>:ruby_eval_allowed</tt> is not explicitely set to true, Ruby code will always evaluate to "" (empty string).

#:code#ruby#
    engine.application_context[:ruby_eval_allowed] = true
#:code#


h3. Decision Tables in Business Processes

As part of business processes, decision tables are used for deciding, the 'if' and the 'case' expressions are then used for applying the decision. This usage could be referred as 'deciding and routing'

There is a second common usage, 'transforming a workitem', changing its payload based on its initial values in that payload.

h4. deciding and routing

h4. transforming a workitem

Taking a decision usually implies taking N elements as input (workitem fields or process variables) and outputting one element (a workitem field). But decision tables can be used to take as input N elements and output M elements, and if all those M elements are workitem fields, then we have a tool for transforming workitems as well.

h4. testing the CsvParticipant

Here is an example of "CsvParticipant testing":http://viewvc.rubyforge.mmmultiworks.com/cgi/viewvc.cgi/trunk/openwfe-ruby/test/extras/ft_19_csv.rb?root=openwferu&view=markup, it comes from the OpenWFEru test suite.


h3. Decisions without the Decision Tables

In certain cases, decision tables are not the best tool for "elaborating" a decision within a business processes.


h4. plain ruby participants

Sometimes, it feel more comfortable to implement a decision with a generic programming language like Ruby :

#:code#ruby#
CAT_PREMIUM = 1
CAT_STANDARD = 0
CAT_BAD = -1

engine.register_participant :decide_customer_category do |workitem|

    workitem.customer_category = if list_first_class_customers.include?(workitem.customer_name)
        CAT_PREMIUM 
    elsif workitem.monthly_fee > 110
        CAT_PREMIUM 
    elsif workitem.debt > 0
        CAT_BAD 
    else
        CAT_STANDARD
    end
end
#:code#

h4. rule engines

