Jump to content

Programming with Moose/Syntax/has

From Wikibooks, open books for an open world

The most important Moose keyword is arguably "has", which is essentially the most beefed up accessor-generator ever devised. Many of these features can be found in the accompanying docs of Moose, and Class::MOP::Accessor.

Attributes of has

[edit | edit source]

Below you can find all of the attributes of has.[1] Asterisk represents the default.

[+]has => (
   isa        => $type_constraint,
   is         => 'ro|rw',
   coerce     => 0*|1,
   weak_ref   => 0*|1,
   does       => $role_name,
   required   => 0*|1
   lazy       => 0*|1
   auto_deref => 0*|1
   init_arg   => $str|undef,
   default    => $default_value|sub { $default_value },
   metaclass  => $voodoo,
   trigger    => $sub_ref,
   initializer=> $sub_ref,
   handles    => Array|Hash|Regex|Role|Code,
   lazy_build => 0*|1,
   clearer    => $clearer_name,
   predicate  => $predicate_name,
   builder    => $builder_name,
   reader     => $reader_name,
   writer     => $writer_name,
);


The second most essential attribute of has is isa. isa is pronounced "Is a", and essentially does little more than tell what the attribute can be set with.

  Any
  Item 
      Bool
      Undef
      Defined
          Value
              Num
                Int
              Str
                ClassName
          Ref
              ScalarRef
              ArrayRef
              HashRef
              CodeRef
              RegexpRef
              GlobRef
                FileHandle
              Object    
                  Role

These are the default choices for isa. In addition to them isa can be set to any class name, for instance isa => "URI", or isa => "HTML::Treebuilder".

has 'foobar' => (
    isa => 'Str',
    is  => 'ro',
);

has 'foobar' => (
  isa => 'MooseObject',
  is  => 'ro',
);

Moose v0.26 introduces parameterized types[1]—no one knows what they really do, but they gave the ability to do such things as:

has 'foobar' => (
  isa => 'ArrayRef[URI]'
);

Now when you try to store to ->foobar, you had better only add URI elements to the ArrayRef, or you will croak.

The heart of the attribute generating has is the attribute is. If something is is => 'ro' the user has a getter (read-only accessor) by the specified name. Conversely, if the value supplied is is => 'rw' the user has a getter/setter hybrid (read-write accessor).

The default value is to not generate an accessor of either kind at all.

coerce

[edit | edit source]

The coerce attribute to has is significantly more complex than is and isa. It tells Moose, "Hey dude, it would be really l33t if you could get this value into the datatype I want it to be without being a pain in the ass all the time."

Moose generally listens when you talk to it as such.

package MyMoose;
use Moose;
use Moose::Util::TypeConstraints;
use URI;

subtype 'URI'
	=> as 'Object'
	=> where { $_->isa('URI') };

coerce 'URI'
	=> from 'Str'
	=> via { URI->new($_) };

has 'myuri' => ( is => 'rw', isa => 'URI', coerce => 1 );

package main;
my $m = MyMoose->new;
$m->myuri( 'foobar' );

print ref $m->myuri;  ## print URI::_generic

To use coerce you must have a supplied a subtype which can coerce.

weak_ref

[edit | edit source]

You cannot use coerce => 1, if you set weak_ref => 1

Weak_ref stops the refcount held in the attribute from being incremented by the object. This means if you have one copy of something, and a hundred objects use it with weak_ref => 1, you can successfully destroy their copies by undef'ing your one copy. Perl's garbage collector will destroy the item when its reference count reaches zero. With weak_ref your object will never increase the reference count. This prevents circular recursion, but leaves you susceptible to having the rug pulled out from under you, so to speak.

package MyMoose;
use Moose;

has 'foo' => (
	isa      => 'ArrayRef',
	is       => 'ro',
	weak_ref => '1',
);

package main;

my $foo = [qw/ foo bar baz/];
my $m = MyMoose->new({ foo => $foo });

print @{ $m->foo };  ## prints foobarbaz

undef $foo;

## Can't use an undefined value as an ARRAY reference at test.pl line 21.
print @{ $m->foo };

This will accept the name of a role which the value stored in this attribute is expected to have consumed.

required

[edit | edit source]

That which is required is required => 1. All things required must have a value specified in a hash whose ref is sent to ->new.

package MyMoose;
use Moose;

has 'foo' => ( required => 1 );

package main;
my $m1 = MyMoose->new();             ## dies with error pinpointing cause
my $m2 = MyMoose->new({ foo => 4 }); ## Blisses in the fact the user is not a git
See also: default|#default|default

