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   3307 use strict;
  1         4  
  1         50  
3 1     1   6 use warnings;
  1         2  
  1         35  
4 1     1   1127 use PPI;
  1         228028  
  1         43  
5 1     1   13 use Path::Class;
  1         2  
  1         82  
6 1     1   568 use App::PRT::Command::ReplaceToken;
  1         4  
  1         40  
7 1     1   509 use App::PRT::Command::AddUse;
  1         2  
  1         34  
8 1     1   487 use App::PRT::Command::AddMethod;
  1         3  
  1         31  
9 1     1   436 use App::PRT::Command::DeleteMethod;
  1         2  
  1         30  
10 1     1   410 use App::PRT::Util::DestinationFile;
  1         3  
  1         1294  
11              
12             sub new {
13 12     12 0 68887 my ($class) = @_;
14 12         131 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 77 my ($self, @arguments) = @_;
29              
30 2 100       28 die "source and destination are required" unless @arguments >= 2;
31              
32 1         5 $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 646 my ($self, $source, $destination) = @_;
44              
45 11 100       171 die 'invalid format' unless $source =~ qr/\A[^#]+#[^#]+\Z/;
46 9 50       44 die 'invalid format' unless $destination;
47              
48 9         75 ($self->{source_class_name}, $self->{source_method_name}) = split '#', $source;
49 9         42 ($self->{destination_class_name}, $self->{destination_method_name}) = split '#', $destination;
50 9   66     57 $self->{destination_method_name} //= $self->{source_method_name};
51             }
52              
53             sub source_class_name {
54 24     24 0 3342 my ($self) = @_;
55              
56 24         134 $self->{source_class_name};
57             }
58              
59             sub source_method_name {
60 24     24 0 49 my ($self) = @_;
61              
62 24         125 $self->{source_method_name};
63             }
64              
65             sub destination_class_name {
66 28     28 0 52 my ($self) = @_;
67              
68 28         247 $self->{destination_class_name};
69             }
70              
71             sub destination_method_name {
72 21     21 0 119 my ($self) = @_;
73              
74 21         163 $self->{destination_method_name};
75             }
76              
77             sub source_method_body {
78 1     1 0 2605 my ($self) = @_;
79              
80 1         7 $self->{source_method_body};
81             }
82              
83             sub destination_method_body {
84 5     5 0 11 my ($self) = @_;
85              
86 5         45 my $document = PPI::Document->new(\$self->{source_method_body});
87 5         25893 my $sub = $document->find_first('PPI::Statement::Sub');
88 5         1439 $sub->schild(1)->set_content($self->destination_method_name);
89 5         40 $document->content;
90             }
91              
92             # refactor a file
93             # argumensts:
94             # $file: filename for refactoring
95             sub execute {
96 9     9 0 12726 my ($self, $file) = @_;
97              
98 9         41 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         7112 my $document = PPI::Document->new($file);
106              
107             # When parse failed
108 9 100       64801 return unless $document;
109              
110 8         52 my $method_body = $self->_try_delete_method_and_get_deleted_code($file);
111 8 100       1672 if ($method_body) {
112 4         16 $self->{source_method_body} = $method_body;
113              
114             # replace $class->$method
115 4         24 $self->_try_replace_tokens_in_target_class($file);
116              
117 4         3452 $document = PPI::Document->new($file);
118 4         28912 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       1346 unless (-e $destination_file) {
125             # prepare destination file
126 3         13 my $fh = file($destination_file)->open('w');
127 3         1455 print $fh <
128 3         12 package @{[ $self->destination_class_name ]};
129              
130             1;
131             CODE
132 3         29 $fh->close;
133             }
134              
135             {
136             # move method
137 4         234 my $command = App::PRT::Command::AddMethod->new;
  4         57  
138 4         21 $command->register($self->destination_method_body);
139 4         17 $command->execute($destination_file);
140             }
141              
142             # copy including modules
143 4         6383 my $packages_in_source = [ map { $_->module } @{ $document->find('PPI::Statement::Include') } ];
  14         10321  
  4         20  
144 4         107 for my $package (@$packages_in_source) {
145 14         12576 my $command = App::PRT::Command::AddUse->new;
146 14         67 $command->register($package);
147 14         63 $command->execute($destination_file);
148             }
149 4         7322 1;
150             }
151              
152 8         33 $replaced;
153             }
154              
155             sub _try_replace_tokens {
156 9     9   21 my ($self, $file) = @_;
157              
158 9         100 my $command = App::PRT::Command::ReplaceToken->new;
159 9         25 $command->register(
160 9         28 "@{[ $self->source_class_name ]}->@{[ $self->source_method_name ]}",
  9         33  
161 9         32 "@{[ $self->destination_class_name ]}->@{[ $self->destination_method_name ]}"
  9         33  
162             );
163 9 100       67 if ($command->execute($file)) {
164 4         4847 $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         70 my $command = App::PRT::Command::ReplaceToken->new;
172 4         12 $command->register(
173 4         22 "\$class->@{[ $self->source_method_name ]}",
174 4         17 "@{[ $self->destination_class_name ]}->@{[ $self->destination_method_name ]}"
  4         16  
175             );
176 4 100       29 if ($command->execute($file)) {
177 1         1772 $self->_try_add_use($file);
178             }
179             }
180              
181             sub _try_add_use {
182 5     5   13 my ($self, $file) = @_;
183              
184 5         64 my $command = App::PRT::Command::AddUse->new;
185 5         22 $command->register($self->destination_class_name);
186 5         20 $command->execute($file);
187             }
188              
189             sub _try_delete_method_and_get_deleted_code {
190 8     8   18 my ($self, $file) = @_;
191              
192 8         99 my $command = App::PRT::Command::DeleteMethod->new;
193 8         36 $command->register($self->source_class_name, $self->source_method_name);
194 8 100       45 if ($command->execute($file)) {
195 4         7582 return $command->deleted_code;
196             }
197             }
198              
199             1;