X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMoose%2FCookbook%2FBasics%2FRecipe3.pod;h=1028ecd7fddb6c16920f9903f3e3be295b2d5aa8;hb=53a4d826caec4b82f5b23e0bc0a4e8e2f44243b9;hp=3f0022337f54dc19553bc32ca039b64f09d95a0a;hpb=9327b01c56a220b4c09f73381bca577f49ad87a9;p=gitmo%2FMoose.git diff --git a/lib/Moose/Cookbook/Basics/Recipe3.pod b/lib/Moose/Cookbook/Basics/Recipe3.pod index 3f00223..1028ecd 100644 --- a/lib/Moose/Cookbook/Basics/Recipe3.pod +++ b/lib/Moose/Cookbook/Basics/Recipe3.pod @@ -25,6 +25,7 @@ Moose::Cookbook::Basics::Recipe3 - A lazy B example predicate => 'has_left', lazy => 1, default => sub { BinaryTree->new( parent => $_[0] ) }, + trigger => \&_set_parent_for_child ); has 'right' => ( @@ -33,21 +34,24 @@ Moose::Cookbook::Basics::Recipe3 - A lazy B example predicate => 'has_right', lazy => 1, default => sub { BinaryTree->new( parent => $_[0] ) }, + trigger => \&_set_parent_for_child ); - before 'right', 'left' => sub { - my ( $self, $tree ) = @_; - if (defined $tree) { - confess "You cannot insert a tree which already has a parent" - if $tree->has_parent; - $tree->parent($self); - } - }; + sub _set_parent_for_child { + my ( $self, $child ) = @_; + + confess "You cannot insert a tree which already has a parent" + if $child->has_parent; + + $child->parent($self); + } =head1 DESCRIPTION This recipe shows how various advanced attribute features can be used -to create complex and powerful behaviors. +to create complex and powerful behaviors. In particular, we introduce +a number of new attribute options, including C, C, +and C. The example class is a classic binary tree. Each node in the tree is itself an instance of C. It has a C, which holds @@ -59,8 +63,7 @@ Let's take a look at the C attribute: has 'node' => ( is => 'rw', isa => 'Any' ); Moose generates a read-write accessor for this attribute. The type -constraint is C, which means literally means it can contain -anything. +constraint is C, which literally means it can contain anything. We could have left out the C option, but in this case, we are including it for the benefit of other programmers, not the computer. @@ -101,18 +104,20 @@ C: predicate => 'has_left', lazy => 1, default => sub { BinaryTree->new( parent => $_[0] ) }, + trigger => \&_set_parent_for_child ); -There are two new options here, C and C. These two -options are linked, and in fact you cannot have a C attribute -unless it has a C (or a C, but we'll cover that -later). If you try to make an attribute lazy without a default, class -creation will fail with an exception. (2) +There are three new options here, C, C, and +C. The C and C options options are linked. In +fact, you cannot have a C attribute unless it has a C +(or a C, but we'll cover that later). If you try to make an +attribute lazy without a default, class creation will fail with an +exception. (2) In the second recipe the B's C attribute had a default value of C<0>. Given a non-reference, Perl copies the I. However, given a reference, it does not do a deep clone, -instead simply copying the reference. If you just specified a simply +instead simply copying the reference. If you just specified a simple reference for a default, Perl would create it once and it would be shared by all objects with that attribute. @@ -123,6 +128,7 @@ reference every time the default is called. In fact, using a non-subroutine reference as a default is illegal in Moose. + # will fail has 'foo' => ( is => 'rw', default => [] ); This will blow up, so don't do it. @@ -151,35 +157,32 @@ these is set, we want to make sure that we update the parent of the C or C attribute's tree. We could write our own accessors, but then why use Moose at all? -Instead, we use method modifiers: - - before 'right', 'left' => sub { - my ( $self, $tree ) = @_; - if (defined $tree) { - confess "You cannot insert a tree which already has a parent" - if $tree->has_parent; - $tree->parent($self); - } - }; - -This is a C modifier, just like we saw in the second recipe, -but with two slight differences. First, we are applying the modifier -to more than one method at a time, because both C and C -attributes need the same behavior. The other difference is that we are -not wrapping an inherited method, but rather a method from our own -local class. Wrapping local methods is no different, the only -requirement is that the wrappee must exist before the wrapper is -defined (after all, you cannot wrap something which doesn't exist, -right?). - -We could also get the same outcome by using an attribute trigger. A -trigger is fired whenever the attribute is I. See -L for more information about -triggers. +Instead, we use a C. A C accepts a subroutine +reference, which will be called as a method whenever the attribute is +set. This can happen both during object construction or later by +passing a new object to the attribute's accessor method. However, it +is not called when a value is provided by a C or C. + + sub _set_parent_for_child { + my ( $self, $child ) = @_; + + confess "You cannot insert a tree which already has a parent" + if $child->has_parent; + + $child->parent($self); + } + +This trigger does two things. First, it ensures that the new child +node does not already have a parent. This is done for the sake of +simplifying the example. If we wanted to be more clever, we would +remove the child from its old parent tree and add it to the new one. + +If the child has no parent, we will add it to the current tree, and we +ensure that is has the correct value for its C attribute. As with all the other recipes, B can be used just like any other Perl 5 class. A more detailed example of its usage can be found -in F. +in F. =head1 CONCLUSION @@ -207,7 +210,7 @@ You I use the C option without the C option if you like, as we showed in the second recipe. Also, you can use C instead of C. See -L for details. +L for details. =back @@ -219,11 +222,112 @@ Dave Rolsky Eautarch@urth.orgE =head1 COPYRIGHT AND LICENSE -Copyright 2006-2009 by Infinity Interactive, Inc. +Copyright 2006-2010 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. +=begin testing + +use Scalar::Util 'isweak'; + +my $root = BinaryTree->new(node => 'root'); +isa_ok($root, 'BinaryTree'); + +is($root->node, 'root', '... got the right node value'); + +ok(!$root->has_left, '... no left node yet'); +ok(!$root->has_right, '... no right node yet'); + +ok(!$root->has_parent, '... no parent for root node'); + +# make a left node + +my $left = $root->left; +isa_ok($left, 'BinaryTree'); + +is($root->left, $left, '... got the same node (and it is $left)'); +ok($root->has_left, '... we have a left node now'); + +ok($left->has_parent, '... lefts has a parent'); +is($left->parent, $root, '... lefts parent is the root'); + +ok(isweak($left->{parent}), '... parent is a weakened ref'); + +ok(!$left->has_left, '... $left no left node yet'); +ok(!$left->has_right, '... $left no right node yet'); + +is($left->node, undef, '... left has got no node value'); + +lives_ok { + $left->node('left') +} '... assign to lefts node'; + +is($left->node, 'left', '... left now has a node value'); + +# make a right node + +ok(!$root->has_right, '... still no right node yet'); + +is($root->right->node, undef, '... right has got no node value'); + +ok($root->has_right, '... now we have a right node'); + +my $right = $root->right; +isa_ok($right, 'BinaryTree'); + +lives_ok { + $right->node('right') +} '... assign to rights node'; + +is($right->node, 'right', '... left now has a node value'); + +is($root->right, $right, '... got the same node (and it is $right)'); +ok($root->has_right, '... we have a right node now'); + +ok($right->has_parent, '... rights has a parent'); +is($right->parent, $root, '... rights parent is the root'); + +ok(isweak($right->{parent}), '... parent is a weakened ref'); + +# make a left node of the left node + +my $left_left = $left->left; +isa_ok($left_left, 'BinaryTree'); + +ok($left_left->has_parent, '... left does have a parent'); + +is($left_left->parent, $left, '... got a parent node (and it is $left)'); +ok($left->has_left, '... we have a left node now'); +is($left->left, $left_left, '... got a left node (and it is $left_left)'); + +ok(isweak($left_left->{parent}), '... parent is a weakened ref'); + +# make a right node of the left node + +my $left_right = BinaryTree->new; +isa_ok($left_right, 'BinaryTree'); + +lives_ok { + $left->right($left_right) +} '... assign to rights node'; + +ok($left_right->has_parent, '... left does have a parent'); + +is($left_right->parent, $left, '... got a parent node (and it is $left)'); +ok($left->has_right, '... we have a left node now'); +is($left->right, $left_right, '... got a left node (and it is $left_left)'); + +ok(isweak($left_right->{parent}), '... parent is a weakened ref'); + +# and check the error + +dies_ok { + $left_right->right($left_left) +} '... cant assign a node which already has a parent'; + +=end testing + =cut