File Coverage

blib/lib/MooX/Purple.pm
Criterion Covered Total %
statement 344 545 63.1
branch 40 64 62.5
condition 27 60 45.0
subroutine 39 40 97.5
pod n/a
total 450 709 63.4


line stmt bran cond sub pod time code
1             package MooX::Purple;
2              
3 12     12   1477216 use 5.006;
  12         49  
4 12     12   68 use strict;
  12         59  
  12         448  
5 12     12   61 use warnings;
  12         38  
  12         1035  
6             our $VERSION = '0.20';
7 12     12   12880 use Keyword::Declare;
  12         2122368  
  12         155  
8             our ($PREFIX, %MACROS);
9              
10 0         0 sub import {
11 14     14   1879 my ($class, %args) = @_;
12 14 100       223 $PREFIX = $args{-prefix} unless $PREFIX;
  14         31  
13 12     12   764966 keytype GATTRS is m{
14             (?:
15             allow (?&PerlNWS)
16             (?:(?!qw)(?&PerlQualifiedIdentifier)|
17             (?&PerlList))
18             |
19             with (?&PerlNWS)
20             (?:(?!qw)(?&PerlPrefixUnaryOperator)*(?&PerlQualifiedIdentifier)|
21             (?&PerlList))
22             |
23             is (?&PerlNWS)
24             (?:(?!qw)(?&PerlPrefixUnaryOperator)*(?&PerlQualifiedIdentifier)|
25             (?&PerlList))
26             |
27             use (?&PerlNWS)
28             (?:(?&PerlQualifiedIdentifier)\s*(?&PerlList)|(?:(?!qw)(?&PerlQualifiedIdentifier)|
29             (?&PerlList)))
30             )?+
31 14         29 }xms;
32 12     12   775218 keytype SATTRS is m{
33             (?:
34             allow (?&PerlNWS)
35             (?:(?!qw)(?&PerlQualifiedIdentifier)|
36             (?&PerlList))
37             |
38             )?+
39 14         25 }xms;
40 12     12   687188 keytype PATTRS is m{
41             (?:
42             describe (?&PerlNWS)
43             (?:(?&PerlString))
44             )?+
45 14         27 }xms;
46 0 50 50 7   0 keyword role (QualIdent $class, GATTRS @roles, Block $block) {
  0         0  
  0         0  
  14         274  
  7         1427953  
  7         18  
  7         21  
47 0         0 _handle_role($class, $block, @roles)
  14         101  
  7         28  
48 0         0 }
  7         235  
  14         445  
49 0 50 50 9   0 keyword role (PrefixUnaryOperator $pre, QualIdent $class, GATTRS @roles, Block $block) {
  0         0  
  0         0  
  14         170  
  9         1756723  
  9         27  
  9         45  
50 0         0 $class = $PREFIX . '::' . $class if $pre;
  0         0  
  0         0  
  7         336  
  7         18  
  14         84  
  9         26  
51 0         0 _handle_role($class, $block, @roles)
  0         0  
  7         37  
  9         442  
52 0   0     0 }
  7   100     189325  
  14         396  
53 0 50 50 12   0 keyword class (QualIdent $class, GATTRS @roles, Block $block) {
  0         0  
  0         0  
  0         0  
  0         0  
  9         463  
  9         28  
  14         201  
  12         3834896  
  12         38  
  12         37  
54 0         0 _handle_class($class, $block, @roles);
  0         0  
  9         31  
  14         89  
  12         42  
55 0         0 }
  0         0  
  9         296  
  12         500  
  14         400  
56 0 50 50 6   0 keyword class (PrefixUnaryOperator $pre, QualIdent $class, GATTRS @roles, Block $block) {
  0         0  
  0         0  
  14         150  
  6         1444481  
  6         21  
  6         20  
57 0         0 $class = $PREFIX . '::' . $class if $pre;
  0         0  
  0         0  
  0         0  
  0         0  
  9         252  
  9         22  
  12         578  
  12         35  
  14         67  
  6         36  
58 0         0 _handle_class($class, $block, @roles);
  0         0  
  0         0  
  9         23  
  12         52  
  6         266  
59 0   0     0 }
  0   0     0  
  9   33     64  
  12   100     464941  
  14         410  
60 0 50 50 0   0 keyword class (PrefixUnaryOperator $pre, QualIdent $class, Block $block) {
  0         0  
  0         0  
  0         0  
  0         0  
  6         275  
  6         20  
  14         180  
  0         0  
  0         0  
  0         0  
61 0         0 $class = $PREFIX . '::' . $class if $pre;
  0         0  
  6         21  
  14         73  
  0         0  
62 0         0 _handle_class($class, $block);
  0         0  
  6         161  
  0         0  
63 14         355 }
64 0 50 50 2   0 keyword macro (Ident $macro, Block $block) {
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  6         186  
  6         38  
  0         0  
  0         0  
  14         249  
  2         497372  
  2         8  
  2         7  
65 0         0 return _handle_macro($macro, $block);
  0         0  
  0         0  
  6         16  
  0         0  
  14         75  
  2         6  
66 0   0     0 }
  0   100     0  
  0         0  
  6         293845  
  0         0  
  2         75  
  14         423  
67 0 50 50 6   0 keyword private (Ident $method, SATTRS @roles, Block $block) {
  0         0  
  0         0  
  14         170  
  6         765154  
  6         15  
  6         17  
68 0         0 return _handle_private($method, $block, @roles);
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  2         86  
  2         250  
  14         67  
  6         22  
69 0         0 }
  0         0  
  0         0  
  0         0  
  2         10  
  6         194  
  14         392  
70 0 50 50 29   0 keyword public (Ident $method, Block $block, PATTRS @pod) {
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  2         82  
  14         143  
  29         4923676  
  29         96  
  29         86  
71 0         0 return _handle_public($method, $block, @pod);
  0         0  
  0         0  
  0         0  
  12         505354  
  6         309  
  6         14  
  14         106  
  29         113  
72 0         0 }
  0         0  
  0         0  
  0         0  
  0         0  
  2         59  
  6         16  
  29         1408  
  14         390  
73 0 0 0 1   0 keyword start (Ident $method, Block $block) {
  0 0 66     0  
  0 50 50     0  
  0         0  
  0         0  
  0         0  
  0         0  
  2         13  
  6         334129  
  14         147  
  1         90372  
  1         3  
  1         2  
74 0         0 return _handle_when('-', $method, $block);
  0         0  
  12         121  
  0         0  
  0         0  
  0         0  
  7         280619  
  0         0  
  14         88  
  29         2332  
  29         90  
  14         85  
  1         3  
75 12         129 }
  0         0  
  0         0  
  14         3995  
  14         585  
  29         1545  
  1         193  
  14         395  
76 0 50 50 12   0 keyword during (Ident $method, Block $block) {
  0     1   0  
  12         772117  
  0         0  
  0         0  
  0         0  
  0         0  
  7         27  
  7         20  
  14         561  
  29         969  
  14         137  
  1         75981  
  1         2  
  1         2  
77 0     12   0 return _handle_when('~', $method, $block);
  12         758395  
  0         0  
  0         0  
  0         0  
  7         1559  
  1         38  
  1         2  
  14         79  
  1         3  
78 0         0 }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  7         296  
  0         0  
  25         809087  
  29         790  
  29         425  
  1         2  
  1         21  
  14         421  
79 0 50 50 1   0 keyword trigger (Ident $method, Block $block) {
  0         0  
  0         0  
  0         0  
  0         0  
  29         341  
  1         22  
  14         188  
  1         129019  
  1         3  
  1         3  
80 0   0     0 return _handle_when('=', $method, $block);
  0   100     0  
  0         0  
  0         0  
  0         0  
  7         160  
  29         85473  
  1         35  
  1         4  
  14         65  
  1         6  
81 0         0 }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  7         44  
  9         28  
  12         439546  
  12         338668  
  1         35  
  1         5  
  1         35  
  14         466  
82 12 50 50 1   121 keyword end (Ident $method, Block $block) {
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  14         160  
  1         7  
  1         89  
  14         154  
  1         74884  
  1         5  
  1         3  
83 0         0 return _handle_when('+', $method, $block);
  0         0  
  0         0  
  0         0  
  12         103  
  0         0  
  0         0  
  0         0  
  14         798  
  9         20  
  9         25  
  12         47  
  12         50  
  14         95  
  1         43  
  1         3  
  14         87  
  1         6  
84 12     12   806975 }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  9         2074  
  12         2683  
  6         347030  
  14         504  
  1         38  
  1         5  
  1         33  
85 0     12   0 }
  0         0  
  0         0  
  12         838772  
  0         0  
  0         0  
  9         244  
  12         521  
  10         282188  
  1         7  
  1         27  
