Verby - A framework for compositing and sequencing steps of execution.
use Verby::Dispatcher; use Verby::Step::Closure qw/step/; my $d = Verby::Dispatcher->new; my $s = step "Verby::Action::Foo"; my $other = step "Verby::Action::Bar"; $s->depends($other); $d->add_step($s); $d->do_all; # first $other, then $s
Verby was originally written to implement the backend of an installer.
An installer conceptually has two inputs, which are combined to get the job done.
The first is the user's configuration (regardless of how it's provided) - the parameters to influence the installation, and the second is the recipe for the actual execution of the installation, a sort of template if you will, that the configuration fills in.
Verby defines the two concepts (not only for installers), and provides some useful code to get them working.
In spirit it's very similar to
Makefiles, except that the data involved are mentally closer to Perl than they are to sh(1).
This core concept discusses the way that user inputs are handed down to the execution sequence.
A config source is basically a key to value mapping.
It's an object where you ask
and you get the value.
If the object makes a query to the user, like displaying a prompt for a certain key, then the answer should be cached, as each key will probably be asked for about 3-5 times for each step.
A config hub is a union of several config sources. For example, a typical command line app would have three config sources:
my $args = Verby::Config::Source::ARGV->new; # foo --key=value my $config_file = Verby::Config::Source::XML->new("config.xml"); my $conifg_prompt = Verby::Config::Source::Prompt; # last resort my $config_hub = Verby::Config::Data->new($args, $config_file, $config_prompt); $config_hub->key;
The config hub is sort of like an aggregate config source. It will ask it's parents for the key.
The key ordering is symmetric (like role composition order), that is if two parents both contain the key it's as if there is no match, and a warning is emitted.
A context is like a lexical config, for each step.
my $context = $config_hub->derive;
It is mutable:
$context->key("foo"); # set it
It can be further derived
my $subcontext = $context->derive; $subcontext->key; # "foo"
$subcontext->key("bar"); $subcontext->key; # "bar" $context->key; # still "foo";
And it can re-export:
$subcontext->export("key"); $context->key; # "bar" instead of "foo"
...as long as it's parent is mutable:
$context->export("key"); # fatal error # because $config_hub is not mutable
It also provides a magic field:
my $l = $c->logger;
See MooseX::LogDispatch. If a logger is in a parent of the context it will be returned instead.
The Verby execution model is much like a
There is a tree of dependant steps, which will all be executed when necessary and possible.
By adding a step to a dispatcher, all it's dependencies are traversed and added too.
Any step that is inserted is immediately asked whether it
Subsequently "do_all" in Verby::Dispatcher is called. Dependencies are resolved, and any step that has no dependencies, and has not yet claimed it's satisfied is executed.
Every step gets it's own context to play around in. This context persists between invocations of all the methods.
A step which
provides_cxt is a special case: Instead of deriving the global context generated for the whole run, an intermediate context is derived first, and then that step's context is derived from the intermediate one. Any step which depends on this step, will have it's context derived from the intermediate context too.
When writing actions to back steps up make sure they will fail properly. For example, missing fields in
verify might be due to the fact that some step did not export a necessary field yet. In this case
verify should just return false, and will be asked again in due time.
Actions should be short and sweet, doing as little as possible. Remember that a step being a delegator for actions is not limited to using only one action, so if you need to combine procedures, still try to refactor them.
Long running steps, especially ones which drive external processes, like ones using Verby::Action::Run should be asynchroneous. This allows non-interdependant steps to be executed in parallel.
Actions should minimize partial side effects. Transactional behavior is desired for the incremental process to be robust. Ideally the
do part of an action will undo previous runs, and the
verify part will only be true if the side effect is marked as consistent.
Context fields should be exported from the verification stage, because sometimes a step will not be executed. If execution is necessary to figure out a field that may be exported, then verification should be false.
None that we are aware of. Of course, if you find a bug, let us know, and we will be sure to fix it.
We use Devel::Cover to test the code coverage of the tests, below is the Devel::Cover report on this module's test suite.
Yuval Kogman, <firstname.lastname@example.org> stevan little, <email@example.com>
Copyright 2005-2008 by Infinity Interactive, Inc.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.