App-Presto

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN

    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  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

README  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

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

bin/presto  view on Meta::CPAN

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

bin/presto  view on Meta::CPAN

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 

bin/presto  view on Meta::CPAN


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}));
{



( run in 0.296 second using v1.01-cache-2.11-cpan-27979f6cc8f )