File Coverage

blib/lib/MooX/Purple/G.pm
Criterion Covered Total %
statement 276 284 97.1
branch 79 88 89.7
condition 6 8 75.0
subroutine 35 35 100.0
pod 0 23 0.0
total 396 438 90.4


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