File Coverage

blib/lib/Dist/Zilla/App/Command/critic.pm
Criterion Covered Total %
statement 69 79 87.3
branch 9 24 37.5
condition n/a
subroutine 11 13 84.6
pod n/a
total 89 116 76.7


line stmt bran cond sub pod time code
1 2     2   131995 use 5.006;
  2         5  
  2         57  
2 2     2   10 use strict;
  2         1  
  2         46  
3 2     2   6 use warnings;
  2         7  
  2         99  
4              
5             package Dist::Zilla::App::Command::critic;
6              
7             our $VERSION = '0.001010';
8              
9             # ABSTRACT: build your dist and run Perl::Critic on the built files.
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 2     2   362 use Dist::Zilla::App '-command';
  2         40955  
  2         17  
14              
15             sub _print {
16 3     3   11 my ( $self, @message ) = @_;
17 3 50       106528 print @message or $self->zilla->log_fatal('Cant write to STDOUT');
18 3         17 return;
19             }
20              
21             sub _colorize {
22 0     0   0 my ( undef, $string, $color ) = @_;
23 0 0       0 return $string if not defined $color;
24 0 0       0 return $string if q[] eq $color;
25              
26             # $terminator is a purely cosmetic change to make the color end at the end
27             # of the line rather than right before the next line. It is here because
28             # if you use background colors, some console windows display a little
29             # fragment of colored background before the next uncolored (or
30             # differently-colored) line.
31 0 0       0 my $terminator = chomp $string ? "\n" : q[];
32 0         0 return Term::ANSIColor::colored( $string, $color ) . $terminator;
33             }
34              
35             sub _colorize_by_severity {
36 1     1   3 my ( $self, $critic, @violations ) = @_;
37 1 50       30 return @violations if $^O =~ m/MSWin32/xms;
38 1 50       3 return @violations if not eval {
39 1         7 require Term::ANSIColor;
40 1         5 require Perl::Critic::Utils::Constants;
41             ## no critic (Variables::ProtectPrivateVars)
42 1         34 Term::ANSIColor->VERSION($Perl::Critic::Utils::Constants::_MODULE_VERSION_TERM_ANSICOLOR);
43 1         8 1;
44             };
45              
46 1         5 my $config = $critic->config();
47 1         9 require Perl::Critic::Utils;
48              
49 1         6 my %color_of = (
50             $Perl::Critic::Utils::SEVERITY_HIGHEST => $config->color_severity_highest(),
51             $Perl::Critic::Utils::SEVERITY_HIGH => $config->color_severity_high(),
52             $Perl::Critic::Utils::SEVERITY_MEDIUM => $config->color_severity_medium(),
53             $Perl::Critic::Utils::SEVERITY_LOW => $config->color_severity_low(),
54             $Perl::Critic::Utils::SEVERITY_LOWEST => $config->color_severity_lowest(),
55             );
56              
57 1         30 return map { $self->_colorize( "$_", $color_of{ $_->severity() } ) } @violations;
  0         0  
58              
59             }
60              
61             sub _report_file {
62 1     1   3 my ( $self, $critic, undef, $rpath, @violations ) = @_;
63              
64 1 50       5 if ( @violations > 0 ) {
65 0         0 $self->_print("\n");
66             }
67 1         6 $self->_print( sprintf "%s : %d violations\n", $rpath, scalar @violations );
68              
69 1 50       10 if ( @violations > 0 ) {
70 0         0 $self->_print("\n");
71             }
72 1         22 my $verbosity = $critic->config->verbose;
73 1         24 my $color = $critic->config->color();
74              
75 1         24 require Perl::Critic::Violation;
76 1         5 require Perl::Critic::Utils;
77              
78             ## no critic (Subroutines::ProhibitCallsToUnexportedSubs)
79 1         8 Perl::Critic::Violation::set_format( Perl::Critic::Utils::verbosity_to_format($verbosity) );
80              
81 1 50       239 if ( not $color ) {
82 1         7 $self->_print(@violations);
83             }
84 1         4 $self->_print( $self->_colorize_by_severity( $critic, @violations ) );
85 1         5 return;
86             }
87              
88             sub _critique_file {
89 1     1   2 my ( $self, $critic, $file, $rpath ) = @_;
90             Try::Tiny::try {
91 1     1   38 my @violations = $critic->critique("$file");
92 1         5604 $self->_report_file( $critic, $file, $rpath, @violations );
93             }
94             Try::Tiny::catch {
95 0     0   0 $self->zilla->log($_); ## no critic (BuiltinFunctions::ProhibitUselessTopic)
96 1         8 };
97 1         37 return;
98             }
99              
100             sub _subdirs {
101 1     1   2 my ( undef, $root, @children ) = @_;
102 1         1 my @out;
103 1         3 for my $child (@children) {
104 3         34 my $path = $root->child($child);
105 3 100       71 next unless -d $path;
106 1         34 push @out, $path->stringify;
107             }
108 1         15 return @out;
109             }
110              
111             sub execute {
112 1     1   32138 my ( $self, undef, undef ) = @_;
113              
114 1         8 my ( $target, undef ) = $self->zilla->ensure_built_in_tmpdir;
115              
116 1         601738 my $critic_config = 'perlcritic.rc';
117              
118 1         2 for my $plugin ( @{ $self->zilla->plugins } ) {
  1         6  
119 9 50       68 next unless $plugin->isa('Dist::Zilla::Plugin::Test::Perl::Critic');
120 0 0       0 $critic_config = $plugin->critic_config if $plugin->critic_config;
121             }
122              
123 1         6 require Path::Tiny;
124 1         4 require Try::Tiny;
125              
126 1         3 my $path = Path::Tiny::path($target);
127              
128 1         593 require Perl::Critic;
129 1         915477 require Perl::Critic::Utils;
130              
131 1         8 my $critic = Perl::Critic->new( -profile => $path->child($critic_config)->stringify );
132              
133 1         353358 $critic->policies();
134              
135             ## no critic (Subroutines::ProhibitCallsToUnexportedSubs)
136 1         14 my @files = Perl::Critic::Utils::all_perl_files( $self->_subdirs( $path, qw( lib bin script ) ) );
137              
138 1         195 for my $file (@files) {
139 1         4 my $rpath = Path::Tiny::path($file)->relative($path);
140 1         113 $self->_critique_file( $critic, $file, $rpath );
141             }
142 1         438 return 0;
143             }
144              
145             1;
146              
147             __END__
148              
149             =pod
150              
151             =encoding UTF-8
152              
153             =head1 NAME
154              
155             Dist::Zilla::App::Command::critic - build your dist and run Perl::Critic on the built files.
156              
157             =head1 VERSION
158              
159             version 0.001010
160              
161             =head1 DESCRIPTION
162              
163             C<critic> is an C<App::Command> for L<< C<Dist::Zilla>|Dist::Zilla >> which streamlines running
164             L<< C<Perl::Critic>|Perl::Critic >> on your built distribution.
165              
166             This competes with the likes of L<< C<[Test::Perl::Critic]>|Dist::Zilla::Plugin::Test::Perl::Critic >>
167             by:
168              
169             =over 4
170              
171             =item * not requiring the rest of the steps in the test life-cycle to execute.
172              
173             =item * not being impeded by the other tests cluttering your output.
174              
175             =item * not suffering the limitations of C<Test::Perl::Critic> which discards profile color settings.
176              
177             =item * carefully formatting output to give a clearer visualization of where failures lie.
178              
179             =item * not requiring your dist have a C<Test::Perl::Critic> test pass for release.
180              
181             =item * not requiring your dist to have any explicit C<Perl::Critic> consumption.
182              
183             =back
184              
185             Behaviorally:
186              
187             dzil critic
188              
189             Behaves very similar to:
190              
191             dzil run --no-build perlcritic -p perlcritic.rc lib/
192              
193             Except with improved verbosity of file name reporting.
194              
195             =head1 CONFIGURATION
196              
197             This module has little configuration at this point.
198              
199             C<perlcritic.rc> is the name of the default profile to use, and it must be in your I<BUILT> tree to be used.
200              
201             Alternatively, I<IF> you are using C<[Test::Perl::Critic]> in your dist, the path specified to C<perlcritic.rc> in that module
202             will be used.
203              
204             =head1 AUTHOR
205              
206             Kent Fredric <kentnl@cpan.org>
207              
208             =head1 COPYRIGHT AND LICENSE
209              
210             This software is copyright (c) 2015 by Kent Fredric <kentfredric@gmail.com>.
211              
212             This is free software; you can redistribute it and/or modify it under
213             the same terms as the Perl 5 programming language system itself.
214              
215             =cut