How to reduce duplication in constants?

I have this Perl script with many specific configuration file constants. For example:

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", }; 

I want to reduce duplication of "/ etc / app1" and "/ var / log", but using variables does not work. Also, the use of previously defined constants does not work in the same "used constant block". For example:

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

does not work.

Using separate "use constant" blocks circumvents this problem, but it adds a lot of unnecessary code.

What is the right way to do this?

Thanks.

+6
perl constants refactoring
source share
5 answers

I would probably write it like this:

 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; 

However, this is still a lot of code, but it removes duplication and that wins.

Why are your log files numerical? If they start at 0, an array is a better choice than a hash. If they are named, they are more descriptive.

+7
source share

Using separate "use constant" blocks does bypass this problem, but adds a lot of unnecessary code.

It's true?

 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", }; 

I do not see a lot of problems with this. You indicated the base path at only one point, while respecting the DRY principle. If you assign BASE_PATH to an environment variable:

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

... then you have a cheap way to reconfigure your constant without having to edit your code. What is not to like there?

If you really want to reduce the repeated concatenation of "BASE_PATH.", You can add some mechanisms for setting constants yourself and not take this into account:

 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"; 

But at this moment I think that the balance was thrown from the right to the opposite :) For starters, you can no longer grep for CONF_FILE1 and see where it is defined.

+8
source share
 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", ), }; 
+4
source share

This will not work, unfortunately. The reason for this is because you use functions ("constants") before defining them. You evaluate them before calling constant->import .

Using variables does not work because usage statements are evaluated at compile time. Variables are assigned only at run time, so they will not be defined yet.

The only solution I can give is to split it into several use constant statements. In this case, two statements will be executed (one for LOG_DIR and CONF_DIR , the other for the rest).

+3
source share

Depending on what you are doing, you may not want constants at all. Basically, I write stuff that other people use to make their stuff, so I solve this problem in a way that gives other programmers the flexibility. I do these things in methods:

  sub base_log_dir { '...' } sub get_log_file { my( $self, $number ) = @_; my $log_file = catfile( $self->base_log_dir, sprintf "foo%03d", $number ); } 

This way I can easily expand or redefine things.

By doing this, you lose the importance of constant bending, so you need to think about how important it is for you.

0
source share

All Articles