Don't want something to happen at compile time... Defer it! Lazy necessitates a default: you must supply one.

This one attribute introduces a whole new way to model problems. It brings a new mentality, don't do work, until it needs to get done. About time computer programs grew the ability to procrastinate without stressing me out. I'd hate to restrict a luxury of that caliber to myself.

Think of the lazy/default combination in terms of a sub that stores its return value so it doesn't have to recompute it on subsequent calls.

In this example foobar, does not get set until you ask for it; and, in this example it pulls down a webpage, and thus we would not want to do this possibly time-consuming operation until we know we need the result.

sub build_foobar { return get( 'http://www.example.com/somedatafeed' ); }

has 'foobar' => (
  isa     => Int,
  is      => 'rw',
  lazy    => 1,
  default => \&build_foobar,
);

auto_deref

[edit | edit source]

Auto_deref is a tricky and deceitful little fellow. It doesn't fix perl, it only makes things slightly more simple. This attribute simply returns an expanded Array for an ArrayRef, or a Hash for a HashRef. To use auto_deref you must have isa=>ArrayRef, or isa=>HashRef or a Parameterized type of one of the two: isa=>ArrayRef[Int]

has 'foo' => ( isa => 'HashRef', is => 'rw', auto_deref => 1 );


What did I mean by deceitful well auto_deref still just simply passes by copy.

package Class;
use Moose;

has 'foo' => ( isa => 'ArrayRef', is => 'rw', auto_deref => 1 );

my $c = Class->new({ foo => [qw/foo bar baz/] });

## Uses auto_deref
s/.*// for $c->foo;
print $_ for $c->foo; # foo bar baz

## Uses manual deref
s/.*// for @{$c->foo};
print $_ for $c->foo; # Nada.

default

[edit | edit source]
See also: lazy|#lazy|lazy

Default has two radically different functions:

  1. set a default static value.
  2. set a dynamic lazy (lazy=>1) default value on the first call to the accessor

The first and most simple application of default, does nothing more than set a fall-back static value:

package Person;
use Moose;

has 'name' => (
 isa     => 'Str',
 is      => 'rw',
 default => "I'm too stupid to fill out a form",
);

package main;
my $p =  Person->new({ name => $_ });
print 'Greetings' . $p->name;

The second implementation of default is much more complex. Using this method, you state the attribute as lazy => 1 (do nothing at compile-time), and then you point the default to a function such as sub { DBI->connect }. When the function is called, it sees the slot is unset (fails a predicate test) and the sub pointed to by default is called setting the slot with the return value. This value is cached away and will not be recomputed unless the slot again becomes unset—as in the case of calling clearer.

metaclass

[edit | edit source]

clearer

[edit | edit source]
See also: predicate|#predicate|predicate, lazy|#lazy|lazy, and default|#default|default

If you ever need to undo all changes made to a value clearer is a great way to go about it. Let's say you have made the first call to a lazy => 1 attribute, which sets the initial value to the corresponding default. Let's assume you don't like this value, and you want to reset it as if it was never called. Calling the clearer-specified method in this context will accomplish that. Internally, clearer deletes the slot.

If you call an accessor on a cleared value it will return undef, unless there is a lazy/default combo specified. In which case it will reinitilize as if it was the first call. If you call clearer on a regular default, the value might appear to be set to undef. In effect the slot is not existence. To test for this see predicate.

package MyMoose;
use Moose;

has 'foo' => (
	isa     => 'Int',
	is      => 'rw',
	clearer => 'clear_foo',
);

package main;
my $m = MyMoose->new;
$m->foo(5);
$m->clear_foo;

print "defined" if defined $m->foo;
print "exists" if exists $m->{foo};
print $m->{foo};
Use of uninitialized value in print at test.pl line 22.

Don't ever access a Moose object's underlying hash unless writing fugly examples for books. Moose is supposed to abstract away what it is blessed. Don't remind people that the hash even exists.

predicate

[edit | edit source]

All predicate does is create a sub that returns true (1) if the attribute's respective slot exists, and false (0) if it doesn't. It's a simple convenience attribute. Without predicate => 'name', you would not easily be able to discern whether or not the slot exists without violating the black-box tenet of Object Orientation. Predicate makes this step easy, and allows you to continue without peering into the Moose internals. Predicate is only useful for:

  • Inter-operating with its family-function clearer->—which deletes the slot. Predicate can be though of as clearer_test in this context.
  • Testing to see if a non-required attribute was supplied to the constructor.[2]
  • Testing to see if a lazy => 1 with a default has fired yet

Predicate takes a string which it uses to generate the predicate-sub's name:

