App-Prolix-0.03000755001750000144 011713167333 12660 5ustar00gaalusers000000000000README100644001750000144 33211713167333 13577 0ustar00gaalusers000000000000App-Prolix-0.03 This archive contains the distribution App-Prolix, version 0.03: trim chatty command outputs This software is Copyright (c) 2012 by Google, Inc. This is free software, licensed under: The MIT (X11) License Changes100644001750000144 26611713167333 14220 0ustar00gaalusers000000000000App-Prolix-0.03Revision history for App-Prolix 0.03 2012-02-04 Update MooseX::Getopt prereq to avoid bug in -v. 0.02 2012-02-03 Doc fixes. 0.01 2011-11-11 First release. t000755001750000144 011713167333 13044 5ustar00gaalusers000000000000App-Prolix-0.03pod.t100644001750000144 21411713167333 14130 0ustar00gaalusers000000000000App-Prolix-0.03/t#!perl -T use Test::More; eval "use Test::Pod 1.14"; plan skip_all => "Test::Pod 1.14 required for testing POD" if $@; all_pod_files_ok(); LICENSE100644001750000144 220611713167333 13746 0ustar00gaalusers000000000000App-Prolix-0.03This software is Copyright (c) 2012 by Google, Inc. This is free software, licensed under: The MIT (X11) License The MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dist.ini100644001750000144 34011713167333 14362 0ustar00gaalusers000000000000App-Prolix-0.03name = App-Prolix author = Gaal Yahas license = MIT copyright_holder = Google, Inc copyright_year = 2012 version = 0.03 [@Basic] [AutoPrereqs] [Prereqs] MooseX::Getopt = 0.39 [PodWeaver] [@Git] META.yml100644001750000144 132611713167333 14214 0ustar00gaalusers000000000000App-Prolix-0.03--- abstract: 'trim chatty command outputs' author: - 'Gaal Yahas ' build_requires: Test::More: 0 configure_requires: ExtUtils::MakeMaker: 6.30 dynamic_config: 0 generated_by: 'Dist::Zilla version 4.300007, CPAN::Meta::Converter version 2.113640' license: mit meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: App-Prolix requires: Data::Munge: 0 Getopt::Long: 0 IO::File: 0 IPC::Run: 0 JSON: 2.0 Moose: 0 Moose::Exporter: 0 Moose::Role: 0 MooseX::ConfigFromFile: 0 MooseX::Getopt: 0.39 String::ShellQuote: 0 Term::ReadKey: 0 Term::ReadLine: 0 Text::Balanced: 0 Try::Tiny: 0 perl: v5.10 strict: 0 warnings: 0 version: 0.03 MANIFEST100644001750000144 27611713167333 14057 0ustar00gaalusers000000000000App-Prolix-0.03Changes LICENSE MANIFEST META.yml Makefile.PL README bin/prolix dist.ini lib/App/Prolix.pm lib/App/Prolix/MooseHelpers.pm t/01-suppress.t t/boilerplate.t t/pod-coverage.t t/pod.t weaver.ini weaver.ini100644001750000144 5611713167333 14674 0ustar00gaalusers000000000000App-Prolix-0.03[@Default] [-Transformer] transformer = List bin000755001750000144 011713167333 13351 5ustar00gaalusers000000000000App-Prolix-0.03prolix100644001750000144 1154511713167333 14777 0ustar00gaalusers000000000000App-Prolix-0.03/bin#!/usr/bin/perl -w package main; # work around old Pod::Weaver::Section::Version bug. # ABSTRACT: trim chatty command outputs # PODNAME: prolix use strict; use warnings; use App::Prolix; my $prolix = App::Prolix->new_with_options(); $prolix->_cmd($prolix->extra_argv); $prolix->run; =pod =head1 NAME prolix - trim chatty command outputs =head1 VERSION version 0.03 =head1 SYNOPSIS # Run spammy_command and log its output, but weed out some output prolix -b '(spam)' -l auto -- spammy_command cmd_opt1 cmd_opt2 # While it's running, hit enter and add another ignore term at the # interactive prompt prolix> ignore_substring (more spam) # Weed uninteresting fields out of a log file. # Pipe mode is not interactive; but it accepts the same filtering # arguments as the full interactive mode. tail -f error.log | prolix -s 's/^\[(.*?\] ){3}//' =head1 DESCRIPTION B launches a command and captures its standard output and error. It suppresses uninteresting lines. Unlike C, it is an interactive program; hit B to add suppression patterns as new anoyances come up on your terminal. You can weed out by full or substring line matches, as well as regexp. You can also apply substitutions to lines, for example shorten overly chatty fields. prolix can be configured to automatically store a filtered log of output from the commands that it captures. When running in a pipeline, prolix can't interact with you, but it does accept the same command line parameters, so it acts like a replacement for C, C, and C. [Planned] You can have prolix remember different profiles for different command invocations, so that if you often debug a server, prolix (which knows your command line) will identify what ignore/snippet patterns to apply to it. =head1 OPTIONS Normally you will run prolix with: prolix [PROLIX OPTIONS] -- somecommand [COMMAND OPTIONS] You can also put prolix in a pipeline. somecommand | prolix [PROLIX OPTIONS] =over 4 =item -v, --verbose When specified, C will add some output of its own, for example saying it's in pipe mode. =item -p, --pipe Force pipe mode. =item -l, --log=FILENAME Log filtered output to FILENAME. If the argument looks like a pathname (contains a "/"), we'll use that. Otherwise, the output will go to the temporary directory (as defined by File::Spec->tmpdir). The special value "auto" lets prolix pick a filename based on the command being run. The substring "%d" is expanded to a timestamp in a path-friendly variation of iso8601. More substitutions may be added in the future. =item -r, --ignore-re=REGEXP Ignore files matching REGEXP. This is case-sensitive; use C<(?i:...)> to ignore case. (-i may be added in the future.) =item -n, --ignore-line=LINE Ignore complete matches on LINE. =item -b, --ignore-substring=SUBSTRING Ignore lines containing SUBSTRING. =item -s, --snippet=s/SEARCH_RE/REPLACE/ Transform lines by applying a Perl substitution on them. Useful, for example, in getting rid of uninteresting fields in a logfile. Here, modifiers C, C, and C are honored. You can also use alternate delimiters instead of C, including balanced ones (C.) We'll add facilities for doing this in a more convenient way for structured lines in future versions. (For example, CSV or Apache CLF.) =back =head1 INTERACTIVE MODE When C launches a program itself (as opposed to pipe mode), pressing B pauses the display of output from the program, and you're dropped into a prompt. Here you can add more patterns to ignore or apply new snippetting rules. These will take effect on subsequent output. Blank input returns you to the normal running of the command. The interactive mode has a "help" command listing available commands. Those that add new filters follow the long command line options. So for example, saying "ignore_substring temptation" will ignore all lines containing "temptation". You can also say "pats" to list all patterns in effect, and "clear_all" to remove them. (Removing a particular rule is not supported but we can add it if it is requested.) =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc App::Prolix You can also contact the maintainer at the address above or look for information at: =over 4 =item * AnnoCPAN: Annotated CPAN documentation L =item * CPAN Ratings L =item * RT: CPAN's request tracker L =item * Search CPAN L =item * Source repository L =back =head1 AUTHOR Gaal Yahas =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2012 by Google, Inc. This is free software, licensed under: The MIT (X11) License =cut __END__ Makefile.PL100644001750000144 262511713167333 14720 0ustar00gaalusers000000000000App-Prolix-0.03 use strict; use warnings; use 5.010000; use ExtUtils::MakeMaker 6.30; my %WriteMakefileArgs = ( "ABSTRACT" => "trim chatty command outputs", "AUTHOR" => "Gaal Yahas ", "BUILD_REQUIRES" => { "Test::More" => 0 }, "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => "6.30" }, "DISTNAME" => "App-Prolix", "EXE_FILES" => [ "bin/prolix" ], "LICENSE" => "mit", "NAME" => "App::Prolix", "PREREQ_PM" => { "Data::Munge" => 0, "Getopt::Long" => 0, "IO::File" => 0, "IPC::Run" => 0, "JSON" => "2.0", "Moose" => 0, "Moose::Exporter" => 0, "Moose::Role" => 0, "MooseX::ConfigFromFile" => 0, "MooseX::Getopt" => "0.39", "String::ShellQuote" => 0, "Term::ReadKey" => 0, "Term::ReadLine" => 0, "Text::Balanced" => 0, "Try::Tiny" => 0, "strict" => 0, "warnings" => 0 }, "VERSION" => "0.03", "test" => { "TESTS" => "t/*.t" } ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) { my $br = delete $WriteMakefileArgs{BUILD_REQUIRES}; my $pp = $WriteMakefileArgs{PREREQ_PM}; for my $mod ( keys %$br ) { if ( exists $pp->{$mod} ) { $pp->{$mod} = $br->{$mod} if $br->{$mod} > $pp->{$mod}; } else { $pp->{$mod} = $br->{$mod}; } } } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); boilerplate.t100644001750000144 232111713167333 15671 0ustar00gaalusers000000000000App-Prolix-0.03/t#!perl -T use strict; use warnings; use Test::More tests => 3; sub not_in_file_ok { my ($filename, %regex) = @_; open my $fh, "<", $filename or die "couldn't open $filename for reading: $!"; my %violated; while (my $line = <$fh>) { while (my ($desc, $regex) = each %regex) { if ($line =~ $regex) { push @{$violated{$desc}||=[]}, $.; } } } if (%violated) { fail("$filename contains boilerplate text"); diag "$_ appears on lines @{$violated{$_}}" for keys %violated; } else { pass("$filename contains no boilerplate text"); } } not_in_file_ok(README => "The README is used..." => qr/The README is used/, "'version information here'" => qr/to provide version information/, ); not_in_file_ok(Changes => "placeholder date/time" => qr(Date/time) ); sub module_boilerplate_ok { my ($module) = @_; not_in_file_ok($module => 'the great new $MODULENAME' => qr/ - The great new /, 'boilerplate description' => qr/Quick summary of what the module/, 'stub function definition' => qr/function[12]/, ); } module_boilerplate_ok('lib/App/Prolix.pm'); 01-suppress.t100644001750000144 412111713167333 15471 0ustar00gaalusers000000000000App-Prolix-0.03/t use Test::More; BEGIN { use_ok("App::Prolix") } { my $p = App::Prolix->new; push @{$p->ignore_line}, ("spam"); is $p->process_line("spam"), undef, "basic line"; is $p->process_line("not spam"), "not spam", "line needs full match"; } { my $p = App::Prolix->new; push @{$p->ignore_substring}, ("spa"); is $p->process_line("spam"), undef, "substring"; is $p->process_line("not spim"), "not spim", "substring not matched"; } { my $p = App::Prolix->new; $p->import_re("spa+m"); is $p->process_line("I come form the caaastle of spaaaaaam"), undef, "re"; is $p->process_line("the spm is great"), "the spm is great", "re no match"; } { my $p = App::Prolix->new; $p->import_snippet("s/a/b/"); is $p->snip_line("aa"), "ba", "simple substitution"; $p->import_snippet("s/BA/MU/i"); is $p->snip_line("aa"), "MU", "/i, and snippet lists"; $p->import_snippet("s{MU}(whew)"); is $p->snip_line("aa"), "whew", "bracketing"; $p->clear_all; is $p->snip_line("aa"), "aa", "clear_all"; $p->import_snippet(q"s/\d/$/g"); # math is hard, let's go shopping. is $p->snip_line(q"It's $123!"), q(It's $$$$!), "/g"; $p->clear_all; $p->import_snippet(q"s/internationalization/i18n/gi"); $p->import_snippet(q"s/localization/l10n/gi"); is $p->snip_line(q"This is a question of localization, " . "internationalization, and certainly localization and " . "internationalization! Internationalization is as important " . "as localization here."), q"This is a question of l10n, " . "i18n, and certainly l10n and " . "i18n! i18n is as important " . "as l10n here.", "prolix prose and /g"; } { my $p = App::Prolix->new; $p->import_re("spa+m"); push @{$p->ignore_substring}, ("bore"); $p->import_snippet("s/bb/bo/"); is $p->process_line("spammy line"), undef; is $p->process_line("this is a bore"), undef; is $p->process_line("bbre gets through"), "bore gets through", "snippet takes place after filter"; } done_testing; pod-coverage.t100644001750000144 42711713167333 15727 0ustar00gaalusers000000000000App-Prolix-0.03/t#!perl -T use Test::More; eval "use Test::Pod::Coverage 1.04"; plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@; plan skip_all => "Test::Pod::Coverage is a little too draconic for my taste" unless $ENV{BITE_THE_BULLET}; all_pod_coverage_ok(); App000755001750000144 011713167333 14067 5ustar00gaalusers000000000000App-Prolix-0.03/libProlix.pm100644001750000144 2571411713167333 16073 0ustar00gaalusers000000000000App-Prolix-0.03/lib/Appuse strict; use warnings; use Getopt::Long qw(:config no_auto_version); package App::Prolix; # ABSTRACT: trim chatty command outputs use Moose; use String::ShellQuote (); use v5.10; { package App::Prolix::ConfigFileRole; use Moose::Role; with "MooseX::ConfigFromFile"; use JSON 2.0; sub get_config_from_file { my($file) = @_; open my $fh, "<", $file or confess "open: $file: $!"; local $/; my $json = <$fh>; close $fh or die "close: $file: $!"; return JSON->new->relaxed->utf8->decode($json); } } use Data::Munge; use IO::File; use IPC::Run (); use Term::ReadKey (); use Term::ReadLine; use Text::Balanced (); use Try::Tiny; use App::Prolix::MooseHelpers; with "MooseX::Getopt"; # Flags affecting overall run style. has_option "verbose" => (isa => "Bool", cmd_aliases => "v", documentation => "Prints extra information."); has_option "pipe" => (isa => "Bool", cmd_aliases => "p", documentation => "Reads from stdin instead of interactively."); has_option "log" => (isa => "Str", cmd_aliases => "l", documentation => q{Logs output to a filename (say "auto" } . q{to let prolix pick one for you)}); # Flags affecting filtering. has_option "ignore_re" => (isa => "ArrayRef", cmd_aliases => "r", "default" => sub { [] }, documentation => "Ignore lines matching this regexp."); has_option "ignore_line" => (isa => "ArrayRef", cmd_aliases => "n", "default" => sub { [] }, documentation => "Ignore lines exactly matching this."); has_option "ignore_substring" => (isa => "ArrayRef", cmd_aliases => "b", "default" => sub { [] }, documentation => "Ignore lines containing this substring."); has_option "snippet" => (isa => "ArrayRef", cmd_aliases => "s", "default" => sub { [] }, documentation => "Snip lines. Use s/search_re/replace/ syntax."); # Internal attributes (leading _ means not GetOpt). has_rw "_cmd" => (isa => "ArrayRef", "default" => sub { [] }); has_rw "_out" => (isa => "ScalarRef[Str]", default => \&_strref); has_rw "_err" => (isa => "ScalarRef[Str]", default => \&_strref); has_rw "_log" => (isa => "FileHandle"); has_rw "_term" => ( isa => "Ref"); # TODO(gaal): figure out how to fix this: # isa => "Term::ReadLine|Term::ReadLine::Perl|Term::ReadLine::Gnu"); has_rw "_snippet" => (isa => "ArrayRef", "default" => sub { [] }); has_rw "_ignore_re" => (isa => "ArrayRef", "default" => sub { [] }); has_counter "_suppressed"; has_counter "_output_lines"; sub run { my($self) = @_; if ($self->verbose) { $SIG{USR1} = \&_dump_stack; } $self->open_log; $self->import_re($_) for @{$self->ignore_re}; $self->import_snippet($_) for @{$self->snippet}; if ($self->need_pipe) { $self->run_pipe; } else { $self->run_spawn; } if ($self->verbose) { say "Done. " . $self->stats; } $self->close_log; } sub need_pipe { my($self) = @_; return $self->pipe || @{$self->_cmd} == 0; } sub open_log { my($self) = @_; return if not defined $self->log; my $now = $self->now_stamp; my $filename = $self->log; $filename = ($self->need_pipe ? "prolix.%d" : ($self->_cmd->[0] . ".%d")) if $filename eq "auto"; $filename = File::Spec->catfile(File::Spec->tmpdir, $filename) if $filename !~ m{[/\\]}; # Put in /tmp/ or similar unless we got a path. $filename =~ s/%d/$now/; # TODO(gaal): implement incrementing %n. say "Logging output to $filename" if $self->verbose; my $fh = IO::File->new($filename, "w") or die "open: $filename: $!"; $self->_log($fh); } sub close_log { my($self) = @_; $self->_log->close if $self->_log; } # Like: (DateTime->new->iso8601 =~ s/[-:]//g), but I didn't want to add # a big dependency. sub now_stamp { my($self) = @_; my(@t) = localtime; # Should this be gmtime? return sprintf "%4d%02d%02dT%02d%02d%02d", $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0]; # Ahh, UNIX. } sub stats { my($self) = @_; return "Suppressed " . $self->_suppressed . "/" . $self->_output_lines . " lines."; } # returns a fresh reference to a string. sub _strref { return \(my $throwaway = ""); } sub run_pipe { my($self) = @_; say "Running in pipe mode" if $self->verbose; while () { chomp; $self->on_out($_) } } sub run_spawn { my($self) = @_; say "Running: " . String::ShellQuote::shell_quote_best_effort(@{$self->_cmd}) if $self->verbose; Term::ReadKey::ReadMode("noecho"); END { Term::ReadKey::ReadMode("normal"); } $self->_term(Term::ReadLine->new("prolix")); my $attribs = $self->_term->Attribs; $attribs->{completion_entry_function} = $attribs->{list_completion_function}; $attribs->{completion_word} = [qw( help ignore_line ignore_re ignore_substring pats quit snippet stats )]; my $t = IPC::Run::timer(0.3); my $ipc = IPC::Run::start $self->_cmd, \undef, # no stdin $self->_out, $self->_err, $t; $t->start; my $pumping = 1; while ($pumping && $ipc->pump) { $self->consume; try { $self->try_user_input; } catch { when (/prolix-quit/) { $ipc->kill_kill; $pumping = 0; } default { die $_ } }; $t->start(0.3); } $t->reset; $ipc->finish; $self->consume_final; Term::ReadKey::ReadMode("normal"); } sub _dump_stack { print Carp::longmess("************"); $SIG{USR1} = \&_dump_stack; } sub try_user_input { my($self) = @_; return if not defined Term::ReadKey::ReadKey(-1); # Enter interactive prompt mode. We hope this will be brief, and # IPC::Run can buffer our watched command in the meanhwile. say q{Press ENTER to go back, or enter "help" for a list of commands.} if $self->verbose; Term::ReadKey::ReadMode("normal"); while (my $cmd = $self->_term->readline("prolix>")) { $self->_term->addhistory($cmd); $self->handle_user_input($cmd); } Term::ReadKey::ReadMode("restore"); # into noecho, we hope! } sub handle_user_input { my($self, $cmd) = @_; (my $nullary = $cmd) =~ s/^\s*(\S+)\s*/$1/; if ($nullary) { given ($nullary) { when ("clear_all") { $self->clear_all } when ("stack") { _dump_stack } when ("bufs") { $self->dump_bufs } when (/q|quit/) { die "prolix-quit\n" } when (/h|help/) { $self->help_interactive } when ("pats") { $self->dump_pats } when ("stats") { say $self->stats } default { say q{Unknown command. Try "help".} } } } else { given ($cmd) { when (/^\s*(ignore_(?:line|re|substring))\s+(.*)/) { my($ignore_type, $pat) = ($1, $2); push @{ $self->$ignore_type }, $pat; $self->import_re($pat) if $ignore_type eq 're'; } when (/^\s*snippet\s(.*)/) { push @{ $self->snippet }, $1; $self->import_snippet($1); } default { say q{Unknown command. Try "help".} } } } } sub import_re { my($self, $pat) = @_; push @{ $self->_ignore_re }, qr/$pat/; } sub import_snippet { my($self, $snippet) = @_; my $help = <<"."; *** Usage: snippet s/find_re/replace/ You may use Perl-like quoting on the substitution operation, so if your pattern contains slashes use a different delimiter. Modifiers that are honored: /igx (m and s aren't meaningful here) . my @extract = Text::Balanced::extract_quotelike($snippet); my($op, $search, $replace, $modifiers) = @extract[3, 5, 8, 10]; die $help unless $op eq "s"; die $help unless defined $search; die $help unless defined $replace; my $mods = ""; for (qw/i x/) { $mods .= $_ if $modifiers =~ /$_/; } my $global = $modifiers =~ /g/ ? "g" : ""; my $search_re = qr/(?$mods:$search)/; push @{ $self->_snippet }, sub { my($line) = @_; return Data::Munge::replace($line, $search_re, $replace, $global); }; } sub dump_pats { my($self) = @_; say "* ignored lines"; say for @{ $self->ignore_line }; say "* ignored patterns"; say for @{ $self->ignore_re }; say "* ignored substrings"; say for @{ $self->ignore_substring }; say "* snippets"; say for @{ $self->snippet }; } sub help_interactive { my($self) = @_; say <<"EOF"; clear_all - clear all patterns ignore_line - add a full match to ignore ignore_re - add an ignore pattern, e.g. ^(FINE|DEBUG) ignore_substring - add a partial match to ignore pats - list ignore patterns quit - terminate running program stats - print stats snippet - add a snippet expression, e.g. s/^(INFO|WARNING|ERROR) // To keep going, just enter an empty line. EOF } sub clear_all { my($self) = @_; @{ $self->ignore_line } = (); @{ $self->ignore_re } = (); @{ $self->_ignore_re } = (); @{ $self->ignore_substring } = (); @{ $self->snippet } = (); @{ $self->_snippet } = (); } sub dump_bufs { my($self) = @_; warn "Out: [" . ${$self->_out} . "]\n" . "Err: [" . ${$self->_err} . "]\n"; } sub consume { my($self) = @_; while (${$self->_out} =~ s/^(.*?)\n//) { $self->on_out($1); } while (${$self->_err} =~ s/^(.*?)\n//) { $self->on_err($1); } } # like consume, but does not require a trailing newline. sub consume_final { my($self) = @_; if (length ${$self->_out} > 0) { $self->on_out($_) for split /\n/, ${$self->_out}; } if (length ${$self->_err} > 0) { $self->on_err($_) for split /\n/, ${$self->_err}; } } sub snip_line { my($self, $line) = @_; $line = $_->($line) for @{$self->_snippet}; return $line; } sub process_line { my($self, $line) = @_; for my $exact (@{$self->ignore_line}) { if ($line eq $exact) { return; } } for my $sub (@{$self->ignore_substring}) { if (index($line, $sub) >= 0) { return; } } for my $pat (@{$self->_ignore_re}) { if ($line =~ $pat) { return; } } return $self->snip_line($line); } # One day, we might paint this in a different color or something. sub on_err { goto &on_out } sub on_out { my($self, $line) = @_; $self->inc__output_lines; if (defined($line = $self->process_line($line))) { say $line; if ($self->_log) { $self->_log->print("$line\n"); } } else { $self->inc__suppressed; } } 6; __END__ =pod =head1 NAME App::Prolix - trim chatty command outputs =head1 VERSION version 0.03 =head1 AUTHOR Gaal Yahas =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2012 by Google, Inc. This is free software, licensed under: The MIT (X11) License =cut Prolix000755001750000144 011713167333 15344 5ustar00gaalusers000000000000App-Prolix-0.03/lib/AppMooseHelpers.pm100644001750000144 233211713167333 20447 0ustar00gaalusers000000000000App-Prolix-0.03/lib/App/Prolixpackage App::Prolix::MooseHelpers; # ABSTRACT: Moose helpers for App::Prolix use Moose (); use Moose::Exporter; use warnings; Moose::Exporter->setup_import_methods( with_meta => [ 'has_counter', 'has_rw', 'has_option' ]); sub has_rw { my ($meta, $name, %options) = @_; $meta->add_attribute( $name, is => 'rw', %options ); } sub has_option { my ($meta, $name, %options) = @_; $meta->add_attribute( $name, is => 'rw', metaclass => 'Getopt', %options ); } sub has_counter { my ($meta, $name, %options) = @_; $meta->add_attribute( $name, traits => ['Counter'], is => 'ro', isa => 'Num', default => 0, handles => { ('inc_' . $name) => 'inc', ('dec_' . $name) => 'dec', ('reset_' . $name) => 'reset', }, %options ); } 6; __END__ =pod =head1 NAME App::Prolix::MooseHelpers - Moose helpers for App::Prolix =head1 VERSION version 0.03 =head1 AUTHOR Gaal Yahas =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2012 by Google, Inc. This is free software, licensed under: The MIT (X11) License =cut