File Coverage

lib/Dist/Zilla/App/Command/critic.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1 1     1   561 use 5.006;
  1         3  
  1         30  
2 1     1   4 use strict;
  1         1  
  1         22  
3 1     1   3 use warnings;
  1         10  
  1         52  
4              
5             package Dist::Zilla::App::Command::critic;
6              
7             our $VERSION = '0.001004';
8              
9             # ABSTRACT: build your dist and run Perl::Critic on the built files.
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 1     1   208 use Dist::Zilla::App '-command';
  0            
  0            
14              
15              
16              
17              
18              
19              
20              
21              
22              
23              
24              
25              
26              
27              
28              
29              
30              
31              
32              
33              
34              
35              
36              
37              
38              
39              
40              
41              
42              
43              
44              
45              
46              
47              
48              
49              
50              
51              
52              
53              
54              
55             sub _print {
56             my ( $self, @message ) = @_;
57             print @message or $self->zilla->log_fatal('Cant write to STDOUT');
58             return;
59             }
60              
61             sub _colorize {
62             my ( undef, $string, $color ) = @_;
63             return $string if not defined $color;
64             return $string if q[] eq $color;
65              
66             # $terminator is a purely cosmetic change to make the color end at the end
67             # of the line rather than right before the next line. It is here because
68             # if you use background colors, some console windows display a little
69             # fragment of colored background before the next uncolored (or
70             # differently-colored) line.
71             my $terminator = chomp $string ? "\n" : q[];
72             return Term::ANSIColor::colored( $string, $color ) . $terminator;
73             }
74              
75             sub _colorize_by_severity {
76             my ( $self, $critic, @violations ) = @_;
77             return @violations if $^O =~ m/MSWin32/xms;
78             return @violations if not eval {
79             require Term::ANSIColor;
80             require Perl::Critic::Utils::Constants;
81             ## no critic (Variables::ProtectPrivateVars)
82             Term::ANSIColor->VERSION($Perl::Critic::Utils::Constants::_MODULE_VERSION_TERM_ANSICOLOR);
83             1;
84             };
85              
86             my $config = $critic->config();
87             require Perl::Critic::Utils;
88              
89             my %color_of = (
90             $Perl::Critic::Utils::SEVERITY_HIGHEST => $config->color_severity_highest(),
91             $Perl::Critic::Utils::SEVERITY_HIGH => $config->color_severity_high(),
92             $Perl::Critic::Utils::SEVERITY_MEDIUM => $config->color_severity_medium(),
93             $Perl::Critic::Utils::SEVERITY_LOW => $config->color_severity_low(),
94             $Perl::Critic::Utils::SEVERITY_LOWEST => $config->color_severity_lowest(),
95             );
96              
97             return map { $self->_colorize( "$_", $color_of{ $_->severity() } ) } @violations;
98              
99             }
100              
101             sub _report_file {
102             my ( $self, $critic, undef, $rpath, @violations ) = @_;
103              
104             if ( @violations > 0 ) {
105             $self->_print("\n");
106             }
107             $self->_print( sprintf "%s : %d violations\n", $rpath, scalar @violations );
108              
109             if ( @violations > 0 ) {
110             $self->_print("\n");
111             }
112             my $verbosity = $critic->config->verbose;
113             my $color = $critic->config->color();
114              
115             require Perl::Critic::Violation;
116             require Perl::Critic::Utils;
117              
118             ## no critic (Subroutines::ProhibitCallsToUnexportedSubs)
119             Perl::Critic::Violation::set_format( Perl::Critic::Utils::verbosity_to_format($verbosity) );
120              
121             if ( not $color ) {
122             $self->_print(@violations);
123             }
124             $self->_print( $self->_colorize_by_severity( $critic, @violations ) );
125             return;
126             }
127              
128             sub _critique_file {
129             my ( $self, $critic, $file, $rpath ) = @_;
130             Try::Tiny::try {
131             my @violations = $critic->critique("$file");
132             $self->_report_file( $critic, $file, $rpath, @violations );
133             }
134             Try::Tiny::catch {
135             $self->zilla->log($_); ## no critic (BuiltinFunctions::ProhibitUselessTopic)
136             };
137             return;
138             }
139              
140             sub _subdirs {
141             my ( undef, $root, @children ) = @_;
142             my @out;
143             for my $child (@children) {
144             my $path = $root->child($child);
145             next unless -d $path;
146             push @out, $path->stringify;
147             }
148             return @out;
149             }
150              
151             sub execute {
152             my ( $self, undef, undef ) = @_;
153              
154             my ( $target, undef ) = $self->zilla->ensure_built_in_tmpdir;
155              
156             my $critic_config = 'perlcritic.rc';
157              
158             for my $plugin ( @{ $self->zilla->plugins } ) {
159             next unless $plugin->isa('Dist::Zilla::Plugin::Test::Perl::Critic');
160             $critic_config = $plugin->critic_config if $plugin->critic_config;
161             }
162              
163             require Path::Tiny;
164             require Try::Tiny;
165              
166             my $path = Path::Tiny::path($target);
167              
168             require Perl::Critic;
169             require Perl::Critic::Utils;
170              
171             my $critic = Perl::Critic->new( -profile => $path->child($critic_config)->stringify );
172              
173             $critic->policies();
174              
175             ## no critic (Subroutines::ProhibitCallsToUnexportedSubs)
176             my @files = Perl::Critic::Utils::all_perl_files( $self->_subdirs( $path, qw( lib bin script ) ) );
177              
178             for my $file (@files) {
179             my $rpath = Path::Tiny::path($file)->relative($path);
180             $self->_critique_file( $critic, $file, $rpath );
181             }
182             return 0;
183             }
184              
185             1;
186              
187             __END__
188              
189             =pod
190              
191             =encoding UTF-8
192              
193             =head1 NAME
194              
195             Dist::Zilla::App::Command::critic - build your dist and run Perl::Critic on the built files.
196              
197             =head1 VERSION
198              
199             version 0.001004
200              
201             =head1 DESCRIPTION
202              
203             I have a hard time understanding the output of C<[Test::Perl::Critic]>, its rather hard to read and is needlessly coated in cruft
204             due to having to run through the C<Test::> framework.
205              
206             It also discards a few preferences from C<perlcritic.rc> such as those that emit color codes.
207              
208             Again, conflated by the need to run through the test framework.
209              
210             I also don't necessarily want to make the tests pass just to release.
211              
212             And I also don't necessarily want to run all the other tests just to test critic.
213              
214             I<TL;DR>
215              
216             dzil critic
217              
218             ~ Happyness ~
219              
220             The result will be similar to doing:
221              
222             dzil run --no-build perlcritic -p perlcritic.rc lib/
223              
224             Except that is useless to me because it doesn't output the file names anywhere unless you have a verbosity level that incorporates
225             a file name in I<EACH> violation, which for me, is undesirable clutter when you have 20 violations in a single file. ( And the most
226             L<< verbose violation levels|perlcritic/verbose-N-FORMAT >>, that is, all except C<1,2,3,5,7> lack C<%f> )
227              
228             =head1 CONFIGURATION
229              
230             This module has little configuration at this point.
231              
232             C<perlcritic.rc> is the name of the default profile to use, and it must be in your I<BUILT> tree to be used.
233              
234             Alternatively, I<IF> you are using C<[Test::Perl::Critic]> in your dist, the path specified to C<perlcritic.rc> in that module
235             will be used.
236              
237             =head1 AUTHOR
238              
239             Kent Fredric <kentnl@cpan.org>
240              
241             =head1 COPYRIGHT AND LICENSE
242              
243             This software is copyright (c) 2014 by Kent Fredric <kentfredric@gmail.com>.
244              
245             This is free software; you can redistribute it and/or modify it under
246             the same terms as the Perl 5 programming language system itself.
247              
248             =cut