Building on from a previous query regarding Class::Struct vs Object::Accessor, I’d like to find the best way to assign package subs from coderefs in object constructors. Hey, I’ve got the OO attributes down, so lets get to the methods 🙂
Note that I said ‘best’ way. I’ve already found two ways of doing it which also seem to be popular answers in similar questions, but they require circumventing strict and warnings respectively.
Note that the example gives the class-user the ability to override the formatting method at construction. Poor example, but you get the idea. To preemptively rebut the OO-purists, this makes more sense in my particular design than making the class-user subclass it with their own overridden version.
Here are my two attempts:
use 5.014;
use autodie;
use strict;
use warnings;
package Account {
use base 'Object::Accessor';
sub new {
my ($type, %args) = @_;
my $self = bless { }, $type;
$self->mk_accessors(qw(
first_name last_name age_in_years activated
));
$self->first_name( $args{first_name } // 'Default First Name' );
$self->last_name( $args{last_name } // 'Default Last Name' );
$self->age_in_years($args{age_in_years} // 'Default Age in Years');
$self->activated( $args{activated } // 'Default Activated' );
{
# Stop skim reading and look here!
no warnings 'once';
*formatted = $args{formatted} || sub {
return 'Default formatting routine';
};
}
return $self;
}
}
my $account = Account->new;
say $account->formatted;
# Output: Default formatting routine
And the other one:
use 5.014;
use autodie;
use strict;
use warnings;
package Account {
use base 'Object::Accessor';
sub new {
my ($type, %args) = @_;
my $self = bless { }, $type;
$self->mk_accessors(qw(
first_name last_name age_in_years activated
));
$self->first_name( $args{first_name } // 'Default First Name' );
$self->last_name( $args{last_name } // 'Default Last Name' );
$self->age_in_years($args{age_in_years} // 'Default Age in Years');
$self->activated( $args{activated } // 'Default Activated' );
{
# Stop skim reading and look here!
no strict 'refs';
*{'formatted'} = $args{formatted} || sub {
return 'Default formatting routine';
};
}
return $self;
}
}
my $account = Account->new;
say $account->formatted;
# Output: Default formatting routine
The former approach should work without warnings, but it doesn’t because Perl thinks I’m not using it anywhere and flags it up as a warning at runtime. The latter doesn’t work without circumventing strict, and that’s how it should be 🙂
All I’m doing is assigning a method to a user-supplied coderef. There should be an easy way of doing this.
Note that defining a new sub that calls the user defined coderef doesn’t work either, since the lexical %args can’t be accessed from it.
Whilst our assigns package variables, it can’t do subs.
Refraining the escape from the garden of strict and warnings, what is the cleanest way to do this? Hell, there’s probably a way to do this using the Object::Accessor module that I just don’t know about.
It could be stored in $self:
Or put in a lexical:
Or just put up with the no strict ‘refs’. As long as it’s in a small, well-contained block like you’re doing above, I’ve never considered it evil. Or rather, it is evil, but a small, well-contained one.