La necesidad de mantener cierta información ordenada es muy
normal en los módulos eBox. También es común ofrecer funciones de
reordenación. Un ejemplo claro son las reglas del firewall, las cuales
necesitan ser aplicadas en un cierto orden, y el usuario ha de
poderles cambiar su orden. La clase EBox::Order
resuelve justamente este problema.
La idea es dar un directorio a cada objeto que se quiere
mantener ordenado. Siguiendo con el ejemplo del firewall, podríamos
tener el directorio rules/ y, por debajo, un
directorio por regla. Cada regla tendría un identificador único, y eso
sería el nombre de su directorio bajo rules/. Una
regla con el id r3561 se almacenaría en
rules/r3561, y debajo de ese directorio se
almacenarían las claves con cada uno de los parámetros de la
regla. Ésta es la forma más natural de organizar elementos como reglas
de firewall, y es con una organización de este tipo para lo que está
diseñado EBox::Order para trabajar.
El mecanismo de ordenación añade un campo a los objetos para ser
ordenados. Lógicamente, se le ha denominado
order. Para usar el API de ordenación ha de
crear una instancia de EBox::Order. Su
constructor toma como argumentos: la instancia del módulo al que
pertenecen los objetos que van a ser ordenados y el directorio base
donde los objetos serán almacenados.
EBox::Order implementa estas
operaciones:
highestDevuelve la clave
order más alta de
todos los objetos.
lowestDevuelve la clave
order más baja de
todos los elementos.
nextnDado un número, devuelve la clave
order del siguiente
objeto.
prevnDado un número, devuelve la clave
order del objeto
anterior.
getDevuelve el identificador del objeto
cuya clave order es
igual a un número dado.
swapEncuentra los objetos cuyas claves
order coinciden con
los dos números dados e intercambia sus valores.
listDevuelve una referencia a un array que contenga los identificadores de todos los objetos, ordenados del más bajo al más alto.
Ejemplo 3.6. Ordenando las reglas del firewall
Vamos a ver como utiliza
EBox::Order el módulo firewall para
mantener sus reglas de forwarding ordenadas. La función
_fwdRulesOrder devuelve la instancia
de EBox::Order de las reglas del firewall:
sub _fwdRulesOrder
{
my $self = shift;
return new EBox::Order($self, "fwdrules");
}fwdrules es el directorio que
contiene todas las reglas. Otra función auxiliar privada es
_fwdRuleNumber, la cual devuelve el
número de orden para el identificador de una regla dada:
sub _fwdRuleNumber # (rule)
{
my ($self, $rule) = @_;
return $self->get_int("fwdrules/$rule/order");
}Las nuevas reglas son añadidas al final de la lista, por
lo que vamos a buscar el número de orden más alto y le
añadiremos una nueva. Este código es parte de la función
addFwdRule:
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 es una función
recubridora trivial que devuelve el número de orden más alto:
sub _lastFwdRule
{
my $self = shift;
my $order = $self->_fwdRulesOrder();
defined($order) or return 0;
return $order->highest;
}Finalmente hay dos funciones que admiten la reordenación
de las reglas, éstas son FwdRuleUp y
FwdRuleDown (mostramos sólo la
primera dado que son prácticamente idénticas):
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);
}