Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl Class::Accessor failure, trivial example - why?

Tags:

perl

cpan

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 " . ref($self) . ", whatever this word means\n";
    }
like image 924
Alex F Avatar asked Jun 04 '10 11:06

Alex F


3 Answers

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.

like image 200
FMc Avatar answered Nov 15 '22 11:11

FMc


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;
}
like image 36
daotoad Avatar answered Nov 15 '22 11:11

daotoad


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;
like image 38
NO WAR WITH RUSSIA Avatar answered Nov 15 '22 11:11

NO WAR WITH RUSSIA