2008-10-23 16 views
6

Tengo este script Perl con muchas constantes definidas de archivos de configuración. Por ejemplo:¿Cómo puedo reducir la duplicación en constantes?

use constant { 
LOG_DIR        => "/var/log/", 
LOG_FILENAME      => "/var/log/file1.log", 
LOG4PERL_CONF_FILE     => "/etc/app1/log4perl.conf", 
CONF_FILE1       => "/etc/app1/config1.xml", 
CONF_FILE2       => "/etc/app1/config2.xml", 
CONF_FILE3       => "/etc/app1/config3.xml", 
CONF_FILE4       => "/etc/app1/config4.xml", 
CONF_FILE5       => "/etc/app1/config5.xml", 
}; 

Quiero reducir la duplicación de "/ etc/app1" y "/ var/log", pero utilizando las variables no funciona. También el uso de constantes previamente definidas no funciona en el mismo "uso constante de bloqueo". Por ejemplo:

use constant { 
LOG_DIR        => "/var/log/", 
FILE_FILENAME      => LOG_DIR . "file1.log" 
}; 

no funciona.

El uso de bloques de "uso constante" separados soluciona este problema, pero eso agrega una gran cantidad de código innecesario.

¿Cuál es la forma correcta de hacerlo?

Gracias.

Respuesta

7

probablemente me escribo de esta manera:

use Readonly; 

Readonly my $LOG_DIR   => "/var/log"; 
Readonly my $LOG_FILENAME  => "$LOG_DIR/file1.log"; 
Readonly my $ETC    => '/etc/app1'; 
Readonly my $LOG4PERL_CONF_FILE => "$ETC/log4perl.con"; 

# hash because we don't have an index '0' 
Readonly my %CONF_FILES => map { $_ => "$ETC/config$_.xml" } 1 .. 5; 

Sin embargo, eso es todavía un montón de código, pero sí elimina la duplicación y eso es una victoria.

¿Por qué sus archivos de registro son numéricos? Si comienzan con 0, una matriz es una mejor opción que un hash. Si se nombran, son más descriptivos.

+0

Gracias por la respuesta, el nombre de registro no es realmente numérico, simplemente los cambié de esa manera para el ejemplo. –

3

Eso no va a funcionar, por desgracia. La razón para esto es que estás usando funciones ('constantes') antes de que se definan. Usted los evalúa antes de llamar al constant->import.

El uso de variables no funciona porque las instrucciones de uso se evalúan en tiempo de compilación. La asignación a variables solo se realiza en tiempo de ejecución, por lo que aún no se definirán.

La única solución que puedo dar es dividirla en múltiples declaraciones use constant. En este caso, dos declaraciones funcionarán (una para LOG_DIR y CONF_DIR, otra para el resto).

8

Usando separada "uso constante" bloques hace solucionar este problema, pero eso añade una gran cantidad de código que no sean necesarios.

¿Realmente?

use constant BASE_PATH => "/etc/app1"; 

use constant { 
    LOG4PERL_CONF_FILE     => BASE_PATH . "/log4perl.conf", 
    CONF_FILE1       => BASE_PATH . "/config1.xml", 
    CONF_FILE2       => BASE_PATH . "/config2.xml", 
    CONF_FILE3       => BASE_PATH . "/config3.xml", 
    CONF_FILE4       => BASE_PATH . "/config4.xml", 
    CONF_FILE5       => BASE_PATH . "/config5.xml", 
}; 

No veo muchos problemas con esto. Ha especificado la ruta base en un solo punto, respetando el principio DRY. Si asigna base_path con una variable de entorno:

use constant BASE_PATH => $ENV{MY_BASE_PATH} || "/etc/app1"; 

... a continuación, tiene una forma barata de reconfigurar su constante sin tener que editar el código. ¿Qué hay para no gustar sobre esto? "Base_path"

Si realmente desea reducir el repetitivo concatenación, se podría añadir un poco de maquinaria para instalar las constantes de sí mismo y de los factores que distancia:

use strict; 
use warnings; 

use constant BASE_PATH => $ENV{MY_PATH} || '/etc/apps'; 

BEGIN { 
    my %conf = (
     FILE1 => "/config1.xml", 
     FILE2 => "/config2.xml", 
    ); 

    for my $constant (keys %conf) { 
     no strict 'refs'; 
     *{__PACKAGE__ . "::CONF_$constant"} 
      = sub() {BASE_PATH . "$conf{$constant}"}; 
    } 
} 

print "Config is ", CONF_FILE1, ".\n"; 

Pero en este punto creo que el balance ha pasado de Correct a Nasty :) Para empezar, ya no puedes grep para CONF_FILE1 y ver dónde está definido.

4
use constant +{ 
    map { sprintf $_, '/var/log' } (
     LOG_DIR   => "%s/", 
     LOG_FILENAME  => "%s/file1.log", 
    ), 
    map { sprintf $_, '/etc/app1' } (
     LOG4PERL_CONF_FILE => "%s/log4perl.conf", 
     CONF_FILE1   => "%s/config1.xml", 
     CONF_FILE2   => "%s/config2.xml", 
     CONF_FILE3   => "%s/config3.xml", 
     CONF_FILE4   => "%s/config4.xml", 
     CONF_FILE5   => "%s/config5.xml", 
    ), 
}; 
0

Dependiendo de lo que esté haciendo, es posible que no desee constantes en absoluto. Sobre todo, escribo cosas que otras personas usan para hacer sus cosas, por lo que resuelvo este problema de una manera que da flexibilidad a otros programadores. Hago estas cosas en métodos:

sub base_log_dir { '...' } 

sub get_log_file 
     { 
     my($self, $number) = @_; 

     my $log_file = catfile( 
     $self->base_log_dir, 
     sprintf "foo%03d", $number 
     ); 
     } 

Al hacerlo de esta manera, puedo ampliar o anular fácilmente las cosas.

Sin embargo, esto pierde el valor del plegado constante, por lo que debe pensar qué tan importante es para usted.

Cuestiones relacionadas