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   1595 use strict;
  1         1  
  1         23  
3 1     1   3 use warnings;
  1         1  
  1         21  
4 1     1   427 use PPI;
  1         82810  
  1         35  
5 1     1   6 use Path::Class;
  1         2  
  1         49  
6 1     1   268 use App::PRT::Command::ReplaceToken;
  1         1  
  1         24  
7 1     1   238 use App::PRT::Command::AddUse;
  1         1  
  1         22  
8 1     1   257 use App::PRT::Command::AddMethod;
  1         1  
  1         24  
9 1     1   258 use App::PRT::Command::DeleteMethod;
  1         2  
  1         22  
10 1     1   239 use App::PRT::Util::DestinationFile;
  1         2  
  1         742  
11              
12             sub new {
13 12     12 0 167509 my ($class) = @_;
14 12         77 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 58 my ($self, @arguments) = @_;
29              
30 2 100       17 die "source and destination are required" unless @arguments >= 2;
31              
32 1         4 $self->register(shift @arguments => shift @arguments);
33              
34 1         3 @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 385 my ($self, $source, $destination) = @_;
44              
45 11 100       134 die 'invalid format' unless $source =~ qr/\A[^#]+#[^#]+\Z/;
46 9 50       23 die 'invalid format' unless $destination;
47              
48 9         47 ($self->{source_class_name}, $self->{source_method_name}) = split '#', $source;
49 9         25 ($self->{destination_class_name}, $self->{destination_method_name}) = split '#', $destination;
50 9   66     37 $self->{destination_method_name} //= $self->{source_method_name};
51             }
52              
53             sub source_class_name {
54 24     24 0 1617 my ($self) = @_;
55              
56 24         102 $self->{source_class_name};
57             }
58              
59             sub source_method_name {
60 24     24 0 31 my ($self) = @_;
61              
62 24         93 $self->{source_method_name};
63             }
64              
65             sub destination_class_name {
66 28     28 0 43 my ($self) = @_;
67              
68 28         132 $self->{destination_class_name};
69             }
70              
71             sub destination_method_name {
72 21     21 0 80 my ($self) = @_;
73              
74 21         100 $self->{destination_method_name};
75             }
76              
77             sub source_method_body {
78 1     1 0 1291 my ($self) = @_;
79              
80 1         4 $self->{source_method_body};
81             }
82              
83             sub destination_method_body {
84 5     5 0 6 my ($self) = @_;
85              
86 5         29 my $document = PPI::Document->new(\$self->{source_method_body});
87 5         11245 my $sub = $document->find_first('PPI::Statement::Sub');
88 5         1088 $sub->schild(1)->set_content($self->destination_method_name);
89 5         24 $document->content;
90             }
91              
92             # refactor a file
93             # argumensts:
94             # $file: filename for refactoring
95             sub execute {
96 9     9 0 6811 my ($self, $file) = @_;
97              
98 9         27 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         3679 my $document = PPI::Document->new($file);
106              
107             # When parse failed
108 9 100       38935 return unless $document;
109              
110 8         33 my $method_body = $self->_try_delete_method_and_get_deleted_code($file);
111 8 100       807 if ($method_body) {
112 4         11 $self->{source_method_body} = $method_body;
113              
114             # replace $class->$method
115 4         11 $self->_try_replace_tokens_in_target_class($file);
116              
117 4         1520 $document = PPI::Document->new($file);
118 4         15888 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       470 unless (-e $destination_file) {
125             # prepare destination file
126 3         9 my $fh = file($destination_file)->open('w');
127 3         811 print $fh <
128 3         8 package @{[ $self->destination_class_name ]};
129              
130             1;
131             CODE
132 3         15 $fh->close;
133             }
134              
135             {
136             # move method
137 4         154 my $command = App::PRT::Command::AddMethod->new;
  4         34  
138 4         16 $command->register($self->destination_method_body);
139 4         7 $command->execute($destination_file);
140             }
141              
142             # copy including modules
143 4         3686 my $packages_in_source = [ map { $_->module } @{ $document->find('PPI::Statement::Include') } ];
  14         5458  
  4         12  
144 4         59 for my $package (@$packages_in_source) {
145 14         6391 my $command = App::PRT::Command::AddUse->new;
146 14         36 $command->register($package);
147 14         30 $command->execute($destination_file);
148             }
149 4         3543 1;
150             }
151              
152 8         17 $replaced;
153             }
154              
155             sub _try_replace_tokens {
156 9     9   14 my ($self, $file) = @_;
157              
158 9         74 my $command = App::PRT::Command::ReplaceToken->new;
159 9         22 $command->register(
160 9         24 "@{[ $self->source_class_name ]}->@{[ $self->source_method_name ]}",
  9         22  
161 9         21 "@{[ $self->destination_class_name ]}->@{[ $self->destination_method_name ]}"
  9         28  
162             );
163 9 100       46 if ($command->execute($file)) {
164 4         2553 $self->_try_add_use($file);
165             }
166             }
167              
168             sub _try_replace_tokens_in_target_class {
169 4     4   7 my ($self, $file) = @_;
170              
171 4         32 my $command = App::PRT::Command::ReplaceToken->new;
172 4         10 $command->register(
173 4         13 "\$class->@{[ $self->source_method_name ]}",
174 4         8 "@{[ $self->destination_class_name ]}->@{[ $self->destination_method_name ]}"
  4         12  
175             );
176 4 100       19 if ($command->execute($file)) {
177 1         688 $self->_try_add_use($file);
178             }
179             }
180              
181             sub _try_add_use {
182 5     5   9 my ($self, $file) = @_;
183              
184 5         41 my $command = App::PRT::Command::AddUse->new;
185 5         14 $command->register($self->destination_class_name);
186 5         16 $command->execute($file);
187             }
188              
189             sub _try_delete_method_and_get_deleted_code {
190 8     8   13 my ($self, $file) = @_;
191              
192 8         72 my $command = App::PRT::Command::DeleteMethod->new;
193 8         27 $command->register($self->source_class_name, $self->source_method_name);
194 8 100       24 if ($command->execute($file)) {
195 4         4008 return $command->deleted_code;
196             }
197             }
198              
199             1;