File Coverage

blib/lib/Code/TidyAll/Role/RunsCommand.pm
Criterion Covered Total %
statement 48 48 100.0
branch 4 6 66.6
condition 1 3 33.3
subroutine 14 14 100.0
pod n/a
total 67 71 94.3


line stmt bran cond sub pod time code
1             package Code::TidyAll::Role::RunsCommand;
2              
3 4     4   2170 use strict;
  4         11  
  4         117  
4 4     4   27 use warnings;
  4         25  
  4         115  
5              
6 4     4   21 use IPC::Run3 qw(run3);
  4         11  
  4         203  
7 4     4   36 use List::SomeUtils qw(any);
  4         10  
  4         191  
8 4     4   25 use Specio::Library::Builtins;
  4         12  
  4         32  
9 4     4   38839 use Specio::Library::Numeric;
  4         11  
  4         37  
10 4     4   34712 use Text::ParseWords qw(shellwords);
  4         2920  
  4         275  
11 4     4   35 use Try::Tiny;
  4         17  
  4         238  
12              
13 4     4   27 use Moo::Role;
  4         12  
  4         28  
14              
15             our $VERSION = '0.83';
16              
17             has ok_exit_codes => (
18             is => 'ro',
19             isa => t( 'ArrayRef', of => t('PositiveOrZeroInt') ),
20             default => sub { [0] },
21             );
22              
23             # We will end up getting $self->argv from the Plugin base class.
24              
25             sub _run_or_die {
26 9     9   19 my $self = shift;
27 9         38 my @argv = @_;
28              
29 9         19 my $output;
30 9         90 my @cmd = ( shellwords( $self->cmd ), shellwords( $self->argv ), @argv );
31             try {
32 9     9   562 local $?;
33 9         54 run3( \@cmd, \undef, \$output, \$output );
34 9         292396 my $code = $? >> 8;
35 9 100       175 if ( $self->_is_bad_exit_code($code) ) {
36 3         83 my $signal = $? & 127;
37 3         73 my $msg = "exited with $code";
38 3 50       94 $msg .= " - received signal $signal" if $signal;
39 3 50 33     133 $msg .= " - output was:\n$output" if defined $output and length $output;
40 3         123 die "$msg\n";
41             }
42             }
43             catch {
44 3     3   231 die sprintf(
45             "Running [%s] failed\n %s",
46             ( join q{ }, @cmd ),
47             $_,
48             );
49 9         2022 };
50              
51 6         800 return $output;
52             }
53              
54             sub _is_bad_exit_code {
55 9     9   64 my $self = shift;
56 9         52 my $code = shift;
57              
58 9     15   222 return !( any { $code == $_ } @{ $self->ok_exit_codes } );
  15         302  
  9         218  
59             }
60              
61             1;
62              
63             # ABSTRACT: A role for plugins which run external commands
64              
65             __END__
66              
67             =pod
68              
69             =encoding UTF-8
70              
71             =head1 NAME
72              
73             Code::TidyAll::Role::RunsCommand - A role for plugins which run external commands
74              
75             =head1 VERSION
76              
77             version 0.83
78              
79             =head1 SYNOPSIS
80              
81             package Whatever;
82             use Moo;
83             with 'Code::TidyAll::Role::RunsCommand';
84              
85             =head1 DESCRIPTION
86              
87             This is a a role for plugins which run external commands
88              
89             =head1 ATTRIBUTES
90              
91             =over
92              
93             =item cmd
94              
95             The command to run. This is just the executable and should not include
96             additional arguments.
97              
98             =back
99              
100             =head1 METHODS
101              
102             =head2 _run_or_die(@argv)
103              
104             This method run the plugin's command, combining any values provided to the
105             plugin's C<argv> attribute with those passed to the method.
106              
107             The plugin's C<argv> attribute is parsed with the C<shellwords> subroutine from
108             L<Text::ParseWords> in order to turn the C<argv> string into a list. This
109             ensures that running the command does not spawn an external shell.
110              
111             The C<@argv> passed to the command comes after the values from C<argv>
112             attribute. The assumption is that this will be what passes a file or source
113             string to the external command.
114              
115             If the command exits with a non-zero status, then this method throws an
116             exception. The error message it throws include the command that was run (with
117             arguments), the exit status, any signal received by the command, and the
118             command's output.
119              
120             Both C<stdout> and C<stderr> from the command are combined into a single string
121             returned by the method.
122              
123             =head2 _is_bad_exit_code($code)
124              
125             This method returns true if the exit code is bad and false otherwise. By
126             default all non-zero codes are bad, but some programs may be expected to exit
127             non-0 when they encounter validation/tidying issues.
128              
129             =head1 SUPPORT
130              
131             Bugs may be submitted at L<https://github.com/houseabsolute/perl-code-tidyall/issues>.
132              
133             =head1 SOURCE
134              
135             The source code repository for Code-TidyAll can be found at L<https://github.com/houseabsolute/perl-code-tidyall>.
136              
137             =head1 AUTHORS
138              
139             =over 4
140              
141             =item *
142              
143             Jonathan Swartz <swartz@pobox.com>
144              
145             =item *
146              
147             Dave Rolsky <autarch@urth.org>
148              
149             =back
150              
151             =head1 COPYRIGHT AND LICENSE
152              
153             This software is copyright (c) 2011 - 2022 by Jonathan Swartz.
154              
155             This is free software; you can redistribute it and/or modify it under
156             the same terms as the Perl 5 programming language system itself.
157              
158             The full text of the license can be found in the
159             F<LICENSE> file included with this distribution.
160              
161             =cut