Perl Class::Accessor failure, trivial example - why?
Can someone tell me why the main does not find the methods generated by Class::Accessor in this very small and trivial example ?
These few lines of code fail with
perl codesnippets/accessor.pl
Can't locate object method "color" via package "Critter" at
codesnippets/accessor.pl line 6.
see the code:
#!/opt/local/bin/perl
# The whole Class::Accessor thing does not work !!
my $a = Critter->new;
$a->color("blue");
$a->display;
exit 0;
package Critter;
use base qw(Class::Accessor );
Critter->mk_accessors ("color" );
sub display {
my $self = shift;
print "i am a $self->color " . r开发者_Python百科ef($self) . ", whatever this word means\n";
}
Your code is out of order. If you want the color
accessor to be available, you need to invoke mk_accessors
before you create your object and start doing stuff with it. For example:
package Critter;
use base qw(Class::Accessor);
Critter->mk_accessors("color");
sub display {
my $self = shift;
print $self->color, ' ', ref($self), "\n";
}
package main;
my $c = Critter->new;
$c->color("blue");
$c->display;
More commonly, the Critter
code would be in its own module (Critter.pm
), and all of the mk_accessor
magic would happen when your main script runs use Critter
-- well before your script starts working with Critter
and Varmint
objects.
FM is giving you good advice. mk_accessors
needs to run before the other code. Also, normally you'd put Critter
in a separate file and use Critter
to load the module.
This works because use
has compile time effects. Doing use Critter;
is the same as doing BEGIN { require Critter; Critter->import; }
This guarantees that your module's initialization code will run before the rest of the code even compiles.
It is acceptable to put multiple packages in one file. Often, I will prototype related objects in one file, since it keeps everything handy while I am prototyping. It's also pretty easy to split the file up into separate bits when the time comes.
Because of this, I find that the best way to keep multiple packages in one file, and work with them as if I were using them, is to put the package definitions in BEGIN
blocks that end in a true value. Using my approach, your example would be written:
#!/opt/local/bin/perl
my $a = Critter->new;
$a->color("blue");
$a->display;
BEGIN {
package Critter;
use base qw(Class::Accessor );
use strict;
use warnings;
Critter->mk_accessors ("color" );
sub display {
my $self = shift;
# Your print was incorrect - one way:
printf "i am a %s %s whatever this word means\n", $self->color, ref $self;
# another:
print "i am a ", $self->color, ref $self, "whatever this word means\n";
}
1;
}
I just wanted to provide you with a better solution -- feel free to downvote this to oblivion if the solution isn't welcome, but C::A is really a bad idea this day and age, use Moose:
package Critter;
use Moose;
has 'color' => ( isa => 'Str', is => 'rw' ); # Notice, this is typed
sub display {
my $self = shift;
printf (
"i am a %s %s whatever this word means\n"
, $self->color
, $self->meta->name
);
}
package main;
use strict;
use warnings;
my $c = Critter->new; # or my $c = Critter->new({ color => blue });
$c->color("blue");
$c->display;
精彩评论