File Coverage

blib/lib/MooseX/FunkyAttributes/Role/Attribute/Delegated.pm
Criterion Covered Total %
statement 17 17 100.0
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 23 23 100.0


line stmt bran cond sub pod time code
1             package MooseX::FunkyAttributes::Role::Attribute::Delegated;
2              
3 6     6   5302 use 5.008;
  6         21  
  6         246  
4 6     6   39 use strict;
  6         11  
  6         216  
5 6     6   34 use warnings;
  6         13  
  6         381  
6              
7             BEGIN {
8 6     6   14 $MooseX::FunkyAttributes::Role::Attribute::Delegated::AUTHORITY = 'cpan:TOBYINK';
9 6         129 $MooseX::FunkyAttributes::Role::Attribute::Delegated::VERSION = '0.003';
10             }
11              
12 6     6   35 use Moose::Role;
  6         11  
  6         70  
13 6     6   35696 use namespace::autoclean;
  6         13  
  6         64  
14              
15             with qw(MooseX::FunkyAttributes::Role::Attribute);
16              
17             has delegated_to => (is => 'ro', isa => 'Str', required => 1);
18             has delegated_accessor => (is => 'ro', isa => 'Str');
19             has delegated_predicate => (is => 'ro', isa => 'Str');
20             has delegated_clearer => (is => 'ro', isa => 'Str');
21              
22             before _process_options => sub
23             {
24             my ($class, $name, $options) = @_;
25            
26             my $to = $options->{delegated_to}
27             or confess "Required option 'delegated_to' missing";
28            
29             # Meh... we should use Moose's introspection to get the name of accessors, clearers, etc.
30             # ... actually we can't do that. We don't know at attribute creation time, what sort of
31             # object $self->$to will be!!
32             #
33            
34             $options->{custom_weaken} ||= sub { 0 }; # :-(
35             $options->{custom_inline_weaken} ||= sub { q() }; # :-(
36            
37             $options->{delegated_accessor} = (
38             my $accessor = exists $options->{delegated_accessor} ? $options->{delegated_accessor} : $name
39             );
40             my $private = !!($accessor =~ /^_/);
41            
42             if ($accessor and not exists $options->{custom_get})
43             {
44             $options->{custom_get} = sub { $_[1]->$to->$accessor };
45             $options->{custom_inline_get} ||= sub {
46             my ($self, $inst, $val) = @_;
47             qq( $inst->$to->$accessor() )
48             };
49             }
50            
51             if ($accessor and not exists $options->{custom_set})
52             {
53             $options->{custom_set} = sub { $_[1]->$to->$accessor($_[2]) };
54             $options->{custom_inline_set} ||= sub {
55             my ($self, $inst, $val) = @_;
56             qq( $inst->$to->$accessor($val) )
57             };
58             }
59            
60             $options->{delegated_predicate} = (
61             my $predicate = exists $options->{delegated_predicate} ? $options->{delegated_predicate} : ($private ? "_has$accessor" : "has_$accessor")
62             );
63            
64             if ($predicate and not exists $options->{custom_has})
65             {
66             $options->{custom_has} = sub { $_[1]->$to->$predicate };
67             $options->{custom_inline_has} ||= sub {
68             my ($self, $inst) = @_;
69             qq( $inst->$to->$predicate() )
70             };
71             }
72            
73             $options->{delegated_clearer} = (
74             my $clearer = exists $options->{delegated_clearer} ? $options->{delegated_clearer} : ($private ? "_clear$accessor" : "clear_$accessor")
75             );
76            
77             if ($clearer and not exists $options->{custom_clear})
78             {
79             $options->{custom_clear} = sub { $_[1]->$to->$clearer };
80             $options->{custom_inline_clear} ||= sub {
81             my ($self, $inst) = @_;
82             qq( $inst->$to->$clearer() )
83             };
84             }
85            
86             delete $options->{$_} for
87             grep { not defined $options->{$_} }
88             grep { /^delegated_/ }
89             keys %$options;
90             };
91              
92             1;
93              
94              
95             __END__
96              
97             =head1 NAME
98              
99             MooseX::FunkyAttributes::Role::Attribute::Delegated - delegate an attribute to another object
100              
101             =head1 SYNOPSIS
102              
103             package Head;
104            
105             use Moose;
106            
107             has mouth => (
108             is => 'ro',
109             isa => 'Mouth',
110             );
111            
112             package Person;
113            
114             use Moose;
115             use MooseX::FunkyAttributes;
116            
117             has head => (
118             is => 'ro',
119             isa => 'Head',
120             );
121            
122             has mouth => (
123             is => 'ro',
124             isa => 'Mouth::Human',
125             traits => [ DelegatedAttribute ],
126             delegated_to => 'head',
127             );
128              
129             =head1 DESCRIPTION
130              
131             This trait delegates the storage of one attribute's value to the object stored
132             in another attribute. The example in the SYNOPSIS might have been written using
133             Moose's native delegation as:
134              
135             package Head;
136            
137             use Moose;
138            
139             has mouth => (
140             is => 'ro',
141             isa => 'Mouth',
142             );
143            
144             package Person;
145            
146             use Moose;
147            
148             has head => (
149             is => 'ro',
150             isa => 'Head',
151             handles => [qw( mouth )],
152             );
153              
154             However, there are some differences. Using native delegation, C<mouth>
155             will be treated as a method using Moose's introspection API
156             (C<< Person->meta->get_all_methods >>) and not as an attribute
157             (C<< Person->meta->get_all_attributes >>). Using this API, C<mouth> is
158             a proper attribute of C<Person>; it just relies on the C<Head> object for
159             storage.
160              
161             Because C<mouth> is a proper attribute of C<Person>, it can perform
162             delegations of its own; can have its own type constraints, etc.
163              
164             has mouth => (
165             is => 'ro',
166             isa => 'Mouth::Human',
167             traits => [ DelegatedAttribute ],
168             delegated_to => 'head',
169             handles => [qw/ speak kiss vomit eat /], # but not necessarily
170             ); # in that order
171              
172             =head2 Options
173              
174             =over
175              
176             =item C<< delegated_to => STR >>
177              
178             The name of the other attribute to delegate this attribute to. This is the
179             only required option.
180              
181             =item C<< delegated_accessor => STR >>
182              
183             This option may be used if you wish to rename the delegated attribute. For
184             example:
185              
186             package Person;
187            
188             has pie_hole => (
189             is => 'ro',
190             isa => 'Mouth::Human',
191             traits => [ DelegatedAttribute ],
192             delegated_to => 'head',
193             delegated_accessor => 'mouth',
194             );
195              
196             Now C<< $person->pie_hole >> is equivalent to C<< $person->head->mouth >>.
197              
198             If omitted, then it is assumed that the attribute has the same name in both
199             classes. If explicitly set to C<undef>, then this assumption is not made, and
200             the accessor is unknown. If the accessor is unknown, then this trait gets
201             somewhat stuck, so you'd need to provide C<custom_get> and C<custom_set>
202             options (see L<MooseX::FunkyAttributes::Role::Attribute>).
203              
204             =item C<< delegated_predicate => STR >>
205              
206             Like C<delegated_accessor>, but for the attribute's predicate. If omitted,
207             assumes the convention of C<< has_$accessor >>. An explicit undef can
208             avoid this assumption, but then you'll need to provide C<custom_has>.
209              
210             =item C<< delegated_clearer => STR >>
211              
212             Like C<delegated_accessor>, but for the attribute's clearer. If omitted,
213             assumes the convention of C<< clear_$accessor >>. An explicit undef can
214             avoid this assumption, but then you'll need to provide C<custom_has> if
215             you want to define a clearer.
216              
217             =back
218              
219             All the C<custom_blah> and C<custom_inline_blah> options from
220             L<MooseX::FunkyAttributes::Role::Attribute> are also available. The
221             C<delegated_blah> options above are essentially just shortcuts
222             for defining them.
223              
224             Your attribute metaobject has the following methods (in addition to the
225             standard L<MooseX::FunkyAttributes::Role::Attribute> and
226             L<Moose::Meta::Attribute> stuff):
227              
228             =over
229              
230             =item C<delegated_to>
231              
232             =item C<delegated_accessor>
233              
234             =item C<delegated_clearer>
235              
236             =item C<delegated_predicate>
237              
238             =back
239              
240             =head1 BUGS
241              
242             Please report any bugs to
243             L<http://rt.cpan.org/Dist/Display.html?Queue=MooseX-FunkyAttributes>.
244              
245             =head1 SEE ALSO
246              
247             L<MooseX::FunkyAttributes>.
248              
249             =head1 AUTHOR
250              
251             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
252              
253             =head1 COPYRIGHT AND LICENCE
254              
255             This software is copyright (c) 2012-2014 by Toby Inkster.
256              
257             This is free software; you can redistribute it and/or modify it under
258             the same terms as the Perl 5 programming language system itself.
259              
260             =head1 DISCLAIMER OF WARRANTIES
261              
262             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
263             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
264             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
265