From: Brian Manning Date: Fri, 9 May 2008 20:17:49 +0000 (+0000) Subject: - initial commit of new snacks (short documents) for ArrayRef's, HashRef's and X-Git-Tag: 0_55~189 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=686a7f0901f4953a6f70ef52f59332045270c76e;p=gitmo%2FMoose.git - initial commit of new snacks (short documents) for ArrayRef's, HashRef's and Perl 5 OO versus Moose OO --- diff --git a/lib/Moose/Cookbook/Snack/ArrayRef.pod b/lib/Moose/Cookbook/Snack/ArrayRef.pod new file mode 100644 index 0000000..eed72f6 --- /dev/null +++ b/lib/Moose/Cookbook/Snack/ArrayRef.pod @@ -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 and/or +L 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 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 C attribute, we pass an array containing both +objects by reference to the C attribute: + + my @fruit = ( $apple, $orange ); + my $store = ProduceStore->new( fruit_aisle => \@fruit ); + +Or you can pass an anonymous array to the C 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 C is parameterized, meaning, that the +C C can contain nothing but C objects as array +values. If you try to pass in a reference to a array using C 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 object attribute, you must first +de-reference the C, and then enumerate over it's keys. You can add +this method for showing the store's inventory to the C +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 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 +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 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 a trivial operation. + +=head2 Clearing an ArrayRef + +Assigning C to clear an C 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 in Perl is not a value, +so it won't work for clearing the C. + +If you assign an empty anonymous hash to a C 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 - Subtypes, and modeling a simple Company +class hierarchy + +=item L - Snippets of code for using Types and +Type Constraints + +=item L - Type constraints that Moose can use + +=back + +=head1 AUTHOR + +Brian Manning + +=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 index 0000000..4282dcb --- /dev/null +++ b/lib/Moose/Cookbook/Snack/HashRef.pod @@ -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 and/or +L 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 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 C attribute, we pass a hash containing both objects by +reference to the C attribute: + + my %fruit = ( orange => $orange, apple => $apple ); + my $store = ProduceStoreHash->new( fruit_aisle => \%fruit ); + +Or you can pass an anonymous hash to the C 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 C example is parameterized, meaning, that the +C C can contain nothing but C objects as hash +values. If you try to pass in a reference to a hash using C 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 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 object attribute, you must first +de-reference the C, 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 C 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 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 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 a trivial operation. + +=head2 Clearing the HashRef + +Assigning C to clear a C 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 in Perl is not a value, +so it won't work for clearing the C. + +If you assign an empty anonymous hash to a C 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 - Snippets of code for using Types and +Type Constraints + +=item L - Type constraints system for Moose + +=back + +=head1 AUTHOR + +Brian Manning + +=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 index 0000000..3c17875 --- /dev/null +++ b/lib/Moose/Cookbook/Snack/Perl5ObjsVsMooseObjs.pod @@ -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. 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 attribute +to it when the object is created. The Moose object automatically checks this +for us when we set C 1> in the C 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 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 function in Perl 5 is how you determine an object's class name. +The proper way to do this with Moose is C<$object-Emeta-Ename> +B $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 rw> for each C statement inside the object +declaration. This is mentioned in L, in the section +labeld B, 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{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 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 - The 'Point' object example + +=item L - Type constraints that Moose can use + +=item L - For when things go wrong with Moose + +=back + +=head1 AUTHOR + +Brian Manning + +=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