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 innocenceJ’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