Commit | Line | Data |
daa0fd7d |
1 | package Moose::Manual::Delegation; |
2 | |
3 | # ABSTRACT: Attribute delegation |
093a09aa |
4 | |
daa0fd7d |
5 | __END__ |
093a09aa |
6 | |
daa0fd7d |
7 | =pod |
093a09aa |
8 | |
9 | =head1 WHAT IS DELEGATION? |
10 | |
54c97a15 |
11 | Delegation is a feature that lets you create "proxy" methods that do nothing |
12 | more than call some other method on an attribute. This lets you simplify a |
13 | complex set of "has-a" relationships and present a single unified API from one |
14 | class. |
093a09aa |
15 | |
d3e02c74 |
16 | With delegation, consumers of a class don't need to know about all the |
17 | objects it contains, reducing the amount of API they need to learn. |
093a09aa |
18 | |
19 | Delegations are defined as a mapping between one or more methods |
20 | provided by the "real" class (the delegatee), and a set of |
21 | corresponding methods in the delegating class. The delegating class |
d3e02c74 |
22 | can re-use the method names provided by the delegatee or provide its |
093a09aa |
23 | own names. |
24 | |
25 | Delegation is also a great way to wrap an existing class, especially a |
26 | non-Moose class or one that is somehow hard (or impossible) to |
27 | subclass. |
28 | |
29 | =head1 DEFINING A MAPPING |
30 | |
31 | Moose offers a number of options for defining a delegation's mapping, |
32 | ranging from simple to complex. |
33 | |
34 | The simplest form is to simply specify a list of methods: |
35 | |
36 | package Website; |
37 | |
38 | use Moose; |
39 | |
40 | has 'uri' => ( |
41 | is => 'ro', |
42 | isa => 'URI', |
43 | handles => [qw( host path )], |
44 | ); |
45 | |
46 | With this definition, we can call C<< $website->host >> and it "just |
47 | works". Under the hood, Moose will call C<< $website->uri->host >> for |
b3b5ff5a |
48 | you. Note that C<$website> is not automatically passed to the C<host> |
49 | method; the invocant is C<< $website->uri >>. |
093a09aa |
50 | |
51 | We can also define a mapping as a hash reference. This allows you to |
52 | rename methods as part of the mapping: |
53 | |
54 | package Website; |
55 | |
56 | use Moose; |
57 | |
58 | has 'uri' => ( |
59 | is => 'ro', |
60 | isa => 'URI', |
61 | handles => { |
62 | hostname => 'host', |
63 | path => 'path', |
64 | }, |
65 | ); |
66 | |
67 | In this example, we've created a C<< $website->hostname >> method, |
68 | rather than using C<URI.pm>'s name, C<host>. |
69 | |
70 | These two mapping forms are the ones you will use most often. The |
54c97a15 |
71 | remaining methods are a bit more complex. |
093a09aa |
72 | |
73 | has 'uri' => ( |
74 | is => 'ro', |
75 | isa => 'URI', |
76 | handles => qr/^(?:host|path|query.*)/, |
77 | ); |
78 | |
79 | This is similar to the array version, except it uses the regex to |
80 | match against all the methods provided by the delegatee. In order for |
81 | this to work, you must provide an C<isa> parameter for the attribute, |
82 | and it must be a class. Moose uses this to introspect the delegatee |
83 | class and determine what methods it provides. |
84 | |
85 | You can use a role name as the value of C<handles>: |
86 | |
87 | has 'uri' => ( |
88 | is => 'ro', |
89 | isa => 'URI', |
90 | handles => 'HasURI', |
91 | ); |
92 | |
93 | Moose will introspect the role to determine what methods it provides |
94 | and create a mapping for each of those methods. |
95 | |
96 | Finally, you can also provide a sub reference to I<generate> a |
97 | mapping. You probably won't need this version often (if ever). See the |
98 | L<Moose> docs for more details on exactly how this works. |
99 | |
e132fd56 |
100 | =head1 NATIVE DELEGATION |
50f346d7 |
101 | |
e132fd56 |
102 | Native delegations allow you to delegate to standard Perl data structures as |
103 | if they were objects. |
f4c5daeb |
104 | |
50f346d7 |
105 | has 'queue' => ( |
f4c5daeb |
106 | traits => ['Array'], |
54c97a15 |
107 | isa => 'ArrayRef[Item]', |
50f346d7 |
108 | default => sub { [ ] }, |
109 | handles => { |
f4c5daeb |
110 | add_item => 'push', |
50f346d7 |
111 | next_item => 'shift', |
9610c1d2 |
112 | }, |
50f346d7 |
113 | ) |
114 | |
e132fd56 |
115 | The C<Array> trait in the C<traits> parameter tells Moose that you would like |
116 | to use the set of Array helpers. Moose will then create C<add_item> and |
117 | C<next_item> methods that "just work". Behind the scenes C<add_item> is |
118 | something like |
50f346d7 |
119 | |
f4c5daeb |
120 | sub add_item { |
50f346d7 |
121 | my ($self, @items) = @_; |
26366034 |
122 | |
f4c5daeb |
123 | for my $item (@items) { |
124 | $Item_TC->validate($item); |
125 | } |
26366034 |
126 | |
50f346d7 |
127 | push @{ $self->queue }, @items; |
128 | } |
129 | |
e132fd56 |
130 | Moose includes the following traits for native delegation: |
54c97a15 |
131 | |
132 | =over 4 |
133 | |
134 | =item * L<Array|Moose::Meta::Attribute::Native::Trait::Array> |
135 | |
136 | =item * L<Bool|Moose::Meta::Attribute::Native::Trait::Bool> |
137 | |
138 | =item * L<Code|Moose::Meta::Attribute::Native::Trait::Code> |
139 | |
140 | =item * L<Counter|Moose::Meta::Attribute::Native::Trait::Counter> |
141 | |
142 | =item * L<Hash|Moose::Meta::Attribute::Native::Trait::Hash> |
143 | |
144 | =item * L<Number|Moose::Meta::Attribute::Native::Trait::Number> |
145 | |
146 | =item * L<String|Moose::Meta::Attribute::Native::Trait::String> |
147 | |
148 | =back |
50f346d7 |
149 | |
150 | =head1 CURRYING |
151 | |
54c97a15 |
152 | Currying allows you to create a method with some pre-set parameters. You can |
153 | create a curried delegation method: |
50f346d7 |
154 | |
155 | package Spider; |
156 | use Moose; |
157 | |
158 | has request => ( |
26366034 |
159 | is => 'ro' |
160 | isa => 'HTTP::Request', |
50f346d7 |
161 | handles => { |
26366034 |
162 | set_user_agent => [ header => 'UserAgent' ], |
9610c1d2 |
163 | }, |
50f346d7 |
164 | ) |
165 | |
8580644d |
166 | With this definition, calling C<< $spider->set_user_agent('MyClient') >> will |
54c97a15 |
167 | call C<< $spider->request->header('UserAgent', 'MyClient') >> behind the |
168 | scenes. |
169 | |
8da1f3d8 |
170 | Note that with currying, the currying always starts with the first parameter to |
013181e1 |
171 | a method (C<$_[0]>). Any arguments you pass to the delegation come after the |
54c97a15 |
172 | curried arguments. |
50f346d7 |
173 | |
093a09aa |
174 | =head1 MISSING ATTRIBUTES |
175 | |
176 | It is perfectly valid to delegate methods to an attribute which is not |
d3e02c74 |
177 | required or can be undefined. When a delegated method is called, Moose |
178 | will throw a runtime error if the attribute does not contain an |
179 | object. |
093a09aa |
180 | |
093a09aa |
181 | =cut |