X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMoose%2FCookbook%2FRecipe3.pod;h=16836bba69a1f3713157abaa71ba241f926ac5bf;hb=ceb8945d376c20e3f4ceecc3d5b70199a773ecd1;hp=5ddd267978ea5e0c03ccfc5a188e79e5ca4d6c4e;hpb=471c4f0999c20c70c19433519b2200171703cc6c;p=gitmo%2FMoose.git diff --git a/lib/Moose/Cookbook/Recipe3.pod b/lib/Moose/Cookbook/Recipe3.pod index 5ddd267..16836bb 100644 --- a/lib/Moose/Cookbook/Recipe3.pod +++ b/lib/Moose/Cookbook/Recipe3.pod @@ -3,32 +3,36 @@ =head1 NAME -Moose::Cookbook::Recipe3 +Moose::Cookbook::Recipe3 - A lazy B example =head1 SYNOPSIS package BinaryTree; - use strict; - use warnings; use Moose; + has 'node' => (is => 'rw', isa => 'Any'); + has 'parent' => ( - is => 'rw', - isa => 'BinaryTree', + is => 'rw', + isa => 'BinaryTree', predicate => 'has_parent', - weak_ref => 1, + weak_ref => 1, ); has 'left' => ( - is => 'rw', - isa => 'BinaryTree', - predicate => 'has_left', + is => 'rw', + isa => 'BinaryTree', + predicate => 'has_left', + lazy => 1, + default => sub { BinaryTree->new(parent => $_[0]) }, ); has 'right' => ( - is => 'rw', - isa => 'BinaryTree', - predicate => 'has_right', + is => 'rw', + isa => 'BinaryTree', + predicate => 'has_right', + lazy => 1, + default => sub { BinaryTree->new(parent => $_[0]) }, ); before 'right', 'left' => sub { @@ -38,17 +42,199 @@ Moose::Cookbook::Recipe3 =head1 DESCRIPTION +In this recipe we take a closer look at attributes, and see how +some of their more advanced features can be used to create fairly +complex behaviors. + +The class in this recipe is a classic binary tree, each node in the +tree is represented by an instance of the B class. Each +instance has a C slot to hold an arbitrary value, a C +slot to hold the right node, a C slot to hold the left node, +and finally a C slot to hold a reference back up the tree. + +Now, let's start with the code. Our first attribute is the C +slot, defined as such: + + has 'node' => (is => 'rw', isa => 'Any'); + +If you recall from the previous recipes, this slot will have a read/write +accessor generated for it, and has a type constraint on it. The new item here is +the type constraint of C. C is the "root" of the +L type hierarchy. It means exactly what it says: +I value passes the constraint. Now, you could just as easily have left out +the C, leaving the C slot unconstrained and retaining this +behavior. But in this case, we are really including the type constraint for the +benefit of other programmers, not the computer. It makes clear my intent that +the C attribute can be of any type, and that the class is a polymorphic +container. + +Next, let's move on to the C slot: + + has 'parent' => ( + is => 'rw', + isa => 'BinaryTree', + predicate => 'has_parent', + weak_ref => 1, + ); + +As you already know, this code tells you that C gets a read/write +accessor and is constrained to only accept instances of B. You will +of course remember from the second recipe that the C type constraint +is automatically created for us by Moose. + +The next attribute option is new, though: the C option. +This option creates a method which can be used to check whether +a given slot (in this case C) has been initialized. In +this case it will create a method called C. Quite simple, +and quite handy too. + +This brings us to our last attribute option, also a new one. Since C is +a circular reference (the tree in C should already have a reference to +this one, in its C or C node), we want to make sure that it is also +a weakened reference to avoid memory leaks. The C attribute option +will do just that, C simply takes a boolean value (C<1> or C<0>) and +then alters the accessor function to weaken the reference to any value stored in +the C slot (1). + +Now, onto the C and C attributes. They are essentially identical, +save for different names, so I will just describe one here: + + has 'left' => ( + is => 'rw', + isa => 'BinaryTree', + predicate => 'has_left', + lazy => 1, + default => sub { BinaryTree->new(parent => $_[0]) }, + ); + +You already know what the C, C and C options do, but now we +have two new options. These two options are actually linked together, in fact: +you cannot use the C option unless you have set the C option. +Class creation will fail with an exception (2). + +Before I go into detail about how C works, let me first +explain how C works, and in particular why it is wrapped +in a CODE ref. + +In the second recipe the B's C slot had a +default value of C<0>. Since Perl will copy strings and numbers +by value, this was all we had to say. But for any other item +(ARRAY ref, HASH ref, object instance, etc) you would need to +wrap it in a CODE reference, so this: + + has 'foo' => (is => 'rw', default => []); + +is actually illegal in Moose. Instead, what you really want is this: + + has 'foo' => (is => 'rw', default => sub { [] }); + +This ensures that each instance of this class will get its own ARRAY ref in the +C slot. + +One other feature of the CODE ref version of the C option is that when +the subroutine is executed (to get the default value), we pass in the instance +where the slot will be stored. This can come in quite handy at times, as +illustrated above, with this code: + + default => sub { BinaryTree->new(parent => $_[0]) }, + +The default value being generated is a new C instance for the +C (or C) slot. Here we set up the correct relationship by passing +the current instance as the C argument to the constructor. + +Now, before we go on to the C option, I want you to think +for a moment. When an instance of this class is created, and the +slots are being initialized, the "normal" behavior would be for +the C and C slots to be populated with a new instance +of B. In creating that instance of the C or +C slots, we would need to create new instances to populate +the C and C slots of I instances. This would +continue in an I until you had +exhausted all available memory on your machine. + +This is, of course, not good :) + +Which brings us to the C attribute option. The C option does just +what it says: it lazily initializes the slot within the instance. This means +that it waits till absolutely the I possible moment to populate the +slot. So if you, the user, store a value in the slot, everything works normally, +and what you pass in is stored. However, if you I the slot I +storing a value in it, then at that I moment (and no sooner), the slot +will be populated with the value of the C option. + +This option is what allows the B class to instantiate +objects without fear of the I +mentioned earlier. + +So, we have described a quite complex set of behaviors here, and not one method +had to be written. But wait, we aren't quite done yet; the autogenerated +C and C accessors are not completely correct. They will not install +the parental relationships that we need. We could write our own accessors, but +that would require us to implement all those features we got automatically (type +constraints, lazy initialization, and so on). Instead, we use method modifiers +again: + + before 'right', 'left' => sub { + my ($self, $tree) = @_; + $tree->parent($self) if defined $tree; + }; + +This is a C modifier, just like we saw in the second recipe, but with +two slight differences. First, we are applying this to more than one method at a +time. Since both the C and C methods need the same feature, it +makes sense. The second difference is that we are not wrapping an inherited +method anymore, but instead a method of our own local class. Wrapping local +methods is no different, the only requirement is that the wrappee be created +before the wrapper (after all, you cannot wrap something which doesn't exist, +right?). + +Now, as with all the other recipes, you can go about using +B like any other Perl 5 class. A more detailed example of its +usage can be found in F. + +=head1 CONCLUSION + +This recipe introduced you to some of the more advanced behavioral +possibilities of Moose's attribute mechanism. I hope that it has +opened your mind to the powerful possibilities of Moose. In the next +recipe we explore how we can create custom subtypes and take +advantage of the plethora of useful modules out on CPAN with Moose. + +=head1 FOOTNOTES + +=over 4 + +=item (1) + +Weak references are tricky things, and should be used sparingly +and appropriately (such as in the case of circular refs). If you +are not careful, you will have slot values disappear "mysteriously" +because perls reference counting garbage collector has gone and +removed the item you are weak-referencing. + +In short, don't use them unless you know what you are doing :) + +=item (2) + +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 Stevan Little Estevan@iinteractive.comE =head1 COPYRIGHT AND LICENSE -Copyright 2006 by Infinity Interactive, Inc. +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 \ No newline at end of file +=cut