whoops
[gitmo/Moose.git] / lib / Moose / Cookbook / Recipe6.pod
index 0e3b0a3..8e5811e 100644 (file)
 Moose::Cookbook::Recipe6 - The Moose::Role example
 
 =head1 SYNOPSIS
-  
-  package Constraint;
-  use strict;
-  use warnings;
+
+  package Eq;
   use Moose::Role;
   
-  has 'value' => (isa => 'Int', is => 'ro');
+  requires 'equal_to';
   
-  around 'validate' => sub {
-      my $c = shift;
-      my ($self, $field) = @_;
-      return undef if $c->($self, $self->validation_value($field));
-      return $self->error_message;        
-  };
-  
-  sub validation_value {
-      my ($self, $field) = @_;
-      return $field;
+  sub not_equal_to { 
+      my ($self, $other) = @_;
+      not $self->equal_to($other);
   }
   
-  sub error_message { confess "Abstract method!" }
-  
-  package Constraint::OnLength;
-  use strict;
-  use warnings;
+  package Comparable;
   use Moose::Role;
   
-  has 'units' => (isa => 'Str', is => 'ro');
+  with 'Eq';
   
-  override 'validation_value' => sub {
-      return length(super());
-  };
+  requires 'compare';
   
-  override 'error_message' => sub {
-      my $self = shift;
-      return super() . ' ' . $self->units;
-  };    
-  
-  package Constraint::AtLeast;
-  use strict;
-  use warnings;
-  use Moose;
+  sub equal_to {
+      my ($self, $other) = @_;
+      $self->compare($other) == 0;
+  }    
   
-  with 'Constraint';
+  sub greater_than {
+      my ($self, $other) = @_;
+      $self->compare($other) == 1;
+  }    
   
-  sub validate {
-      my ($self, $field) = @_;
-      ($field >= $self->value);
+  sub less_than {
+      my ($self, $other) = @_;
+      $self->compare($other) == -1;
   }
   
-  sub error_message { 'must be at least ' . (shift)->value; }
+  sub greater_than_or_equal_to {
+      my ($self, $other) = @_;
+      $self->greater_than($other) || $self->equal_to($other);
+  }        
   
-  package Constraint::NoMoreThan;
-  use strict;
-  use warnings;
+  sub less_than_or_equal_to {
+      my ($self, $other) = @_;
+      $self->less_than($other) || $self->equal_to($other);
+  }  
+  
+  package Printable;
+  use Moose::Role;
+  
+  requires 'to_string';    
+  
+  package US::Currency;
   use Moose;
   
-  with 'Constraint';
+  with 'Comparable', 'Printable';
+  
+  has 'amount' => (is => 'rw', isa => 'Num', default => 0);
   
-  sub validate {
-      my ($self, $field) = @_;
-      ($field <= $self->value);
+  sub compare {
+      my ($self, $other) = @_;
+      $self->amount <=> $other->amount;
   }
   
-  sub error_message { 'must be no more than ' . (shift)->value; }
+  sub to_string {
+      my $self = shift;
+      sprintf '$%0.2f USD' => $self->amount
+  }
+
+=head1 DESCRIPTION
+
+In this recipe we examine the role support provided in Moose. "Roles" may be
+described in many ways, but there are two main ways in which they are used: as
+interfaces, and as a means of code reuse. This recipe demonstrates the
+construction and incorporation of roles that define comparison and display of
+objects.
+
+Let's start by examining B<Eq>. You'll notice that instead of the familiar C<use
+Moose> you might be expecting, here we use C<Moose::Role> to make it clear that
+this is a role. We encounter a new keyword, C<requires>:
+
+  requires 'equal_to';
+
+What this does is to indicate that any class which "consumes" (that is to say,
+"includes using C<with>", as we'll see a little later) the B<Eq> role I<must>
+include an C<equal_to> method, whether this is provided by the class itself, one
+of its superclasses, or another role consumed by the class (1).
+
+In addition to requiring an C<equal_to> method, B<Eq> defines a C<not_equal_to>
+method, which simply inverts the result of C<equal_to>. Defining additional
+methods in this way, by using only a few base methods that target classes must
+define, is a useful pattern to provide maximum functionality with minimum
+effort.
+
+After the minimal B<Eq>, we next move on to B<Comparable>. The first thing you
+will notice is another new keyword, C<with>:
+
+  with 'Eq';
+
+C<with> is used to provide a list of roles which this class (or role) consumes.
+Here, B<Comparable> only consumes one role (B<Eq>). In effect, it is as if we
+defined a C<not_equal_to> method within Comparable, and also promised to fulfill
+the requirement of an C<equal_to> method.
+
+B<Comparable> itself states that it requires C<compare>. Again, it means that
+any classes consuming this role must implement a C<compare> method.
+
+  requires 'compare';
+
+B<Comparable> defines an C<equal_to> method which satisfies the B<Eq> role's
+requirements. This, along with a number of other methods (C<greater_than>,
+C<less_than>, C<greater_than_or_equal_to>, and C<less_than_or_equal_to>) is
+simply defined in terms of C<compare>, once again demonstrating the pattern of
+defining a number of utility methods in terms of only a single method that the
+target class need implement.
+
+  sub equal_to {
+      my ($self, $other) = @_;
+      $self->compare($other) == 0;
+  }
   
-  package Constraint::LengthNoMoreThan;
-  use strict;
-  use warnings;
-  use Moose;
+  sub greater_than {
+      my ($self, $other) = @_;
+      $self->compare($other) == 1;
+  }    
   
-  extends 'Constraint::NoMoreThan';
-     with 'Constraint::OnLength';
-     
-  package Constraint::LengthAtLeast;
-  use strict;
-  use warnings;
-  use Moose;
+  sub less_than {
+      my ($self, $other) = @_;
+      $self->compare($other) == -1;
+  }
   
-  extends 'Constraint::AtLeast';
-     with 'Constraint::OnLength';
+  sub greater_than_or_equal_to {
+      my ($self, $other) = @_;
+      $self->greater_than($other) || $self->equal_to($other);
+  }        
   
-=head1 DESCRIPTION
+  sub less_than_or_equal_to {
+      my ($self, $other) = @_;
+      $self->less_than($other) || $self->equal_to($other);
+  }
+
+Next up is B<Printable>. This is a very simple role, akin to B<Eq>. It merely
+requires a C<to_string> method.
+
+Finally, we come to B<US::Currency>, a class that allows us to reap the benefits
+of our hard work. This is a regular Moose class, so we include the normal C<use
+Moose>. It consumes both B<Comparable> and B<Printable>, as the following line
+shows:
+
+  with 'Comparable', 'Printable';
+
+It also defines a regular Moose attribute, C<amount>, with a type constraint of
+C<Num> and a default of C<0>:
+
+  has 'amount' => (is => 'rw', isa => 'Num', default => 0);
+
+Now we come to the core of the class. First up, we define a C<compare> method:
+
+  sub compare {
+      my ($self, $other) = @_;
+      $self->amount <=> $other->amount;
+  }
+
+As you can see, it simply compares the C<amount> attribute of this object with
+the C<amount> attribute of the other object passed to it. With the single
+definition of this method, we gain the following methods for free: C<equal_to>,
+C<greater_than>, C<less_than>, C<greater_than_or_equal_to> and
+C<less_than_or_equal_to>.
+
+We end the class with a definition of the C<to_string> method, which formats the
+C<amount> attribute for display:
+
+  sub to_string {
+      my $self = shift;
+      sprintf '$%0.2f USD' => $self->amount
+  }
+
+=head1 CONCLUSION
+
+This recipe has shown that roles can be very powerful and immensely useful, and
+save a great deal of repetition.
+
+=head1 FOOTNOTES
+
+=over 4
+
+=item (1)
 
-Coming Soon. 
+At present, method requirements from roles cannot be satisfied by attribute
+accessors. This is a limitation of Moose, and will most likely be rectified in a
+future release.
 
-(the other 4 recipes kinda burned me out a bit)
+=back
 
 =head1 AUTHOR
 
@@ -100,7 +202,7 @@ Stevan Little E<lt>stevan@iinteractive.comE<gt>
 
 =head1 COPYRIGHT AND LICENSE
 
-Copyright 2006 by Infinity Interactive, Inc.
+Copyright 2006-2008 by Infinity Interactive, Inc.
 
 L<http://www.iinteractive.com>