convert all uses of Test::Exception to Test::Fatal.
[gitmo/Moose.git] / lib / Moose / Cookbook / Basics / Recipe4.pod
index f6748f7..ee11206 100644 (file)
@@ -1,6 +1,15 @@
 
 =pod
 
+=begin testing-SETUP
+
+use Test::Requires {
+    'Locale::US'     => '0',
+    'Regexp::Common' => '0',
+};
+
+=end testing-SETUP
+
 =head1 NAME
 
 Moose::Cookbook::Basics::Recipe4 - Subtypes, and modeling a simple B<Company> class hierarchy
@@ -43,19 +52,15 @@ Moose::Cookbook::Basics::Recipe4 - Subtypes, and modeling a simple B<Company> cl
 
   sub BUILD {
       my ( $self, $params ) = @_;
-      if ( @{ $self->employees } ) {
-          foreach my $employee ( @{ $self->employees } ) {
-              $employee->company($self);
-          }
+      foreach my $employee ( @{ $self->employees || [] } ) {
+          $employee->employer($self);
       }
   }
 
   after 'employees' => sub {
       my ( $self, $employees ) = @_;
-      if ($employees) {
-          foreach my $employee ( @{$employees} ) {
-              $employee->company($self);
-          }
+      foreach my $employee ( @{ $employees || [] } ) {
+          $employee->employer($self);
       }
   };
 
@@ -101,12 +106,12 @@ declaratively create type constraints without building an entire
 class.
 
 In the recipe we also make use of L<Locale::US> and L<Regexp::Common>
-to build constraints, showing how how constraints can make use of
-existing CPAN tools for data validation.
+to build constraints, showing how constraints can make use of existing
+CPAN tools for data validation.
 
 Finally, we introduce the C<required> attribute option.
 
-The the C<Address> class we define two subtypes. The first uses the
+In the C<Address> class we define two subtypes. The first uses the
 L<Locale::US> module to check the validity of a state. It accepts
 either a state abbreviation of full name.
 
@@ -175,7 +180,7 @@ where each element of the array is an C<Employee> object. It's worth
 noting that an I<empty> array reference also satisfies this
 constraint.
 
-Parameterizable type constraints (or "container types), such as
+Parameterizable type constraints (or "container types"), such as
 C<ArrayRef[`a]>, can be made more specific with a type parameter. In
 fact, we can arbitrarily nest these types, producing something like
 C<HashRef[ArrayRef[Int]]>. However, you can also just use the type by
@@ -200,17 +205,15 @@ C<employer> attribute:
 
   sub BUILD {
       my ( $self, $params ) = @_;
-      if ( $self->employees ) {
-          foreach my $employee ( @{ $self->employees } ) {
-              $employee->company($self);
-          }
+      foreach my $employee ( @{ $self->employees || [] } ) {
+          $employee->employer($self);
       }
   }
 
-The C<BUILD> method is executed after type constraints are checked, so
-it is safe to assume that C<< $self->employees >> will return an array
-reference, and that the elements of that array will be C<Employee>
-objects.
+The C<BUILD> method is executed after type constraints are checked, so it is
+safe to assume that if C<< $self->employees >> has a value, it will be an
+array reference, and that the elements of that array reference will be
+C<Employee> objects.
 
 We also want to make sure that whenever the C<employees> attribute for
 a C<Company> is changed, we also update the C<employer> for each
@@ -220,18 +223,16 @@ To do this we can use an C<after> modifier:
 
   after 'employees' => sub {
       my ( $self, $employees ) = @_;
-      if ($employees) {
-          foreach my $employee ( @{$employees} ) {
-              $employee->company($self);
-          }
+      foreach my $employee ( @{ $employees || [] } ) {
+          $employee->employer($self);
       }
   };
 
-Again, as with the C<BUILD> method, we know that the type constraint
-check has already happened, so we can just check for definedness on the
-C<$employees> argument.
+Again, as with the C<BUILD> method, we know that the type constraint check has
+already happened, so we know that if C<$employees> is defined it will contain
+an array reference of C<Employee> objects..
 
-The B<Person> class does have demonstrate anything new. It has several
+The B<Person> class does not really demonstrate anything new. It has several
 C<required> attributes. It also has a C<predicate> method, which we
 first used in L<recipe 3|Moose::Cookbook::Basics::Recipe3>.
 
@@ -249,7 +250,7 @@ arguments to C<super>. Instead, Moose simply passes the same
 parameters that were passed to the method.
 
 A more detailed example of usage can be found in
-F<t/000_recipes/004_recipe.t>.
+F<t/000_recipes/moose_cookbook_basics_recipe4.t>.
 
 =head1 CONCLUSION
 
@@ -296,11 +297,219 @@ Dave Rolsky E<lt>autarch@urth.orgE<gt>
 
 =head1 COPYRIGHT AND LICENSE
 
-Copyright 2006-2009 by Infinity Interactive, Inc.
+Copyright 2006-2010 by Infinity Interactive, Inc.
 
 L<http://www.iinteractive.com>
 
 This library is free software; you can redistribute it and/or modify
 it under the same terms as Perl itself.
 
+=begin testing
+
+{
+    package Company;
+
+    sub get_employee_count { scalar @{(shift)->employees} }
+}
+
+use Scalar::Util 'isweak';
+
+my $ii;
+ok ! exception {
+    $ii = Company->new(
+        {
+            name    => 'Infinity Interactive',
+            address => Address->new(
+                street   => '565 Plandome Rd., Suite 307',
+                city     => 'Manhasset',
+                state    => 'NY',
+                zip_code => '11030'
+            ),
+            employees => [
+                Employee->new(
+                    first_name => 'Jeremy',
+                    last_name  => 'Shao',
+                    title      => 'President / Senior Consultant',
+                    address =>
+                        Address->new( city => 'Manhasset', state => 'NY' )
+                ),
+                Employee->new(
+                    first_name => 'Tommy',
+                    last_name  => 'Lee',
+                    title      => 'Vice President / Senior Developer',
+                    address =>
+                        Address->new( city => 'New York', state => 'NY' )
+                ),
+                Employee->new(
+                    first_name     => 'Stevan',
+                    middle_initial => 'C',
+                    last_name      => 'Little',
+                    title          => 'Senior Developer',
+                    address =>
+                        Address->new( city => 'Madison', state => 'CT' )
+                ),
+            ]
+        }
+    );
+},
+'... created the entire company successfully';
+isa_ok( $ii, 'Company' );
+
+is( $ii->name, 'Infinity Interactive',
+    '... got the right name for the company' );
+
+isa_ok( $ii->address, 'Address' );
+is( $ii->address->street, '565 Plandome Rd., Suite 307',
+    '... got the right street address' );
+is( $ii->address->city,     'Manhasset', '... got the right city' );
+is( $ii->address->state,    'NY',        '... got the right state' );
+is( $ii->address->zip_code, 11030,       '... got the zip code' );
+
+is( $ii->get_employee_count, 3, '... got the right employee count' );
+
+# employee #1
+
+isa_ok( $ii->employees->[0], 'Employee' );
+isa_ok( $ii->employees->[0], 'Person' );
+
+is( $ii->employees->[0]->first_name, 'Jeremy',
+    '... got the right first name' );
+is( $ii->employees->[0]->last_name, 'Shao', '... got the right last name' );
+ok( !$ii->employees->[0]->has_middle_initial, '... no middle initial' );
+is( $ii->employees->[0]->middle_initial, undef,
+    '... got the right middle initial value' );
+is( $ii->employees->[0]->full_name,
+    'Jeremy Shao, President / Senior Consultant',
+    '... got the right full name' );
+is( $ii->employees->[0]->title, 'President / Senior Consultant',
+    '... got the right title' );
+is( $ii->employees->[0]->employer, $ii, '... got the right company' );
+ok( isweak( $ii->employees->[0]->{employer} ),
+    '... the company is a weak-ref' );
+
+isa_ok( $ii->employees->[0]->address, 'Address' );
+is( $ii->employees->[0]->address->city, 'Manhasset',
+    '... got the right city' );
+is( $ii->employees->[0]->address->state, 'NY', '... got the right state' );
+
+# employee #2
+
+isa_ok( $ii->employees->[1], 'Employee' );
+isa_ok( $ii->employees->[1], 'Person' );
+
+is( $ii->employees->[1]->first_name, 'Tommy',
+    '... got the right first name' );
+is( $ii->employees->[1]->last_name, 'Lee', '... got the right last name' );
+ok( !$ii->employees->[1]->has_middle_initial, '... no middle initial' );
+is( $ii->employees->[1]->middle_initial, undef,
+    '... got the right middle initial value' );
+is( $ii->employees->[1]->full_name,
+    'Tommy Lee, Vice President / Senior Developer',
+    '... got the right full name' );
+is( $ii->employees->[1]->title, 'Vice President / Senior Developer',
+    '... got the right title' );
+is( $ii->employees->[1]->employer, $ii, '... got the right company' );
+ok( isweak( $ii->employees->[1]->{employer} ),
+    '... the company is a weak-ref' );
+
+isa_ok( $ii->employees->[1]->address, 'Address' );
+is( $ii->employees->[1]->address->city, 'New York',
+    '... got the right city' );
+is( $ii->employees->[1]->address->state, 'NY', '... got the right state' );
+
+# employee #3
+
+isa_ok( $ii->employees->[2], 'Employee' );
+isa_ok( $ii->employees->[2], 'Person' );
+
+is( $ii->employees->[2]->first_name, 'Stevan',
+    '... got the right first name' );
+is( $ii->employees->[2]->last_name, 'Little', '... got the right last name' );
+ok( $ii->employees->[2]->has_middle_initial, '... got middle initial' );
+is( $ii->employees->[2]->middle_initial, 'C',
+    '... got the right middle initial value' );
+is( $ii->employees->[2]->full_name, 'Stevan C. Little, Senior Developer',
+    '... got the right full name' );
+is( $ii->employees->[2]->title, 'Senior Developer',
+    '... got the right title' );
+is( $ii->employees->[2]->employer, $ii, '... got the right company' );
+ok( isweak( $ii->employees->[2]->{employer} ),
+    '... the company is a weak-ref' );
+
+isa_ok( $ii->employees->[2]->address, 'Address' );
+is( $ii->employees->[2]->address->city, 'Madison', '... got the right city' );
+is( $ii->employees->[2]->address->state, 'CT', '... got the right state' );
+
+# create new company
+
+my $new_company
+    = Company->new( name => 'Infinity Interactive International' );
+isa_ok( $new_company, 'Company' );
+
+my $ii_employees = $ii->employees;
+foreach my $employee (@$ii_employees) {
+    is( $employee->employer, $ii, '... has the ii company' );
+}
+
+$new_company->employees($ii_employees);
+
+foreach my $employee ( @{ $new_company->employees } ) {
+    is( $employee->employer, $new_company,
+        '... has the different company now' );
+}
+
+## check some error conditions for the subtypes
+
+ok exception {
+    Address->new( street => {} ),;
+},
+'... we die correctly with bad args';
+
+ok exception {
+    Address->new( city => {} ),;
+},
+'... we die correctly with bad args';
+
+ok exception {
+    Address->new( state => 'British Columbia' ),;
+},
+'... we die correctly with bad args';
+
+ok ! exception {
+    Address->new( state => 'Connecticut' ),;
+},
+'... we live correctly with good args';
+
+ok exception {
+    Address->new( zip_code => 'AF5J6$' ),;
+},
+'... we die correctly with bad args';
+
+ok ! exception {
+    Address->new( zip_code => '06443' ),;
+},
+'... we live correctly with good args';
+
+ok exception {
+    Company->new(),;
+},
+'... we die correctly without good args';
+
+ok ! exception {
+    Company->new( name => 'Foo' ),;
+},
+'... we live correctly without good args';
+
+ok exception {
+    Company->new( name => 'Foo', employees => [ Person->new ] ),;
+},
+'... we die correctly with good args';
+
+ok ! exception {
+    Company->new( name => 'Foo', employees => [] ),;
+},
+'... we live correctly with good args';
+
+=end testing
+
 =cut