Saturday, March 31, 2012

class variables in Moose

Moose attribute didn't allow the reference rather than a CODE reference as a default value. That prevents the objects shared a attribute when initializing. Actually this is a good design. But How should I do when needing a shared attribute between objects? I may refer this as the concept of class variable.

The state variable in CODE reference may be competent with this role.

A Counter as an example here. We define a Counter class here.

use Modern::Perl;
use MooseX::Declare;
class Counter {
has value => (
is => "rw",
isa => "Num",
traits => ['Counter'],
default => 0,
handles => {
inc => "inc",
},
);
};
view raw Counter.pm hosted with ❤ by GitHub


A counter has a attribute "value" and a method "inc" to increment it.

Then We define a Person class.

use Modern::Perl;
use MooseX::Declare;
use Counter;
class Person {
has name => (
is => "ro",
isa => "Str",
default => "nobody",
required => 1,
);
has count => (
is => "rw",
isa => "Counter",
default => sub {
state $count = Counter->new;
$count;
},
);
sub BUILD {
my ($self) = shift;
$self->count->inc;
}
};
view raw Person.pm hosted with ❤ by GitHub


The Person class has a attribute "name" and a shared attribute "count". The Counter is a state variable return from a anonymous CODE reference. That's the trick.

The BUILD hook increment the counter everytime a new Person is constructed.

Then we examinate the shared variable.

#!/usr/bin/env perl
#-*- mode: cperl -*-
use Modern::Perl;
use Person;
my $shelling = Person->new(name => "shelling");
my $count = $shelling->count;
say $count->value; #=> 1
my $sherry = Person->new(name => "sherry");
say $count->value; #=> 2
my $appollo = Person->new(name => "appollo");
say $count->value; #=> 3


A global variable in package name may still work. But this way uses 'has' syntax sugar. :p

If you use builder to replace the default option. the subroutine used by the builder is just the class variable accessor from Class name, returning value as $person->count().

No comments: