Commit | Line | Data |
686a7f09 |
1 | #!/usr/bin/env perl |
2 | =pod |
3 | |
4 | =head1 NAME |
5 | |
6 | Moose::Cookbook::Snack::HashRef - (Ab)using the HashRef type constraint |
7 | provided by the L<Moose::Util::TypeConstraint> and/or |
8 | L<Moose::Util::TypeConstraints::OptimizedConstraints> classes. |
9 | |
10 | =head1 SYNOPSIS |
11 | |
12 | package Fruit; |
13 | use Moose; |
14 | |
15 | has q(species) => ( is => q(rw), required => 1 ); |
16 | |
17 | package ProduceStoreHash; |
18 | use Moose; |
19 | use Moose::Util::TypeConstraints; |
20 | |
21 | has q(fruit_aisle) => ( is => q(rw), isa => q(HashRef[Fruit]) ); |
22 | |
23 | sub show_inventory { |
24 | my $self = shift; |
25 | foreach my $item ( keys(%{$self->fruit_aisle}) ) { |
26 | my $fruit = $self->{fruit_aisle}{$item}; |
27 | print qq(Item: $item, type: ) . blessed($fruit) |
28 | . q( species: ) . $fruit->species . qq(\n); |
29 | } # foreach my $item |
30 | } # sub show_inventory |
31 | |
32 | package main; |
33 | use Moose; |
34 | |
35 | # we need something to put in the fruit aisle |
36 | my $orange = Fruit->new( species => q(C. sinensis) ); |
37 | my $apple = Fruit->new( species => q(M. domestica) ); |
38 | my %fruit = ( orange => $orange, apple => $apple ); |
39 | my $store = ProduceStoreHash->new( fruit_aisle => \%fruit ); |
40 | print qq(First inventory:\n); |
41 | $store->show_inventory; |
42 | |
43 | # this replaces the existing HashRef contents |
44 | my $grape = Fruit->new( species => q(V. vinifera) ); |
45 | my $tomato = Fruit->new( species => q(S. lycopersicum)); |
46 | $store->fruit_aisle( { grape => $grape, tomato => $tomato } ); |
47 | print qq(Second inventory:\n); |
48 | $store->show_inventory; |
49 | |
50 | # this clears the HashRef |
51 | $store->fruit_aisle( { } ); |
52 | print qq(Third inventory:\n); |
53 | $store->show_inventory; |
54 | |
55 | =head1 DESCRIPTION |
56 | |
57 | The HashRef type constraint is used to store a reference to a Perl hash |
58 | variable as an attribute of a Moose object. |
59 | |
60 | =head2 Assigning hashes to a HashRef attribute |
61 | |
62 | Once a Moose-based object with a C<HashRef> attribute has been created, you |
63 | can pass a hash (by reference) to that attribute using that attribute's |
64 | accessor. This is how we assign the apple and orange to the store's |
65 | C<fruit_aisle> C<HashRef> attribute, we pass a hash containing both objects by |
66 | reference to the C<fruit_aisle> attribute: |
67 | |
68 | my %fruit = ( orange => $orange, apple => $apple ); |
69 | my $store = ProduceStoreHash->new( fruit_aisle => \%fruit ); |
70 | |
71 | Or you can pass an anonymous hash to the C<HashRef> attribute as well. |
72 | This is shown in the example when the grape and tomato replace the apple |
73 | and the orange in the store's fruit aisle. |
74 | |
75 | $store->fruit_aisle( { grape => $grape, tomato => $tomato } ); |
76 | |
77 | Our C<fruit_aisle> C<HashRef> example is parameterized, meaning, that the |
78 | C<fruit_aisle> C<HashRef> can contain nothing but C<Fruit> objects as hash |
79 | values. If you try to pass in a reference to a hash using C<Int> objects as |
80 | hash values for example, Moose will complain: |
81 | |
82 | Attribute (fruit_aisle) does not pass the type constraint (HashRef[Int]) |
83 | |
84 | =head2 Assigning to a HashRef attribute will overwrite |
85 | |
86 | Once you create an object containing a C<HashRef> attribute, if you assign a |
87 | new hash reference to that attribute, it will replace any existing hash |
88 | reference: |
89 | |
90 | print qq(First inventory:\n); |
91 | $store->show_inventory; |
92 | # First inventory: |
93 | # Item: apple, type: Fruit species: M. domestica |
94 | # Item: orange, type: Fruit species: C. sinensis |
95 | |
96 | |
97 | # this replaces the existing HashRef contents |
98 | my $grape = Fruit->new( species => q(V. vinifera) ); |
99 | my $tomato = Fruit->new( species => q(S. lycopersicum)); |
100 | $store->fruit_aisle( { grape => $grape, tomato => $tomato } ); |
101 | |
102 | print qq(Second inventory:\n); |
103 | $store->show_inventory; |
104 | # Second inventory: |
105 | # Item: tomato, type: Fruit species: S. lycopersicum |
106 | # Item: grape, type: Fruit species: V. vinifera |
107 | |
108 | =head2 Dumping the contents of the HashRef |
109 | |
110 | In order to dump the contents of a C<HashRef> object attribute, you must first |
111 | de-reference the C<HashRef>, and then enumerate over it's keys. |
112 | |
113 | foreach my $item ( keys(%{$self->fruit_aisle}) ) { |
114 | my $fruit = $self->{fruit_aisle}{$item}; |
115 | print qq(Item: $item, type: ) . blessed($fruit) |
116 | . q( species: ) . $fruit->species . qq(\n); |
117 | } # foreach my $item |
118 | |
119 | If the above de-referencing of the C<fruit_aisle> C<HashRef> is a little too |
120 | noisy, you could create a copy of it, and then enumerate over that copy: |
121 | |
122 | my %fruit_aisle_copy = %{$self->fruit_aisle}; |
123 | foreach my $item ( keys(%fruit_aisle_copy) ) { |
124 | my $fruit = $fruit_aisle_copy{$item}; |
125 | # 'print' statement from above example goes here |
126 | } |
127 | |
128 | =head2 Appending/Deleting key/value pairs to a HashRef |
129 | |
130 | In order to append or delete key/value pairs to the hash referred to by the |
131 | C<HashRef> attribute, you will need to make a copy of the hash first, add or |
132 | delete the desired key/value pairs, then assign your modified copy back to the |
133 | C<HashRef> attribute. Here's an example of appending new key/value pars: |
134 | |
135 | my %fruit_aisle_copy = %{$store->fruit_aisle}; |
136 | my $avocado = Fruit->new( species => q(P. americana) ); |
137 | $fruit_aisle_copy{avocado} = $avocado; |
138 | $store->fruit_aisle( \%fruit_aisle_copy ); |
139 | |
140 | And here's an example of deleting existing key/value pairs: |
141 | |
142 | # delete an attribute from the HashRef |
143 | %fruit_aisle_copy = %{$store->fruit_aisle}; |
144 | delete($fruit_aisle_copy{tomato}); |
145 | $store->fruit_aisle( \%fruit_aisle_copy ); |
146 | |
147 | Putting the above code into their own object methods would make appending to |
148 | and deleting from a C<HashRef> a trivial operation. |
149 | |
150 | =head2 Clearing the HashRef |
151 | |
152 | Assigning C<undef> to clear a C<HashRef> will not work because the attribute |
153 | was originally defined with a type constraint, meaning that attribute must have |
154 | 0 or more of that type of value to be valid. B<undef> in Perl is not a value, |
155 | so it won't work for clearing the C<HashRef>. |
156 | |
157 | If you assign an empty anonymous hash to a C<HashRef> attribute, this will |
158 | clear out that attribute yet still satisfy the type constraint. |
159 | |
160 | # this clears the HashRef |
161 | $store->fruit_aisle( { } ); |
162 | |
163 | =head1 SEE ALSO |
164 | |
165 | =over 4 |
166 | |
167 | =item L<Moose::Cookbook::Snack::Types> - Snippets of code for using Types and |
168 | Type Constraints |
169 | |
170 | =item L<Moose::Util::TypeConstraints> - Type constraints system for Moose |
171 | |
172 | =back |
173 | |
174 | =head1 AUTHOR |
175 | |
176 | Brian Manning <elspicyjack at gmail dot com> |
177 | |
178 | =head1 COPYRIGHT AND LICENSE |
179 | |
180 | Copyright (c)2008 by Brian Manning |
181 | |
182 | This documentation is free software; you can redistribute it and/or modify |
183 | it under the same terms as Perl itself. |
184 | |
185 | =cut |