X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMoose%2FCookbook%2FRecipe11.pod;h=0db6c620960b8c5b636f320e8bd3cb77cf62381f;hb=634c8bdabaa067fd7ebd2d45f280f4b02626e970;hp=b2d1e4ce6446429250e6f99947d64c0ad85181c2;hpb=778db3ac5dc266115efefdeb6dcbcf9b2444d9c9;p=gitmo%2FMoose.git diff --git a/lib/Moose/Cookbook/Recipe11.pod b/lib/Moose/Cookbook/Recipe11.pod index b2d1e4c..0db6c62 100644 --- a/lib/Moose/Cookbook/Recipe11.pod +++ b/lib/Moose/Cookbook/Recipe11.pod @@ -3,298 +3,111 @@ =head1 NAME -Moose::Cookbook::Recipe11 - The meta-attribute example +Moose::Cookbook::Recipe11 - Advanced Role Composition - method exclusion and aliasing =head1 SYNOPSIS - package MyApp::Meta::Attribute::Labeled; - use Moose; - extends 'Moose::Meta::Attribute'; - - has label => ( - is => 'rw', - isa => 'Str', - predicate => 'has_label', - ); + package Restartable; + use Moose::Role; - package Moose::Meta::Attribute::Custom::Labeled; - sub register_implementation { 'MyApp::Meta::Attribute::Labeled' } + has 'is_paused' => ( + is => 'rw', + isa => 'Boo', + default => 0, + ); - package MyApp::Website; - use Moose; - use MyApp::Meta::Attribute::Labeled; + requires 'save_state', 'load_state'; - has url => ( - metaclass => 'Labeled', - isa => 'Str', - is => 'rw', - label => "The site's URL", - ); + sub stop { ... } - has name => ( - is => 'rw', - isa => 'Str', - ); + sub start { ... } - sub dump { - my $self = shift; + package Restartable::ButUnreliable; + use Moose::Role; - # iterate over all the attributes in $self - my %attributes = %{ $self->meta->get_attribute_map }; - while (my ($name, $attribute) = each %attributes) { + with 'Restartable' => { alias => { stop => '_stop', + start => '_start' } }; - # 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; - } + sub stop { + my $self = shift; - # print the attribute's value - my $reader = $attribute->get_read_method; - print ": " . $self->$reader . "\n"; - } - } + $self->explode() if rand(1) > .5; - package main; - my $app = MyApp::Website->new(url => "http://google.com", name => "Google"); - $app->dump; + $self->_stop(); + } -=head1 SUMMARY + sub start { + my $self = shift; -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. + $self->explode() if rand(1) > .5; -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. + $self->_start(); + } -=head1 REAL ATTRIBUTES 101 + package Restartable::ButBroken; + use Moose::Role; -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. + with 'Restartable' => { excludes => [ 'stop', 'start' ] }; - has 'x' => (isa => 'Int', is => 'ro'); - has 'y' => (isa => 'Int', is => 'rw'); + sub stop { + my $self = shift; -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. + $self->explode(); + } -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: + sub start { + my $self = shift; - $point->meta->get_attribute_map() + $self->explode(); + } -C returns a hash reference that maps attribute names to -their objects. In our case, C might return something that -looks like the following: +=head1 DESCRIPTION - { - x => Moose::Meta::Attribute=HASH(0x196c23c), - y => Moose::Meta::Attribute=HASH(0x18d1690), - } +Sometimes when you include a role in a class, you may want to leave +out some of its methods. In this example, we have a role C +which provides an C attribute, and two methods, C and +C. The implementation of those two methods is irrelevant. -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: - - print $point->meta->get_attribute('x')->type_constraint; - => Int - -(As an aside, it's not called C<< ->isa >> because C<< $obj->isa >> is already -taken) +Then we have two more roles which also implement the same interface, +each putting their own spin on the C and C method. -So to actually beef up attributes, what we need to do is: +In the C role, we want to provide a new +implementation of C and C, but still have access to the +original implementation. To do this, we alias the methods from +C to private methods, and provide wrappers around the +originals (1). -=over 4 - -=item Create a new attribute metaclass - -=item Create attributes using that new metaclass - -=back - -Moose makes both of these easy! - -Let's start dissecting the recipe's code. - -=head1 DISSECTION - -We get the ball rolling by creating a new attribute metaclass. It starts off -somewhat ungloriously. - - package MyApp::Meta::Attribute::Labeled; - use Moose; - extends 'Moose::Meta::Attribute'; - -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?) - - has label => ( - is => 'rw', - isa => 'Str', - predicate => 'has_label', - ); - -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. - -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?" - - package Moose::Meta::Attribute::Custom::Labeled; - sub register_implementation { 'MyApp::Meta::Attribute::Labeled' } - -This lets Moose discover our new metaclass. That way attributes can actually -use it. More on what this is doing in a moment. +In the C role, we want to provide an entirely +new behavior for C and C, so we exclude them when +composing the C role into C. -Note that we're done defining the new metaclass! Only nine lines of code, and -not particularly difficult lines, either. Now to start using the metaclass. +It's worth noting that the C parameter also accepts a single +string as an argument if you just want to exclude one method. - package MyApp::Website; - use Moose; - use MyApp::Meta::Attribute::Labeled; - -Nothing new here. We do have to actually load our metaclass to be able to use -it. - - has url => ( - metaclass => 'Labeled', - isa => 'Str', - is => 'rw', - label => "The site's URL", - ); - -Ah ha! Now we're using the metaclass. We're adding a new attribute, C, to -C. C lets you set the metaclass of the attribute. -Ordinarily (as we've seen), the metaclass is C. - -When C sees that you're using a new metaclass, it will take the -metaclass's name, prepend C, and call the -C function in that package. So here Moose calls -C. We defined -that function in the beginning -- it just returns our "real" metaclass' -package, C. So Moose uses that metaclass for -the attribute. It may seem a bit convoluted, but the alternative would be to -use C<< metaclass => 'MyApp::Meta::Attribute::Labeled' >> on every attribute. -As usual, Moose optimizes in favor of the end user, not the metaprogrammer. :) -We also could have just defined the metaclass in -C, but it's probably better to keep to -your own namespaces. - -Finally, we see that C is setting our new meta-attribute, C