File Coverage

blib/lib/MooX/HandlesVia.pm
Criterion Covered Total %
statement 47 51 92.1
branch 16 20 80.0
condition 4 9 44.4
subroutine 10 11 90.9
pod 1 1 100.0
total 78 92 84.7


line stmt bran cond sub pod time code
1             package MooX::HandlesVia;
2             # ABSTRACT: NativeTrait-like behavior for Moo.
3             $MooX::HandlesVia::VERSION = '0.001008';
4 13     13   235027 use strict;
  13         23  
  13         357  
5 13     13   45 use warnings;
  13         19  
  13         243  
6              
7 13     13   518 use Moo ();
  13         1941  
  13         159  
8 13     13   4937 use Moo::Role ();
  13         66860  
  13         391  
9 13     13   87 use Module::Runtime qw/require_module/;
  13         19  
  13         130  
10              
11             # reserved hardcoded mappings for classname shortcuts.
12             my %RESERVED = (
13             'Array' => 'Data::Perl::Collection::Array::MooseLike',
14             'Hash' => 'Data::Perl::Collection::Hash::MooseLike',
15             'String' => 'Data::Perl::String::MooseLike',
16             'Bool' => 'Data::Perl::Bool::MooseLike',
17             'Number' => 'Data::Perl::Number::MooseLike',
18             'Code' => 'Data::Perl::Code',
19             );
20             my %REVERSED = reverse %RESERVED;
21              
22             sub import {
23 31     31   93167 my ($class) = @_;
24              
25 13     13   999 no strict 'refs';
  13         20  
  13         344  
26 13     13   49 no warnings 'redefine';
  13         15  
  13         4375  
27              
28 31         75 my $target = caller;
29 31 100       463 if (my $has = $target->can('has')) {
30             my $newsub = sub {
31 36     36   135893 $has->(process_has(@_));
32 30         121 };
33              
34 30 100       131 if (Moo::Role->is_role($target)) {
35 3         61 Moo::Role::_install_tracked($target, "has", $newsub);
36             }
37             else {
38 27         709 Moo::_install_tracked($target, "has", $newsub);
39             }
40             }
41             }
42              
43             sub process_has {
44 36     36 1 180 my ($name, %opts) = @_;
45 36         59 my $handles = $opts{handles};
46 36 100 100     296 return ($name, %opts) if not $handles or ref $handles ne 'HASH';
47              
48 34 100       125 if (my $via = delete $opts{handles_via}) {
49 30 50       81 $via = ref $via eq 'ARRAY' ? $via->[0] : $via;
50              
51             # try to load the reserved mapping, if it exists, else the full name
52 30   33     96 $via = $RESERVED{$via} || $via;
53 30         99 require_module($via);
54              
55             # clone handles for HandlesMoose support
56 30         2790 my %handles_clone = %$handles;
57              
58 30         139 while (my ($target, $delegation) = each %$handles) {
59             # if passed an array, handle the curry
60 325 100       522 if (ref $delegation eq 'ARRAY') {
    50          
61 107         145 my ($method, @curry) = @$delegation;
62 107 100       504 if ($via->can($method)) {
63 33         102 $handles->{$target} = ['${\\'.$via.'->can("'.$method.'")}', @curry];
64             }
65             }
66             elsif (ref $delegation eq '') {
67 218 100       1198 if ($via->can($delegation)) {
68 76         258 $handles->{$target} = '${\\'.$via.'->can("'.$delegation.'")}';
69             }
70             }
71             }
72              
73             # install our support for moose upgrading of class/role
74             # we deleted the handles_via key above, but install it as a native trait
75 30         98 my $inflator = $opts{moosify};
76             $opts{moosify} = sub {
77 0     0   0 my ($spec) = @_;
78              
79 0         0 $spec->{handles} = \%handles_clone;
80 0   0     0 $spec->{traits} = [$REVERSED{$via} || $via];
81              
82             # pass through if needed
83 0 0       0 $inflator->($spec) if ref($inflator) eq 'CODE';
84 30         188 };
85             }
86              
87 34         219 ($name, %opts);
88             }
89              
90             1;
91              
92             =pod
93              
94             =encoding UTF-8
95              
96             =head1 NAME
97              
98             MooX::HandlesVia - NativeTrait-like behavior for Moo.
99              
100             =head1 VERSION
101              
102             version 0.001008
103              
104             =head1 SYNOPSIS
105              
106             {
107             package Hashy;
108             use Moo;
109             use MooX::HandlesVia;
110              
111             has hash => (
112             is => 'rw',
113             handles_via => 'Hash',
114             handles => {
115             get_val => 'get',
116             set_val => 'set',
117             all_keys => 'keys'
118             }
119             );
120             }
121              
122             my $h = Hashy->new(hash => { a => 1, b => 2});
123              
124             $h->get_val('b'); # 2
125              
126             $h->set_val('a', 'BAR'); # sets a to BAR
127              
128             my @keys = $h->all_keys; # returns a, b
129              
130             =head1 DESCRIPTION
131              
132             MooX::HandlesVia is an extension of Moo's 'handles' attribute functionality. It
133             provides a means of proxying functionality from an external class to the given
134             atttribute. This is most commonly used as a way to emulate 'Native Trait'
135             behavior that has become commonplace in Moose code, for which there was no Moo
136             alternative.
137              
138             =head1 SHORTCOMINGS
139              
140             Due to current Moo implementation details there are some deficiencies in how
141             MooX::HandlesVia in comparison to what you would expect from Moose native
142             traits.
143              
144             =over 4
145              
146             =item * methods delegated via the Moo 'handles' interface are passed the
147             attribue value directly. and there is no way to access the parent class. This
148             means if an attribute is updated any triggers or type coercions B
149             fire.
150              
151             =item * Moo attribute method delegations are passed the attribute value. This
152             is fine for references (objects, arrays, hashrefs..) it means simple scalar
153             types are B. This unfortunately means Number, String, Counter, Bool
154             cannot modify the attributes value, rendering them largely useless.
155              
156             =back
157              
158             =head1 PROVIDED INTERFACE/FUNCTIONS
159              
160             =over 4
161              
162             =item B
163              
164             MooX::HandlesVia preprocesses arguments passed to has() attribute declarations
165             via the process_has function. In a given Moo class, If 'handles_via' is set to
166             a ClassName string, and 'handles' is set with a hashref mapping of desired moo
167             class methods that should map to ClassName methods, process_has() will create
168             the appropriate binding to create the mapping IF ClassName provides that named
169             method.
170              
171             has options => (
172             is => 'rw',
173             handles_via => 'Array',
174             handles => {
175             mixup => 'shuffle',
176             unique_options => 'uniq',
177             all_options => 'elements'
178             }
179             );
180              
181             =back
182              
183             The following handles_via keywords are reserved as shorthand for mapping to
184             L:
185              
186             =over 4
187              
188             =item * B maps to L
189              
190             =item * B maps to L
191              
192             =item * B maps to L
193              
194             =item * B maps to L
195              
196             =item * B maps to L
197              
198             =item * B maps to L
199              
200             =back
201              
202             =head1 SEE ALSO
203              
204             =over 4
205              
206             =item * L
207              
208             =item * L
209              
210             =back
211              
212             =head1 AUTHOR
213              
214             Matthew Phillips
215              
216             =head1 COPYRIGHT AND LICENSE
217              
218             This software is copyright (c) 2015 by Matthew Phillips .
219              
220             This is free software; you can redistribute it and/or modify it under
221             the same terms as the Perl 5 programming language system itself.
222              
223             =cut
224              
225             __END__