File Coverage

blib/lib/App/WithSound.pm
Criterion Covered Total %
statement 92 106 86.7
branch 19 28 67.8
condition 11 20 55.0
subroutine 18 19 94.7
pod 0 2 0.0
total 140 175 80.0


line stmt bran cond sub pod time code
1             package App::WithSound;
2              
3 7     7   43846 use warnings;
  7         12  
  7         183  
4 7     7   32 use strict;
  7         9  
  7         217  
5             our $VERSION = '1.2.1';
6              
7 7     7   39 use Carp;
  7         13  
  7         391  
8 7     7   6330 use Config::Simple;
  7         342872  
  7         88  
9 7     7   5952 use File::Path::Expand;
  7         196243  
  7         353  
10 7     7   5713 use File::Which;
  7         6266  
  7         347  
11 7     7   1409 use File::Spec::Functions qw/devnull/;
  7         1468  
  7         321  
12 7     7   5023 use IPC::Open3;
  7         27502  
  7         8979  
13              
14             our $SIGTERM = 15;
15              
16             sub new {
17 14     14 0 22031 my ( $class, $config_file_path, $env ) = @_;
18 14         117 bless {
19             config_file_path => $config_file_path,
20             env => $env,
21             success_sound_path => undef,
22             failure_sound_path => undef,
23             running_sound_path => undef,
24             sound_player => undef,
25             }, $class;
26             }
27              
28             sub run {
29 2     2 0 3642 my ( $self, @argv ) = @_;
30 2 50       9 unless (@argv) {
31 0         0 croak 'Usage: $ with-sound [command] ([argument(s)])' . "\n";
32             }
33              
34 2         10 $self->_init( $argv[0] );
35              
36 2         12 my $retval = $self->_execute_command(@argv);
37 2 100       29 $retval = 1 if $retval > 255;
38              
39 2         33 $self->_play_sound($retval);
40 2         34 return $retval;
41             }
42              
43             sub _init {
44 4     4   202 my ( $self, $command ) = @_;
45              
46 4         21 $self->_load_sound_paths($command);
47 4         32 $self->_detect_sound_play_command;
48              
49 4         106 return $self;
50             }
51              
52             sub _execute_command {
53 2     2   4 my ( $self, @argv ) = @_;
54              
55 2         11 my $pid = $self->_play_sound;
56 2         13173 my $retval = system(@argv);
57 2 50       58 kill( $SIGTERM, $pid ) if $pid;
58              
59 2         84 return $retval;
60             }
61              
62             sub _detect_sound_play_command {
63 3     3   7 my ($self) = @_;
64              
65 3         5 my $player;
66 3   33     46 $player ||= which('mpg123');
67 3   33     918 $player ||= which('mpg321');
68 3   33     641 $player ||= which('afplay');
69              
70 3         560 $self->{sound_player} = $player;
71 3         6 return $self;
72             }
73              
74             sub _load_sound_paths_from_env {
75 9     9   95 my ($self) = @_;
76              
77 9         54 my %deprecated_envs = (
78             WITH_SOUND_SUCCESS => "success_sound_path",
79             WITH_SOUND_FAILURE => "failure_sound_path",
80             WITH_SOUND_RUNNING => "running_sound_path",
81             );
82 9         61 for my $env_name ( sort keys %deprecated_envs ) {
83 27 100       107 if ( my $sound_file_path = $self->{env}->{$env_name} ) {
84 3         47 carp
85             qq{[WARNING] "$env_name" is deprecated. Please use "PERL_$env_name"\n};
86 3         1575 $self->{ $deprecated_envs{$env_name} } =
87             expand_filename($sound_file_path);
88             }
89             }
90              
91 9         63 my %envs = (
92             PERL_WITH_SOUND_SUCCESS => "success_sound_path",
93             PERL_WITH_SOUND_FAILURE => "failure_sound_path",
94             PERL_WITH_SOUND_RUNNING => "running_sound_path",
95             );
96 9         27 for my $env_name ( sort keys %envs ) {
97 27 100       135 if ( my $sound_file_path = $self->{env}->{$env_name} ) {
98 16         41 $self->{ $envs{$env_name} } = expand_filename($sound_file_path);
99             }
100             }
101              
102 9         56 $self;
103             }
104              
105             sub _load_sound_paths_from_config {
106 12     12   127 my ( $self, $command ) = @_;
107              
108 12   100     60 $command ||= '';
109              
110             # Not exists config file.
111 12 100       426 unless ( -f $self->{config_file_path} ) {
112 1         31 carp
113             "[WARNNING] Please put config file in '$self->{config_file_path}'\n";
114 1         608 return;
115             }
116 11         17 my %config;
117 11         18 eval { Config::Simple->import_from( $self->{config_file_path}, \%config ) };
  11         339  
118 11 50       17897 print STDERR "Configuration file has some errors."
119             . "Please check your '.withsound-rc' file.\n"
120             . "(Didn't you write plural format in configuration file?)\n"
121             if $@;
122              
123             $self->{success_sound_path} =
124             expand_filename( $config{"$command.SUCCESS"}
125             || $config{'default.SUCCESS'}
126 11   66     109 || $config{'SUCCESS'} );
127             $self->{failure_sound_path} =
128             expand_filename( $config{"$command.FAILURE"}
129             || $config{'default.FAILURE'}
130 11   66     145 || $config{'FAILURE'} );
131             $self->{running_sound_path} =
132             expand_filename( $config{"$command.RUNNING"}
133             || $config{'default.RUNNING'}
134 11   66     112 || $config{'RUNNING'} );
135 11         76 $self;
136             }
137              
138             sub _load_sound_paths {
139 6     6   26 my ( $self, $command ) = @_;
140 6         31 $self->_load_sound_paths_from_config($command);
141              
142             # load from env after config so environment variables are prior to config
143 6         32 $self->_load_sound_paths_from_env;
144 6         12 $self;
145             }
146              
147             sub _play_mp3_in_child {
148 0     0   0 my ( $self, $play_command, $mp3_file_path ) = @_;
149              
150 0         0 my ( $devnull, $pid );
151 0 0       0 unless ( open( $devnull, '>', devnull ) ) {
152 0         0 carp "[WARNING] Couldn't open devnull : $!";
153 0         0 return;
154             }
155 0         0 eval {
156 0         0 my $wtr;
157 0         0 $pid = open3( $wtr, '>&' . fileno($devnull),
158             0, $play_command, $mp3_file_path, );
159 0         0 close $wtr;
160             };
161 0 0       0 carp "[WARNING] Couldn't exec $play_command in sound process: $@" if $@;
162 0         0 return $pid;
163             }
164              
165             sub _play_mp3 {
166 6     6   36 my ( $self, $mp3_file_path, $status ) = @_;
167              
168 6 50       16 return unless $mp3_file_path;
169              
170             # not exists mp3 file
171 6 50       240 unless ( -f $mp3_file_path ) {
172 0         0 carp "[WARNING] Sound file not found for $status. : $mp3_file_path";
173 0         0 return;
174             }
175              
176 6         16 my $play_command = $self->{sound_player};
177 6 100       17 unless ($play_command) {
178 4         918 carp "[WARNING] No sound player is installed."
179             . "please install mpg123 or mpg321";
180 4         965 return;
181             }
182              
183 2         9 $self->_play_mp3_in_child( $play_command, $mp3_file_path );
184             }
185              
186             sub _play_sound {
187 8     8   2536 my ( $self, $command_retval ) = @_;
188              
189 8         25 my $pid;
190 8 100       45 if ( !defined($command_retval) ) {
    100          
191              
192             # running
193 2         9 $pid = $self->_play_mp3( $self->{running_sound_path}, 'running' );
194             }
195             elsif ( $command_retval == 0 ) {
196              
197             # success
198 3         22 $pid = $self->_play_mp3( $self->{success_sound_path}, 'success' );
199             }
200             else {
201             # failure
202 3         45 $pid = $self->_play_mp3( $self->{failure_sound_path}, 'failure' );
203             }
204 8         3756 return $pid;
205             }
206              
207             1;
208             __END__