view release on metacpan or search on metacpan
opposite end of the spectrum) just using curl directly on the
command-line. This tool attempts to find some sort of middle ground by
providing a quasi-DSL for interacting with a RESTful service in an
interactive way.
FEATURES
Basic HTTP methods
All HTTP methods are implemented as commands in presto. The URL that is
given is appended to the endpoint specified when presto is invoked as
shown in the SYNOPSIS above.
Request Building
If the endpoint contains a * character the URL fragment specified in
the GET/POST/etc command is inserted at that point. This allows you to
do things like auto-append a file extension to all URLs. For instance:
bash$ presto http://my-server.com*.json
http://my-server.com> GET /product/1
In this case, the full URL would be
http://my-server.com/product/1.json. If no * is found in the URL, the
URL fragment is simply appended at the end of the endpoint.
All arguments after the first will be treated as query parameters (for
GET/HEAD/DELETE requests) or request content (for POST/PUT requests).
For instance:
http://my-server.com> GET /products limit=10 offset=20
# request goes to http://my-sever.com/products?limit=10&offset=20
http://my-server.com> POST /products '{"name":"A New Product"}'
# request goes to http://my-sever.com/products with the body as specified
like the -B file-test operator), the output is not printed to STDOUT.
Instead, you may want to use output redirection as show above and send
the response body to a file.
http://my-server.com> GET /some-image.jpg >foo.jpg
Persistent Configuration
As demonstrated above, you can use the config command to change the
behavior of presto. These configuration options are persisted in a
config file specific to the endpoint provided at the command-line and
will be reloaded the next time you invoke presto with the same
endpoint.
Current valid config keys are:
* verbose
Boolean, when enabled, dumps request/response to STDOUT (defaults to
"0")
* deserialize_response
* pretty_printer
Must be one of the supported modules (i.e. Data::Dumper or JSON). Use
tab completion to see currently supported values (defaults to
"JSON").
* binmode
Used to set encoding of STDIN and STDOUT handles (defaults to "utf8")
TODO: provide a means for aliasing endpoints so that configuration is
shared across multiple endpoints.
History and Scripting
Just like configuration, command history is maintained separately for
each endpoint specified on the command-line and is persisted across
sessions (assuming you have a capable Term::Readline library
installed). You can interrogate the history using the (surprisingly
named) history command. It supports a small subset of the bash history
command:
# dump all history
http://my-server.com> history
# dump last 5 entries
http://my-server.com> history 5
README.mkdn view on Meta::CPAN
answers typically point to some horrible GUI or (on the complete opposite
end of the spectrum) just using `curl` directly on the command-line.
This tool attempts to find some sort of middle ground by providing a
quasi-DSL for interacting with a RESTful service in an interactive way.
# FEATURES
## Basic HTTP methods
All HTTP methods are implemented as commands in presto. The URL that is
given is appended to the endpoint specified when presto is invoked as
shown in the SYNOPSIS above.
## Request Building
If the endpoint contains a `*` character the URL fragment specified in
the GET/POST/etc command is inserted at that point. This allows you to
do things like auto-append a file extension to all URLs. For instance:
bash$ presto http://my-server.com*.json
http://my-server.com> GET /product/1
In this case, the full URL would be
`http://my-server.com/product/1.json`. If no `*` is found in the URL,
the URL fragment is simply appended at the end of the endpoint.
All arguments after the first will be treated as query parameters (for
GET/HEAD/DELETE requests) or request content (for POST/PUT requests). For
instance:
http://my-server.com> GET /products limit=10 offset=20
# request goes to http://my-sever.com/products?limit=10&offset=20
http://my-server.com> POST /products '{"name":"A New Product"}'
# request goes to http://my-sever.com/products with the body as specified
README.mkdn view on Meta::CPAN
like the `-B` file-test operator), the output is not printed to STDOUT.
Instead, you may want to use output redirection as show above and send
the response body to a file.
http://my-server.com> GET /some-image.jpg >foo.jpg
## Persistent Configuration
As demonstrated above, you can use the `config` command to change the
behavior of presto. These configuration options are persisted in a
config file specific to the endpoint provided at the command-line and
will be reloaded the next time you invoke presto with the same endpoint.
Current valid config keys are:
- verbose
Boolean, when enabled, dumps request/response to STDOUT (defaults to "0")
- deserialize\_response
Boolean, when enabled response body is parsed based on the `Content-Type`
README.mkdn view on Meta::CPAN
- pretty\_printer
Must be one of the supported modules (i.e. Data::Dumper or JSON).
Use tab completion to see currently supported values (defaults to "JSON").
- binmode
Used to set encoding of STDIN and STDOUT handles (defaults to "utf8")
**TODO:** provide a means for aliasing endpoints so that configuration
is shared across multiple endpoints.
## History and Scripting
Just like configuration, command history is maintained separately for each
endpoint specified on the command-line and is persisted across sessions
(assuming you have a capable Term::Readline library installed). You can
interrogate the history using the (surprisingly named) `history` command.
It supports a small subset of the `bash` history command:
# dump all history
http://my-server.com> history
# dump last 5 entries
http://my-server.com> history 5
answers typically point to some horrible GUI or (on the complete opposite
end of the spectrum) just using C<curl> directly on the command-line.
This tool attempts to find some sort of middle ground by providing a
quasi-DSL for interacting with a RESTful service in an interactive way.
=head1 FEATURES
=head2 Basic HTTP methods
All HTTP methods are implemented as commands in presto. The URL that is
given is appended to the endpoint specified when presto is invoked as
shown in the SYNOPSIS above.
=head2 Request Building
If the endpoint contains a C<*> character the URL fragment specified in
the GET/POST/etc command is inserted at that point. This allows you to
do things like auto-append a file extension to all URLs. For instance:
bash$ presto http://my-server.com*.json
http://my-server.com> GET /product/1
In this case, the full URL would be
C<http://my-server.com/product/1.json>. If no C<*> is found in the URL,
the URL fragment is simply appended at the end of the endpoint.
All arguments after the first will be treated as query parameters (for
GET/HEAD/DELETE requests) or request content (for POST/PUT requests). For
instance:
http://my-server.com> GET /products limit=10 offset=20
# request goes to http://my-sever.com/products?limit=10&offset=20
http://my-server.com> POST /products '{"name":"A New Product"}'
# request goes to http://my-sever.com/products with the body as specified
like the C<-B> file-test operator), the output is not printed to STDOUT.
Instead, you may want to use output redirection as show above and send
the response body to a file.
http://my-server.com> GET /some-image.jpg >foo.jpg
=head2 Persistent Configuration
As demonstrated above, you can use the C<config> command to change the
behavior of presto. These configuration options are persisted in a
config file specific to the endpoint provided at the command-line and
will be reloaded the next time you invoke presto with the same endpoint.
Current valid config keys are:
=over 4
=item * verbose
Boolean, when enabled, dumps request/response to STDOUT (defaults to "0")
=item * deserialize_response
Must be one of the supported modules (i.e. Data::Dumper or JSON).
Use tab completion to see currently supported values (defaults to "JSON").
=item * binmode
Used to set encoding of STDIN and STDOUT handles (defaults to "utf8")
=back
B<TODO:> provide a means for aliasing endpoints so that configuration
is shared across multiple endpoints.
=head2 History and Scripting
Just like configuration, command history is maintained separately for each
endpoint specified on the command-line and is persisted across sessions
(assuming you have a capable Term::Readline library installed). You can
interrogate the history using the (surprisingly named) C<history> command.
It supports a small subset of the C<bash> history command:
# dump all history
http://my-server.com> history
# dump last 5 entries
http://my-server.com> history 5
lib/App/Presto.pm view on Meta::CPAN
is => 'lazy',
);
sub _build_client {
my $self = shift;
return App::Presto::Client->new(config => $self->config);
}
has config => (
is => 'rw',
handles => ['endpoint'],
);
has _stash => (
is => 'lazy',
handles => ['stash'],
);
sub _build__stash {
my $self = shift;
return App::Presto::Stash->new;
lib/App/Presto.pm view on Meta::CPAN
},
"history" => {
exclude_from_completion => 1,
exclude_from_history => 1,
desc => "Prints the command history",
args => "[-c] [-d] [number]",
method => sub { shift->history_call(@_) },
doc => "Specify a number to list the last N lines of history Pass -c to clear the command history, -d NUM to delete a single item\n",
},
},
prompt => sprintf( '%s> ', $self->endpoint ),
history_file => $self->config->file('history'),
);
$term->ornaments('md,me,,');
return $term;
}
has command_factory => (
is => 'lazy',
);
sub _build_command_factory { return App::Presto::CommandFactory->new }
lib/App/Presto.pm view on Meta::CPAN
my $SINGLETON;
sub instance {
my $class = shift;
return $SINGLETON ||= $class->new(@_);
}
sub run {
my $class = shift;
my $self = $class->instance;
my @args = shift;
my $config;
if(my $endpoint = shift(@args)){
$self->config( $config = App::Presto::Config->new( endpoint => $endpoint ) );
} else {
die "Base endpoint (i.e. http://some-host.com) must be specified as command-line argument\n";
}
$config->init_defaults;
$self->command_factory->install_commands($self);
my $binmode = $config->get('binmode');
binmode(STDOUT,":encoding($binmode)");
binmode(STDIN,":encoding($binmode)");
lib/App/Presto/Client.pm view on Meta::CPAN
my $uri = $self->_make_uri(shift);
$self->_rest_client->PUT( $uri, shift );
}
sub _make_uri {
my $self = shift;
my $local_uri = shift;
my @args = @_;
my $config = $self->config;
my $endpoint;
$local_uri = '/' if ! defined $local_uri;
$local_uri = URI->new($local_uri);
if ( $local_uri->scheme ) {
$endpoint = $local_uri;
} else {
my $local_path = $local_uri->path;
$endpoint = $config->endpoint;
$endpoint .= '*' unless $endpoint =~ m/\*/;
$endpoint =~ s{\*}{$local_path};
$endpoint .= '?' . $local_uri->query if $local_uri->query;
}
my $u = $self->_append_query_params( $endpoint, @args );
return "$u";
}
sub _append_query_params {
my $self = shift;
my $u = URI->new(shift);
my @args = @_;
foreach my $next (@args) {
$u->query_param_append( split( /=/, $next, 2 ) );
}
lib/App/Presto/Config.pm view on Meta::CPAN
package App::Presto::Config;
our $AUTHORITY = 'cpan:MPERRY';
$App::Presto::Config::VERSION = '0.010';
# ABSTRACT: Manage configuration for a given endpoint
use Moo;
use JSON qw(decode_json encode_json);
use File::HomeDir;
use File::Path 2.08 qw(make_path);
has endpoint => (
is => 'rw',
required => 1,
trigger => sub { delete shift->{endpoint_dir} }
);
has config => (
is => 'lazy',
clearer => 'reload',
isa => sub { die "not a HashRef" if ref($_[0]) ne 'HASH'; },
);
sub _build_config {
my $self = shift;
my $config_file = $self->file('config.json');
if (! -e $config_file ) {
lib/App/Presto/Config.pm view on Meta::CPAN
my $json_text = <$fh>;
$config = decode_json( $json_text || '{}' );
1;
} or do {
warn "Ignoring invalid config file $config_file: $@\n";
$config = {};
};
return $config;
}
has endpoint_dir => (
is => 'lazy',
);
sub _build_endpoint_dir {
my $self = shift;
my $root = $ENV{APP_REST_CLI_DIR} || sprintf('%s/.app-presto', File::HomeDir->my_home);
(my $endpoint_dir = lc $self->endpoint) =~ s/\W+/-/g;
my $dir = sprintf( '%s/%s', $root, $endpoint_dir);
if(!-d $dir){
warn "creating directory $dir\n";
make_path($dir);
}
return $dir;
}
sub file {
my $self = shift;
(my $file = shift) =~ s{^[\./]+}{}g; # remove leading dots or slashes
return sprintf('%s/%s', $self->endpoint_dir, $file);
}
sub is_set {
my $self = shift;
my $key = shift;
return exists $self->config->{$key};
}
sub set {
my $self = shift;
my $key = shift;
my $value = shift;
if($key eq 'endpoint'){
return $self->endpoint($value);
}
if(!defined $value){
delete $self->config->{$key};
} else {
$self->config->{$key} = $value;
}
eval {
$self->write_config;
1;
} or do {
lib/App/Presto/Config.pm view on Meta::CPAN
return;
}
sub unset {
my $self = shift;
return $self->set($_[0],undef);
}
sub get {
my $self = shift;
my $key = shift;
return $self->endpoint if $key eq 'endpoint';
return exists $self->config->{$key} ? $self->config->{$key} : undef;
}
sub keys {
my $self = shift;
return keys %{ $self->config };
}
sub write_config {
my $self = shift;
lib/App/Presto/Config.pm view on Meta::CPAN
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
App::Presto::Config - Manage configuration for a given endpoint
=head1 VERSION
version 0.010
=head1 AUTHORS
=over 4
=item *
t/02-config.t view on Meta::CPAN
my $EMPTY = $d . 'empty-config';
my $EXISTING = $d . 'config';
my $FAKE_HOME = $d . 'config/fake-home';
subtest 'empty config' => sub {
local $ENV{APP_REST_CLI_DIR} = $EMPTY;
#pre-clear
remove_tree( $EMPTY, { keep_root => 1 } );
my $config = App::Presto::Config->new( endpoint => 'http://myserver.com' );
is $config->get('endpoint'), 'http://myserver.com', 'endpoint';
my @warnings = capture_warnings {
like $config->endpoint_dir, qr{myserver},
'config dir reflects endpoint name';
};
like $warnings[0], qr{creating directory},
'warnings about creating directory';
@warnings = capture_warnings {
$config->set(foo => 'bar');
};
like $warnings[0], qr{creating new config file}, 'warns about creating new config file';
is $config->get('foo'), 'bar', 'get';
t/02-config.t view on Meta::CPAN
$config->unset('foo');
is_deeply [$config->keys], [], 'empty keys';
# empty it out again
remove_tree( $EMPTY );
};
subtest 'existing config' => sub {
local $ENV{APP_REST_CLI_DIR} = $EXISTING;
my $config = App::Presto::Config->new( endpoint => 'http://myserver.com' );
my @warnings = capture_warnings {
like $config->endpoint_dir, qr{myserver},
'config dir reflects endpoint name';
};
ok !@warnings, 'no warnings' or diag explain $config, \@warnings;
like $config->file('foo'), qr{/foo}, 'file has correct name';
$config->set(endpoint => 'http://anotherserver.com');
like $config->endpoint_dir, qr{anotherserver}, 'new endpoint == new endpoint_dir';
};
subtest 'new endpoint' => sub {
local $ENV{APP_REST_CLI_DIR} = $EXISTING;
my $config = App::Presto::Config->new( endpoint => 'http://myserver.com' );
my @warnings = capture_warnings {
like $config->endpoint_dir, qr{myserver},
'config dir reflects endpoint name';
};
ok !@warnings, 'no warnings' or diag explain $config, \@warnings;
is $config->get('foo'), 1, 'loaded correctly';
like $config->file('foo'), qr{/foo}, 'file has correct name';
$config->set(endpoint => 'http://anotherserver.com');
like $config->endpoint_dir, qr{anotherserver}, 'new endpoint == new endpoint_dir';
};
subtest 'home directory' => sub {
no warnings 'redefine';
*File::HomeDir::my_home = sub { $FAKE_HOME };
my $config = App::Presto::Config->new( endpoint => 'http://myserver.com' );
my @warnings = capture_warnings {
like $config->endpoint_dir, qr{fake-home/\.app-presto/http-myserver-com}, 'created in home directory';
};
ok @warnings, 'warns about creating new directory';
remove_tree $FAKE_HOME;
};
done_testing;
t/04-client.t view on Meta::CPAN
use strict;
use warnings;
use Test::More;
use Test::MockObject;
use App::Presto::Client;
my $config = Test::MockObject->new;
$config->set_always( endpoint => 'http://my-server.com');
my $rest_client = Test::MockObject->new;
$rest_client->set_true('GET','DELETE','PUT','POST','HEAD','request');
my %headers;
$rest_client->mock('addHeader', sub { shift; my($k,$v) = @_; $headers{$k} = $v; });
$rest_client->{_headers} = \%headers;
my $client = App::Presto::Client->new(config=>$config, _rest_client => $rest_client);
isa_ok($client, 'App::Presto::Client');
t/04-client.t view on Meta::CPAN
is $args->[2], 'http://another-server.com/blah', 'allows URI override';
}
$client->PUT('/bar', 'foobar');
{
my ( $m, $args ) = $rest_client->next_call;
is $m, 'PUT', 'rest_client PUT';
is $args->[2], 'foobar', 'PUT body';
}
$config->set_always( endpoint => 'http://my-server.com*.json');
$client->PUT('/bar?blah=1', 'foobar');
{
my ( $m, $args ) = $rest_client->next_call;
is $m, 'PUT', 'rest_client PUT';
is $args->[1], 'http://my-server.com/bar.json?blah=1', 'has suffix + query params';
}
$config->set_always( endpoint => 'http://my-server.com');
$client->HEAD;
{
my ( $m, $args ) = $rest_client->next_call;
is $m, 'HEAD', 'rest_client HEAD (no uri)';
is $args->[1], 'http://my-server.com/', 'default URI';
}
$client->POST('/foo', q({"a":1}));
{