File Coverage

blib/lib/MooX/Purple/G.pm
Criterion Covered Total %
statement 273 281 97.1
branch 79 88 89.7
condition 6 8 75.0
subroutine 35 35 100.0
pod 0 23 0.0
total 393 435 90.3


line stmt bran cond sub pod time code
1             package MooX::Purple::G;
2 9     9   6620 use strict;
  9         20  
  9         340  
3 9     9   55 use warnings;
  9         18  
  9         309  
4 9     9   254 use 5.006;
  9         33  
5             our $VERSION = '0.16';
6 9     9   51 use PPR;
  9         18  
  9         196  
7 9     9   9245 use Perl::Tidy;
  9         2590791  
  9         1098  
8 9     9   102 use Cwd qw/abs_path/;
  9         25  
  9         2132  
9             our %POD;
10              
11             our (%HAS, $GATTRS, $SATTRS, $PATTRS, $PREFIX, %MACROS, $DIST_VERSION, $AUTHOR, $AUTHOR_EMAIL);
12             BEGIN {
13 9     9   42 $DIST_VERSION = '-version';
14 9         21 $AUTHOR = '-author';
15 9         22 $AUTHOR_EMAIL = '-author';
16 9         19 $GATTRS = '(
17             allow (?&PerlNWS)
18             (?:(?!qw)(?&PerlQualifiedIdentifier)|
19             (?&PerlList))
20             |
21             with (?&PerlNWS)
22             (?:(?!qw)(?&PerlPrefixUnaryOperator)*(?&PerlQualifiedIdentifier)|
23             (?&PerlList))
24             |
25             is (?&PerlNWS)
26             (?:(?!qw)(?&PerlPrefixUnaryOperator)*(?&PerlQualifiedIdentifier)|
27             (?&PerlList))
28             |
29             use (?&PerlNWS)
30             (?:(?&PerlQualifiedIdentifier)\s*(?&PerlList)|(?:(?!qw)(?&PerlQualifiedIdentifier)|
31             (?&PerlList)))
32             |
33             (?:(?&PerlNWS)*)
34             )';
35 9         22 $SATTRS = '(
36             allow (?&PerlNWS)
37             (?:(?!qw)(?&PerlQualifiedIdentifier)|
38             (?&PerlList))
39             |
40             (?:(?&PerlNWS)*)
41             )';
42 9         18 $PATTRS = '(
43             describe (?&PerlNWS)
44             (?:(?&PerlString))
45             |
46             (?:(?&PerlNWS)*)
47             )';
48 9         157 %HAS = (
49             ro => '"ro"',
50             ro => '"ro"',
51             is_ro => 'is => "ro"',
52             rw => '"rw"',
53             is_rw => 'is => "rw"',
54             nan => 'undef',
55             lzy => 'lazy => 1',
56             bld => 'builder => 1',
57             lzy_bld => 'lazy_build => 1',
58             trg => 'trigger => 1',
59             clr => 'clearer => 1',
60             req => 'required => 1',
61             coe => 'coerce => 1',
62             lzy_hash => 'lazy => 1, default => sub { {} }',
63             lzy_array => 'lazy => 1, default => sub { [] }',
64             lzy_str => 'lazy => 1, default => sub { "" }',
65             dhash => 'default => sub { {} }',
66             darray => 'default => sub { [] }',
67             dstr => 'default => sub { "" }',
68             );
69 9         112 $HAS{compile_regex} = sprintf q|[\[\s]+(%s)[\s,]+|, join '|', keys %HAS;
70 9         36 $HAS{compile_value_regex} = sprintf q|[\[\s]+(%s)[\s,]+|, join '|', map { quotemeta($_) }
  126         40755  
71             qw/default lazy required trigger clearer coerce handles builder predicate reader writer weak_ref init_arg moosify/;
72             };
73              
74             sub g {
75 245     245 0 1129 my ($source, $keyword, $callback, $lib, $pod) = @_;
76 245         8432978 while ($$source =~ m/
77             $keyword
78             $PPR::GRAMMAR
79             /xms) {
80 105         134175 my %hack = %+;
81 105         710 $hack{generate_pod} = $pod;
82 105         721 my ($make, %makes) = $callback->(%hack);
83 105         1107 $hack{match} = quotemeta($hack{match});
84 105 100       387 if ($lib) {
85 26         1859 $make =~ s/(^\{\s*)|(\}\s*$)//g;
86 26         436 $make =~ s/^\t//gm;
87 26         174 $make .= render_pod($makes{class});
88             write_file(sprintf("%s/%s.pmc", $lib, $makes{class}), $make)
89 26 50       357 if $makes{class};
90 26         923116 $$source =~ s/$hack{match}//;
91             } else {
92 79         1770 $$source =~ s/$hack{match}/$make/e;
  79         2994636  
93             }
94             }
95 245         24634 $source;
96             }
97              
98             sub p {
99 26     26 0 211 g(
100             g(
101             g(
102             g(
103             g(
104             g(
105             g(
106             g(
107             $_[0],
108             qq|(?<match>start\\s*
109             (?<method>(?&PerlIdentifier))\\s*
110             (?<block>(?&PerlBlock)))|,
111             \&start
112             ),
113             qq|(?<match>end\\s*
114             (?<method>(?&PerlIdentifier))\\s*
115             (?<block>(?&PerlBlock)))|,
116             \&end
117             ),
118             qq|(?<match>during\\s*
119             (?<method>(?&PerlIdentifier))\\s*
120             (?<block>(?&PerlBlock)))|,
121             \&during
122             ),
123             qq|(?<match>trigger\\s*
124             (?<method>(?&PerlIdentifier))\\s*
125             (?<block>(?&PerlBlock)))|,
126             \&trigger
127             ),
128             qq|(?<match>macro\\s*
129             (?<macro> (?&PerlIdentifier))\\s*
130             (?<block> (?&PerlBlock));\n*)|,
131             \&macro
132             ),
133             qq|(?<match> private\\s*
134             (?<method> (?&PerlIdentifier))
135             (?<attrs> (?: $SATTRS*))
136             (?<block> (?&PerlBlock)))|,
137             \&private,
138             ),
139             qq|(?<match> public\\s*
140             (?<method> (?&PerlIdentifier))
141             (?:(?&PerlNWS))*
142             (?<block> (?&PerlBlock))
143             (?<pod> (?: $PATTRS*)))|,
144             \&public,
145             undef,
146             $_[1]
147             ),
148             qq|(?<match> attributes\\s* (?<list> (?&PerlList))\\s*\;)|,
149             \&attributes
150             );
151             }
152              
153             sub i {
154 49     49 0 133 my $i = shift;
155 49         107 my @s;
156 49         1750733 while ( $i =~ s/
157             (?<match>\s*(?:
158             (?<hash>\s*(?&PerlAnonymousHash))|
159             (?<array>\s*(?&PerlAnonymousArray))|
160             (?<sub>\s*(?&PerlAnonymousSubroutine))|
161             (?<bless>\s*(bless\s*(?&PerlExpression)))|
162             (?<ident>\s*(?&PerlIdentifier))|
163             (?<string>\s*(?&PerlString))|
164             (?<num>\s*(?&PerlNumber))
165             )+)\s*(?&PerlComma)*
166             $PPR::GRAMMAR
167             //xms ) {
168 66         2332246 push @s, {%+}
169             }
170 49         8337 return @s;
171             }
172              
173             sub r {
174 24     24 0 90 my $i = shift;
175 24         595 while ($i =~ m/$_[0]/xms) {
176 13         96 my $m = $1;
177 13         266 $i =~ s/$m/$_[1]->{$m}/;
178             }
179 24         107 $i;
180             }
181              
182             sub kv {
183 4     4 0 37 my ($i, %a) = @_;
184 4         190602 while (
185             $i =~ s/
186             \s*(?<key> (?&PerlTerm))\s*
187             (?&PerlComma)
188             \s*(?<value> (?&PerlTerm))\s*
189             $PPR::GRAMMAR
190             //xms
191             ) {
192 6         1107 my %h = %+;
193 6         85 $h{key} =~ s/(^\s*)|(\s*$)//g;
194 6         280567 $a{$h{key}} = $h{value};
195             }
196 4         780 return %a;
197             }
198              
199             sub import {
200 11     11   379 my ($class, %args) = @_;
201 11 100       103 $PREFIX = $args{-prefix} unless $PREFIX;
202 11 100       48 if ($args{-author}) {
203 1         9 $args{-author} =~ m/(.*)\s*\<(.*)\>/;
204 1         5 $AUTHOR_EMAIL = $2;
205 1         5 ($AUTHOR = $1) =~ s/\s$//;
206 1         4 $AUTHOR_EMAIL =~ s/\@/ at /;
207             }
208 11 100       37 $DIST_VERSION = $args{-version} if $args{-version};
209 11         86 my $lib = $args{-lib};
210 11 100       119 my $file = $args{-module} ? [caller(1)]->[1] : $0;
211 11         541 open FH, "<$file";
212 11         576 my $source = \join '', <FH>;
213 11         166 close FH;
214 11         65 g(
215             g(
216             g(
217             $source,
218             qq/(?<match>(?&PerlPod))/,
219             \&parse_pod
220             ),
221             qq/(?<match> role\\s*
222             (?<class>(?&PerlPrefixUnaryOperator)*(?&PerlQualifiedIdentifier))
223             (?<attrs> (?: $GATTRS*))
224             (?<block> (?&PerlBlock)))/,
225             \&roles,
226             $lib
227             ),
228             qq/(?<match> class\\s*
229             (?<class>(?&PerlPrefixUnaryOperator)*(?&PerlQualifiedIdentifier))
230             (?<attrs> (?: $GATTRS*))
231             (?<block> (?&PerlBlock)))/,
232             \&classes,
233             $lib
234             );
235 11 50       1509 unless ($lib) {
236 0         0 $source =~ s/use MooX\:\:Purple;\n*//;
237 0         0 $source =~ s/use MooX\:\:Purple\:\:G;\n*//;
238 0         0 my $current = [caller()]->[1];
239 0         0 $current =~ s/\.(.*)/\.pmc/;
240 0         0 write_file($current, $$source);
241             }
242             }
243              
244             sub make_path {
245 26     26 0 606 my $path = abs_path();;
246 26         314 for (split '/', $_[0]) {
247 77         235 $path .= "/$_";
248 77 100       1957 if (! -d $path) {
249 5 50       524 mkdir $path or Carp::croak(qq/
250             Cannot open file for writing $!
251             /);
252             }
253             }
254             }
255              
256             sub write_file {
257 26     26 0 71 my $f = $_[0];
258 26         112 $f =~ s/\:\:/\//g;
259 26         228 make_path(substr($f, 0, rindex($f, '/')));
260 26 50       3229 open FH, '>', $f or die "$f cannot open file to write $!";
261 26         218 print FH perl_tidy($_[1]);
262 26         2581 close FH;
263             }
264              
265             sub macro {
266 2     2 0 9 my %args = @_;
267 2         28 $args{block} =~ s/^\n*\{\n*\s*|;\n*\t*\}\n*$//g;
268 2         11 $MACROS{$args{macro}} = $args{block};
269 2         9 return '';
270             }
271              
272             sub start {
273 1     1 0 6 push @_, pre => '-';
274 1         57 when(@_);
275             }
276              
277             sub end {
278 1     1 0 6 push @_, pre => '+';
279 1         8 when(@_);
280             }
281              
282             sub during {
283 1     1 0 6 push @_, pre => '~';
284 1         8 when(@_);
285             }
286              
287             sub trigger {
288 1     1 0 7 push @_, pre => '=';
289 1         6 when(@_);
290             }
291              
292             sub when {
293 4     4 0 23 my %args = @_;
294 4         26 my %map = (
295             '-' => 'before',
296             '+' => 'after',
297             '~' => 'around',
298             '=' => 'around'
299             );
300              
301 4         61 $args{block} =~ s/(^{)|(}$)//g;
302 4 100       25 if ($args{pre} eq '~') {
    100          
303 1         5 $args{block} = "{
304             my (\$orig, \$self) = (shift, shift);
305             $args{block};
306             }";
307             } elsif ($args{pre} eq '=') {
308 1         7 $args{block} = "{
309             my (\$orig, \$self) = (shift, shift);
310             my \$out = \$self->\$orig(\@_);
311             $args{block};
312             }";
313             } else {
314 2         10 $args{block} = "{
315             my (\$self) = (shift);
316             $args{block};
317             }";
318             }
319 4         39 return "$map{$args{pre}} $args{method} => sub $args{block};";
320             }
321              
322             sub attributes {
323 4     4 0 23 my %args = @_;
324 4         134 my @attr;
325             g(
326             \$args{list},
327             qq/(?<match>
328             \\s*(?<key> (?&PerlTerm))\\s*
329             (?&PerlComma)
330             \\s*(?<value> (?&PerlTerm))\\s*
331             )/,
332             sub {
333 24     24   139 my %hack = _construct_attribute(@_);
334 24         830901 $hack{key} =~ m/\s*(?<array> (?&PerlAnonymousArray)) $PPR::GRAMMAR/xms;
335 24 100       3555 for my $key ( ($+{array} ? @{ eval $+{array} } : $hack{key}) ) {
  3         605  
336 29         445 $key =~ s/(^\s*)|(\s*$)//g;
337             push @attr, sprintf(
338             q/has %s => (
339             %s
340             );/,
341             $key, join( ",\n\t", (map {
342 36         183 $hack{$_} =~ s/(["']+)/"/g;
343 36         186 qq/\t$_ => $hack{$_}/
344 174         418 } grep { defined $hack{$_} } qw/is isa trigger builder lazy clearer/), (map {
345 25         127 my $hak = [i($hack{$_})]->[0];
346 25 100       280 $hack{$_} = defined $hak->{sub} ? $hak->{sub} : qq/sub { $hack{$_} }/;
347 25         609 qq/\t$_ => $hack{$_}/;
348 29         168 } grep { $hack{$_} } qw/default/)));
  29         176  
349             }
350             }
351 4         64 );
352 4         181 return join "\n\n", @attr;
353             }
354              
355             sub _construct_attribute {
356 24     24   135 my (%attr) = @_;
357 24         193 $attr{value} = r($attr{value}, $HAS{compile_regex}, \%HAS);
358 24         308 $attr{value} =~ s/(^\s*\[)|(\s*\]$)//g;
359 24         126 my @spec = i($attr{value});
360 24         112 my $oc = scalar @spec;
361 24 100       230 unshift @spec, { string => '"ro"' } if (!$spec[0]->{string});
362             $attr{is} = $spec[0]->{string} =~ m/[\'\"\s]+(ro|rw)[\'\"\s]+/
363             ? shift(@spec)->{string}
364 24 100       438 : '"ro"';
365             ($spec[0]->{ident} eq 'undef')
366             ? shift(@spec)
367             : do {
368 1         6 $attr{isa} = shift(@spec)->{ident};
369 24 100       146 } if $spec[0]->{ident};
    100          
370 24 100       433 my $attrHash = $spec[0]->{hash} ? $spec[0]->{match} =~ m/$HAS{compile_value_regex}/g : 0;
371 24 100 66     158 if ($spec[0] && keys %{$spec[0]}) {
  24         188  
372 22 100 66     267 $attr{default} = !$attrHash && $oc <= 3 ? $spec[0]->{sub} ? shift(@spec)->{sub} : qq/sub { / . shift(@spec)->{match} . qq/ }/ : '';
    100          
373 22 100       114 %attr = kv($spec[0]->{match}, %attr) if ($spec[0]);
374             }
375 24         115 delete $attr{value};
376 24         249 return %attr;
377             }
378              
379             sub roles {
380 12     12 0 119 my %args = @_;
381 12 100       413768 my @hack = grep {$_ && $_ !~ m/^\s*$/} $args{attrs} =~ m/(?:$GATTRS) $PPR::GRAMMAR/gx;
  3528         5669  
382 12         1093 my ($body, %attrs) = _set_class_role_attrs($args{block}, _parse_role_attrs(@hack));
383 12         189 $body =~ s/\s*$//;
384            
385 12         104 $args{class} =~ s/^\+/$PREFIX\:\:/;
386              
387 12         60 my $pod = prepare_pod($args{class});
388              
389 12         122 my $r = \qq|{
390             package $args{class};
391             use Moo::Role;
392             use MooX::LazierAttributes;
393             use MooX::ValidateSubs;
394             use Data::LnArray qw/arr/;
395             $attrs{with}$attrs{use}$body
396             1;
397             }|;
398 12         62 p($r, !$pod);
399 12         272 return ($$r, %args);
400             }
401              
402             sub parse_pod {
403 24     24 0 80 my %h = @_;
404 24 100       141 if ($h{match} =~ m/=head1 NAME\n*([^\s]+)/) {
405 8         65 $POD{$1} = $POD{CURRENT} = { PARSED => 1, DATA => [] };
406             }
407 24         49 push @{$POD{CURRENT}{DATA}}, $h{match};
  24         148  
408             }
409              
410             sub prepare_pod {
411 26     26 0 83 my $class = shift;
412 26 100       129 if (!$POD{$class}) {
413 18         122 $POD{$class} = $POD{CURRENT} = { PARSED => 0, DATA => [] };
414 18         44 push @{$POD{$class}{DATA}}, "=head1 NAME
  18         121  
415              
416             $class - The great new $class!
417              
418             =cut";
419 18         46 push @{$POD{$class}{DATA}}, "=head1 Version
  18         99  
420              
421             Version $DIST_VERSION
422              
423             =cut";
424 18         43 push @{$POD{$class}{DATA}}, "=head1 SYNOPSIS
  18         93  
425              
426             use $class;
427              
428             $class\-\>new(\\%args)
429              
430             =cut";
431 18         41 push @{$POD{$class}{DATA}}, "=head1 SUBROUTINES/METHODS
  18         61  
432              
433             =cut";
434 18         64 return 0;
435             }
436 8         24 return 1;
437             }
438              
439             sub render_pod {
440 26     26 0 88 my $class = shift;
441 26 50       150 if ($POD{$class}) {
442 26 100       146 if (!$POD{$class}{PARSED}) {
443 18         76 (my $url_class = $class) =~ s/\:\:/-/g;
444 18         48 push @{$POD{$class}{DATA}}, "=head1 AUTHOR
  18         168  
445              
446             $AUTHOR, C<< <$AUTHOR_EMAIL> >>
447              
448             =cut";
449 18         44 push @{$POD{$class}{DATA}}, "=head1 BUGS
  18         102  
450              
451             Please report any bugs or feature requests to C<bug-moox-purple at rt.cpan.org>, or through
452             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=$url_class>. I will be notified, and then you'll
453             automatically be notified of progress on your bug as I make changes.
454              
455             =cut";
456 18         49 push @{$POD{$class}{DATA}}, "=head1 SUPPORT
  18         152  
457              
458             You can find documentation for this module with the perldoc command.
459              
460             perldoc $class
461              
462              
463             You can also look for information at:
464              
465             =over 4
466              
467             =item * RT: CPAN's request tracker (report bugs here)
468              
469             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=$url_class>
470              
471             =item * AnnoCPAN: Annotated CPAN documentation
472              
473             L<http://annocpan.org/dist/$url_class>
474              
475             =item * CPAN Ratings
476              
477             L<http://cpanratings.perl.org/d/$url_class>
478              
479             =item * Search CPAN
480              
481             L<http://search.cpan.org/dist/$url_class/>
482              
483             =back
484              
485             =cut";
486 18         47 push @{$POD{$class}{DATA}}, "=head1 ACKNOWLEDGEMENTS
  18         65  
487              
488             =cut";
489              
490 18         41 push @{$POD{$class}{DATA}}, "=head1 LICENSE AND COPYRIGHT
  18         1039  
491              
492             Copyright 2019 $AUTHOR.
493              
494             This program is free software; you can redistribute it and/or modify it
495             under the terms of the the Artistic License (2.0). You may obtain a
496             copy of the full license at:
497              
498             L<http://www.perlfoundation.org/artistic_license_2_0>
499              
500             Any use, modification, and distribution of the Standard or Modified
501             Versions is governed by this Artistic License. By using, modifying or
502             distributing the Package, you accept this license. Do not use, modify,
503             or distribute the Package, if you do not accept this license.
504              
505             If your Modified Version has been derived from a Modified Version made
506             by someone other than you, you are nevertheless required to ensure that
507             your Modified Version complies with the requirements of this license.
508              
509             This license does not grant you the right to use any trademark, service
510             mark, tradename, or logo of the Copyright Holder.
511              
512             This license includes the non-exclusive, worldwide, free-of-charge
513             patent license to make, have made, use, offer to sell, sell, import and
514             otherwise transfer the Package with respect to any patent claims
515             licensable by the Copyright Holder that are necessarily infringed by the
516             Package. If you institute patent litigation (including a cross-claim or
517             counterclaim) against any party alleging that the Package constitutes
518             direct or contributory patent infringement, then this Artistic License
519             to you shall terminate on the date that such litigation is filed.
520              
521             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
522             AND CONTRIBUTORS 'AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
523             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
524             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
525             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
526             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
527             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
528             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
529              
530             =cut";
531              
532             }
533 26         74 return join "\n", @{$POD{$class}{DATA}};
  26         279  
534             }
535 0         0 return '';
536             }
537              
538             sub classes {
539 14     14 0 445 my %args = @_;
540 14 100       485544 my @hack = grep {$_ && $_ !~ m/^\s*$/} $args{attrs} =~ m/(?:$GATTRS) $PPR::GRAMMAR/gx;
  5796         9413  
541 14         1440 my ($body, %attrs) = _set_class_role_attrs($args{block}, _parse_role_attrs(@hack));
542 14         414 $body =~ s/\s*$//;
543 14         139 $args{class} =~ s/^\+/$PREFIX\:\:/;
544              
545 14         80 my $pod = prepare_pod($args{class});
546 14         125 my $r = \qq|{
547             package $args{class};
548             use Moo;
549             use MooX::LazierAttributes;
550             use MooX::ValidateSubs;
551             use Data::LnArray qw/arr/;
552             $attrs{is}$attrs{with}$attrs{use}$body
553             1;
554             }|;
555 14         80 p($r, !$pod);
556 14         435 return ($$r, %args);
557             }
558              
559             sub macro_replacement {
560 21     21 0 62 my $block = shift;
561 21         113 my $mac = join '|', keys %MACROS;
562 21         194 $block =~ s/&($mac)/$MACROS{$1}/g;
563 21         85 return $block;
564             }
565              
566             sub private {
567 2     2 0 12 my %args = @_;
568 2 100       68985 my @hack = grep {$_ && $_ !~ m/^\s*$/} $args{attrs} =~ m/(?:$SATTRS) $PPR::GRAMMAR/gx;
  756         1244  
569 2         246 my %attrs = _parse_role_attrs(@hack);
570 2 50       14 my $allowed = $attrs{allow} ? sprintf 'qw(%s)', join ' ', @{$attrs{allow}} : 'qw//';
  2         13  
571 2         12 $args{block} = macro_replacement($args{block});
572 2         19 $args{block} =~ s/(^{)|(}$)//g;
573 2         16 $args{block} =~ s/^\s*//;
574 2         26 return "sub $args{method} {
575             my (\$self) = shift;
576             my \$caller = caller();
577             my \@allowed = $allowed;
578             unless (\$caller eq __PACKAGE__ || grep { \$_ eq \$caller } \@allowed) {
579             die \"cannot call private method $args{method} from \$caller\";
580             }
581             $args{block}
582             }";
583             }
584              
585             sub public {
586 19     19 0 114 my %args = @_;
587 19 50       159 if ($args{pod}) {
588 19         67 $args{pod} =~ m/describe\s*(.*)/i;
589 19         3415 $args{pod} = eval $1;
590             }
591 19   100     215 $args{pod} //= '';
592 11         101 push @{ $POD{CURRENT}{DATA} }, "=head2 $args{method}
593              
594             $args{pod}
595              
596             \$class->$args{method}
597              
598 19 100       82 =cut" if $args{generate_pod};
599 19         101 $args{block} = macro_replacement($args{block});
600 19         213 $args{block} =~ s/(^{)|(}$)//g;
601 19         143 return "sub $args{method} {
602             my (\$self) = shift;
603             $args{block}
604             }";
605             }
606              
607             =pod
608             sub _parse_role_attrs {
609             my @roles = @_;
610             my %attrs;
611             for (@roles) {
612             if ($_ =~ m/\s*use\s*((?!qw)(?&PerlQualifiedIdentifier))\s*((?&PerlList)) $PPR::GRAMMAR/xms) {
613             push @{$attrs{use}}, sprintf "%s %s", $1, $2;
614             next;
615             }
616             $_ =~ m/(with|allow|is|use)(.*)/i;
617             push @{$attrs{$1}}, eval $2 || do { (my $g = $2) =~ s/^\s*//; $g; };
618             }
619             return %attrs;
620             }
621             =cut
622              
623             sub _parse_role_attrs {
624 28     28   123 my @roles = @_;
625 28         99 my %attrs;
626 28         80 my $i = 0;
627 28         124 for (@roles) {
628 17 100       581399 if ($_ =~ m/\s*use\s*((?!qw)(?&PerlQualifiedIdentifier))\s*((?&PerlList)) $PPR::GRAMMAR/xms) {
629 1         16 $attrs{use}{sprintf "%s %s", $1, $2}++;
630 1         111 next;
631             }
632 16         1933 $_ =~ m/(with|allow|is|use)(.*)/i;
633 16         2608 my @list = eval($2); # || $2
634 16 100       132 push @list, do { (my $g = $2) =~ s/^\s*//; $g; } unless @list;
  8         72  
  8         37  
635 16         59 for (@list) {
636 24         173 $attrs{$1}{$_} = $i++;
637             }
638             }
639 28         108 for my $o (qw/with allow is use/) {
640 112 100       314 $attrs{$o} = [sort { $attrs{$o}{$a} <=> $attrs{$o}{$b} } keys %{$attrs{$o}}] if $attrs{$o};
  15         46  
  16         134  
641             }
642 28         199 return %attrs;
643             }
644              
645             sub _set_class_role_attrs {
646 26     26   117 my ($body, %attrs) = @_;
647 26 100       116 if ($attrs{allow}) {
648 2         5 my $allow = join ' ', @{$attrs{allow}};
  2         8  
649 2         30 $body =~ s{private\s*(\p{XIDS}\p{XIDC}*)}{private $1 allow qw/$allow/}g;
650             }
651 26 100       152 $attrs{is} = $attrs{is} ? sprintf "extends qw/%s/;\n", join(' ', map { my $l = $_; $l =~ s/^\s*\+/$PREFIX\:\:/; $l; } @{$attrs{is}}) : '';
  5         15  
  5         40  
  5         51  
  5         24  
652 26         67 my $last;
653             $attrs{with} = $attrs{with}
654             ? sprintf "with qw/%s/;\n", join(' ', map {
655 14         30 my $l = $_;
656 14         41 $l =~ s/^\s*\+/$PREFIX\:\:/;
657 14 100       66 unless($l =~ s/^\s*\-/$last\:\:/) {
658 6         18 $last = $l;
659             }
660 14 100       55 if ($l =~ s/^\s*\~//) {
661 2 50       13 $last = $PREFIX ? ($PREFIX . '::' . $l) : $l;
662 2         4 $l = '';
663             }
664 14         67 $l;
665 26 100       123 } @{$attrs{with}})
  6         23  
666             : '';
667 26 100       110 $attrs{use} = $attrs{use} ? join('', map { sprintf("\tuse %s;\n", $_) } @{$attrs{use}}) : '';
  2         8  
  1         3  
668 26         892 $body =~ s/(^{)|(}$)//g;
669 26         196 return $body, %attrs;
670             }
671              
672             sub perl_tidy {
673 26     26 0 81 my $source = shift;
674            
675 26         108 my $dest_string;
676             my $stderr_string;
677 26         0 my $errorfile_string;
678 26         97 my $argv = "-npro"; # Ignore any .perltidyrc at this site
679 26         92 $argv .= " -pbp"; # Format according to perl best practices
680 26         64 $argv .= " -nst"; # Must turn off -st in case -pbp is specified
681 26         62 $argv .= " -se"; # -se appends the errorfile to stderr
682 26         65 $argv .= " -nola"; # Disable label indent
683 26         68 $argv .= " -t"; # Use tab instead of 4 spaces
684            
685 26         250 my $error = Perl::Tidy::perltidy(
686             argv => $argv,
687             source => \$source,
688             destination => \$dest_string,
689             stderr => \$stderr_string,
690             errorfile => \$errorfile_string, # ignored when -se flag is set
691             ##phasers => 'stun', # uncomment to trigger an error
692             );
693            
694 26 50       4612765 if ($error) {
695             # serious error in input parameters, no tidied output
696 0         0 print "<<STDERR>>\n$stderr_string\n";
697 0         0 die "Exiting because of serious errors\n";
698             }
699              
700 26         607 return $dest_string;
701             }
702              
703             1;
704              
705             __END__
706              
707             =head1 NAME
708              
709             MooX::Purple - MooX::Purple::G
710              
711             =head1 VERSION
712              
713             Version 0.16
714              
715             =cut
716              
717             =head1 SYNOPSIS
718              
719             use MooX::Purple;
720             use MooX::Purple::G;
721              
722             role Before {
723             public seven { return '7' }
724             };
725              
726             role World allow Hello with Before {
727             private six { 'six' }
728             };
729              
730             class Hello with qw/World/ allow qw/main/ use Scalar::Util qw/reftype/ use qw/JSON/ {
731             use Types::Standard qw/Str HashRef ArrayRef Object/;
732              
733             attributes
734             one => [{ okay => 'one'}],
735             [qw/two three/] => [rw, Str, { default => 'the world is flat' }];
736              
737             validate_subs
738             four => {
739             params => {
740             message => [Str, sub {'four'}]
741             }
742             };
743              
744             public four { return $_[1]->{message} }
745             private five { return $_[0]->six }
746             public ten { reftype bless {}, 'Flat::World' }
747             public eleven { encode_json { flat => "world" } }
748             };
749              
750             class Night is qw/Hello/ {
751             public nine { return 'nine' }
752             };
753              
754             Night->new()->five();
755              
756             ... writes to same/path/yourfile.pmc
757              
758             {
759             package Before;
760             use Moo::Role;
761              
762             sub seven { return '7' }
763             };
764              
765             {
766             package World;
767             use Moo::Role;
768             with qw/Before/;
769              
770             sub six {
771             my $caller = caller();
772             my @allowed = qw(Hello);
773             unless ($caller eq __PACKAGE__ || grep { $_ eq $caller } @allowed) {
774             die "cannot call private method six from $caller";
775             }
776             'six'
777             }
778             };
779              
780             {
781             package Hello;
782             use Moo;
783             use MooX::LazierAttributes;
784             use MooX::ValidateSubs;
785             with qw/World/;
786             use Scalar::Util qw/reftype/ ;
787             use JSON;
788              
789             use Types::Standard qw/Str HashRef ArrayRef Object/;
790              
791             attributes
792             one => [{ okay => 'one'}],
793             [qw/two three/] => [rw, Str, { default => 'the world is flat' }];
794              
795             validate_subs
796             four => {
797             params => {
798             message => [Str, sub {'four'}]
799             }
800             };
801              
802             sub four { return $_[1]->{message} }
803             sub five {
804             my $caller = caller();
805             my @allowed = qw(main);
806             unless ($caller eq __PACKAGE__ || grep { $_ eq $caller } @allowed) {
807             die "cannot call private method five from $caller";
808             }
809             return $_[0]->six
810             }
811             sub ten { reftype bless {}, 'Flat::World' }
812             sub eleven { encode_json { flat => "world" } }
813             1;
814             };
815              
816             {
817             package Night;
818             use Moo;
819             use MooX::LazierAttributes;
820             use MooX::ValidateSubs;
821             extends qw/Hello/;
822              
823             sub nine { return 'nine' }
824             1;
825             };
826              
827              
828             =head1 AUTHOR
829              
830             lnation, C<< <thisusedtobeanemail at gmail.com> >>
831              
832             =head1 BUGS
833              
834             Please report any bugs or feature requests to C<bug-moox-purple at rt.cpan.org>, or through
835             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=MooX-Purple>. I will be notified, and then you'll
836             automatically be notified of progress on your bug as I make changes.
837              
838             =head1 SUPPORT
839              
840             You can find documentation for this module with the perldoc command.
841              
842             perldoc MooX::Purple
843              
844              
845             You can also look for information at:
846              
847             =over 4
848              
849             =item * RT: CPAN's request tracker (report bugs here)
850              
851             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=MooX-Purple>
852              
853             =item * AnnoCPAN: Annotated CPAN documentation
854              
855             L<http://annocpan.org/dist/MooX-Purple>
856              
857             =item * CPAN Ratings
858              
859             L<http://cpanratings.perl.org/d/MooX-Purple>
860              
861             =item * Search CPAN
862              
863             L<http://search.cpan.org/dist/MooX-Purple/>
864              
865             =back
866              
867              
868             =head1 ACKNOWLEDGEMENTS
869              
870              
871             =head1 LICENSE AND COPYRIGHT
872              
873             Copyright 2019 lnation.
874              
875             This program is free software; you can redistribute it and/or modify it
876             under the terms of the the Artistic License (2.0). You may obtain a
877             copy of the full license at:
878              
879             L<http://www.perlfoundation.org/artistic_license_2_0>
880              
881             Any use, modification, and distribution of the Standard or Modified
882             Versions is governed by this Artistic License. By using, modifying or
883             distributing the Package, you accept this license. Do not use, modify,
884             or distribute the Package, if you do not accept this license.
885              
886             If your Modified Version has been derived from a Modified Version made
887             by someone other than you, you are nevertheless required to ensure that
888             your Modified Version complies with the requirements of this license.
889              
890             This license does not grant you the right to use any trademark, service
891             mark, tradename, or logo of the Copyright Holder.
892              
893             This license includes the non-exclusive, worldwide, free-of-charge
894             patent license to make, have made, use, offer to sell, sell, import and
895             otherwise transfer the Package with respect to any patent claims
896             licensable by the Copyright Holder that are necessarily infringed by the
897             Package. If you institute patent litigation (including a cross-claim or
898             counterclaim) against any party alleging that the Package constitutes
899             direct or contributory patent infringement, then this Artistic License
900             to you shall terminate on the date that such litigation is filed.
901              
902             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
903             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
904             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
905             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
906             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
907             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
908             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
909             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
910              
911              
912              
913             1; # End of MooX::Purple