=head1 NAME
-Moose::Cookbook::Meta::Recipe3 - The attribute trait example
+Moose::Cookbook::Meta::Recipe3 - Labels implemented via attribute traits
=head1 SYNOPSIS
- package MyApp::Meta::Attribute::Trait::Labeled;
- use Moose::Role;
-
- has label => (
- is => 'rw',
- isa => 'Str',
- predicate => 'has_label',
- );
-
- package Moose::Meta::Attribute::Custom::Trait::Labeled;
- sub register_implementation { 'MyApp::Meta::Attribute::Trait::Labeled' }
-
- package MyApp::Website;
- use Moose;
- use MyApp::Meta::Attribute::Trait::Labeled;
-
- has url => (
- traits => [qw/Labeled/],
- is => 'rw',
- isa => 'Str',
- label => "The site's URL",
- );
-
- has name => (
- is => 'rw',
- isa => 'Str',
- );
-
- sub dump {
- my $self = shift;
-
- # iterate over all the attributes in $self
- my %attributes = %{ $self->meta->get_attribute_map };
- while (my ($name, $attribute) = each %attributes) {
-
- # print the label if available
- if ($attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
- && $attribute->has_label) {
- print $attribute->label;
- }
- # otherwise print the name
- else {
- print $name;
- }
-
- # print the attribute's value
- my $reader = $attribute->get_read_method;
- print ": " . $self->$reader . "\n";
- }
- }
-
- package main;
- my $app = MyApp::Website->new(url => "http://google.com", name => "Google");
- $app->dump;
+ package MyApp::Meta::Attribute::Trait::Labeled;
+ use Moose::Role;
+
+ has label => (
+ is => 'rw',
+ isa => 'Str',
+ predicate => 'has_label',
+ );
+
+ package Moose::Meta::Attribute::Custom::Trait::Labeled;
+ sub register_implementation {'MyApp::Meta::Attribute::Trait::Labeled'}
+
+ package MyApp::Website;
+ use Moose;
+ use MyApp::Meta::Attribute::Trait::Labeled;
+
+ has url => (
+ traits => [qw/Labeled/],
+ is => 'rw',
+ isa => 'Str',
+ label => "The site's URL",
+ );
+
+ has name => (
+ is => 'rw',
+ isa => 'Str',
+ );
+
+ sub dump {
+ my $self = shift;
+
+ # iterate over all the attributes in $self
+ my %attributes = %{ $self->meta->get_attribute_map };
+ while ( my ( $name, $attribute ) = each %attributes ) {
+
+ # print the label if available
+ if ( $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
+ && $attribute->has_label ) {
+ print $attribute->label;
+ }
+
+ # otherwise print the name
+ else {
+ print $name;
+ }
+
+ # print the attribute's value
+ my $reader = $attribute->get_read_method;
+ print ": " . $self->$reader . "\n";
+ }
+ }
+
+ package main;
+ my $app = MyApp::Website->new( url => "http://google.com", name => "Google" );
+ $app->dump;
=head1 BUT FIRST
-This recipe is a continuation of L<Moose::Cookbook::Meta::Recipe21>. Please read that
-first.
+This recipe is a continuation of
+L<Moose::Cookbook::Meta::Recipe2>. Please read that recipe first.
=head1 MOTIVATION
-In Recipe 21, we created an attribute metaclass that gives attributes a "label"
-that can be set in L<Moose/has>. That works well until you want a second
-meta-attribute, or until you want to adjust the behavior of the attribute. You
-could define a specialized attribute metaclass to use in every attribute.
-However, you may want different attributes to have different behaviors. You
-might end up with a unique attribute metaclass for B<every single attribute>,
-with a lot of code copying and pasting!
+In L<Moose::Cookbook::Meta::Recipe2>, we created an attribute
+metaclass that gives attributes a "label" that can be set in
+L<Moose/has>. That works well until you want a second meta-attribute,
+or until you want to adjust the behavior of the attribute. You could
+define a specialized attribute metaclass to use in every attribute.
+However, you may want different attributes to have different
+behaviors. You might end up with a unique attribute metaclass for
+B<every single attribute>, with a lot of code copying and pasting!
Or, if you've been drinking deeply of the Moose kool-aid, you'll have a role
for each of the behaviors. One role would give a label meta-attribute. Another
=head1 DISSECTION
-A side-by-side look of the code examples in this recipe and recipe 21 should
+A side-by-side look of the code examples in this recipe and recipe 2 should
indicate that defining and using a trait is very similar to defining and using
a new attribute metaclass.
- package MyApp::Meta::Attribute::Trait::Labeled;
- use Moose::Role;
+ package MyApp::Meta::Attribute::Trait::Labeled;
+ use Moose::Role;
- has label => (
- is => 'rw',
- isa => 'Str',
- predicate => 'has_label',
- );
+ has label => (
+ is => 'rw',
+ isa => 'Str',
+ predicate => 'has_label',
+ );
Instead of subclassing L<Moose::Meta::Attribute>, we define a role. Traits
don't need any special methods or attributes. You just focus on whatever it is
you actually need to get done. Here we're adding a new meta-attribute for use
in our application.
- package Moose::Meta::Attribute::Custom::Trait::Labeled;
- sub register_implementation { 'MyApp::Meta::Attribute::Trait::Labeled' }
+ package Moose::Meta::Attribute::Custom::Trait::Labeled;
+ sub register_implementation { 'MyApp::Meta::Attribute::Trait::Labeled' }
Much like when we define a new attribute metaclass, we can provide a shorthand
name for the trait. Moose looks at the C<register_implementation> method in
name of the trait.
Now we begin writing our application logic. I'll only cover what has changed
-since recipe 21.
+since recipe 2.
- has url => (
- traits => [qw/Labeled/],
- is => 'rw',
- isa => 'Str',
- label => "The site's URL",
- );
+ has url => (
+ traits => [qw/Labeled/],
+ is => 'rw',
+ isa => 'Str',
+ label => "The site's URL",
+ );
L<Moose/has> provides a C<traits> option. Just pass the list of trait names and
it will compose them together to form the (anonymous) attribute metaclass used
by the attribute. We provide a label for the attribute in the same way.
- # print the label if available
- if ($attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
- && $attribute->has_label) {
- print $attribute->label;
- }
+ # print the label if available
+ if ( $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
+ && $attribute->has_label ) {
+ print $attribute->label;
+ }
Previously, this code asked the question "Does this attribute use our attribute
metaclass?" Since we're now using a trait, we ask "Does this attribute's
easily get a regular metaclass extension out of it. You just compose the trait
in the attribute metaclass, as normal.
- package MyApp::Meta::Attribute::Labeled;
- use Moose;
- extends 'Moose::Meta::Attribute';
- with 'MyApp::Meta::Attribute::Trait::Labeled';
+ package MyApp::Meta::Attribute::Labeled;
+ use Moose;
+ extends 'Moose::Meta::Attribute';
+ with 'MyApp::Meta::Attribute::Trait::Labeled';
- package Moose::Meta::Attribute::Custom::Labeled;
- sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
+ package Moose::Meta::Attribute::Custom::Labeled;
+ sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
Unfortunately, going the other way (providing a trait created from a metaclass)
is more tricky. Thus, defining your extensions as traits is just plain better
=head1 COPYRIGHT AND LICENSE
-Copyright 2006-2008 by Infinity Interactive, Inc.
+Copyright 2006-2009 by Infinity Interactive, Inc.
L<http://www.iinteractive.com>