File Coverage

blib/lib/App/YG.pm
Criterion Covered Total %
statement 30 205 14.6
branch 0 66 0.0
condition 0 28 0.0
subroutine 10 31 32.2
pod 3 3 100.0
total 43 333 12.9


line stmt bran cond sub pod time code
1             package App::YG;
2 1     1   27625 use strict;
  1         3  
  1         35  
3 1     1   5 use warnings;
  1         2  
  1         30  
4 1     1   5 use Carp qw/croak/;
  1         6  
  1         71  
5 1     1   1229 use Getopt::Long qw/GetOptionsFromArray/;
  1         21892  
  1         6  
6 1     1   1737 use Pod::Usage;
  1         86243  
  1         170  
7 1     1   1039 use IO::Interactive qw/is_interactive/;
  1         12942  
  1         6  
8 1     1   1195 use Term::ANSIColor qw/colored/;
  1         10073  
  1         659  
9              
10             our $VERSION = '0.062';
11              
12             our $CONFIG_FILE = '.ygconfig';
13             our $DEFAULT_PARSER = 'apache-combined';
14             our $DIGEST_LENGTH = 6;
15             our $DEFAULT_COLOR = +{
16             label => 'blue',
17             colon => 'cyan',
18             value => 'green',
19             hr => 'yellow',
20             count => 'red',
21             digest => 'magenta',
22             };
23              
24             use Class::Accessor::Lite (
25 1         9 new => 1,
26             rw => [qw/
27             config
28             parse_class
29             parse_func
30             labels
31             label_format
32             count
33             hr
34             /],
35 1     1   903 );
  1         1060  
