The need to keep certain information ordered is fairly common
across eBox modules. It is also common to provide reordering functions.
An example of this are firewall rules, which need to be applied in
a given order, and the user has to be able to change their order.
The EBox::Order
class solves just this
problem.
The idea is to give a directory to each item you want to
keep ordered. Following the firewall example, we could have a
rules/
directory and, below it, one directory
per rule. Each rule would get a unique identifier, and that
would be its directory name below rules/
.
A rule with id r3561
would be stored in
rules/r3561
, and below that directory we would
store keys with each one of the rule's parameters. This is the most
natural way of organizing items such as firewall rules, and it is with
this organization that EBox::Order
is designed
to work.
The ordering mechanism adds one field to the
items being ordered. Not surprisingly it is called
order
. To use the ordering API you have
to create an EBox::Order
instance. Its
constructor takes to arguments: the instance of the module that owns
the items being ordered, and the base directory where the items are
stored.
EBox::Order
implements these
operations:
highest
Returns the highest order
key of
all the items.
lowest
Returns the lowest order
key of
all the items.
nextn
Given a number, returns the
order
key for the next item.
prevn
Given a number, returns the
order
key for the previous
item.
get
Returns the identifier for the item whose
order
key equals a given
number.
swap
It finds the items whose order
keys match two given numbers and swaps their values.
list
Returns a reference to an array that holds the identifiers of all items, ordered from lowest to highest order.
Example 3.6. Ordering firewall rules
Let's see how the firewall module uses
EBox::Order
to keep its forwarding rules
ordered. The _fwdRulesOrder
returns the
EBox::Order
instance for the firewall
rules:
sub _fwdRulesOrder { my $self = shift; return new EBox::Order($self, "fwdrules"); }
fwdrules
is the directory that
holds all the rules. Another private helper function is
_fwdRuleNumer
, it returns the order number
for a given rule identifier:
sub _fwdRuleNumber # (rule) { my ($self, $rule) = @_; return $self->get_int("fwdrules/$rule/order"); }
New rules are appended at the end of the list, so we find the
highest order number and add one to it, this code is part of the
addFwdRule
method:
my $order = $self->_lastFwdRule() + 1; $self->set_string("fwdrules/$id/name", $id); $self->set_string("fwdrules/$id/action", $action); $self->set_bool("fwdrules/$id/active", 1); $self->set_int("fwdrules/$id/order", $order);
_lastFwdRule
is a trivial wrapper that
returns the highest ordered number:
sub _lastFwdRule { my $self = shift; my $order = $self->_fwdRulesOrder(); defined($order) or return 0; return $order->highest; }
Finally there are two methods to allow rule
reordering, they are FwdRuleUp
and
FwdRuleDown
(only the first one is shown
here since they are almost identical):
sub FwdRuleUp # (rule) { my ($self, $rule) = @_; my $order = $self->_fwdRulesOrder(); defined($order) or return; my $num = $self->_fwdRuleNumber($rule); if ($num == 0) { return; } my $prev = $order->prevn($num); $order->swap($num, $prev); }