Generating accessors

BooK on 2004-04-18T14:40:20

After discovering AUTOLOAD, I wrote my accessors as (excerpt from Log::Procmail 0.05):

sub AUTOLOAD {
    # don't DESTROY
    return if $AUTOLOAD =~ /::DESTROY/;

    # fetch the attribute name
    $AUTOLOAD =~ /.*::(\w+)/;
    my $attr = $1;
    if ( $attr eq lc $attr ) {    # accessors are lowercase
        no strict 'refs';

        # create the method
        *{$AUTOLOAD} = sub {
            my $self = shift;
            @_ ? $self->{$attr} = shift: $self->{$attr};
        };

        # now do it
        goto &{$AUTOLOAD};
    }
}

This looks cargo-cultish, and I can't remember why I put that if ( $attr eq lc $attr ) line in the code.

Now I prefer generating the accessors from the beginning (this is an excerpt from Log::Procmail 0.06):

for my $attr (qw( from date subject size folder source ) ) {
    no strict 'refs';
    *{"Log::Procmail::Abstract::$attr"} = sub {
        my $self = shift;
        @_ ? $self->{$attr} = shift: $self->{$attr};
    }
}

I tend to prefer this way of doing things:

  • Adding a new accessors is as simple as adding its name to the list
  • No risk of AUTOLOADing the wrong method when doing a typo
  • All the methods exist from the beginning

What did I miss?


Alternatively...

gav on 2004-04-18T16:51:29

use base qw( Class::Accessor::Fast );
__PACKAGE__->mk_accesors(qw( from date subject size folder source ));

See: Class::Accessor

Another alternative

Juerd on 2004-04-19T10:59:48

Attribute::Property
use Attribute::Property;

package My::Class;
sub foo : Property;
sub bar : Property;
sub baz : Property { /^\d+$/ }
This has the advantage of being able to use them as lvalues, like in Perl 6:
$foo->bar =~ s/foo/bar/;
instead of:
my ($temp = $foo->bar) =~ s/foo/bar/;
$foo->bar($temp);