86 0         0  
  0         0  
  0         0  
  12         112  
  0         0  
  0         0  
  6         24  
  6         25  
  4         116998  
  14         83  
  1         49  
  1         4  
87 0         0 sub _handle_macro {
  0         0  
  0         0  
  0         0  
  0         0  
  9         195  
  12         270  
  6         29  
  14         570  
  1         45  
  1         4  
88 0 0   12   0 my ($macro, $block) = @_;
  0 50   2   0  
  0         0  
  0         0  
  12         767918  
  0         0  
  0         0  
  9         145  
  12         88  
  6         265509  
  6         362  
  1         8  
  1         29  
  2         6  
89 0         0 $block =~ s/^\n*\{\n*\s*|;\n*\t*\}\n*$//g;
  12         115  
  0         0  
  12         118  
  9         267  
  14         100  
  29         113434  
  14         115  
  2         12  
90 12         110 $MACROS{$macro} = $block;
  0         0  
  0         0  
  0         0  
  0         0  
  14         107  
  14         576  
  6         20  
  6         22  
  6         304  
  14         569  
  1         26  
  2         53  
91 12     12   777075 return '';
  0     12   0  
  0         0  
  0         0  
  12         813258  
  0         0  
  14         613  
  6         805  
  6         55  
  29         76  
  1         5  
  2         24  
92 12     12   815500 }
  0         0  
  12         170  
  0         0  
  12         132  
  6         236  
  14         92  
  29         172  
  14         127  
