X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMoose%2FCookbook%2FMeta%2FRecipe2.pod;h=f9e3db544c131e955882cb47a958450465e501a4;hb=9327b01c56a220b4c09f73381bca577f49ad87a9;hp=d5f37541f7a2dc5447fdd830bc307754a68ef952;hpb=43aa5bf9dc7789a424aa69d6bac3ff456a979480;p=gitmo%2FMoose.git diff --git a/lib/Moose/Cookbook/Meta/Recipe2.pod b/lib/Moose/Cookbook/Meta/Recipe2.pod index d5f3754..f9e3db5 100644 --- a/lib/Moose/Cookbook/Meta/Recipe2.pod +++ b/lib/Moose/Cookbook/Meta/Recipe2.pod @@ -7,288 +7,258 @@ Moose::Cookbook::Meta::Recipe2 - A meta-attribute, attributes with labels =head1 SYNOPSIS - package MyApp::Meta::Attribute::Labeled; - use Moose; - extends 'Moose::Meta::Attribute'; - - has label => ( - is => 'rw', - isa => 'Str', - predicate => 'has_label', - ); - - package Moose::Meta::Attribute::Custom::Labeled; - sub register_implementation { 'MyApp::Meta::Attribute::Labeled' } - - package MyApp::Website; - use Moose; - use MyApp::Meta::Attribute::Labeled; - - has url => ( - metaclass => '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->isa('MyApp::Meta::Attribute::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::Labeled; + use Moose; + extends 'Moose::Meta::Attribute'; + + has label => ( + is => 'rw', + isa => 'Str', + predicate => 'has_label', + ); + + package Moose::Meta::Attribute::Custom::Labeled; + sub register_implementation {'MyApp::Meta::Attribute::Labeled'} + + package MyApp::Website; + use Moose; + use MyApp::Meta::Attribute::Labeled; + + has url => ( + metaclass => '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->isa('MyApp::Meta::Attribute::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 SUMMARY -In this recipe, we begin to really delve into the wonder of meta-programming. -Some readers may scoff and claim that this is the arena only of the most -twisted Moose developers. Absolutely not! Any sufficiently twisted developer -can benefit greatly from going more meta. +In this recipe, we begin to delve into the wonder of meta-programming. +Some readers may scoff and claim that this is the arena of only the +most twisted Moose developers. Absolutely not! Any sufficiently +twisted developer can benefit greatly from going more meta. -The high-level goal of this recipe's code is to allow each attribute to have a -human-readable "label" attached to it. Such labels would be used when showing -data to an end user. In this recipe we label the "url" attribute with "The -site's URL" and create a simple method to demonstrate how to use that label. +Our goal is to allow each attribute to have a human-readable "label" +attached to it. Such labels would be used when showing data to an end +user. In this recipe we label the C attribute with "The site's +URL" and create a simple method showing how to use that label. -=head1 REAL ATTRIBUTES 101 +=head1 META-ATTRIBUTE OBJECTS -All the attributes of a Moose-based object are actually objects themselves. -These objects have methods and (surprisingly) attributes. Let's look at a -concrete example. +All the attributes of a Moose-based object are actually objects +themselves. These objects have methods and attributes. Let's look at +a concrete example. - has 'x' => (isa => 'Int', is => 'ro'); - has 'y' => (isa => 'Int', is => 'rw'); + has 'x' => ( isa => 'Int', is => 'ro' ); + has 'y' => ( isa => 'Int', is => 'rw' ); -Ahh, the veritable x and y of the Point example. Internally, every Point has an -x object and a y object. They have methods (such as "get_value") and attributes -(such as "is_lazy"). What class are they instances of? -L. You don't normally see the objects lurking behind -the scenes, because you usually just use C<< $point->x >> and C<< $point->y >> -and forget that there's a lot of machinery lying in such methods. +Internally, the metaclass for C has two +L. There are several methods for getting +meta-attributes out of a metaclass, one of which is +C. This method is called on the metaclass object. -So you have a C<$point> object, which has C and C methods. How can you -actually access the objects behind these attributes? Here's one way: +The C method returns a hash reference that maps +attribute names to their objects. In our case, C +might return something that looks like the following: - $point->meta->get_attribute_map() + { + x => $attr_object_for_x, + y => $attr_object_for_y, + } -C returns a hash reference that maps attribute names to -their objects. In our case, C might return something that -looks like the following: +You can also get a single L with +C. Once you have this meta-attribute object, +you can call methods on it like this: - { - x => Moose::Meta::Attribute=HASH(0x196c23c), - y => Moose::Meta::Attribute=HASH(0x18d1690), - } + print $point->meta->get_attribute('x')->type_constraint; + => Int -Another way to get a handle on an attribute's object is -C<< $self->meta->get_attribute('name') >>. Here's one thing you can do now that -you can interact with the attribute's object directly: +To add a label to our attributes there are two steps. First, we need a +new attribute metaclass that can store a label for an +attribute. Second, we need to create attributes that use that +attribute metaclass. - print $point->meta->get_attribute('x')->type_constraint; - => Int +=head1 RECIPE REVIEW -(As an aside, it's not called C<< ->isa >> because C<< $obj->isa >> is already -taken) +We start by creating a new attribute metaclass. -So to actually beef up attributes, what we need to do is: + package MyApp::Meta::Attribute::Labeled; + use Moose; + extends 'Moose::Meta::Attribute'; -=over 4 +We can subclass a Moose metaclass in the same way that we subclass +anything else. -=item Create a new attribute metaclass + has label => ( + is => 'rw', + isa => 'Str', + predicate => 'has_label', + ); -=item Create attributes using that new metaclass +Again, this is standard Moose code. -=back +Then we need to register our metaclass with Moose: -Moose makes both of these easy! + package Moose::Meta::Attribute::Custom::Labeled; + sub register_implementation { 'MyApp::Meta::Attribute::Labeled' } -Let's start dissecting the recipe's code. +This is a bit of magic that lets us use a short name, "Labeled", when +referring to our new metaclass. -=head1 DISSECTION +That was the whole attribute metaclass. -We get the ball rolling by creating a new attribute metaclass. It starts off -somewhat ungloriously. +Now we start using it. - package MyApp::Meta::Attribute::Labeled; - use Moose; - extends 'Moose::Meta::Attribute'; + package MyApp::Website; + use Moose; + use MyApp::Meta::Attribute::Labeled; -You subclass metaclasses the same way you subclass regular classes. (Extra -credit: how in the actual hell can you use the MOP to extend itself?) +We have to load the metaclass to use it, just like any Perl class. - has label => ( - is => 'rw', - isa => 'Str', - predicate => 'has_label', - ); +Finally, we use it for an attribute: -Hey, this looks pretty reasonable! This is plain jane Moose code. Recipe 1 -fare. This is merely making a new attribute. An attribute that attributes have. -A meta-attribute. It may sound scary, but it really isn't! Reread -L if this really is terrifying. + has url => ( + metaclass => 'Labeled', + is => 'rw', + isa => 'Str', + label => "The site's URL", + ); -The name is "label", it will have a regular accessor, and is a string. -C is a standard part of C. It just creates a method that asks -the question "Does this attribute have a value?" +This looks like a normal attribute declaration, except for two things, +the C and C