eco has asked for the wisdom of the Perl Monks concerning the following question:

Hello,

I have a class A which inherits from a NonMoose class. Therefor I use FOREIGNBUILDARGS to set the default for the nonMoose class constructor argument:

package ClassA; use Moose; use MooseX::NonMoose; extends 'aNonMooseClass'; has 'param2' => ( isa=>'Str', default=>'Default param2 in class A' ); sub FOREIGNBUILDARGS { my $class = shift; my %args = @_; my $param1 = exists $args{param1} ? $args{param1} : 'Default param +1 in class A'; return ( $param1 ); }

Now I have a regular Moose class B which inherits from class A.

param1 is to be fixed to 'PARAM1-CLASSB' and param2 is to be fixed to 'PARAM2-CLASSB' for all instances. So, I figured this declaration:

package ClassB; use Moose; extends 'ClassA'; around FOREIGNBUILDARGS => sub { my $orig = shift; my $class = shift; return $class->$orig(param1=>'PARAM1-CLASSB',@_); }; around BUILDARGS => sub { my $orig = shift; my $class = shift; return $class->$orig(param2=>'PARAM2-CLASSB', @_); };

As ClassB is based on ClassA I'd prefer to have a uniform way of setting param1 and param2 in ClassB. I don't bother in making ClassA more complex, but I want ClassB to be as user-friendly as possible. Can this be achieved ?

Thanks!

-- ecocode

Replies are listed 'Best First'.
Re: NonMoose and (clean) inheritance
by tobyink (Canon) on Mar 19, 2014 at 13:40 UTC

    Not quite sure what you're asking. Are you looking for a way to change the default for an attribute in a subclass?

    package ClassB; use Moose; extends 'ClassA'; has '+param2' => (default => 'PARAM2-CLASSB'); around FOREIGNBUILDARGS => sub { my $orig = shift; my $class = shift; return $class->$orig(param1=>'PARAM1-CLASSB',@_); };

    Or better yet, use builders instead of defaults:

    package ClassA; use Moose; use MooseX::NonMoose; extends 'aNonMooseClass'; has 'param2' => ( isa=>'Str', builder => '_build_param2'); sub FOREIGNBUILDARGS { my $class = shift; my %args = @_; my $param1 = exists $args{param1} ? $args{param1} : $class->_build +_param1; return ( $param1 ); } sub _build_param1 { return 'Default param1 in class A'; } sub _build_param2 { return 'Default param2 in class A'; } package ClassB; use Moose; extends 'ClassA'; sub _build_param1 { return 'PARAM1-CLASSB'; } sub _build_param2 { return 'PARAM2-CLASSB'; }
    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name

      Thanks for your answer! My preferred declaration of ClassB is as follows:

      package ClassB; use Moose; extends 'ClassA'; has '+param1' => (default => 'PARAM1-CLASSB'); has '+param2' => (default => 'PARAM2-CLASSB');

      But since this doesn't work for the param1, I figured to use FOREIGNBUILDARGS and BUILDARGS to have some similarity in usage. Your builders solution seems to do this better though. So thank you very much for the idea!

      But if I could change ClassA to allow above syntax for ClassB that would be awesome

      -- ecocode

        Builders are just generally a better idea than defaults. (MooseX::Manual::BestPractices even tells you so.) They give you a nice clean way to override the behaviour in subclasses.

        If you use Moo or MooseX::AttributeShortcuts then you can even do:

        has foo => (is => 'ro', builder => sub { ... });

        ... and it will automatically get "translated" to:

        has foo => (is => 'ro', builder => '_build_foo'); sub _build_foo { ... }

        ... so builders can be just as convenient as defaults are.

        use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name