DFA::Set::File - DFA implemented on the filesystem
use DFA::Set::File; $dfa=DFA::Set::File->new( Directory=>"$ENV{HOME}/Maildir", States=>{ cur=>[], tmp=>[qw(new)], new=>[qw(cur)], }, Terminal=>'cur');
my $unique="12341234123.foo.com" $fd=$dfa->open_fd(">", $unique, "tmp"); eval { # read $data from somewhere $fd->print($data); }; $fd->close; if($@) { $dfa->remove($unique); die $@; }
$dfa->transition($unique, 'new');
DFA::Set::File is an implementation of DFA::Set that uses files and directories to make sure that transitions are atomic. This has the advantage of never being able to loose information if the program crashes unexpectedly (unless the filesystem crashes, in which case there's not much you can do).
An example of an application that uses this approche is qmail with Maildirs. New mail is crated in a 'tmp' state. Then moved to a 'new' state. After it has been looked at once, it is moved to a 'cur' state.
DFA::Set::File uses File::Spec to create pathnames. This allows it to be as portable as possible.
All methods work as documented in DFA::Set
, with a few extenstions and caveats detailed here.
my $dfa=DFA::Set::File->new(..., Directory=>$dir, ...);
Each state is it's own directory. All directories are created under the one
specified via the Directory
parameter. All directories are created at instansiation time. If they can't
be created, new
will croak.
$dfa->add($name, $payload, $state);
Creates a file and writes $payload
to it. This means that
$payload
MUST be a scalar and not a reference. Serialisation
is left as an excercise to the caller.
my $fd=$dfa->open_fd($mode, $name[, $state]);
Opens the file of the token $name
and returns a IO::File
object. While this breaks strict OO, it allows you to receive and save data
as robustly as possible. Otherwise, you would have to save all data into
memory, then use add()
to write to the file. That wouldn't be
as safe.
See IO::File
for details.
$dfa->remove($name[, $state]);
Uses unlink
to remove the token from the filesystem. Because
find_state
is relatively expensive, you can specify $state
. However, if the token moved and you didn't know it, this method will
croak.
@names=$dfa->list($state);
This function returns all the tokens in a given state. This could be useful for cronjobs to get a list of tokens to process as a batch.
Uses opendir/readdir/closedir
so it's relatively expensive.
$dfa->transition($name, $newstate);
Uses rename
to move token $name
from it's current state to
$newstate
. Because rename
is an atomic operation on most useful systems, the token should never be
lost. Barring catastrphic filesystem corruption, that is.
my $state=$dfa->find_state($name);
Returns the state that the token $name
is in.
Relatively expensive operation, however.
my $payload=$dfa->find_payload($name[, $state]);
Returns the payload of token $name
. Because find_state
is relatively expensive, you can specify $state
. However, if the token moved and you didn't know it, this method will
croak.
Philip Gwyn <cpan at pied.nu>
DFA::Set, DFA::Set::Memory, perl(1).