
=pod

=head1 NAME

Moose::Cookbook::Snack::HashRef - Using the HashRef type constraint

=head1 SYNOPSIS

    package Fruit;
    use Moose;

    has 'species' => ( is => 'rw', required => 1 );

    package ProduceStore;
    use Moose;
    use Moose::Util::TypeConstraints;

    has 'fruit_aisle' => ( is => 'rw', isa => 'HashRef[Fruit]' );

    package main;
    use Moose;

    # we need something to put in the fruit aisle
    my $orange = Fruit->new( species => 'C. sinensis' );
    my $apple = Fruit->new( species => 'M. domestica' );
    my %fruit = ( orange => $orange, apple => $apple );
    my $store = ProduceStore->new( fruit_aisle => \%fruit );

=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 Disclaimer

The code in this document will work on Moose as advertised, but the developers
strongly recommend using something like L<Moose::Autobox> or
L<MooseX::AttributeHelpers> when working with hash references in order to
help keep your Moose objects nice and encapsulated.  The reason why this POD
exists is to show potential users of L<Moose> that Moose objects are just like
Plain Ol' Perl Objects (POPO), albeit with some extra metadata syntatic sugar.

=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 = ProduceStore->new( fruit_aisle => \%fruit );

Or you can pass an anonymous hash to the C<HashRef> attribute as well.  If you
created two new objects, C<$grape> and C<$tomato>, and assigned them to the
C<HashRef>, they would 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 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 "Item: $item, type: " . $fruit->meta->name
            . " species: " . $fruit->species . "\n";
    }

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 "Item: $item, type: " . $fruit->meta->name
            . " species: " . $fruit->species . "\n";
    } 

=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:

    # this replaces the existing HashRef contents
    my $grape = Fruit->new( species => 'V. vinifera' );
    my $tomato = Fruit->new( species => 'S. lycopersicum');
    $store->fruit_aisle( { grape => $grape, tomato => $tomato } );

=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 => 'P. americana' );
    $fruit_aisle_copy{avocado} = $avocado;
    $store->fruit_aisle( \%fruit_aisle_copy );
    $store->fruit_aisle->{avocado};

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 );
    delete $mooseObj->hashref->{foo};

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 that Moose can use
and the tools to extend them or create your own.

=item L<Moose::Autobox> - Autoboxed wrappers for Native Perl datatypes

=item L<MooseX::AttributeHelpers> - Extends attribute interfaces

=back

=head1 AUTHOR

Brian Manning <elspicyjack at gmail dot com>

=head1 COPYRIGHT AND LICENSE

Copyright 2006-2008 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.

=cut
