Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl Moose with multiple mutually-dependent attributes

How can I create my Perl Moose class such that multiple mutually-dependent attributes are built in the proper order? In my case, I want to configure my Log::Log4perl object from a configuration file that is specified in my main configuration file.

like image 734
Mark Leighton Fisher Avatar asked Apr 26 '26 03:04

Mark Leighton Fisher


2 Answers

If the initialization is truly mutually-dependent, you have a problem, since one of the attributes must necessarily be initialized before the other. But nothing about your description supports this. It sounds like creating the logger requires the config file, and that's it.

Just make the creation of logger lazy, giving config a chance to be set.

package Class;

use Moose;

has config => ( ... );

has logger => (
    isa     => 'Str',
    is      => 'ro',
    lazy    => 1,
    default => sub {
        my $self = shift;
        my $config = $self->config
            or die(...);

        return Log::Log4perl->get_logger( $config->{logger} );
    },
    handles => [qw( info warn error fatal )],
);

Sample usage

my $o = Class->new( config => "..." );
$o->warn("...");

or

# Assuming config isn't required=>1.
my $o = Class->new();
$o->config("...");
$o->warn("...");
like image 80
ikegami Avatar answered Apr 28 '26 18:04

ikegami


You can use the before method modifier (method hook) to force the attributes to build in a specific order:

package Z;
use Moose;

has config => (
    isa     => 'HashRef',
    is      => 'ro',
    lazy    => 1,
    default => sub { print STDERR "called 'config'\n"; return { a => 'b' }; },
);

has logger => (
    isa     => 'Str',
    is      => 'ro',
    lazy    => 1,
    default => sub { print STDERR "called 'logger'\n"; return 'Fred'; }
);

before 'logger' => sub {
    my $self = shift;
    print STDERR "called before 'logger'\n";
    die "No logger!: $!\n" if !defined $self->config;
    return;
};

package A;

my $z = Z->new();

print "logger: ", $z->logger, "\n";
print "config{a}: ", $z->config->{a}, "\n";

The output of this sample code, showing that config is built before logger via the before method modifier:

called before 'logger'
called 'config'
called 'logger'
logger: Fred
config{a}: b
like image 38
Mark Leighton Fisher Avatar answered Apr 28 '26 19:04

Mark Leighton Fisher



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!