predicate => 'my_predicate_sub'

Here we will see it in action:

package MyMoose;
use Moose;

# To accept undef also as a valid data.
has 'ulcer' => (
  isa       => 'Str|Undef',
  is        => 'rw',
  predicate => 'is_sick',
);

package main;

my $m = MyMoose->new({ ulcer => 'painful' });
print $m->is_sick # true

## User doesn't know affliction
## Here Ulcer description isn't defined but we still have an ulcer
my $m = MyMoose->new({ ulcer => undef });
print $m->is_sick # true

trigger

[edit | edit source]

A trigger is a function that gets called after you write to an accessor or when you set a value using the constructor. However, as of the time of writing they do not fire on attributes set with lazy_build, default, or builder. A trigger is set with a CodeRef, which receives the following values: $self, $value, $oldValue.

use Moose;
sub shout { print 'Moosegasm' }
has 'foo' => ( is => 'rw', trigger => \&shout );

If you want a trigger to only execute when you run the setter try something more on the lines of:[3]

after 'foo' => sub {};
after $self->meta->get_attribute('foo')->get_write_method => sub {}

If you want a trigger to fire on something that is built from a builder/default/lazy_build you will either have to explicitly unroll the trigger in the builder/default/lazy_build code, or hack a wrapper around the builder. This is because the trigger can presume the attribute is set and read from it. But, Moose only sets the attribute after the builder has returned—so you can't simply call the trigger from within the builder.

around '_build_foo' => sub {
	my ( $sub, $self, @args ) = @_;
	my $foo = $self->$sub;
	$self->foo( $foo );
	$self->_init_foo;
	$foo;
};


See also: initializer|#initializer|initializer and comparison with initializer|#trigger_vs_initializer|comparison with initializer

init_arg

[edit | edit source]

This attribute serves two distinct purposes:

  • It can alter the key from the constructor's hash that Moose will use use to set your attribute.
  • You can ignore any values sent in the constructor by setting to it to undef.

You can tell Moose to use a different key in the constructor's hash (->new( $constructorsHash )) by explicitly setting init_arg to something other than the attribute name. For example:

package Class;
use Moose;

has 'foo' => ( isa => 'Str', is => 'rw', init_arg => 'bar' );

package main;
say Class->new({ bar => "BarValue" })->foo ## outputs "BarValue"

Or, you can have Moose ignore the constructor's hash. This will even work with a default.

package Class;
use Moose;
has 'foo' => ( isa => 'Bool', is => 'rw', default => 1, init_arg => undef );

package main;
say Class->new({ foo => 0 })->foo; ## returns true

This attribute can not be inherited and modified with +attr:

package Class;
use Moose::Role;
has 'foo' => ( isa => "Int", is => "ro" );
has "+foo" => (init_arg => "FoO");
Class->new({FoO=>1})

initializer

[edit | edit source]

This feature assists in initializing (setting the slot). It fires when one of the following are met:

  • A default is present
  • A value is sent in the constructor
  • A value is computed from lazy_build
  • A value is computed from a builder
  • Or, a triggering through the meta such as Class::MOP::Attribute's set_initial_value

If you don't set the slot in the initializer, the slot will effectively be set to undef, assuming the type system permits it.

The initializer sub receives four values: $self, $value, $writerSubRef, $attributeMeta. With $attributeMeta probably being an instance of Class::MOP::Attribute. The $writerSub is not a method.

One very important point about an initializer is it fires *after* the type system. The value must pass through a type coercion or be a valid type prior to the initializer firing.

package MooseClass;
use Moose;

has "foo" => (
  isa => 'Value',
  is  => 'rw',

  initializer => sub {
    my ( $self, $value, $writer_sub_ref, $attribute_meta ) = @_;

    $writer_sub_ref->($value);
  }
);

my $c = MooseClass->new( { foo => 5 } );
say $c->foo;
See also: trigger|#trigger|trigger and comparison with initializer|#trigger_vs_initializer|comparison with initializer

lazy_build

[edit | edit source]

Lazy_build is an attribute that is made to simplify quick construction of a class. Here is what comprises it:[4]

   #If your attribute name starts with an underscore:
   has '_foo' => (lazy_build => 1);
   #is the same as
   has '_foo' => (lazy => 1, required => 1, predicate => '_has_foo', clearer => '_clear_foo', builder => '_build__foo');
   # or
   has '_foo' => (lazy => 1, required => 1, predicate => '_has_foo', clearer => '_clear_foo', default => sub{shift->_build__foo});

   #If your attribute name does not start with an underscore:
   has 'foo' => (lazy_build => 1);
   #is the same as
   has 'foo' => (lazy => 1, required => 1, predicate => 'has_foo', clearer => 'clear_foo', builder => '_build_foo');
   # or
   has 'foo' => (lazy => 1, required => 1, predicate => 'has_foo', clearer => 'clear_foo', default => sub{shift->_build_foo});