36              
37             sub run {
38 0     0 1   my $self = shift;
39 0           $self->pre(\@_)->loop;
40             }
41              
42             sub loop {
43 0     0 1   my $self = shift;
44              
45 0           $self->count(1);
46 0 0         if ( !is_interactive() ) {
  0 0          
47 0           while ( my $line = ) {
48 0           $self->_out_put(\$line);
49             }
50             }
51             elsif ( scalar @{ $self->config->{file} } ) {
52 0           for my $file (@{$self->config->{file}}) {
  0            
53 0 0         open my $fh, '<', $file or croak $!;
54 0           while ( my $line = <$fh> ) {
55 0           $self->_out_put(\$line);
56             }
57 0           close $fh;
58             }
59             }
60              
61 0           return 1;
62             }
63              
64             sub _out_put {
65 0     0     my ($self, $line_ref) = @_;
66              
67 0           chomp ${$line_ref};
  0            
68              
69 0 0 0       if ( ( !$self->config->{match} && !$self->config->{regexp} )
      0        
      0        
      0        
      0        
70             || ( $self->config->{match} && $self->_match($line_ref) )
71             || ( $self->config->{regexp} && $self->_regexp($line_ref) )
72             ) {
73 0           $self->__out($line_ref);
74             }
75             else {
76 0           return;
77             }
78             }
79              
80             sub __out {
81 0     0     my ($self, $line_ref) = @_;
82              
83 0 0         if ($self->config->{through}) {
84 0           print "${$line_ref}\n";
  0            
85 0           return;
86             }
87              
88 0 0         if (!$self->config->{nohr}) {
89 0           my $digest = '';
90 0 0         if ($self->config->{digest}) {
91 0           $digest = substr(Digest::SHA1::sha1_hex(${$line_ref}), 0, $DIGEST_LENGTH);
  0            
92             }
93 0           $self->_output_head($self->count, $digest);
94             }
95              
96 0 0         $self->_output_raw($line_ref) if $self->config->{raw};
97              
98 0 0 0       if ($self->config->{ltsv}) {
    0          
99 0           $self->_output_ltsv_line($line_ref);
100             }
101             elsif ( $self->config->{tab} || $self->config->{space} ) {
102 0           $self->_output_splited_line($line_ref);
103             }
104             else {
105 0           $self->_output_parsed_line($line_ref);
106             }
107              
108 0           $self->count( $self->count() + 1 );
109 0           return;
110             }
111              
112             sub _match {
113 0     0     my ($self, $line_ref) = @_;
114              
115 0 0         return 1 if index(${$line_ref}, $self->{config}->{match}) > -1;
  0            
116             }
117              
118             sub _regexp {
119 0     0     my ($self, $line_ref) = @_;
120              
121 0 0         if ($self->config->{ignore_case}) {
122 0 0         return 1 if ${$line_ref} =~ m!$self->{config}->{regexp}!i;
  0            
123             }
124             else {
125 0 0         return 1 if ${$line_ref} =~ m!$self->{config}->{regexp}!;
  0            
126             }
127             }
128              
129             sub _output_head {
130 0     0     my ($self, $count, $digest) = @_;
131              
132 0 0         my $colon = $digest ? ': ' : '';
133              
134 0 0         if ($self->config->{color}) {
135 0           print colored($self->hr.' ', $self->config->{_color}{hr});
136 0           print colored($count, $self->config->{_color}{count});
137 0           print colored($colon, $self->config->{_color}{colon});
138 0           print colored($digest, $self->config->{_color}{digest});
139 0           print colored(' '.$self->hr, $self->config->{_color}{hr});
140 0           print "\n";
141             }
142             else {
143 0           print $self->hr. " $count$colon$digest ". $self->hr. "\n";
144             }
145             }
146              
147             sub _output_raw {
148 0     0     print "${$_[1]}\n";
  0            
149             }
150              
151             sub _output_ltsv_line {
152 0     0     my ($self, $line_ref) = @_;
153              
154 0           my $delimiter = "\t";
155              
156 0           my @cols = split $delimiter, ${$line_ref};
  0            
157 0           my @labels;
158 0           for my $col (@cols) {
159 0           my ($label, $value) = split ':', $col;
160 0           push @labels, $label;
161             }
162 0           my $lablel_width = _max_label_len(\@labels);
163              
164 0           for my $col (@cols) {
165 0           my ($label, $value) = split ':', $col;
166 0           $self->__output_line( sprintf("%${lablel_width}s", $label), $value );
167             }
168 0           print "\n";
169             }
170              
171             sub _output_parsed_line {
172 0     0     my ($self, $line_ref) = @_;
173              
174 0           my $logs;
175             {
176 1     1   1095 no strict 'refs'; ## no critic
  1         2  
  1         486  
  0            
177 0           $logs = &{ $self->parse_func }(${$line_ref});
  0            
  0            
178             }
179 0           my $i = 0;
180 0           for my $label (@{$self->labels}) {
  0            
181 0           $self->__output_line( sprintf($self->label_format, $label), $logs->[$i] );
182 0           $i++;
183             }
184 0           print "\n";
185             }
186              
187             sub _output_splited_line {
188 0     0     my ($self, $line_ref) = @_;
189              
190 0 0         my $delimiter = $self->config->{space} ? ' ' : "\t";
191 0           my $i = 1;
192 0           my @cols = split $delimiter, ${$line_ref};
  0            
193              
194 0 0         if ($self->config->{number}) {
195 0           my $j = length(scalar @cols);
196 0           for my $col (@cols) {
197 0           $self->__output_line( sprintf("%${j}d", $i), $col );
198 0           $i++;
199             }
200             }
201             else {
202 0           for my $col (@cols) {
203 0           $self->__output_line( '', $col );
204 0           $i++;
205             }
206             }
207 0           print "\n";
208             }
209              
210             sub __output_line {
211 0     0     my ($self, $label, $value) = @_;
212              
213 0 0         if ($self->config->{color}) {
214 0           print colored($label, $self->config->{_color}{label});
215 0 0         print colored(': ', $self->config->{_color}{colon}) if $label;
216 0           print colored("$value\n", $self->config->{_color}{value});
217             }
218             else {
219 0 0         print "$label: " if $label;
220 0           print "$value\n";
221             }
222             }
223              
224             sub pre {
225 0     0 1   my ($self, $argv) = @_;
226              
227 0           my $config = $self->_set_config;
228 0           $self->_merge_opt($config, $argv);
229 0           $self->config($config);
230 0   0       $self->parse_class(
231             $self->_load_parser($config->{parser} || $DEFAULT_PARSER)
232             );
233 0           $self->parse_func( $self->parse_class. '::parse');
234             {
235 1     1   6 no strict 'refs'; ## no critic
  1         1  
  1         1057  
  0            
236 0           $self->labels( &{ $self->parse_class. '::labels' }() );
  0            
237             }
238             $self->label_format(
239 0           '%'. _max_label_len($self->labels). 's'
240             );
241              
242 0 0         if ($self->config->{digest}) {
243 0           eval { require Digest::SHA1; };
  0            
244 0 0         croak $@ if $@;
245             }
246              
247 0 0         if ($self->config->{color}) {
248 0           for my $k (keys %{$DEFAULT_COLOR}) {
  0            
249 0   0       $self->config->{_color}{$k}
250             = $self->config->{"color-$k"} || $DEFAULT_COLOR->{$k};
251             }
252             }
253              
254 0           $self->hr($self->_hr);
255              
256 0           $self;
257             }
258              
259             sub _hr {
260 0     0     my $self = shift;
261              
262 0   0       my $hr = $self->config->{hr} || '*';
263 0   0       my $hr_num = $self->config->{'hr-count'} || 20;
264 0           return scalar($hr x $hr_num);
265             }
266              
267             sub _set_config {
268 0     0     my $self = shift;
269              
270 0           my %config;
271 0           for my $dir ($ENV{YG_DIR}, $ENV{HOME}) {
272 0 0         next unless $dir;
273 0 0         next unless -e "$dir/$CONFIG_FILE";
274 0           $self->__read_config("$dir/$CONFIG_FILE" => \%config);
275             }
276              
277 0           return \%config;
278             }
279              
280             sub __read_config {
281 0     0     my ($self, $file, $config) = @_;
282              
283 0 0         open my $fh, '<', $file or croak $!;
284 0           while (<$fh>) {
285 0           chomp;
286 0 0         next if /\A\s*\Z/sm;
287 0 0         if (/\A(\w+):\s*(.+)\Z/sm) {
288 0           my ($key, $value) = ($1, $2);
289 0 0         if ($key eq 'file') {
290 0           push @{$config->{$key}}, $value;
  0            
291             }
292             else {
293 0           $config->{$key} = $value;
294             }
295             }
296             }
297 0           close $fh;
298             }
299              
300             sub _merge_opt {
301 0     0     my ($self, $config, $argv) = @_;
302              
303 0           Getopt::Long::Configure('bundling');
304             GetOptionsFromArray(
305             $argv,
306             'f|file=s@' => \$config->{file},
307             'p|parser=s' => \$config->{parser},
308             'tab' => \$config->{tab},
309             'space' => \$config->{space},
310             'n|number!' => \$config->{number},
311             'm|match=s' => \$config->{match},
312             're|regexp=s' => \$config->{regexp},
313             'i|ignore-case!' => \$config->{ignore_case},
314             'r|raw' => \$config->{raw},
315             't|through' => \$config->{through},
316             'digest!' => \$config->{digest},
317             'ltsv' => \$config->{ltsv},
318             'c|color!' => \$config->{color},
319             'color-label=s' => \$config->{color_label},
320             'color-colon=s' => \$config->{color_colon},
321             'color-value=s' => \$config->{color_value},
322             'color-hr=s' => \$config->{color_hr},
323             'color-count=s' => \$config->{color_count},
324             'color-digest=s' => \$config->{color_digest},
325             'hr=s' => \$config->{hr},
326             'hr-count=i' => \$config->{'hr-count'},
327             'nohr' => \$config->{'nohr'},
328             'h|help' => sub {
329 0     0     pod2usage(1);
330             },
331             'v|version' => sub {
332 0     0     print "yg v$App::YG::VERSION\n";
333 0           exit 1;
334             },
335 0 0         ) or pod2usage(2);
336              
337 0           push @{$config->{file}}, @{$argv};
  0            
  0            
338             }
339              
340             sub _load_parser {
341 0     0     my ($self, $parser) = @_;
342              
343 0           my $class = __PACKAGE__. join('', map { '::'.ucfirst($_) } split('-', $parser));
  0            
344 0           my $file = $class;
345 0           $file =~ s!::!/!g;
346 0           eval {
347 0           require "$file.pm"; ## no critic
348             };
349 0 0         if ($@) {
350 0           croak "wrong parser: $parser, $@";
351             }
352 0           return $class;
353             }
354              
355             sub _max_label_len {
356 0     0     my $labels = shift;
357              
358 0           my $max = 0;
359 0           for my $label (@{$labels}) {
  0            
360 0           my $len = length($label);
361 0 0         $max = $len if $max < $len;
362             }
363 0           return $max;
364             }
365              
366             1;
367              
368             __END__