93 12         123  
  14         593  
  14         116  
  14         594  
94 0     12   0 sub _handle_public {
  12     12   880915  
  12         922331  
  6         152  
  14         568  
95 0 0   12   0 my ($method, $block) = @_;
  12 50   29   827963  
  6         55  
  29         107  
96 0         0 my $mac = join '|', keys %MACROS;
  6         168  
  29         152  
97 12         156 $block =~ s/&($mac)/$MACROS{$1}/g;
  14         117  
  29         600  
98 14         565 $block =~ s/(^{)|(}$)//g;
  29         352  
99 12     12   768479 return "sub $method {
  29         509  
100             my (\$self) = shift;
101             $block
102             }";
103             }
104              
105             sub _handle_private {
106 6     6   31 my ($method, $block, @roles) = @_;
107 6         36 my %attrs = _parse_role_attrs(@roles);
108 6 50       41 my $allowed = $attrs{allow} ? sprintf 'qw(%s)', join ' ', @{$attrs{allow}} : 'qw//';
  6         55  
109 6         33 my $mac = join '|', keys %MACROS;
110 6         160 $block =~ s/&($mac)/$MACROS{$1}/g;
111 6         109 $block =~ s/(^{)|(}$)//g;
112 6         140 return "sub $method {
113             my (\$self) = shift;
114             my \$caller = caller();
115             my \@allowed = $allowed;
116             unless (\$caller eq __PACKAGE__ || grep { \$_ eq \$caller } \@allowed) {
117             die \"cannot call private method $method from \$caller\";
118             }
119             $block
120             }";
121             }
122              
123             sub _handle_when {
124 4     4   23 my ($pre, $method, $block) = @_;
125 4         44 my %map = (
126             '-' => 'before',
127             '+' => 'after',
128             '~' => 'around',
129             '=' => 'around'
130             );
131 4         22 $block =~ s/(^{)|(}$)//g;
132 4 100       93 if ($pre eq '~') {
    100          
133 1         7 $block = "{
134             my (\$orig, \$self) = (shift, shift);
135             $block;
136             }";
137             } elsif ($pre eq '=') {
138 1         5 $block = "{
139             my (\$orig, \$self) = (shift, shift);
140             my \$out = \$self->\$orig(\@_);
141             $block;
142             }";
143             } else {
144 2         5 $block = "{
145             my (\$self) = (shift);
146             $block;
147             }"
148             }
149 4         20 return "$map{$pre} $method => sub $block;";
150             }
151              
152             sub _handle_class {
153 18     18   95 my ($class, $block, @roles) = @_;
154 18         123 my ($body, %attrs) = _set_class_role_attrs($block, _parse_role_attrs(@roles));
155 18         326 my $out = qq|{
156             package $class;
157             use Moo;
158             use MooX::LazierAttributes;
159             use MooX::ValidateSubs;
160             use Data::LnArray qw/arr/;
161             $attrs{is}
162             $attrs{with}
163             $attrs{use}
164             $body
165             1;
166             }|;
167 18         348 return $out;
168             }
169              
170             sub _handle_role {
171 16     16   95 my ($class, $block, @roles) = @_;
172 16         120 my ($body, %attrs) = _set_class_role_attrs($block, _parse_role_attrs(@roles));
173 16         311 return qq|{
174             package $class;
175             use Moo::Role;
176             use MooX::LazierAttributes;
177             use MooX::ValidateSubs;
178             use Data::LnArray qw/arr/;
179             $attrs{with}
180             $attrs{use}
181             $body
182             1;
183             }|;
184             }
185              
186             sub _parse_role_attrs {
187 40     40   158 my @roles = @_;
188 40         99 my %attrs;
189 40         108 my $i = 0;
190 40         151 for (@roles) {
191 59 100       3334738 if ($_ =~ m/\s*use\s*((?!qw)(?&PerlQualifiedIdentifier))\s*((?&PerlList)) $PPR::GRAMMAR/xms) {
192 2         186 $attrs{use}{sprintf "%s %s", $1, $2}++;
193 2         1684 next;
194             }
195 57         32892 $_ =~ m/(with|allow|is|use)(.*)/i;
196 57         22860 my @list = eval($2); # || $2
197 57 100       556 push @list, $2 unless @list;
198 57         212 for (@list) {
199 73         674 $attrs{$1}{$_} = $i++;
200             }
201             }
202 40         136 for my $o (qw/with allow is use/) {
203 160 100       493 $attrs{$o} = [sort { $attrs{$o}{$a} <=> $attrs{$o}{$b} } keys %{$attrs{$o}}] if $attrs{$o};
  19         89  
  32         251  
204             }
205 40         330 return %attrs;
206             }
207              
208             sub _set_class_role_attrs {
209 34     34   160 my ($body, %attrs, %args) = @_;
210 34 100       188 if ($attrs{allow}) {
211 6         16 my $allow = join ' ', @{$attrs{allow}};
  6         28  
212 6         89 $body =~ s{private\s*(\p{XIDS}\p{XIDC}*)}{private $1 allow qw/$allow/}g;
213             }
214 34 100       255 $attrs{is} = $attrs{is} ? sprintf "extends qw/%s/;\n", join(' ', map { my $l = $_; $l =~ s/^\s*\+/$PREFIX\:\:/; $l; } @{$attrs{is}}) : '';
  7         39  
  7         63  
  7         70  
  7         32  
215 34         81 my $last;
216             $attrs{with} = $attrs{with}
217             ? sprintf "with qw/%s/;\n", join(' ', map {
218 20         69 my $l = $_;
219 20         56 $l =~ s/^\s*\+/$PREFIX\:\:/;
220 20 100       97 unless($l =~ s/^\s*\-/$last\:\:/) {
221 12         29 $last = $l;
222             }
223 20 100       79 if ($l =~ s/^\s*\~//) {
224 2 50       12 $last = $PREFIX ? ($PREFIX . '::' . $l) : $l;
225 2         5 $l = '';
226             }
227 20         86 $l;
228 34 100       197 } @{$attrs{with}})
  11         40  
229             : '';
230 34 100       208 $attrs{use} = $attrs{use} ? join('', map { sprintf("use %s;\n", $_) } @{$attrs{use}}) : '';
  4         16  
  2         6  
231 34         1234 $body =~ s/(^{)|(}$)//g;
232 34         303 return $body, %attrs;
233             }
234              
235             1;
236              
237             __END__
238              
239             =head1 NAME
240              
241             MooX::Purple - MooX::Purple
242              
243             =head1 VERSION
244              
245             Version 0.20
246              
247             =cut
248              
249             =head1 SYNOPSIS
250              
251             use MooX::Purple;
252              
253             role Before {
254             public seven { return '7' }
255             };
256              
257             role World allow Hello with Before {
258             private six { 'six' }
259             };
260              
261             class Hello with qw/World/ allow qw/main/ use Scalar::Util qw/reftype/ use qw/JSON/ {
262             use Types::Standard qw/Str HashRef ArrayRef Object/;
263              
264             attributes
265             one => [{ okay => 'one'}],
266             [qw/two three/] => [rw, Str, { default => 'the world is flat' }];
267              
268             validate_subs
269             four => {
270             params => {
271             message => [Str, sub {'four'}]
272             }
273             };
274              
275             public four { return $_[1]->{message} }
276             private five { return $_[0]->six }
277             public ten { reftype bless {}, 'Flat::World' }
278             public eleven { encode_json { flat => "world" } }
279             };
280              
281             class Night is qw/Hello/ {
282             public nine { return 'nine' }
283             };
284              
285             Night->new()->five();
286              
287             ...
288              
289             # ../Foo.pm
290             package Foo;
291             use MooX::Purple -prefix => 'Foo';
292             use Foo::Roles;
293             class +Class with qw/~Role -One -Two -Three -Four/ {
294             public print {
295             return $_[1];
296             }
297             }
298            
299             # ../Foo/Roles.pm
300             package Foo::Roles;
301             use MooX::Purple;
302             role +Role::One {
303             public one {
304             $_[0]->print(1);
305             }
306             }
307             role +Role::Two {
308             public two {
309             $_[0]->print(2);
310             }
311             }
312             role +Role::Three {
313             public three {
314             $_[0]->print(3);
315             }
316             }
317             role +Role::Four {
318             public four {
319             $_[0]->print(4);
320             }
321             }
322              
323             ...
324              
325             use MooX::Purple -prefix => 'Macro';
326              
327             class +Simple {
328             macro generic {
329             'crazy';
330             };
331             macro second {
332             my $x = 0;
333             $x++ for (0..100);
334             return $x;
335             };
336             public one { &generic; }
337            
338             start one {
339             print "before\n";
340             }
341            
342             during one {
343             print "around\n";
344             $self->$orig();
345             };
346              
347             trigger one {
348             print "trigger\n";
349             return 'insane';
350             }
351              
352             end one {
353             print "after\n";
354             }
355              
356             public two {
357             print &generic
358             &second;
359             } describe "Add Documentation for method 'two'"
360             };
361              
362             class +Inherit is +Simple {};
363              
364              
365              
366             =head1 AUTHOR
367              
368             lnation, C<< <thisusedtobeanemail at gmail.com> >>
369              
370             =head1 BUGS
371              
372             Please report any bugs or feature requests to C<bug-moox-purple at rt.cpan.org>, or through
373             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=MooX-Purple>. I will be notified, and then you'll
374             automatically be notified of progress on your bug as I make changes.
375              
376             =head1 SUPPORT
377              
378             You can find documentation for this module with the perldoc command.
379              
380             perldoc MooX::Purple
381              
382              
383             You can also look for information at:
384              
385             =over 4
386              
387             =item * RT: CPAN's request tracker (report bugs here)
388              
389             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=MooX-Purple>
390              
391             =item * Search CPAN
392              
393             L<http://search.cpan.org/dist/MooX-Purple/>
394              
395             =back
396              
397              
398             =head1 ACKNOWLEDGEMENTS
399              
400              
401             =head1 LICENSE AND COPYRIGHT
402              
403             Copyright 2019->2025 lnation.
404              
405             This program is free software; you can redistribute it and/or modify it
406             under the terms of the the Artistic License (2.0). You may obtain a
407             copy of the full license at:
408              
409             L<http://www.perlfoundation.org/artistic_license_2_0>
410              
411             Any use, modification, and distribution of the Standard or Modified
412             Versions is governed by this Artistic License. By using, modifying or
413             distributing the Package, you accept this license. Do not use, modify,
414             or distribute the Package, if you do not accept this license.
415              
416             If your Modified Version has been derived from a Modified Version made
417             by someone other than you, you are nevertheless required to ensure that
418             your Modified Version complies with the requirements of this license.
419              
420             This license does not grant you the right to use any trademark, service
421             mark, tradename, or logo of the Copyright Holder.
422              
423             This license includes the non-exclusive, worldwide, free-of-charge
424             patent license to make, have made, use, offer to sell, sell, import and
425             otherwise transfer the Package with respect to any patent claims
426             licensable by the Copyright Holder that are necessarily infringed by the
427             Package. If you institute patent litigation (including a cross-claim or
428             counterclaim) against any party alleging that the Package constitutes
429             direct or contributory patent infringement, then this Artistic License
430             to you shall terminate on the date that such litigation is filed.
431              
432             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
433             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
434             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
435             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
436             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
437             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
438             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
439             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
440              
441              
442             =cut
443              
444             1; # End of MooX::Purple