RevBank::Plugins - Plugin mechanism for RevBank
RevBank itself consists of a simple command line interface and a really brain dead shopping cart. All transactions, even deposits and withdrawals, are handled by plugins.
Plugins are defined in the revbank.plugins
file. Each plugin is a Perl source file in the plugins
directory. Plugins are always iterated over in the order they were defined in.
Reads the revbank.plugins
file and load the plugins.
Returns a list of fresh plugin instances.
Registers a plugin.
Calls the given hook in each of the plugins. Non-standard hooks, called only by plugins, SHOULD be prefixed with the name of the plugin, and an underscore. For example, a plugin called cow
can call a hook called cow_moo
(which calls the hook_cow_moo
methods).
There is no protection against infinite loops. Be careful!
*** CAUTION ***
It is the responsibility of the PLUGINS to verify and normalize all
input. Behaviour for bad input is UNDEFINED. Weird things could
happen. Always use parse_user() and parse_amount() and test the
outcome for defined()ness. Use the result of the parse_*() functions
because that's canonicalised.
Don't do this:
$entry->add_contra($u, $a, "Bad example");
But do this:
$u = parse_user($u) or return REJECT, "$u: No such user.";
$a = parse_amount($a) or return REJECT, "$a: Invalid amount.";
$entry->add_contra($u, $a, 'Good, except that $a is special in Perl :)');
There are two kinds of plugin methods: input methods and hooks. A plugin may define one command
input method, and can have any number of hooks.
Whenever a command is given in the 'outer' loop of revbank, the command
method of the plugins is called until one of the plugins returns either ACCEPT
or DONE
. An input method receives three arguments: the plugin object, the shopping cart, and the given input string. The plugin object (please call it $self
) is temporary but persists as long as your plugin keeps control. It can be used as a scratchpad for carrying over values from one method call to the next.
A command method MUST return with one of the following statements:
- return NEXT;
-
The plugin declines handling of the given command, and revbank should proceed with the next one.
Input methods other than
command
MUST NOT returnNEXT
. - return REJECT, "Reason";
-
The plugin decides that the input should be rejected for the given reason. RevBank will either query the user again, or (if there is any remaining input in the buffer) abort the transaction to avoid confusion.
- return ABORT, "Reason";
- return ABORT;
-
The plugin decides that the transaction should be aborted.
- return ACCEPT;
-
The plugin has finished processing the command. No other plugins will be called.
- return "Prompt", $method;
-
The plugin requires arguments for the command, which will be taken from the input buffer if extra input was given, or else, requested interactively.
The given method, which can be a reference or the name of the method, will be called with the given input.
The literal input string
abort
is a hard coded special case, and will never reach the plugin's input methods.
Hooks are called at specific points in the processing flow, and MAY introspect the shopping cart. They SHOULD NOT manipulate the shopping cart, but this option is provided anyway, to allow for interesting hacks. If you do manipulate the cart, re-evaluate your assumptions when upgrading!
Hooks SHOULD NOT prompt for input or execute programs that do so.
Hooks are called as class methods. The return value MUST be either ABORT
, which causes the ongoing transaction to be aborted, or a non-reference, which will be ignored.
Hooks SHOULD have a dummy @
parameter at the end of their signatures, so they don't break when more information is added
The following hooks are available, with their respective arguments:
- hook_register($class, $plugin, @)
-
Called when a new plugin is registered.
- hook_abort($class, $cart, @)
-
Called when a transaction is being aborted, right before the shopping cart is emptied.
- hook_prompt($class, $cart, $prompt, @)
-
Called just before the user is prompted for input interactively. The prompt MAY be altered by the plugin.
- hook_input($class, $cart, $input, $split_input, @)
-
Called when user input was given.
$split_input
is a boolean that is true if the input will be split on whitespace, rather than treated as a whole. The input MAY be altered by the plugin. - hook_add($class, $cart, $user, $item, @)
-
Called when something is added to the cart. Of course, like in
$cart->add
,$user
will be undef if the product is added for the current user.$item
is a reference to a hash with the keysamount
,description
and the metadata given in theadd
call. Changing the values changes the actual item going into the cart!Be careful to avoid infinite loops if you add new stuff.
- hook_checkout($class, $cart, $user, $transaction_id, @)
-
Called when the transaction is finalized, before accounts are updated.
- hook_checkout_done($class, $cart, $user, $transaction_id, @)
-
Called when the transaction is finalized, after accounts were updated.
- hook_reject($class, $plugin, $reason, $abort, @)
-
Called when input is rejected by a plugin.
$abort
is true when the transaction will be aborted because of the rejection. - hook_invalid_input($class, $cart, $word, @)
-
Called when input was not recognised by any of the plugins.
- hook_plugin_fail($class, $plugin, $error, @)
-
Called when a plugin fails.
- hook_user_created($class, $username, @)
-
Called when a new user account was created.
- hook_user_balance($class, $username, $old, $delta, $new, $transaction_id, @)
-
Called when a user account is updated.
Default messages can be silenced by overriding the hooks in RevBank::Messages
. Such a hack might look like:
undef &RevBank::Messages::hook_abort;
sub hook_abort($class, $cart, @) {
print "This message is much better!\n"
}
Several global utility functions are available. See RevBank::Global
Juerd Waalboer <#####@juerd.nl>
Pick your favorite OSI license.