Elk: override the `required`ness attribute in a derived class

I have a base class and a dozen derived classes. All but one derived class require an attribute with a name key. So I could add it to the eleven derived classes and leave the eleventh one.

However, laziness is what it is, I would like to add an attribute to the base class, thereby avoiding repeating the declaration eleven times and adding what I think is consistency and simplicity.

Now this creates a problem for a single class that does not require an attribute key. Note that there is no harm if this class has this attribute, but it does not require it.

My idea was to resolve this using a flag method is_strictthat is called from BUILDARGSto decide whether keyor not to. Here is a simple script example to illustrate this (well, I inverted the concept, an attribute keyis required only in one case (instead of all cases except one), but this problem remains under the influence of this inversion):

#!perl
package Bla;
use Moose;
use Carp ();
has grop => is => 'ro', isa => 'Str'; # optional
has key  => is => 'ro', isa => 'Int'; # required in all but one cases
# required => should depend on $class->is_strict;
sub is_strict { 0 } # not strict by default as per this base class
# imagine a bunch of other stuff here shared by all derived classes
around BUILDARGS => sub {
    my $orig = shift;
    my $class = shift;
    my $args = @_ == 1 ? shift : { @_ };
    Carp::croak 'key missing'
        if not exists $args->{key}
        and $class->is_strict;
    return $class->$orig( @_ );
};
no Moose; __PACKAGE__->meta->make_immutable;

package Bla::Eins;
use Moose; extends 'Bla';
no Moose; __PACKAGE__->meta->make_immutable;

package Bla::Zwei;
use Moose; extends 'Bla';
no Moose; __PACKAGE__->meta->make_immutable;

package Bla::Drei;
use Moose; extends 'Bla';
override is_strict => sub { 1 }; # but here it is required
no Moose; __PACKAGE__->meta->make_immutable;

package main;
use Test::More;
use Test::Exception;
lives_ok  { Bla::Eins->new };
lives_ok  { Bla::Zwei->new };
throws_ok { Bla::Drei->new } qr/key missing/;
lives_ok  { Bla::Drei->new( key => 99 ) };
done_testing;

It works, but is there a better way to achieve what I want?

+3
source share
2 answers

Well, I was a little dumb because I did not try the most obvious solution, just overriding the attribute definition in a derived class, where it requireddiffers from the default. Here we go:

#!perl
package Bla;
use Moose;
use Carp ();
has grop => is => 'ro', isa => 'Str'; # optional
has key  => is => 'ro', isa => 'Int'; # required in all but one cases
# imagine a bunch of other stuff here shared by all derived classes
no Moose; __PACKAGE__->meta->make_immutable;

package Bla::Eins;
use Moose; extends 'Bla';
no Moose; __PACKAGE__->meta->make_immutable;

package Bla::Zwei;
use Moose; extends 'Bla';
no Moose; __PACKAGE__->meta->make_immutable;

package Bla::Drei;
use Moose; extends 'Bla';
# prefix an attribute you're overriding with a "+" sign
has '+key' => is => 'ro', isa => 'Int', required => 1;
no Moose; __PACKAGE__->meta->make_immutable;

package main;
use Test::More;
use Test::Exception;
lives_ok  { Bla::Eins->new };
lives_ok  { Bla::Zwei->new };
throws_ok { Bla::Drei->new } qr/\bkey\b.*\brequired\b/;
lives_ok  { Bla::Drei->new( key => 99 ) };
done_testing;

, , Mooseland , .

, , , +, Moose croak, , . .

+6

has '+key' => (requires => 1) requires=> 1 has '+key' => (requires => 0), . 11 , , , 11 .

+1
source

All Articles