12

I need a class attribute in Moose. Right now I am saying:

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;
use MooseX::Declare;

class User {
    has id      => (isa => "Str", is => 'ro', builder => '_get_id');
    has name    => (isa => "Str", is => 'ro');
    has balance => (isa => "Num", is => 'rw', default => 0);

    #FIXME: this should use a database  
    method _get_id {
        state $id = 0; #I would like this to be a class attribute
        return $id++;
    }
}

my @users;
for my $name (qw/alice bob charlie/) {
    push @users, User->new(name => $name);
};

for my $user (@users) {
    print $user->name, " has an id of ", $user->id, "\n";
}
brian d foy
  • 129,424
  • 31
  • 207
  • 592
Chas. Owens
  • 64,182
  • 22
  • 135
  • 226

2 Answers2

8

I found MooseX::ClassAttribute, but it looks ugly. Is this the cleanest way?

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;
use MooseX::Declare;

class User {
    use MooseX::ClassAttribute;

    class_has id_pool => (isa => "Int", is => 'rw', default => 0);

    has id      => (isa => "Str", is => 'ro', builder => '_get_id');
    has name    => (isa => "Str", is => 'ro');
    has balance => (isa => "Num", is => 'rw', default => 0);

    #FIXME: this should use a database  
    method _get_id {
        return __PACKAGE__->id_pool(__PACKAGE__->id_pool+1);
    }
}

my @users;
for my $name (qw/alice bob charlie/) {
    push @users, User->new(name => $name);
};

for my $user (@users) {
    print $user->name, " has an id of ", $user->id, "\n";
}
Chas. Owens
  • 64,182
  • 22
  • 135
  • 226
  • That is the correct way. You can also create a class class (as it were) if you prefer them in a separate package, look what ClassAttribute does under the hood – castaway Jul 08 '09 at 07:09
  • Isn't `use strict` and `use warnings` redundant with `use MooseX::Declare` ? – Robert P Dec 09 '09 at 23:41
  • @Robert P Probably, I was just starting to use MooseX::Declare when I wrote this. – Chas. Owens Dec 10 '09 at 15:17
  • 1
    Note that as of 0.11, MooseX::ClassAttribute now plays well with roles. Thanks David Rolsky!! – Ether Feb 10 '10 at 18:14
  • MooseX::Declare is now deprecated. (he said as it installed. Now what?) – Ron Feb 16 '19 at 21:03
2

Honestly, I don't think it's necessary to all that trouble for class attributes. For read-only class attributes, I just use a sub that returns a constant. For read-write attributes, a simple state variable in the package usually does the trick (I haven't yet run into any scenarios where I needed something more complicated.)

state $count = 0;
method _get_id { 
    return ++$count;
}

A private block with a lexical can be used if you need pre-5.10 compatibility.

friedo
  • 65,762
  • 16
  • 114
  • 184
  • 2
    ah, but id_pool is not necessarily read only, I want a true class attribute that has an accessor. I don't want to have to rewrite the code when I decide to move the ID pool to a database. If it were an accessor, I would just need to change change how the accessor worked, with a state variable I would need to either change all of the references to it or make it a tied scalar. – Chas. Owens Jun 29 '09 at 12:56