File Coverage

blib/lib/Dist/Zilla/Util/SimpleMunge.pm
Criterion Covered Total %
statement 80 117 68.3
branch 15 38 39.4
condition 13 33 39.3
subroutine 17 21 80.9
pod 10 10 100.0
total 135 219 61.6


line stmt bran cond sub pod time code
1 3     3   92837 use 5.006; # our
  3         8  
2 3     3   9 use strict;
  3         3  
  3         53  
3 3     3   16 use warnings;
  3         4  
  3         216  
4              
5             package Dist::Zilla::Util::SimpleMunge;
6              
7             our $VERSION = '1.000002';
8              
9             # ABSTRACT: Make munging File::FromCode and File::InMemory easier.
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 3         19 use Sub::Exporter -setup => { exports =>
14 3     3   1297 [qw[ munge_file munge_files to_InMemory to_FromCode munge_InMemory munge_FromCode inplace_replace auto_munge_file ]], };
  3         24270  
15              
16              
17              
18              
19              
20              
21              
22              
23              
24              
25              
26              
27              
28              
29              
30              
31              
32              
33              
34              
35              
36              
37              
38              
39              
40              
41              
42              
43              
44              
45              
46              
47              
48              
49              
50              
51              
52              
53              
54              
55              
56              
57              
58              
59              
60              
61              
62              
63              
64              
65              
66              
67              
68              
69              
70              
71              
72              
73              
74              
75              
76              
77              
78              
79              
80              
81              
82              
83              
84              
85              
86              
87              
88              
89              
90              
91              
92              
93              
94              
95              
96              
97              
98              
99              
100              
101              
102              
103              
104              
105              
106              
107              
108              
109              
110              
111              
112              
113              
114              
115              
116              
117              
118              
119              
120             my $ex_auto_munge_file_params_excess = {
121             tags => [qw( parameters excess auto_munge_file )],
122             ## no critic (RequireInterpolationOfMetachars)
123             message => q[auto_munge_file only accepts 2 parameters, $FILE and $CALLBACK],
124             id => 'auto_munge_file_params_excess',
125             };
126              
127             my $ex_auto_munge_file_param_file_bad = {
128             tags => [qw( parameters file bad mismatch invalid )],
129             id => 'auto_munge_file_param_file_bad',
130             ## no critic (ValuesAndExpressions::RestrictLongStrings)
131             message => 'auto_munge_file must be passed a Dist::Zilla File or a compatible object for parameter 0',
132             };
133              
134             sub auto_munge_file {
135 0     0 1 0 my (@all) = @_;
136 0         0 my ( $file, $callback, @rest ) = @all;
137 0 0       0 if (@rest) {
138             __PACKAGE__->_error(
139 0         0 %{$ex_auto_munge_file_params_excess},
  0         0  
140             payload => {
141             parameters => \@all,
142             errors => \@rest,
143             understood => {
144             qw( $file ) => $file,
145             qw( $callback ) => $callback,
146             },
147             },
148             );
149             }
150 0 0 0     0 if ( not $file or not $file->can('content') ) {
151             __PACKAGE__->_error(
152 0         0 %{$ex_auto_munge_file_param_file_bad},
  0         0  
153             payload => {
154             parameter_no => 0,
155             expects => [qw[ defined ->can(content) ]],
156             got => $file,
157             },
158             );
159             }
160 0 0 0     0 if ( not defined $callback or not 'CODE' eq ref $callback ) {
161 0         0 __PACKAGE__->_error(
162             message => 'auto_munge_file must be passed a subroutine as parameter 1',
163             payload => {
164             parameter_no => '1',
165             expects => [qw[ defined ref Code ]],
166             got => $callback,
167             },
168             id => 'auto_munge_file_callback_bad',
169             tags => [qw( parameters config via bad mismatch invalid )],
170             );
171             }
172 0 0       0 if ( $file->can('code') ) {
173 0         0 return munge_FromCode( $file, $callback );
174             }
175 0         0 return munge_InMemory( $file, $callback );
176              
177             }
178              
179              
180              
181              
182              
183              
184              
185              
186              
187              
188             sub to_InMemory {
189 2     2 1 2 my ($file) = @_;
190 2         51 my $encoding = $file->encoding;
191 2         121 my %args = (
192             name => $file->name,
193             encoding => $encoding,
194             added_by => $file->added_by,
195             mode => $file->mode,
196             );
197 2 50       304 if ( 'bytes' eq $encoding ) {
198 0         0 $args{encoded_content} = $file->encoded_content;
199             }
200             else {
201 2         5 $args{content} = $file->content;
202             }
203 2         90 require Dist::Zilla::File::InMemory;
204 2         44 return Dist::Zilla::File::InMemory->new(%args);
205             }
206              
207              
208              
209              
210              
211              
212              
213              
214              
215             sub to_FromCode {
216 2     2 1 3 my ($file) = @_;
217 2         53 my $encoding = $file->encoding;
218 2         24 my %args = (
219             name => $file->name,
220             added_by => $file->added_by,
221             mode => $file->mode,
222             );
223 2 50       287 if ( 'bytes' eq $encoding ) {
224 0         0 my $ec = $file->encoded_content;
225 0     0   0 $args{code} = sub { return $ec };
  0         0  
226 0         0 $args{code_return_type} = 'bytes';
227             }
228             else {
229 2         7 my $c = $file->content;
230 2     4   105 $args{code} = sub { return $c };
  4         9  
231 2         4 $args{code_return_type} = 'text';
232             }
233 2         9 require Dist::Zilla::File::FromCode;
234 2         46 return Dist::Zilla::File::FromCode->new(%args);
235             }
236              
237              
238              
239              
240              
241              
242              
243              
244              
245              
246              
247              
248              
249              
250              
251             sub munge_InMemory {
252 5     5 1 18 my ( $file, $coderef ) = @_;
253 5 50       129 if ( 'bytes' eq $file->encoding ) {
254 0         0 return $file->encoded_content( $coderef->( $file, $file->content, 'bytes' ) );
255             }
256 5         58 $file->content( $coderef->( $file, $file->content, 'text' ) );
257 5         9653 return 1;
258             }
259              
260              
261              
262              
263              
264              
265              
266              
267              
268              
269              
270              
271              
272              
273              
274              
275              
276              
277              
278              
279              
280              
281              
282              
283              
284              
285              
286              
287             sub munge_FromCode {
288 4     4 1 16 my ( $file, $coderef ) = @_;
289 4         103 my $oldcoderef = $file->code;
290 4         101 my $return_type = $file->code_return_type;
291             $file->code(
292             sub {
293 10     10   5226 $coderef->( $file, $oldcoderef->(), $return_type );
294             },
295 4         99 );
296 4         29 return 1;
297             }
298              
299 3     3   2157 use Scalar::Util qw(blessed);
  3         4  
  3         1892  
