File Coverage

lib/App/PRT/Command/MoveClassMethod.pm
Criterion Covered Total %
statement 108 108 100.0
branch 17 18 94.4
condition 2 3 66.6
subroutine 23 23 100.0
pod 0 10 0.0
total 150 162 92.5


line stmt bran cond sub pod time code
1             package App::PRT::Command::MoveClassMethod;
2 1     1   3257 use strict;
  1         2  
  1         32  
3 1     1   4 use warnings;
  1         2  
  1         27  
4 1     1   531 use PPI;
  1         109041  
  1         54  
5 1     1   10 use Path::Class;
  1         3  
  1         71  
6 1     1   426 use App::PRT::Command::ReplaceToken;
  1         3  
  1         40  
7 1     1   404 use App::PRT::Command::AddUse;
  1         3  
  1         42  
8 1     1   355 use App::PRT::Command::AddMethod;
  1         3  
  1         37  
9 1     1   374 use App::PRT::Command::DeleteMethod;
  1         3  
  1         37  
10 1     1   346 use App::PRT::Util::DestinationFile;
  1         2  
  1         1213  
11              
12             sub new {
13 12     12 0 65633 my ($class) = @_;
14 12         91 bless {
15             source_class_name => undef,
16             source_method_name => undef,
17             destination_class_name => undef,
18             destination_method_name => undef,
19             }, $class;
20             }
21              
22             # parse arguments from CLI
23             # arguments:
24             # @arguments
25             # returns:
26             # @rest_arguments
27             sub parse_arguments {
28 2     2 0 112 my ($self, @arguments) = @_;
29              
30 2 100       23 die "source and destination are required" unless @arguments >= 2;
31              
32 1         6 $self->register(shift @arguments => shift @arguments);
33              
34 1         4 @arguments;
35             }
36              
37              
38             # register a replacing rule
39             # arguments:
40             # $source: source class name and method name, joined by '#'
41             # $dest: destination class name and method name, joined by '#'
42             sub register {
43 11     11 0 624 my ($self, $source, $destination) = @_;
44              
45 11 100       174 die 'invalid format' unless $source =~ qr/\A[^#]+#[^#]+\Z/;
46 9 50       35 die 'invalid format' unless $destination;
47              
48 9         52 ($self->{source_class_name}, $self->{source_method_name}) = split '#', $source;
49 9         40 ($self->{destination_class_name}, $self->{destination_method_name}) = split '#', $destination;
50 9   66     43 $self->{destination_method_name} //= $self->{source_method_name};
51             }
52              
53             sub source_class_name {
54 24     24 0 78 my ($self) = @_;
55              
56 24         123 $self->{source_class_name};
57             }
58              
59             sub source_method_name {
60 24     24 0 56 my ($self) = @_;
61              
62 24         123 $self->{source_method_name};
63             }
64              
65             sub destination_class_name {
66 28     28 0 164 my ($self) = @_;
67              
68 28         205 $self->{destination_class_name};
69             }
70              
71             sub destination_method_name {
72 21     21 0 140 my ($self) = @_;
73              
74 21         129 $self->{destination_method_name};
75             }
76              
77             sub source_method_body {
78 1     1 0 2145 my ($self) = @_;
79              
80 1         7 $self->{source_method_body};
81             }
82              
83             sub destination_method_body {
84 5     5 0 17 my ($self) = @_;
85              
86 5         35 my $document = PPI::Document->new(\$self->{source_method_body});
87 5         22520 my $sub = $document->find_first('PPI::Statement::Sub');
88 5         1599 $sub->schild(1)->set_content($self->destination_method_name);
89 5         32 $document->content;
90             }
91              
92             # refactor a file
93             # argumensts:
94             # $file: filename for refactoring
95             sub execute {
96 9     9 0 15294 my ($self, $file) = @_;
97              
98 9         56 my $replaced = $self->_try_replace_tokens($file);
99              
100             # TODO:
101             # - Move comment before method?
102             # - Move pod?
103             # - Move test code?
104              
105 9         6513 my $document = PPI::Document->new($file);
106              
107             # When parse failed
108 9 100       80163 return unless $document;
109              
110 8         40 my $method_body = $self->_try_delete_method_and_get_deleted_code($file);
111 8 100       1410 if ($method_body) {
112 4         18 $self->{source_method_body} = $method_body;
113              
114             # replace $class->$method
115 4         17 $self->_try_replace_tokens_in_target_class($file);
116              
117 4         2942 $document = PPI::Document->new($file);
118 4         36503 my $destination_file = App::PRT::Util::DestinationFile::destination_file(
119             $self->source_class_name,
120             $self->destination_class_name,
121             $file
122             );
123              
124 4 100       689 unless (-e $destination_file) {
125             # prepare destination file
126 3         17 my $fh = file($destination_file)->open('w');
127 3         1158 print $fh <
128 3         11 package @{[ $self->destination_class_name ]};
129              
130             1;
131             CODE
132 3         23 $fh->close;
133             }
134              
135             {
136             # move method
137 4         578 my $command = App::PRT::Command::AddMethod->new;
  4         46  
138 4         19 $command->register($self->destination_method_body);
139 4         19 $command->execute($destination_file);
140             }
141              
142             # copy including modules
143 4         6030 my $packages_in_source = [ map { $_->module } @{ $document->find('PPI::Statement::Include') } ];
  14         7957  
  4         17  
144 4         101 for my $package (@$packages_in_source) {
145 14         11688 my $command = App::PRT::Command::AddUse->new;
146 14         70 $command->register($package);
147 14         41 $command->execute($destination_file);
148             }
149 4         6976 1;
150             }
151              
152 8         26 $replaced;
153             }
154              
155             sub _try_replace_tokens {
156 9     9   40 my ($self, $file) = @_;
157              
158 9         89 my $command = App::PRT::Command::ReplaceToken->new;
159 9         30 $command->register(
160 9         39 "@{[ $self->source_class_name ]}->@{[ $self->source_method_name ]}",
  9         32  
161 9         59 "@{[ $self->destination_class_name ]}->@{[ $self->destination_method_name ]}"
  9         27  
162             );
163 9 100       50 if ($command->execute($file)) {
164 4         4752 $self->_try_add_use($file);
165             }
166             }
167              
168             sub _try_replace_tokens_in_target_class {
169 4     4   12 my ($self, $file) = @_;
170              
171 4         53 my $command = App::PRT::Command::ReplaceToken->new;
172 4         11 $command->register(
173 4         17 "\$class->@{[ $self->source_method_name ]}",
174 4         16 "@{[ $self->destination_class_name ]}->@{[ $self->destination_method_name ]}"
  4         17  
175             );
176 4 100       21 if ($command->execute($file)) {
177 1         1647 $self->_try_add_use($file);
178             }
179             }
180              
181             sub _try_add_use {
182 5     5   16 my ($self, $file) = @_;
183              
184 5         52 my $command = App::PRT::Command::AddUse->new;
185 5         22 $command->register($self->destination_class_name);
186 5         18 $command->execute($file);
187             }
188              
189             sub _try_delete_method_and_get_deleted_code {
190 8     8   25 my ($self, $file) = @_;
191              
192 8         86 my $command = App::PRT::Command::DeleteMethod->new;
193 8         39 $command->register($self->source_class_name, $self->source_method_name);
194 8 100       34 if ($command->execute($file)) {
195 4         7173 return $command->deleted_code;
196             }
197             }
198              
199             1;