handles

[edit | edit source]

This feature provides another way to delegate methods to a different module.

The handles attribute can be configured in a different ways with varying degrees of syntactic sugar:

  • Array
  • Hash
  • Regex

Here is an example from HTML::TreeBuilderX::ASP_NET which uses the Regex configuration:

has 'hrf' => (
  isa          => 'HTTP::Request::Form',
  is         => 'ro',
  handles    => qr/.*/,
  lazy_build => 1,
);

A good and general use case is handles => qr/.*/, which tells Moose to defer to the module specified in isa for all functions which it declares and aren't overridden in your package. This will often allow you to avoid subclassing and munging ->new.

traits

[edit | edit source]

Traits, or more specifically Attribute Traits, are a mechanism for expanding the functionality of attributes.

An example of this would be the Moose functionality provided by the Native traits. These will provide helper methods for all container types: Hash, Array; as well as the strongly typed Number, String, Bool, Couter, and Code. The Native traits have functionality that can be turned on as required through an additional handles attribute.

has 'attributes' => (
    isa     => 'HashRef',
    traits  => ['Hash'],
    is      => 'ro',
    handles => { get_attr => 'get' },
);

Clearing up confusion

[edit | edit source]

initializer vs default

[edit | edit source]

Initializers are part of the default process. The layer that sets the default, is the same layer that fires the initializer. A default value initializes a slot to a set value. The fire order of defined and initializer is defined for an attribute; but, totally undefined amongst all of the modules attributes.

Defined for an attribute:

has 'foo' => (
	isa => 'Str'
	, is => 'rw'
	, default => 'foobarbaz'
	, initializer => sub {
		my ( $self, $arg, $writerSub, $attributeMeta ) = @_;
		print $arg; # prints 'foobarbaz'
	}
);

Undefined for set of attributes: one attribute (foo) might fire its initialer, write to another attribute (bar), just to have (bar) overwritten by its own default.

has 'bar' => ( isa => 'Str', is => 'rw', default => 'bar' )
has 'foo' => (
	isa => 'Str'
	, is => 'rw'
	, default => 'foobarbaz'
	, initializer => sub {
		my ( $self, $arg, $writerSub, $attributeMeta ) = @_;
		$self->bar('foo');
	}
);
...
print MooseObject->new->bar ## prints bar
See also: default|#default|default and initializer|#initializer|initializer

initializer vs trigger

[edit | edit source]

Arguments:

Both trigger and initializer utilize $self, $value, and $attributeMeta.

trigger
$self, $value, $attributeMeta.
initializer
$self, $value, $writerSub, $attributeMeta.


Firing order:

It might be considered a bug that triggers do not fire in any event that writes to a slot.

Comparison of Trigger and Initializer in Moose 0.58
condition example trigger initializer
constructor Class->new({foo=>1}) fires fires
lazy_build Class->new->foo nada fires
builder Class->new nada fires
default Class->new nada fires
runtime explicit Class->new->foo(5) fires nada

Coexisting:

If a trigger is available, initializer will always fire before the trigger. However, it still does not fire when a value is explicitly set to the slot in runtime.

You can use trigger to have a sub fire when a value is explicitly provided in runtime. The downside to this approach is a double fire when the value is constructor-provided: one from trigger, one from initializer.

has 'foo' => ( isa => 'Value', is => 'rw', initializer => \&bar, trigger => \&bar );
sub bar { stuff }
See also: trigger|#trigger|trigger and initializer|#initializer|initializer

Footnotes

[edit | edit source]
  1. ^ It is almost impossible to be certain we have them all listed because of the inheritance of Moose, and the lack of external documentation. For instance, no where in the official Moose docs does it mention ->clear or ->predicate; but, they're documented in Class::MOP::Attribute.

References

[edit | edit source]
  1. Little, Stevan (2007-09-27). "Changes/Revision History". Retrieved 2007-10-20.
  2. well required ones too, but if they aren't supplied, you die()
  3. Thanks to irc.perl.org's Sartak for info
  4. Little, Stevan. "Moose::Meta::Attribute". Moose 0.32. Infinity Interactive. {{cite web}}: Unknown parameter |access_date= ignored (help); Unknown parameter |coauthors= ignored (|author= suggested) (help)