processi

about processes and engines

volute

Vos luttes partent en fumée
Vers des flûtes enchantées Et de cruelles espérances
Me lancent Des dagues et des lances En toute innocence

J’cloue des clous sur des nuages Un marteau au fond du garage
J’cloue des clous sur des nuages Sans échafaudage

volutes Alain Bashung (funny auto-translation)

 

This started out in a notebook. I wanted to do something about ‘state’ and ‘lifecyle’, something like a state machine for multiple objects or a rule system for families of resources.

It ended up as something that feels like the subset of an aspect oriented framework. Subset because in its most vanilla usage it only cares about calls to set methods, and there is no “before” (at first glance).

This Ruby gem is named volute. Here is an example of its usage :

require 'volute'

class Package
  include Volute

  attr_accessor :location
  attr_accessor :delivered
end

volute Package do
  # filters non Package instance out

  volute :delivered => true do
    # applies when delivered switches to true
    object.emit_invoice
  end

  volute :location do
    # filters out changes that are not for the :location attribute

    volute 'SFO' => :any, 'FRA' => :any do
      # SFO and FRA are our international hubs, for any package transiting from there,
      # initial 'international' tracking
      TrackingSystem.track_international(object)
    end
  end
end

The “include Volute” reworks the attr_accessor, and triggers the evaluation of the volutes on each set.

It’s not always necessary to include Volute. Here is a example of [business] rules derived from Ruleby (volute is very dumb compared to Ruleby, the example doesn’t do justice to that project).

Here is the diagnosis example with volute :

require 'volute'

class Patient

  attr_reader :name
  attr_accessor :fever
  attr_accessor :rash
  attr_accessor :spots
  attr_accessor :sore_throat
  attr_accessor :innoculated

  attr_accessor :diagnosis

  def initialize(name)
    @name = name
    @symptoms = {}
    @innoculated = false
  end

  def diagnose!
    Volute.apply(self) # trigger evalution of volutes
    return @diagnosis
  end
end

volute Patient do

  volute :fever => :high, :spots => :true, :innoculated => true do
    object.diagnosis = 'measles'
    over # prevent further evals
  end
  volute :spots => true do
    object.diagnosis = 'allergy (spots but no measles)'
  end
  volute :rash => true do
    object.diagnosis = 'allergy (rash)'
  end
  volute :sore_throat => true, :fever => :mild do
    object.diagnosis = 'flu'
  end
  volute :sore_throat => true, :fever => :high do
    object.diagnosis = 'flu'
  end
end

pat = Patient.new('alice')
pat.rash = true

puts "#{pat.name} : diagnosed with #{pat.diagnose!}"

pat = Patient.new('bob')
pat.sore_throat = true
pat.fever = :high

puts "#{pat.name} : diagnosed with #{pat.diagnose!}"

 

The readme for volute is quite extensive. There are a few examples included.

You’ve already seen the diagnosis one, it aimed at using volute for some mini rule engine (nothing fancy at all).

There is an example about a simple (kph = km/h) equation where each time an attribute is changed, other attributes get adjusted.

The light example simply keeps tracks of the last time an attribute got modified.

There is a book in a bookshop state machine example with a variation, to explore state volutes and transition volutes.

Note that state machines built with volute are incomplete, they know nothing about further transitions (could I even call them state machines ? Rather a ‘state transition system’).

Traffic could be fun : it tracks the state of two “lines” and adjust the colour of their lights according to the number of cars waiting. It’s more in line with the “multiple objects” goal I had initially (still here somehow, but examples with 1 class are smaller).

Not sure if it’s worth moving logic out of objects, but it is a fun experiment.

 

http://github.com/jmettraux/volute

 

Written by John Mettraux

October 12, 2010 at 7:28 am

Follow

Get every new post delivered to your Inbox.