File Coverage

blib/lib/PPIx/Refactor.pm
Criterion Covered Total %
statement 27 31 87.1
branch 2 2 100.0
condition 0 2 0.0
subroutine 10 11 90.9
pod 2 2 100.0
total 41 48 85.4


line stmt bran cond sub pod time code
1             package PPIx::Refactor;
2             $PPIx::Refactor::VERSION = '0.10';
3              
4 2     2   30715 use Moo;
  2         19497  
  2         9  
5 2     2   3276 use Path::Tiny;
  2         19867  
  2         94  
6             BEGIN {
7 2     2   10 use File::Path;
  2         5  
  2         82  
8 2     2   6 use constant { CACHE => '/tmp/ppix-refactor_cache', };
  2         2  
  2         115  
9 2     2   4 our $cache = CACHE;
10 2 100       274 File::Path::mkpath($cache) unless -e CACHE;
11             }
12              
13 2     2   914 use PPI;
  2         175903  
  2         73  
14 2     2   749 use PPI::Cache path => CACHE;
  2         7241  
  2         8  
15 2     2   974 use PPI::Find;
  2         1362  
  2         512  
16              
17             =head1 NAME
18              
19             PPIx::Refactor - Hooks for refactoring perl via L
20              
21             =head1 SYNOPSIS
22              
23             use PPIx::Refactor;
24             my $p = PPIx::Refactor->new(file => '/path/to/perl/code/file.pl',
25             ppi_find => sub {
26             my ($elem, $doc) = @_;
27             return 1 if $elem->class eq 'PPI::Statement::Sub',
28             return 0;
29             }
30             [ writer => \&found ]);
31             my $finds = $p->finds; # for examining them interactively
32             $p->rewrite; # rewrites the file in place. You are using version control yes?
33              
34             =head1 SUMMARY
35              
36             This is a really simple module to make rewriting perl code via L
37             debugger friendly and easy. See the test in
38             L
39             of this distribution for a working example. Pretty much all the real work
40             happens in the coderef you set up in C<< $p->ppi_find >> and C<< $p->writer >>.
41              
42             For an example of a simple script for checking statements in code for being
43             syntactically identical (i.e. a crude copypasta detector) see C<
44             similar_statements.pl > in the examples directory of the distribution.
45              
46             NOTE L is used to store a cached representation of the source
47             parse in C
48              
49             =head2 RATIONALE
50              
51             Rewriting code via ppi is a fiddly pain. L provides a
52             minimal interface so you can concentrate on the fiddlyness and minimise the
53             pain.
54              
55             =head2 TODO
56              
57             Would be nice to specify a rewriter via roles, and it would be nice to have
58             $self in C<< $p->ppi_find >>. On the other hand rewrite/refactoring code like
59             this can either be simple throwaways, or really really complicated. This
60             code is so far optimised for the throwaway case.
61              
62             =cut
63              
64             =head2 ATTRIBUTES
65              
66             =head3 file
67              
68             required string that coerces into a Path::Tiny
69              
70             =cut
71              
72             has file => (
73             is => 'ro',
74             coerce => sub {
75             path($_[0]);
76             }
77             );
78              
79             =head3 doc
80              
81             lazily built PPI::Document
82              
83             =cut
84              
85             has doc => (
86             is => 'ro',
87             lazy => 1,
88             default => sub {
89             my ($self) = @_;
90             return PPI::Document::File->new($self->file->stringify);
91             },
92             );
93              
94             =head3 element
95              
96             If you're using prior finds (e.g. subroutines you're trying to analyse)
97             you'll want to pass an element into new rather than a doc. Element
98             defaults to the document you passed in.
99              
100             =cut
101              
102             has element => (
103             is => 'ro',
104             lazy => 1,
105             builder => sub {
106 2     2   33 $_[0]->doc;
107             },
108             );
109              
110             =head3 ppi_find
111              
112             required coderef with which to find the elements of interest
113              
114             =cut
115              
116             has ppi_find => (
117             is => 'ro',
118             # isa CodeRef
119             required => 1,
120             );
121              
122              
123             =head3 writer
124              
125             optional coderef with which to rewrite the code.
126              
127             =cut
128              
129             has writer => (
130             is => 'ro',
131             default => sub {},
132             );
133              
134             =head3 finds
135              
136             lazy built arrayref of all the elements of interest found
137              
138             =cut
139              
140             has finds => (
141             is => 'ro',
142             lazy => 1,
143             default => sub {
144             my ($self) = @_;
145             my $find = PPI::Find->new($self->ppi_find);
146             my @results = $find->in($self->element);
147             return \@results;
148             }
149             );
150              
151             =head1 METHODS
152              
153             =head2 $self->rewrite
154              
155             Worker sub that rewrites the code. Operates on what it finds in
156             C<<$self->finds>>
157              
158             =cut
159              
160             sub rewrite {
161 1     1 1 236 my ($self) = @_;
162 1         18 $self->writer->($self->finds);
163 1         35 $self->element->save($self->file);
164             }
165              
166             =head2 $self->dump($elem, $whitespace);
167              
168             For debugging. Prints a dump of the passed in element. If C<$whitespace>
169             is true it will include whitespace in the dump. Defaults to false.
170              
171             =cut
172              
173             sub dump {
174 0     0 1   my ($self, $doc, $whitespace) = @_;
175 0   0       $whitespace ||=0;
176 0           my $dump = PPI::Dumper->new($doc, whitespace => $whitespace);
177 0           $dump->print;
178             }
179              
180              
181             =head1 AUTHOR
182              
183             Kieren Diment, C<< >>
184              
185             =head1 BUGS
186              
187             Please report any bugs or feature requests via github:
188             L.
189              
190             =head1 SUPPORT
191              
192             Jump on to #web-simple on irc.perl.org
193              
194             =head1 ACKNOWLEDGEMENTS
195              
196              
197             =head1 LICENSE AND COPYRIGHT
198              
199             Copyright 2015 Kieren Diment.
200              
201             This program is free software; you can redistribute it and/or modify it
202             under the same terms as perl itself.
203             =cut
204              
205             1;