La capacidad de un sistema o módulo para hacer que la vida o ámbito de los datos de una aplicación se prolongen mas allá de la ejecución de la aplicación se conoce con el nombre de persistencia.
Se conoce por serialización a los procesos encargados de empaquetar estructuras de datos anidadas como arrays de hashes etc. en una estructura plana y lineal. Es deseable que la representación de los datos sea independiente de la máquina, evitando problemas de representación de enteros y flotantes o del orden de los bytes.
Es posible añadir un filtro que procese los accesos a un DBM. Podemos definir una subrutina que es llamada cada vez que almacenamos un valor en el hash DBM y otra que sea llamada cada vez que se lee una entrada del hash DBM.
El formato general de uso es:
$db = tie %hash, 'DBM', ... $old_filter = $db->filter_store_key ( sub { ... } ); $old_filter = $db->filter_store_value( sub { ... } ); $old_filter = $db->filter_fetch_key ( sub { ... } ); $old_filter = $db->filter_fetch_value( sub { ... } );
Usando estos filtros podemos serializar valores del hash que sean estructuras de datos complejas.
En el siguiente código el hash DBM %h
contiene como valores referencias a
cadenas. El filtro vuelca con Data::Dumper
el valor complejo y lo comprime
usando la función compress del módulo Compress::Zlib.
El proceso inverso consiste en descomprimir y evaluar:
$ cat -n dbwithfilter.pl 1 #!/usr/bin/perl 2 use warnings; 3 use Compress::Zlib; 4 use DB_File; 5 use Data::Dumper; 6 7 unlink 'mldbmtest.dat'; 8 9 $h = tie my %db1, 'DB_File', 'mldbmtest.dat', O_CREAT | O_RDWR, 0666 10 or die "No se pudo inicializar el fichero MLDBM: $!\n"; 11 12 $h->filter_store_value(sub { $_ = compress(Dumper($_)) }); 13 $h->filter_fetch_value(sub { $_ = eval(uncompress($_)) }); 14 15 %db1 = ( 16 'alu2511' => [ 'a'x30 ], 17 'alu2233' => [ 'b'x30 ] 18 ); 19 20 print Data::Dumper->Dump( [ \%db1 ] );
Este otro programa lee el hash DBM creado por el programa anterior:
$ cat -n dbwithfilterretrieve.pl 1 #!/usr/bin/perl 2 use warnings; 3 use Compress::Zlib; 4 use DB_File; 5 use Data::Dumper; 6 7 my $h = tie my %db2, 'DB_File', 'mldbmtest.dat', O_RDWR, 0666 8 or die "No se pudo leer el fichero MLDBM: $!\n"; 9 $h->filter_store_value(sub { $_ = compress(Dumper($_)) }); 10 $h->filter_fetch_value(sub { $_ = eval(uncompress($_)) }); 11 12 print Data::Dumper->Dump( [ \%db2 ] );
Cuando se ejecutan estos dos programas obtenemos la siguiente salida:
$ dbwithfilter.pl $VAR1 = { 'alu2233' => [ 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' ], 'alu2511' => [ 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' ] }; lhp@nereida:~/Lperl/src$ dbwithfilterretrieve.pl $VAR1 = { 'alu2233' => [ 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' ], 'alu2511' => [ 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' ] };
El módulo Multi level DBM MLDBM permite guardar estructuras de datos Perl en ficheros
DBM (Véase 9.2). Para tenerlo instalado es necesario tener
previamente instaladas las rutinas Berkeley Database Manager. El módulo utiliza DBM,
proveyendo la funcionalidad para serializar estructuras de datos anidadas.
DBM es una librería que maneja tablas hash en disco.
La librería ha dado lugar a un buen número de variantes: SDBM, NDBM, GDBM, etc
las cuales pueden ser accedidas a través de los correspondientes
módulos Perl, los cualés hacen uso de tie
para proporcionar
un acceso transparente a la tabla almacenada en el disco.
Es por esto que cuando se usa admite como parámetros
una especificacción de las librerías de manejo de
DBM
y del serializador. Por ejemplo:
use MLDBM; # Manejadores por defecto: SDBM #use MLDBM qw(DB_File FreezeThaw); # Usaremos FreezeThaw para serialización #use MLDBM qw(DB_File Storable); # Usaremos Storable para serialización
Véase un ejemplo de uso del módulo MLDBM:
$ cat -n mldbmtest2.pl 1 #!/usr/bin/perl 2 use warnings; 3 use Data::Dumper; 4 use MLDBM qw( DB_File Storable ); 5 use Fcntl; # Para importar constantes O_CREAT O_RDWR 6 7 unlink 'mldbmtest.dat'; 8 9 tie my %db1, 'MLDBM', 'mldbmtest.dat', O_CREAT | O_RDWR, 0666 10 or die "No se pudo inicializar el fichero MLDBM: $!\n"; 11 12 %db1 = ( 13 'alu2511' => { 14 nombre => 'Josefina Fernández Pérez', 15 tel => '922 00 00 00', 16 fecha => '22/07/84' 17 }, 18 'alu2233' => { 19 nombre => 'Ana Feliú Forner', 20 tel => '922 00 11 22', 21 fecha => '14/06/85' 22 } 23 ); 24 25 untie %db1; 26 27 tie my %db2, 'MLDBM', 'mldbmtest.dat', O_RDWR, 0666 28 or die "No se pudo leer el fichero MLDBM: $!\n"; 29 30 print Data::Dumper->Dump( [ \%db2 ] ); 31 32 untie %db2; 33 34 exit;Cuando se ejecuta se obtiene el siguiente resultado:
./mldbmtest2 $VAR1 = { 'alu2511' => { 'fecha' => '22/07/84', 'tel' => '922 00 00 00', 'nombre' => 'Josefina Fernández Pérez' }, 'alu2233' => { 'fecha' => '14/06/85', 'tel' => '922 00 11 22', 'nombre' => 'Ana Feliú Forner' } };
Una limitación que proviene de que los MLDBM y los DBM son atados
es que sólo la escrituras directas del tipo
$db1{one} = { a => 'e' }
produce actualización.
Una escritura indirecta como $db1{one}{a} = 'e'
no produce actualización.
Véase el siguiente ejemplo:
$ cat -n dbwithfilter2.pl 1 #!/usr/bin/perl 2 use warnings; 3 use DB_File; 4 use Data::Dumper; 5 6 unlink 'mldbmtest.dat'; 7 8 $h = tie my %db1, 'DB_File', 'mldbmtest.dat', O_CREAT | O_RDWR, 0666 9 or die "No se pudo inicializar el fichero MLDBM: $!\n"; 10 11 $h->filter_store_value(sub { $_ = Dumper($_) }); 12 $h->filter_fetch_value(sub { $_ = eval($_) }); 13 14 %db1 = ( 15 'one' => { a => 'b' }, 16 'two' => { c => 'd' } 17 ); 18 19 $db1{one}{a} = 'e'; 20 21 $Data::Dumper::Indent = 0; 22 print "Asignacion indirecta:\n",Dumper( \%db1 ),"\n"; 23 24 $db1{one} = { a => 'e' }; 25 26 print "Asignacion directa:\n",Dumper( \%db1 ),"\n";Cuando se ejecuta produce la siguiente salida:
lhp@nereida:~/Lperl/src$ dbwithfilter2.pl Asignacion indirecta: $VAR1 = {'one' => {'a' => 'b'},'two' => {'c' => 'd'}}; Asignacion directa: $VAR1 = {'one' => {'a' => 'e'},'two' => {'c' => 'd'}};
La explicación reside en que al hacer $db1{one}{a} = 'e'
no se produce una llamada a STORE
; de hecho se produce una llamada a FETCH
.
Como no se llama a STORE
no se produce la modificación de la estructura de datos atada.