300              
301              
302              
303              
304              
305              
306              
307              
308              
309              
310              
311              
312              
313              
314              
315              
316              
317              
318              
319              
320              
321              
322              
323              
324              
325              
326              
327              
328              
329              
330              
331              
332              
333              
334              
335              
336              
337              
338              
339              
340             # This is a little bit nasty, but can you suggest a better way?
341             # TODO
342             sub inplace_replace {
343 4     4 1 974 my ( $orig, $replacement ) = @_;
344 4         10 my $newclass = blessed $replacement;
345 4         5 for my $oldkey ( keys %{$orig} ) {
  4         12  
346 24         27 delete $orig->{$oldkey};
347             }
348 4         5 for my $newkey ( keys %{$replacement} ) {
  4         9  
349 18         20 $orig->{$newkey} = $replacement->{$newkey};
350             }
351 4         5 bless $orig, $newclass;
352 4         4 return 1;
353             }
354              
355              
356              
357              
358              
359              
360              
361              
362              
363             sub inplace_to_FromCode {
364 2     2 1 3 my ($file) = @_;
365 2         4 return inplace_replace( $file, to_FromCode($file) );
366             }
367              
368              
369              
370              
371              
372              
373              
374              
375              
376             sub inplace_to_InMemory {
377 2     2 1 2 my ($file) = @_;
378 2         5 return inplace_replace( $file, to_InMemory($file) );
379             }
380              
381              
382              
383              
384              
385              
386              
387              
388              
389              
390              
391              
392              
393              
394              
395              
396              
397              
398              
399              
400              
401              
402              
403              
404              
405              
406              
407              
408              
409              
410              
411              
412              
413              
414              
415              
416              
417              
418              
419              
420              
421              
422              
423              
424              
425              
426              
427              
428              
429              
430              
431              
432              
433              
434              
435              
436              
437              
438              
439              
440              
441             sub _fromcode_munge {
442 4     4   5 my ( $file, $config ) = @_;
443 4 100 66     15 if ( defined $config->{lazy} and 0 == $config->{lazy} ) {
444 2         6 inplace_to_InMemory($file);
445 2         47 munge_InMemory( $file, $config->{via} );
446 2         5 return 1;
447             }
448 2         7 munge_FromCode( $file, $config->{via} );
449 2         4 return 1;
450             }
451              
452             sub _scalar_munge {
453 5     5   7 my ( $file, $config ) = @_;
454 5 100 66     17 if ( defined $config->{lazy} and 1 == $config->{lazy} ) {
455 2         5 inplace_to_FromCode($file);
456 2         45 munge_FromCode( $file, $config->{via} );
457 2         5 return 1;
458             }
459 3         7 munge_InMemory( $file, $config->{via} );
460 3         8 return 1;
461             }
462              
463             my $ex_munge_file_params_excess = {
464             tags => [qw( parameters excess munge_file )],
465             id => 'munge_file_params_excess',
466             ## no critic (RequireInterpolationOfMetachars)
467             message => q[munge_file only accepts 2 parameters, $FILE and \%CONFIG],
468             };
469             my $ex_munge_file_param_file_bad = {
470             id => 'munge_file_param_file_bad',
471             tags => [qw( parameters file bad mismatch invalid )],
472             ## no critic (ValuesAndExpressions::RestrictLongStrings)
473             message => 'munge_file must be passed a Dist::Zilla File or a compatible object for parameter 0',
474             };
475              
476             sub munge_file {
477 9     9 1 5645 my (@all) = @_;
478 9         15 my ( $file, $config, @rest ) = @all;
479              
480 9 50       21 if (@rest) {
481             __PACKAGE__->_error(
482 0         0 %{$ex_munge_file_params_excess},
  0         0  
483             payload => {
484             parameters => \@all,
485             errors => \@rest,
486             understood => {
487             qw( $file ) => $file,
488             qw( $config ) => $config,
489             },
490             },
491             );
492             }
493              
494 9 50 33     59 if ( not $file or not $file->can('content') ) {
495             __PACKAGE__->_error(
496 0         0 %{$ex_munge_file_param_file_bad},
  0         0  
497             payload => {
498             parameter_no => 0,
499             expects => [qw[ defined ->can(content) ]],
500             got => $file,
501             },
502             );
503             }
504              
505 9 50 33     33 if ( not ref $config or not 'HASH' eq ref $config ) {
506 0         0 __PACKAGE__->_error(
507             message => 'munge_file must be passed a HashReference for parameter 1',
508             payload => {
509             parameter_no => 1,
510             expects => [qw[ defined ref Hash ]],
511             got => $file,
512             },
513             id => 'munge_file_param_config_bad',
514             tags => [qw( parameters config bad mismatch invalid )],
515             );
516             }
517              
518 9 50 33     42 if ( not exists $config->{via} or not defined $config->{via} or not 'CODE' eq ref $config->{via} ) {
      33        
519             __PACKAGE__->_error(
520             message => 'munge_file must be passed a subroutine in the configuration hash as \'via\'',
521             payload => {
522             parameter_name => 'via',
523             expects => [qw[ exists defined ref Code ]],
524             got => $config->{via},
525             },
526 0         0 id => 'munge_file_config_via_bad',
527             tags => [qw( parameters config via bad mismatch invalid )],
528             );
529             }
530              
531 9 50 66     41 if (
      66        
532             exists $config->{lazy}
533             and not( ( not defined $config->{lazy} )
534             or ( 0 == $config->{lazy} )
535             or ( 1 == $config->{lazy} ) )
536             )
537             {
538             __PACKAGE__->_error(
539             message => 'munge_file configuration value \'lazy\' must be un-set, undef, 0 or 1',
540             payload => {
541             parameter_name => 'lazy',
542             expects_one => [qw[ unset undef 0 1 ]],
543             got => $config->{lazy},
544             },
545 0         0 id => 'munge_file_config_lazy_bad',
546             tags => [qw( parameters config lazy bad mismatch invalid )],
547             );
548             }
549              
550             # This codeblock exists for permitting one or more forms of "native" munging.
551             # Presently undocumented as the underlying support is still non-existent.
552             #
553             # There is only presently one supported option
554             # { native => "filemungeapi" }
555             # which will call the ->munge method on the file instance
556             # using the form currently defined by this pull request:
557             #
558             # https://github.com/rjbs/dist-zilla/pull/24
559             #
560             # This allows for per-file custom class methods for defining exactly how munge is performed
561             # but presently lacks passing arbitrary munge control flags ( ie: forced lazy etc )
562             #
563             # If it doesn't look like the file in question conforms to the requested munge api,
564             # then it falls back to traditional dzil.
565             #
566             # An object with a ->code method is assumed to be from code,
567             #
568             # and everything else is assumed to be in-memory scalars.
569             #
570 9 50 33     20 if ( exists $config->{native} and defined $config->{native} ) {
571 0 0       0 if ( 'filemungeapi' eq $config->{native} ) { # The API as proposed by Kentnl
572 0 0       0 if ( $file->can('munge') ) {
573 0         0 return $file->munge( $config->{via} );
574             }
575             }
576             }
577 9 100       29 if ( $file->can('code') ) {
578 4         10 return _fromcode_munge( $file, $config );
579             }
580 5         12 return _scalar_munge( $file, $config );
581             }
582              
583              
584              
585              
586              
587              
588              
589              
590              
591              
592              
593              
594              
595              
596              
597              
598              
599              
600              
601              
602              
603              
604              
605              
606              
607              
608              
609              
610              
611              
612              
613              
614              
615              
616             sub munge_files {
617 0     0 1   my ( $array, $config ) = @_;
618 0           for my $file ( @{$array} ) {
  0            
619 0 0         return unless munge_file( $file, $config );
620             }
621 0           return 1;
622             }
623              
624             sub _error {
625 0     0     my ( undef, %config ) = @_;
626 0           require Carp;
627 0           return Carp::croak( $config{message} );
628             }
629              
630             1;
631              
632             __END__
633              
634             =pod
635              
636             =encoding UTF-8
637              
638             =head1 NAME
639              
640             Dist::Zilla::Util::SimpleMunge - Make munging File::FromCode and File::InMemory easier.
641              
642             =head1 VERSION
643              
644             version 1.000002
645              
646             =head1 SYNOPSIS
647              
648             use Dist::Zilla::Util::SimpleMunge qw( auto_munge_file );
649             ...;
650              
651             sub somesub {
652             ...;
653              
654             next if $file->is_bytes;
655              
656             if ( $file->can('code') ) {
657              
658             auto_munge_file $file_from_zilla, sub {
659             my ( $file, $content , $encoding ) = @_;
660             return $mangled if $encoding ne 'text'; # bytes or text
661             ... mangle $content here ...;
662             return $mangled;
663             };
664             }
665              
666             =head1 DESCRIPTION
667              
668             =head2 NOTE: STOPGAP
669              
670             This module is mostly a stopgap and a implementation experiment in lieu of something better in Dist::Zilla eventually transpiring.
671              
672             =head2 BASIC USAGE
673              
674             Munging files in Dist::Zilla can be a pain.
675              
676             Its mostly the same:
677              
678             $file->content( substr( $file->content, 0, 10 ) ); # etc.
679              
680             Except when you come to C<CodeRef>s, that all changes.
681              
682             my $orig_code = $file->code();
683             $file->code( sub {
684             $file->$orig_code() =~ s/foo/bar/
685             });
686              
687             Which quickly gets messy.
688              
689             So this module is as simple as I think I can get it without hacking Dist::Zilla directly.
690              
691             auto_munge_file $file, sub {
692             my ( $thefile, $content, $encoding ) = @_;
693             };
694              
695             The callback will be called as appropriate.
696              
697             =over 4
698              
699             =item * C<$content> will contain the content, I<decoded if possible>
700              
701             =item * C<$encoding> will be either C<text> or C<bytes>, the latter if decoding is not possible.
702              
703             =item * C<InMemory> will apply the code immediately
704              
705             =item * C<FromCode> will take your code and create a chained system so your code will be evaluated when the file itself is written out.
706              
707             =back
708              
709             And this is the most useful and straight forward interface that doesn't invoke any weird re-blessing magic.
710              
711             =head2 ADVANCED USAGE
712              
713             There are a few less simple utilities that may also prove useful.
714              
715             =over 4
716              
717             =item * L<< C<munge_InMemory>|/munge_InMemory >> - trusts you know what you're dealing with and munges an C<InMemory> instance via the callback.
718              
719             =item * L<< C<munge_FromCode>|/munge_FromCode >> - trusts you when you say you have a C<FromCode>, and munges with C<CodeRef> chaining.
720              
721             =item * L<< C<inplace_replace>|/inplace_replace >> - A bit of magic to replace an object in-place without modifying any containers that point to it and without changing the reference address.
722              
723             =item * L<< C<to_InMemory>|/to_InMemory >> - returns a C<FromCode> represented as a new C<InMemory> object.
724              
725             =item * L<< C<to_FromCode>|/to_FromCode >> - returns an C<InMemory> represented as a new C<FromCode> object.
726              
727             =item * L<< C<inplace_to_InMemory>|/inplace_to_InMemory >> - like C<to_InMemory>, but replaces the object in-place.
728              
729             =item * L<< C<inplace_to_FromCode>|/inplace_to_FromCode >> - like C<to_FromCode>, but replaces the object in-place.
730              
731             =item * L<< C<munge_file>|/munge_file >> - combines all of the above behaviors based on configuration values.
732              
733             =item * L<< C<munge_files>|/munge_files >> - applies a single configuration and callback to a collection of files.
734              
735             =back
736              
737             =head1 FUNCTIONS
738              
739             =head2 C<auto_munge_file>
740              
741             # auto_munge_file ( $FILE, $CODEREF )
742              
743             auto_munge_file( $zilla_file, sub {
744             my ( $file, $content, $encoding ) = @_;
745             return $new_content # must still be in form $encoding
746             });
747              
748             =head2 C<to_InMemory>
749              
750             Given a C<FromCode>, return an equivalent C<InMemory> file, flattening the callback
751             in the process into simply a string.
752              
753             my $in_memory = to_InMemory( $from_code );
754              
755             =head2 C<to_FromCode>
756              
757             Given a C<InMemory> or C<OnDisk>, return an equivalent C<FromCode> file, converting the content into a callback that yields that content.
758              
759             my $from_code = to_FromCode( $in_memory_or_from_disk );
760              
761             =head2 C<munge_InMemory>
762              
763             Munge an C<InMemory> ( or similar ) item using a callback.
764              
765             munge_InMemory( $xfile, sub {
766             my ( $file, $content, $encoding ) = @_;
767             ...
768             return $content;
769             });
770              
771             This munging is applied immediately.
772              
773             =head2 C<munge_FromCode>
774              
775             Munge a C<FromCode> object by replacing the C<CodeRef> with a new one that yields the former.
776              
777             munge_FromCode( $xfile, sub {
778             my ( $file, $content, $encoding ) = @_;
779              
780             $content =~ s/foo/bar/;
781              
782             return $content;
783             });
784              
785             Note: this code is equivalent to:
786              
787             my $orig_code = $xfile->code;
788             my $encoding = $xfile->core_return_type;
789             $xfile->code( sub {
790              
791             my $content = $xfile->$orig_code();
792              
793             $content =~ s/a/b/;
794              
795             return $content;
796             });
797              
798             =head2 C<inplace_replace>
799              
800             This is a rather nasty way to replace an Object in place without breaking references held on it.
801              
802             Consider:
803              
804             source = ADDR=0x015 = data = { x => y }
805             = class = Foo
806              
807             target = ADDR=0x017 = data = { z => a }
808             = class = Bar
809              
810             array = ADDR=0x016 = data = [ 0x015 ]
811              
812             Then:
813              
814             delete source->{x}
815             source->{z} = target->{z}
816             bless source, 'Bar'
817              
818             This should result in:
819              
820             source = ADDR=0x015 = data = { z => a }
821             = class = Bar
822              
823             target = ADDR=0x017 = data = { z => a }
824             = class = Bar
825              
826             array = ADDR=0x016 = data = [ 0x015 ]
827              
828             Yes, this is rather nasty to do this, but no good alternatives at the moment :).
829              
830             inplace_replace( $original_object, $replacement_object );
831              
832             This will mirror all the keys from C<$replacement_object> to C<$original_object>, and subsequently
833             ensure C<$original_object> is C<reblessed> into the class of C<$replacement_object>
834              
835             =head2 C<inplace_to_FromCode>
836              
837             Shorthand for
838              
839             inplace_replace( $file, to_FromCode($file) );
840              
841             =head2 C<inplace_to_InMemory>
842              
843             Shorthand for
844              
845             inplace_replace( $file, to_InMemory($file) );
846              
847             =head2 C<munge_file>
848              
849             # munge_file ( $FILE , \%CONFIGURATION )
850              
851             munge_file(
852             $zilla_file,
853             {
854             via => sub { ... },
855             lazy => $laziness
856             }
857             );
858              
859             =head4 $FILE
860              
861             A L<< C<::Role::File> |Dist::Zilla::Role::File >> object to munge.
862              
863             =head4 %CONFIGURATION
864              
865             {
866             via => $CODEREF,
867             lazy => $LAZINESS,
868             }
869              
870             =head4 $CODEREF
871              
872             Called to munge the file itself.
873              
874             Passed a reference to the L<< C<::Role::File> |Dist::Zilla::Role::File >> instance, and a scalar containing
875             the contents of that file.
876              
877             Return new content for the file via C<return>
878              
879             sub {
880             my ( $file, $content ) = @_ ;
881             ...;
882             return $newcontent;
883             }
884              
885             =head4 $LAZINESS
886              
887             Specify how lazy you want the munge to be performed. Normally, what this is set to is dependent on the type of file
888             being munged.
889              
890             $LAZINESS = undef ; # use default for the file type
891             $LAZINESS = 0 ; # Munge immediately
892             $LAZINESS = 1 ; # Defer munging till as late as possible.
893              
894             For things that are normally backed by scalar values, such as L<< C<::File::OnDisk> |Dist::Zilla::File::OnDisk >> and
895             L<< C<::File::InMemory> |Dist::Zilla::File::InMemory >>, the laziness is equivalent to C< $LAZINESS = 0 >, which is not lazy at all, and
896             munges the file content immediately.
897              
898             For things backed by code, such as L<< C<::File::FromCode> |Dist::Zilla::File::FromCode >>, munging defaults to C< $LAZINESS = 1 >, where the
899             actual munging sub you specify is executed as late as possible.
900              
901             You can specify the C< $LAZINESS > value explicitly if you want to customize the behavior, i.e.: Make something that
902             is presently a scalar type get munged as late as possible ( converting the file into a C<FromCode> file ), or make
903             something currently backed by code get munged "now", ( converting the file into a C<InMemory> file )
904              
905             =head2 munge_files
906              
907             This is mostly a convenience utility for munging a lot of files without having to hand-code the looping logic.
908              
909             It basically just proxies for L</munge_file>.
910              
911             # munge_files ( \@FILEARRAY , \%CONFIGURATION )
912              
913             munge_files( [ $zilla_file_one, $zilla_file_two, ], {
914             via => sub { ... },
915             lazy => $laziness,
916             });
917              
918             =head4 @FILEARRAY
919              
920             An C<ArrayRef> of L</$FILE>
921              
922             =head4 See Also
923              
924             =over 4
925              
926             =item * L</%CONFIGURATION>
927              
928             =item * L</$CODEREF>
929              
930             =item * L</$FILE>
931              
932             =item * L</$LAZINESS>
933              
934             =back
935              
936             =head1 AUTHOR
937              
938             Kent Fredric <kentnl@cpan.org>
939              
940             =head1 COPYRIGHT AND LICENSE
941              
942             This software is copyright (c) 2017 by Kent Fredric <kentnl@cpan.org>.
943              
944             This is free software; you can redistribute it and/or modify it under
945             the same terms as the Perl 5 programming language system itself.
946              
947             =cut