- initial commit of new snacks (short documents) for ArrayRef's, HashRef's and
Brian Manning [Fri, 9 May 2008 20:17:49 +0000 (20:17 +0000)]
  Perl 5 OO versus Moose OO

lib/Moose/Cookbook/Snack/ArrayRef.pod [new file with mode: 0644]
lib/Moose/Cookbook/Snack/HashRef.pod [new file with mode: 0644]
lib/Moose/Cookbook/Snack/Perl5ObjsVsMooseObjs.pod [new file with mode: 0644]

diff --git a/lib/Moose/Cookbook/Snack/ArrayRef.pod b/lib/Moose/Cookbook/Snack/ArrayRef.pod
new file mode 100644 (file)
index 0000000..eed72f6
--- /dev/null
@@ -0,0 +1,154 @@
+#!/usr/bin/env perl
+=pod
+
+=head1 NAME
+
+Moose::Cookbook::Snack::ArrayRef - (Ab)using the ArrayRef type constraint
+provided by the L<Moose::Util::TypeConstraint> and/or
+L<Moose::Util::TypeConstraints::OptimizedConstraints> classes.
+
+=head1 SYNOPSIS
+
+    package Fruit;
+    use Moose;
+
+    has q(name) => ( is => q(rw), required => 1 );
+    has q(species) => ( is => q(rw), required => 1 );
+
+    package ProduceStoreArray;
+    use Moose;
+    use Moose::Util::TypeConstraints;
+
+    has q(fruit_aisle) => ( is => q(rw), isa => q(ArrayRef[Fruit]) );
+
+    package main;
+
+    # we need something to put in the fruit aisle
+    my $orange = Fruit->new( 
+        name => q(orange), species => q(C. sinensis) );
+    my $apple = Fruit->new( 
+        name => q(apple), species => q(M. domestica) );
+    my @fruit = ( $apple, $orange );
+    my $store = ProduceStore->new( fruit_aisle => \@fruit );
+    
+=head1 DESCRIPTION
+
+The ArrayRef type constraint is used to store a reference to a Perl list or
+array variable as an attribute of a Moose object.
+
+=head2 Assigning arrays to an ArrayRef attribute
+
+Once a Moose-based object with an C<ArrayRef> attribute has been created, you
+can pass an array (by reference) to that object attribute using that
+attribute's accessor.  This is how we assign the apple and orange to the
+store's C<fruit_aisle> C<ArrayRef> attribute, we pass an array containing both
+objects by reference to the C<fruit_aisle> attribute:
+
+    my @fruit = ( $apple, $orange );
+    my $store = ProduceStore->new( fruit_aisle => \@fruit );
+
+Or you can pass an anonymous array to the C<ArrayRef> attribute as well.
+This is shown in the example when the grape and tomato replace the apple
+and the orange in the store's fruit aisle.
+
+    $store->fruit_aisle( [ $grape, $tomato ] );
+
+Our C<fruit_aisle> C<ArrayRef> is parameterized, meaning, that the
+C<fruit_aisle> C<ArrayRef> can contain nothing but C<Fruit> objects as array
+values.  If you try to pass in a reference to a array using C<Str> objects as
+array values for example, Moose will complain: 
+
+    Attribute (fruit_aisle) does not pass the type constraint (ArrayRef[Str])
+
+=head2 Dumping the contents of an ArrayRef
+
+In order to dump the contents of a C<ArrayRef> object attribute, you must first
+de-reference the C<ArrayRef>, and then enumerate over it's keys.  You can add
+this method for showing the store's inventory to the C<ProduceStoreArray>
+object shown in the SYNOPSIS:
+
+    sub show_inventory {
+        my $self = shift;
+        foreach my $item ( @{$self->fruit_aisle} ) {
+            # access each Fruit object
+        } # foreach my $item ( @{$self->fruit_aisle} ) 
+    }
+
+=head2 Assigning arrays to an ArrayRef will overwrite existing arrays
+
+Once you create an object containing a C<ArrayRef> attribute, if you assign a
+new array reference to that attribute, it will replace any existing array
+reference:
+
+    # replace existing inventory
+    my $grape = Fruit->new( 
+        name => q(grape), species => q(V. vinifera) );
+    my $tomato = Fruit->new( 
+        name => q(tomato), species => q(S. lycopersicum));
+    $store->fruit_aisle( [ $grape, $tomato ] ); 
+
+=head2 Appending/Deleting values to/from an ArrayRef
+
+In order to append new elements to an array referred to by the C<ArrayRef>
+attribute, you will need to make a copy of the array first, add your new array
+elements, then assign your modified copy back to the C<ArrayRef> attribute:
+
+    my @fruit_aisle_copy = @{$store->fruit_aisle};
+    my $avocado = Fruit->new( 
+        name => q(avocado), species => q(P. americana) );
+    push(@fruit_aisle_copy, $avocado);
+    $store->fruit_aisle( \@fruit_aisle_copy );
+
+And here's an example of deleting an object stored in an ArrayRef:
+
+    my @fruit_aisle_copy = @{$store->fruit_aisle};
+    # new array to hold the fruit objects that won't be deleted
+    my @reworked_fruit_aisle;
+    for my $fruit_obj ( @fruit_aisle_copy ) {
+        if ( $fruit_obj->name ne q(tomato) ) {
+            push(@reworked_fruit_aisle, $fruit_obj);
+        } # if ( $fruit_obj->name ne q(tomato) )
+    } # for my $fruit_obj ( @fruit_aisle_copy )
+    $store->fruit_aisle( \@reworked_fruit_aisle );
+
+Putting the above code into their own object methods would make appending to or deleting from an C<ArrayRef> a trivial operation.
+
+=head2 Clearing an ArrayRef
+
+Assigning C<undef> to clear an C<ArrayRef> will not work because the attribute
+was originally defined with a type constraint, meaning that attribute must have
+0 or more of that type of value to be valid.  C<undef> in Perl is not a value,
+so it won't work for clearing the C<ArrayRef>.
+
+If you assign an empty anonymous hash to a C<ArrayRef> attribute, this will
+clear out that attribute yet still satisfy the type constraint.
+
+    # this clears the ArrayRef 
+    $store->fruit_aisle( [ ] );
+
+=head1 SEE ALSO
+
+=over 4
+
+=item L<Moose::Cookbook::Recipe4> - Subtypes, and modeling a simple Company
+class hierarchy
+
+=item L<Moose::Cookbook::Snack::Types> - Snippets of code for using Types and
+Type Constraints
+
+=item L<Moose::Util::TypeConstraints> - Type constraints that Moose can use
+
+=back
+
+=head1 AUTHOR
+
+Brian Manning <elspicyjack at gmail dot com>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (c)2008 by Brian Manning
+
+This documentation is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
diff --git a/lib/Moose/Cookbook/Snack/HashRef.pod b/lib/Moose/Cookbook/Snack/HashRef.pod
new file mode 100644 (file)
index 0000000..4282dcb
--- /dev/null
@@ -0,0 +1,185 @@
+#!/usr/bin/env perl
+=pod
+
+=head1 NAME
+
+Moose::Cookbook::Snack::HashRef - (Ab)using the HashRef type constraint
+provided by the L<Moose::Util::TypeConstraint> and/or 
+L<Moose::Util::TypeConstraints::OptimizedConstraints> classes.
+
+=head1 SYNOPSIS
+
+    package Fruit;
+    use Moose;
+
+    has q(species) => ( is => q(rw), required => 1 );
+
+    package ProduceStoreHash;
+    use Moose;
+    use Moose::Util::TypeConstraints;
+
+    has q(fruit_aisle) => ( is => q(rw), isa => q(HashRef[Fruit]) );
+
+    sub show_inventory {
+        my $self = shift;
+        foreach my $item ( keys(%{$self->fruit_aisle}) ) {
+            my $fruit = $self->{fruit_aisle}{$item};
+            print qq(Item: $item, type: ) . blessed($fruit)
+                . q( species: ) . $fruit->species . qq(\n);
+        } # foreach my $item
+    } # sub show_inventory
+
+    package main;
+    use Moose;
+
+    # we need something to put in the fruit aisle
+    my $orange = Fruit->new( species => q(C. sinensis) );
+    my $apple = Fruit->new( species => q(M. domestica) );
+    my %fruit = ( orange => $orange, apple => $apple );
+    my $store = ProduceStoreHash->new( fruit_aisle => \%fruit );
+    print qq(First inventory:\n);
+    $store->show_inventory;
+
+    # this replaces the existing HashRef contents
+    my $grape = Fruit->new( species => q(V. vinifera) );
+    my $tomato = Fruit->new( species => q(S. lycopersicum));
+    $store->fruit_aisle( { grape => $grape, tomato => $tomato } );
+    print qq(Second inventory:\n);
+    $store->show_inventory;
+
+    # this clears the HashRef
+    $store->fruit_aisle( { } );
+    print qq(Third inventory:\n);
+    $store->show_inventory;
+
+=head1 DESCRIPTION
+
+The HashRef type constraint is used to store a reference to a Perl hash
+variable as an attribute of a Moose object.
+
+=head2 Assigning hashes to a HashRef attribute
+
+Once a Moose-based object with a C<HashRef> attribute has been created, you
+can pass a hash (by reference) to that attribute using that attribute's
+accessor.  This is how we assign the apple and orange to the store's
+C<fruit_aisle> C<HashRef> attribute, we pass a hash containing both objects by
+reference to the C<fruit_aisle> attribute:
+
+    my %fruit = ( orange => $orange, apple => $apple );
+    my $store = ProduceStoreHash->new( fruit_aisle => \%fruit );
+
+Or you can pass an anonymous hash to the C<HashRef> attribute as well.
+This is shown in the example when the grape and tomato replace the apple
+and the orange in the store's fruit aisle.
+
+    $store->fruit_aisle( { grape => $grape, tomato => $tomato } );
+
+Our C<fruit_aisle> C<HashRef> example is parameterized, meaning, that the
+C<fruit_aisle> C<HashRef> can contain nothing but C<Fruit> objects as hash
+values.  If you try to pass in a reference to a hash using C<Int> objects as
+hash values for example, Moose will complain:
+
+    Attribute (fruit_aisle) does not pass the type constraint (HashRef[Int])
+
+=head2 Assigning to a HashRef attribute will overwrite
+
+Once you create an object containing a C<HashRef> attribute, if you assign a
+new hash reference to that attribute, it will replace any existing hash
+reference:
+
+    print qq(First inventory:\n);
+    $store->show_inventory;
+    # First inventory:
+    # Item: apple, type: Fruit species: M. domestica
+    # Item: orange, type: Fruit species: C. sinensis
+
+
+    # this replaces the existing HashRef contents
+    my $grape = Fruit->new( species => q(V. vinifera) );
+    my $tomato = Fruit->new( species => q(S. lycopersicum));
+    $store->fruit_aisle( { grape => $grape, tomato => $tomato } );
+
+    print qq(Second inventory:\n);
+    $store->show_inventory;
+    # Second inventory:
+    # Item: tomato, type: Fruit species: S. lycopersicum
+    # Item: grape, type: Fruit species: V. vinifera
+
+=head2 Dumping the contents of the HashRef
+
+In order to dump the contents of a C<HashRef> object attribute, you must first
+de-reference the C<HashRef>, and then enumerate over it's keys.  
+
+    foreach my $item ( keys(%{$self->fruit_aisle}) ) {
+        my $fruit = $self->{fruit_aisle}{$item};
+        print qq(Item: $item, type: ) . blessed($fruit)
+            . q( species: ) . $fruit->species . qq(\n);
+    } # foreach my $item
+
+If the above de-referencing of the C<fruit_aisle> C<HashRef> is a little too
+noisy, you could create a copy of it, and then enumerate over that copy:
+
+    my %fruit_aisle_copy = %{$self->fruit_aisle};
+    foreach my $item ( keys(%fruit_aisle_copy) ) {
+        my $fruit = $fruit_aisle_copy{$item};
+        # 'print' statement from above example goes here
+    } 
+
+=head2 Appending/Deleting key/value pairs to a HashRef
+
+In order to append or delete key/value pairs to the hash referred to by the
+C<HashRef> attribute, you will need to make a copy of the hash first, add or
+delete the desired key/value pairs, then assign your modified copy back to the
+C<HashRef> attribute.  Here's an example of appending new key/value pars:
+
+    my %fruit_aisle_copy = %{$store->fruit_aisle};
+    my $avocado = Fruit->new( species => q(P. americana) );
+    $fruit_aisle_copy{avocado} = $avocado;
+    $store->fruit_aisle( \%fruit_aisle_copy );
+
+And here's an example of deleting existing key/value pairs:
+
+    # delete an attribute from the HashRef
+    %fruit_aisle_copy = %{$store->fruit_aisle};
+    delete($fruit_aisle_copy{tomato});
+    $store->fruit_aisle( \%fruit_aisle_copy );
+
+Putting the above code into their own object methods would make appending to
+and deleting from a C<HashRef> a trivial operation.
+
+=head2 Clearing the HashRef
+
+Assigning C<undef> to clear a C<HashRef> will not work because the attribute
+was originally defined with a type constraint, meaning that attribute must have
+0 or more of that type of value to be valid.  B<undef> in Perl is not a value,
+so it won't work for clearing the C<HashRef>.
+
+If you assign an empty anonymous hash to a C<HashRef> attribute, this will
+clear out that attribute yet still satisfy the type constraint.
+
+    # this clears the HashRef
+    $store->fruit_aisle( { } );
+
+=head1 SEE ALSO
+
+=over 4
+
+=item L<Moose::Cookbook::Snack::Types> - Snippets of code for using Types and
+Type Constraints
+
+=item L<Moose::Util::TypeConstraints> - Type constraints system for Moose
+
+=back
+
+=head1 AUTHOR
+
+Brian Manning <elspicyjack at gmail dot com>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (c)2008 by Brian Manning
+
+This documentation is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
diff --git a/lib/Moose/Cookbook/Snack/Perl5ObjsVsMooseObjs.pod b/lib/Moose/Cookbook/Snack/Perl5ObjsVsMooseObjs.pod
new file mode 100644 (file)
index 0000000..3c17875
--- /dev/null
@@ -0,0 +1,176 @@
+
+=pod
+
+=head1 NAME
+
+Moose::Cookbook::Snack::Perl5ObjsVsMooseObjs - Short comparison between Perl 5
+objects and Moose objects
+
+=head1 SYNOPSIS
+
+    package Moose::Demo;
+    use Moose; # automagically sets 'strict' and 'warnings'
+
+    has q(script_name) => ( is => q(rw), required => 1);
+
+    package main;
+    use Moose; # needed for the call to 'blessed' below
+    
+    # '$0' is the name of this script, set automatically by Perl
+    my $demo = Moose::Demo->new( script_name => $0 );
+
+    print qq(My name is ) . $demo->script_name . qq(\n);
+    print qq(I am a ) . blessed $demo . qq( type of object\n);
+
+=head1 DESCRIPTION
+
+So what's the big stink about Moose?  Perl 5 comes with objects and object
+oriented programming already.  Given the above Moose code, what would similar
+code look like in the existing Perl 5 object-oriented style of programming?
+Let's take a look and find out...
+
+=head2 Perl 5 OO Example
+
+    # Perl 5 Object, as taught by the 'perltoot' POD page
+    package Perl5::Demo;
+    use strict;
+    use warnings;
+
+    
+    sub new {
+        my $class = shift;
+        # assign the rest of the method arguments to a temp hash
+        my %args = @_;
+
+        # create the object out of a blessed hash reference
+        my $self = bless ( {}, ref($class) || $class );
+        # create the script_name attribute
+        $self->{script_name} = undef;
+
+        # verify that the user passed in the 'script_name' attribute
+        if ( exists $args{script_name} ) {
+            $self->script_name($args{script_name});
+        } else {
+            die q(ERROR: can't create object without 'script_name' );
+        } # if ( exists $args{script_name} )
+
+        # return the object reference back to the caller
+        return $self;
+    } # sub new
+    
+    sub script_name {
+        my $self = shift;
+        # check for arguments; use the argument if passed in, otherwise 
+        # return the existing value (if any)
+        if (@_) { $self->{script_name} = shift }
+        return $self->{script_name};
+    } # sub script_name
+
+    package main;
+    use strict;
+    use warnings;
+
+    my $demo = Perl5::Demo->new( script_name => $0 );
+
+    print qq(My name is ) . $demo->script_name . qq(\n);
+    print qq(I am a ) . ref($demo) . qq( type of object\n);
+
+Looks more complex, right?  Moose does a lot of the labor when working with
+Perl objects, so that you don't have to.  What are some of the specific
+differences between Moose and Perl 5 Objects?
+
+=head3 Difference #1 - declaration of object attributes
+
+Both the Moose and Perl 5 objects have one attribute, C<script_name>.  It's a
+good programming practice to always validate user input, so we have the Perl 5
+object check to make sure that the user passes in the C<script_name> attribute
+to it when the object is created.  The Moose object automatically checks this
+for us when we set C<required =E<gt> 1> in the C<has> function for the Moose
+object.
+
+In more advanced Moose usage, you can use something called 'type constraints'
+when creating your Moose objects.  Type constraints are used to validate what
+the user passes in when setting Moose object attributes.  If the user passes
+in a type of data that Moose is not expecting, then the type constraints in
+Moose (specifically, the L<Moose::Util::TypeConstraint> module) will let the
+user know this in no uncertain terms.  Type constraints in Moose can be as
+simple as strings or numbers, or as complex as other Moose objects.
+
+=head3 Difference #2 - strict and warning pragmas
+
+Moose sets the 'strict' and 'warnings' pragmas for you automatically.  We have
+to do this for ourselves in the Perl 5 example.
+
+=head3 Difference #3 - Determining an object's class name
+
+The C<ref()> function in Perl 5 is how you determine an object's class name.
+The proper way to do this with Moose is C<$object-E<gt>meta-E<gt>name>
+B<FIXME> $obj->meta->name
+    
+    # an object's class name in Perl 5 OO
+    print qq(I am a ) . ref($demo) . qq( type of object\n);
+
+    # an object's class name in Moose
+    print qq(I am a ) . blessed $demo->meta->name . qq( type of object\n);
+
+=head3 Difference #4 - Assigning values to Moose object attributes
+
+When you wish to assign a value directly to an object attribute for a Perl 5
+object, you can either create an object method that handles the value for you;
+
+    package Perl5Object;
+    sub set_x { # some code here }
+    package main;
+    # later on...
+    $self->set_x(0);
+
+or you can assign the value directly to the Perl 5 object attribute like this:
+
+    $self->{x} = 0; 
+
+Moose creates object methods for handling attributes for you, as long as you
+specified C<is =E<gt> rw> for each C<has> statement inside the object
+declaration.  This is mentioned in L<Moose::Cookbook::WTF>, in the section
+labeld B<Accessors>, but briefly:
+
+    package MooseObject;
+    has 'x' => (is => 'rw');
+    package main; 
+    # later on...
+    $self->x(0);    
+
+The syntax shown for the Perl 5 object (C<$self-E<gt>{x} = 0>) will also work
+on the Moose object, as Moose objects are blessed hashes just like the average
+Perl object is.  However, if you access the object's hash reference directly
+via the latter syntax:
+
+1) Moose will no longer be to enforce having that attribute be read-only if
+you used (C<is =E<gt> ro>) in the object's declaration.
+
+2) You break that object's encapsulation, which is one of the reasons you want
+to use objects in the first place, right?
+
+=head1 SEE ALSO
+
+=over 4
+
+=item L<Moose::Cookbook::Recipe1> - The 'Point' object example
+
+=item L<Moose::Util::TypeConstraints> - Type constraints that Moose can use
+
+=item L<Moose::Cookbook::WTF> - For when things go wrong with Moose
+
+=back
+
+=head1 AUTHOR
+
+Brian Manning <elspicyjack at gmail dot com>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (c)2008 by Brian Manning
+
+This documentation is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut