From: Dave Rolsky Date: Fri, 2 May 2008 00:45:39 +0000 (+0000) Subject: Wrote recipe9, builder and lazy_build. X-Git-Tag: 0_55~200 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=ceb8945d376c20e3f4ceecc3d5b70199a773ecd1;p=gitmo%2FMoose.git Wrote recipe9, builder and lazy_build. Added recipe9 (and 7) to changes for 0.44 --- diff --git a/Changes b/Changes index aa9bde1..ef49b07 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,11 @@ Revision history for Perl extension Moose +0.44 + * Moose::Cookbook::Recipe7 + - added new recipe for immutable functionality (Dave Rolsky) + * Moose::Cookbook::Recipe9 + - added new recipe for builder and lazy-builder (Dave Rolsky) + 0.43 Wed. April, 30, 2008 * NOTE TO SELF: drink more coffee before diff --git a/MANIFEST b/MANIFEST index beedbb0..d225c5d 100644 --- a/MANIFEST +++ b/MANIFEST @@ -22,6 +22,7 @@ lib/Moose/Cookbook/Recipe4.pod lib/Moose/Cookbook/Recipe5.pod lib/Moose/Cookbook/Recipe6.pod lib/Moose/Cookbook/Recipe7.pod +lib/Moose/Cookbook/Recipe9.pod lib/Moose/Cookbook/Snack/Types.pod lib/Moose/Cookbook/WTF.pod lib/Moose/Meta/Attribute.pm diff --git a/lib/Moose/Cookbook.pod b/lib/Moose/Cookbook.pod index 9261653..59e192e 100644 --- a/lib/Moose/Cookbook.pod +++ b/lib/Moose/Cookbook.pod @@ -61,9 +61,11 @@ I Work off of this http://code2.0beta.co.uk/moose/svn/Moose/trunk/t/200_examples/007_Child_Parent_attr_inherit.t -=item L - ?? +=item L - Builder methods and lazy_build -I +Lazy attributes do not get set via their defaults until they are +accessed. The builder feature provides an inheritable and +role-composable way to provide a default attribute value. =back diff --git a/lib/Moose/Cookbook/Recipe3.pod b/lib/Moose/Cookbook/Recipe3.pod index da06f7b..16836bb 100644 --- a/lib/Moose/Cookbook/Recipe3.pod +++ b/lib/Moose/Cookbook/Recipe3.pod @@ -219,6 +219,9 @@ In short, don't use them unless you know what you are doing :) You I use the C option without the C option if you like, as we showed in the second recipe. +And actually, you can use C instead of C. See +L for details. + =back =head1 AUTHOR diff --git a/lib/Moose/Cookbook/Recipe9.pod b/lib/Moose/Cookbook/Recipe9.pod new file mode 100644 index 0000000..d028087 --- /dev/null +++ b/lib/Moose/Cookbook/Recipe9.pod @@ -0,0 +1,193 @@ + +=pod + +=head1 NAME + +Moose::Cookbook::Recipe9 - Builder methods and lazy_build + +=head1 SYNOPSIS + + package BinaryTree; + use Moose; + + has 'node' => (is => 'rw', isa => 'Any'); + + has 'parent' => ( + is => 'rw', + isa => 'BinaryTree', + predicate => 'has_parent', + weak_ref => 1, + ); + + has 'left' => ( + is => 'rw', + isa => 'BinaryTree', + predicate => 'has_left', + lazy => 1, + builder => '_build_child_tree', + ); + + has 'right' => ( + is => 'rw', + isa => 'BinaryTree', + predicate => 'has_right', + lazy => 1, + builder => '_build_child_tree', + ); + + before 'right', 'left' => sub { + my ($self, $tree) = @_; + $tree->parent($self) if defined $tree; + }; + + sub _build_child_tree { + my $self = shift; + + return BinaryTree->new( parent => $self ); + } + +=head1 DESCRIPTION + +If you've already read L, then this example +should look awfully familiar. In fact, all we've done here is replace +the attribute C with a C method. + +In this particular case, the C and C act exactly the +same. When the C or C attribute is first accessed before +it has been set, Moose will call the specified C method to +populate the attribute. + +=head2 Subclassable + +There are some differences between C and C. Because +C is called I, it goes through Perl's normal +inheritance system. This means that builder methods are both +inheritable and overrideable. + +For example, we might make a C subclass: + + package TrinaryTree; + use Moose; + + extends 'BinaryTree'; + + has 'middle' => ( + is => 'rw', + isa => 'BinaryTree', + predicate => 'has_middle', + lazy => 1, + builder => '_build_child_tree', + ); + +This doesn't quite work though. If you look closely at the +C<_build_child_tree> method defined in C, you'll notice +that it hard-codes a class name. Naughty us! + +Also, as a bonus, we'll pass C<@_> through, so subclasses can override +the method to pass additional options to the constructor. + +Good object-oriented code should allow itself to be subclassed +gracefully. Let's tweak C<_build_child_tree>: + + sub _build_child_tree + my $self = shift; + + return (ref $self)->new( parent => $self, @_ ); + } + +Now C<_build_child_tree> can be gracefully inherited and overridden. + +=head2 Composable + +There's more to builders than just subclassing, though. The fact that +builders are called by name also makes them suitable for use in a +role. + + package HasAnimal; + use Moose::Role; + + requires '_build_animal'; + + has 'animal' => ( + is => 'ro', + isa => 'Animal', + lazy => 1, + builder => '_build_animal', + ); + +This role provides an animal attribute, but requires that the consumer +of the role provide a builder method it. + + package CatLover; + use Moose; + + with 'HasAnimal'; + + sub _build_animal { + return Cat->new(); + } + +This simply could not be done using a C. + +=head2 The lazy_build shortcut + +The C attribute parameter can be used as sugar to specify +a whole bunch of options at once. + + has 'animal' => ( + is => 'ro', + isa => 'Animal', + lazy_build => 1, + ); + +This is a shorthand for this: + + has 'animal' => ( + is => 'ro', + isa => 'Animal', + required => 1, + lazy => 1, + builder => '_build_animal', + predicate => 'has_animal', + clearer => 'clear_animal', + ); + +If your attribute starts with an underscore, Moose is smart and will +do the right thing with the C and C, making them +both start with an underscore. The C method I starts +with an underscore, since you will want this to be private the vast +majority of the time. + +=head1 CONCLUSION + +The C option is a more OO-friendly version of the C +functionality. It also has the property of separating out the code +into a separate well-defined method. This alone makes it valuable. It +is quite ugly to jam a long default code reference into your attribute +definition. + +Here are some good rules for determining when to use C vs +C. + +If the default value is a simple scalar that only needs to be +calculated once (or a constant), use C. + +If the default value is an empty reference that needs to be wrapped in +a coderef like C, use C. + +Otherwise, use C. + +=head1 AUTHOR + +Dave Rolsky Eautarch@urth.orgE + +=head1 COPYRIGHT AND LICENSE + +Copyright 2006-2008 by Infinity Interactive, Inc. + +L + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut