Skip to content

Commit

Permalink
added table function to Mojo::Util
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Feb 6, 2014
1 parent 2e32224 commit a729dfc
Show file tree
Hide file tree
Showing 22 changed files with 96 additions and 80 deletions.
3 changes: 2 additions & 1 deletion Changes
@@ -1,11 +1,12 @@

4.77 2014-02-05
4.77 2014-02-06
- Deprecated Mojo::DOM::text_after and Mojo::DOM::text_before in favor of
Mojo::DOM::contents.
- Deprecated Mojo::DOM::content_xml and Mojo::DOM::replace_content in favor
of Mojo::DOM::content.
- Deprecated Mojo::DOM::to_xml in favor of Mojo::DOM::to_string.
- Added wrap_content method to Mojo::DOM.
- Added table function to Mojo::Util.
- Improved wrap method in Mojo::DOM to allow wrapping of the root node.

4.76 2014-02-04
Expand Down
34 changes: 31 additions & 3 deletions lib/Mojo/Util.pm
Expand Up @@ -8,7 +8,7 @@ use Digest::SHA qw(hmac_sha1_hex sha1 sha1_hex);
use Encode 'find_encoding';
use File::Basename 'dirname';
use File::Spec::Functions 'catfile';
use List::Util 'min';
use List::Util qw(max min);
use MIME::Base64 qw(decode_base64 encode_base64);
use Time::HiRes ();

Expand Down Expand Up @@ -52,8 +52,8 @@ our @EXPORT_OK = (
qw(decode deprecated dumper encode get_line hmac_sha1_sum html_unescape),
qw(md5_bytes md5_sum monkey_patch punycode_decode punycode_encode quote),
qw(secure_compare sha1_bytes sha1_sum slurp split_header spurt squish),
qw(steady_time trim unindent unquote url_escape url_unescape xml_escape),
qw(xor_encode)
qw(steady_time table trim unindent unquote url_escape url_unescape),
qw(xml_escape xor_encode)
);

sub b64_decode { decode_base64($_[0]) }
Expand Down Expand Up @@ -296,6 +296,26 @@ sub steady_time () {
: Time::HiRes::time;
}

sub table {
my $columns = shift;

my $spec = shift @$columns;
for my $i (0 .. $#$spec) {
my @len;
for my $j (0 .. $#$columns) {
$columns->[$j][$i] =~ s/[\r\n]//g;
push @len, length $columns->[$j][$i];
}
next unless $spec->[$i + 1] && (my $max = max @len) < $spec->[$i];
$spec->[$i + 1] += $spec->[$i] - $max;
$spec->[$i] = $max;
}

my $format = join ' ', map {"\%-${_}.${_}s"} @$spec[0 .. $#$spec - 1];
$format .= ($format ? ' %.' : '%.') . $spec->[-1] . "s\n";
return join '', map { sprintf $format, @$_ } @$columns;
}

sub trim {
my $str = shift;
$str =~ s/^\s+|\s+$//g;
Expand Down Expand Up @@ -627,6 +647,14 @@ consecutive groups of whitespace into one space each.
High resolution time, resilient to time jumps if a monotonic clock is
available through L<Time::HiRes>.
=head2 table
my $table = table [[20, 20], ['foo', 'bar'], ['baz', 'yada']];
Simple row-oriented table builder for command line tools, the first row
contains the maximum width for each column, if one column doesn't require its
full quota, the next one inherits it.
=head2 trim
my $trimmed = trim $str;
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command.pm
Expand Up @@ -104,7 +104,7 @@ Mojolicious::Command - Command base class
use Mojo::Base 'Mojolicious::Command';
# Short description
has description => "My first Mojo command.\n";
has description => 'My first Mojo command.';
# Short usage message
has usage => <<EOF;
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/cgi.pm
Expand Up @@ -4,7 +4,7 @@ use Mojo::Base 'Mojolicious::Command';
use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
use Mojo::Server::CGI;

has description => "Start application with CGI.\n";
has description => 'Start application with CGI.';
has usage => sub { shift->extract_usage };

sub run {
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/cpanify.pm
Expand Up @@ -5,7 +5,7 @@ use File::Basename 'basename';
use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
use Mojo::UserAgent;

has description => "Upload distribution to CPAN.\n";
has description => 'Upload distribution to CPAN.';
has usage => sub { shift->extract_usage };

sub run {
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/daemon.pm
Expand Up @@ -4,7 +4,7 @@ use Mojo::Base 'Mojolicious::Command';
use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
use Mojo::Server::Daemon;

has description => "Start application with HTTP and WebSocket server.\n";
has description => 'Start application with HTTP and WebSocket server.';
has usage => sub { shift->extract_usage };

sub run {
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/eval.pm
Expand Up @@ -3,7 +3,7 @@ use Mojo::Base 'Mojolicious::Command';

use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);

has description => "Run code against application.\n";
has description => 'Run code against application.';
has usage => sub { shift->extract_usage };

sub run {
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/generate.pm
@@ -1,7 +1,7 @@
package Mojolicious::Command::generate;
use Mojo::Base 'Mojolicious::Commands';

has description => "Generate files and directories from templates.\n";
has description => 'Generate files and directories from templates.';
has hint => <<EOF;
See 'APPLICATION generate help GENERATOR' for more information on a specific
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/generate/app.pm
Expand Up @@ -3,7 +3,7 @@ use Mojo::Base 'Mojolicious::Command';

use Mojo::Util qw(class_to_file class_to_path);

has description => "Generate Mojolicious application directory structure.\n";
has description => 'Generate Mojolicious application directory structure.';
has usage => sub { shift->extract_usage };

sub run {
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/generate/lite_app.pm
@@ -1,7 +1,7 @@
package Mojolicious::Command::generate::lite_app;
use Mojo::Base 'Mojolicious::Command';

has description => "Generate Mojolicious::Lite application.\n";
has description => 'Generate Mojolicious::Lite application.';
has usage => sub { shift->extract_usage };

sub run {
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/generate/makefile.pm
Expand Up @@ -3,7 +3,7 @@ use Mojo::Base 'Mojolicious::Command';

use Mojolicious;

has description => qq{Generate "Makefile.PL".\n};
has description => 'Generate "Makefile.PL".';
has usage => sub { shift->extract_usage };

sub run { shift->render_to_rel_file('makefile', 'Makefile.PL') }
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/generate/plugin.pm
Expand Up @@ -4,7 +4,7 @@ use Mojo::Base 'Mojolicious::Command';
use Mojo::Util qw(camelize class_to_path);
use Mojolicious;

has description => "Generate Mojolicious plugin directory structure.\n";
has description => 'Generate Mojolicious plugin directory structure.';
has usage => sub { shift->extract_usage };

sub run {
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/get.pm
Expand Up @@ -10,7 +10,7 @@ use Mojo::UserAgent;
use Mojo::Util qw(decode encode);
use Scalar::Util 'weaken';

has description => "Perform HTTP request.\n";
has description => 'Perform HTTP request.';
has usage => sub { shift->extract_usage };

sub run {
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/inflate.pm
Expand Up @@ -4,7 +4,7 @@ use Mojo::Base 'Mojolicious::Command';
use Mojo::Loader;
use Mojo::Util 'encode';

has description => "Inflate embedded files to real files.\n";
has description => 'Inflate embedded files to real files.';
has usage => sub { shift->extract_usage };

sub run {
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/prefork.pm
Expand Up @@ -5,7 +5,7 @@ use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
use Mojo::Server::Prefork;

has description =>
"Start application with preforking HTTP and WebSocket server.\n";
'Start application with preforking HTTP and WebSocket server.';
has usage => sub { shift->extract_usage };

sub run {
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/psgi.pm
Expand Up @@ -3,7 +3,7 @@ use Mojo::Base 'Mojolicious::Command';

use Mojo::Server::PSGI;

has description => "Start application with PSGI.\n";
has description => 'Start application with PSGI.';
has usage => sub { shift->extract_usage };

sub run { Mojo::Server::PSGI->new(app => shift->app)->to_psgi_app }
Expand Down
76 changes: 28 additions & 48 deletions lib/Mojolicious/Command/routes.pm
Expand Up @@ -3,70 +3,50 @@ use Mojo::Base 'Mojolicious::Command';

use re 'regexp_pattern';
use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
use Mojo::Util 'encode';
use Mojo::Util qw(encode table);

has description => "Show available routes.\n";
has description => 'Show available routes.';
has usage => sub { shift->extract_usage };

sub run {
my ($self, @args) = @_;

GetOptionsFromArray \@args, 'v|verbose' => \my $verbose;

my $routes = [];
$self->_walk($_, 0, $routes) for @{$self->app->routes->children};
$self->_draw($routes, $verbose);
my $table = $verbose ? [[20, 16, 18, 20]] : [[26, 25, 25]];
$self->_walk($_, 0, $table, $verbose) for @{$self->app->routes->children};
print encode('UTF-8', table($table));
}

sub _draw {
my ($self, $routes, $verbose) = @_;

my @table = (0, 0, 0);
for my $node (@$routes) {

# Methods
my $via = $node->[0]->via;
$node->[2] = !$via ? '*' : uc join ',', @$via;

# Name
my $name = $node->[0]->name;
$node->[3] = $node->[0]->has_custom_name ? qq{"$name"} : $name;

# Check column width
$table[$_] = _max($table[$_], length $node->[$_ + 1]) for 0 .. 2;
}

for my $node (@$routes) {
my @parts = map { _padding($node->[$_ + 1], $table[$_]) } 0 .. 2;

# Regex (verbose)
my $pattern = $node->[0]->pattern;
$pattern->match('/', $node->[0]->is_endpoint);
my $regex = (regexp_pattern $pattern->regex)[0];
my $format = (regexp_pattern($pattern->format_regex || ''))[0];
my $optional
= !$pattern->constraints->{format} || $pattern->defaults->{format};
$regex .= $optional ? "(?:$format)?" : $format
if $format && !$node->[0]->partial;
push @parts, $regex if $verbose;

say encode('UTF-8', join(' ', @parts));
}
}

sub _max { $_[1] > $_[0] ? $_[1] : $_[0] }

sub _padding { $_[0] . ' ' x ($_[1] - length $_[0]) }

sub _walk {
my ($self, $route, $depth, $routes) = @_;
my ($self, $route, $depth, $table, $verbose) = @_;

# Pattern
my $prefix = '';
if (my $i = $depth * 2) { $prefix .= ' ' x $i . '+' }
push @$routes, [$route, $prefix . ($route->pattern->pattern || '/')];
push @$table, my $row = [$prefix . ($route->pattern->pattern || '/')];

# Methods
my $via = $route->via;
push @$row, !$via ? '*' : uc join ',', @$via;

# Name
my $name = $route->name;
push @$row, $route->has_custom_name ? qq{"$name"} : $name;

# Regex (verbose)
my $pattern = $route->pattern;
$pattern->match('/', $route->is_endpoint);
my $regex = (regexp_pattern $pattern->regex)[0];
my $format = (regexp_pattern($pattern->format_regex || ''))[0];
my $optional
= !$pattern->constraints->{format} || $pattern->defaults->{format};
$regex .= $optional ? "(?:$format)?" : $format
if $format && !$route->partial;
push @$row, $regex if $verbose;

$depth++;
$self->_walk($_, $depth, $routes) for @{$route->children};
$self->_walk($_, $depth, $table, $verbose) for @{$route->children};
$depth--;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/test.pm
Expand Up @@ -7,7 +7,7 @@ use File::Spec::Functions qw(abs2rel catdir splitdir);
use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
use Mojo::Home;

has description => "Run unit tests.\n";
has description => 'Run unit tests.';
has usage => sub { shift->extract_usage };

sub run {
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Command/version.pm
Expand Up @@ -5,7 +5,7 @@ use Mojo::IOLoop::Server;
use Mojo::UserAgent;
use Mojolicious;

has description => "Show versions of installed modules.\n";
has description => 'Show versions of installed modules.';
has usage => sub { shift->extract_usage };

sub run {
Expand Down
14 changes: 5 additions & 9 deletions lib/Mojolicious/Commands.pm
Expand Up @@ -2,8 +2,8 @@ package Mojolicious::Commands;
use Mojo::Base 'Mojolicious::Command';

use Getopt::Long 'GetOptions';
use List::Util 'max';
use Mojo::Server;
use Mojo::Util 'table';

has hint => <<EOF;
Expand Down Expand Up @@ -79,14 +79,10 @@ sub run {
}

# Print list of all available commands
my $max = max map { length $_->[0] } @commands;
print $self->message;
for my $command (sort { $a->[0] cmp $b->[0] } @commands) {
my $name = $command->[0];
my $description = $command->[1]->new->description;
print " $name", (' ' x ($max - length $name)), " $description";
}
return print $self->hint;
my $table = [[30, 48]];
push @$table, [' ' . $_->[0], $_->[1]->new->description]
for sort { $a->[0] cmp $b->[0] } @commands;
return print $self->message, table($table), $self->hint;
}

sub start_app {
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Guides/Cookbook.pod
Expand Up @@ -1102,7 +1102,7 @@ that they will be picked up automatically by the command line interface?
package Mojolicious::Command::spy;
use Mojo::Base 'Mojolicious::Command';

has description => "Spy on application.\n";
has description => 'Spy on application.';
has usage => "Usage: APPLICATION spy [TARGET]\n";

sub run {
Expand Down
15 changes: 13 additions & 2 deletions t/mojo/util.t
Expand Up @@ -13,8 +13,8 @@ use Mojo::Util
qw(decode dumper encode get_line hmac_sha1_sum html_unescape md5_bytes),
qw(md5_sum monkey_patch punycode_decode punycode_encode quote),
qw(secure_compare sha1_bytes sha1_sum slurp split_header spurt squish),
qw(steady_time trim unindent unquote url_escape url_unescape xml_escape),
qw(xor_encode);
qw(steady_time table trim unindent unquote url_escape url_unescape),
qw(xml_escape xor_encode);

# camelize
is camelize('foo_bar_baz'), 'FooBarBaz', 'right camelized result';
Expand Down Expand Up @@ -418,6 +418,17 @@ is MojoMonkeyTest::yin(), 'yin', 'right result';
ok !!MojoMonkeyTest->can('yang'), 'function "yang" exists';
is MojoMonkeyTest::yang(), 'yang', 'right result';

# table
is table([[5, 5], ["f\r\no o", 'bar']]), "fo o bar\n", 'right result';
is table([[50], ['foo']]), "foo\n", 'right result';
is table([[3], ['foo'], ['bar'], ['yada']]), "foo\nbar\nyad\n", 'right result';
is table([[3, 3], ['yada', 'yada'], ['foo', 'yada']]), "yad yad\nfoo yad\n",
'right result';
is table([[5, 3], ['foo', 'bar'], ['baz', 'yada']]), "foo bar\nbaz yada\n",
'right result';
is table([[10, 3, 1], ['foo', 'bar', 'baz'], ['yada', 'yada', 'yada']]),
"foo bar baz\nyada yada yada\n", 'right result';

# deprecated
{
my ($warn, $die) = @_;
Expand Down

0 comments on commit a729dfc

Please sign in to comment.