From 3e3dc478fc9b4eb90681df89156dfcc8f7f81481 Mon Sep 17 00:00:00 2001 From: franck cuny Date: Mon, 13 Sep 2010 13:31:56 +0200 Subject: initial import --- lib/Net/HTTP/Spore/Meta/Class.pm | 13 +++ lib/Net/HTTP/Spore/Meta/Method.pm | 159 ++++++++++++++++++++++++++++++++ lib/Net/HTTP/Spore/Meta/Method/Spore.pm | 113 +++++++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 lib/Net/HTTP/Spore/Meta/Class.pm create mode 100644 lib/Net/HTTP/Spore/Meta/Method.pm create mode 100644 lib/Net/HTTP/Spore/Meta/Method/Spore.pm (limited to 'lib/Net/HTTP/Spore/Meta') diff --git a/lib/Net/HTTP/Spore/Meta/Class.pm b/lib/Net/HTTP/Spore/Meta/Class.pm new file mode 100644 index 0000000..4ddd5c6 --- /dev/null +++ b/lib/Net/HTTP/Spore/Meta/Class.pm @@ -0,0 +1,13 @@ +package Net::HTTP::Spore::Meta::Class; + +# ABSTRACT: metaclass for all API client + +use Moose::Role; + +with qw/Net::HTTP::Spore::Meta::Method::Spore/; + +1; + +=head1 SYNOPSIS + +=head1 DESCRIPTION diff --git a/lib/Net/HTTP/Spore/Meta/Method.pm b/lib/Net/HTTP/Spore/Meta/Method.pm new file mode 100644 index 0000000..0087147 --- /dev/null +++ b/lib/Net/HTTP/Spore/Meta/Method.pm @@ -0,0 +1,159 @@ +package Net::HTTP::Spore::Meta::Method; + +# ABSTRACT: create api method + +use Moose; +use Moose::Util::TypeConstraints; + +use MooseX::Types::Moose qw/Str Int ArrayRef/; +use MooseX::Types::URI qw/Uri/; + +extends 'Moose::Meta::Method'; +use Net::HTTP::Spore::Response; + +subtype UriPath + => as 'Str' + => where { $_ =~ m!^/! } + => message {"path must start with /"}; + +enum Method => qw(HEAD GET POST PUT DELETE); + +has path => ( is => 'ro', isa => 'UriPath', required => 1 ); +has method => ( is => 'ro', isa => 'Method', required => 1 ); +has description => ( is => 'ro', isa => 'Str', predicate => 'has_description' ); + +has authentication => ( + is => 'ro', + isa => 'Bool', + predicate => 'has_authentication', + default => 0 +); +has api_base_url => ( + is => 'ro', + isa => Uri, + coerce => 1, + predicate => 'has_api_base_url', +); +has expected => ( + traits => ['Array'], + is => 'ro', + isa => ArrayRef [Int], + auto_deref => 1, + required => 0, + predicate => 'has_expected', + handles => {find_expected_code => 'grep',}, +); +has params => ( + traits => ['Array'], + is => 'ro', + isa => ArrayRef [Str], + required => 0, + default => sub { [] }, + auto_deref => 1, + handles => {find_request_parameter => 'first',} +); +has required => ( + traits => ['Array'], + is => 'ro', + isa => ArrayRef [Str], + default => sub { [] }, + auto_deref => 1, + required => 0, +); +has documentation => ( + is => 'ro', + isa => 'Str', + lazy => 1, + default => sub { + my $self = shift; + my $doc; + $doc .= "name: " . $self->name . "\n"; + $doc .= "description: " . $self->description . "\n" + if $self->has_description; + $doc .= "method: " . $self->method . "\n"; + $doc .= "path: " . $self->path . "\n"; + $doc .= "arguments: " . join(', ', $self->params) . "\n" + if $self->params; + $doc .= "required: " . join(', ', $self->required) . "\n" + if $self->required; + $doc; + } +); + +sub wrap { + my ( $class, %args ) = @_; + + my $code = sub { + my ( $self, %method_args ) = @_; + + my $method = $self->meta->find_spore_method_by_name( $args{name} ); + + my $payload = + ( defined $method_args{spore_payload} ) + ? delete $method_args{spore_payload} + : delete $method_args{payload}; + + foreach my $required ( $method->required ) { + if ( !grep { $required eq $_ } keys %method_args ) { + die Net::HTTP::Spore::Response->new( + 599, + [], + { + error => + "$required is marked as required but is missing", + } + ); + } + } + + my $params; + foreach (keys %method_args) { + push @$params, $_, $method_args{$_}; + } + + my $api_base_url = + $method->has_api_base_url + ? $method->api_base_url + : $self->api_base_url; + + my $env = { + REQUEST_METHOD => $method->method, + SERVER_NAME => $api_base_url->host, + SERVER_PORT => $api_base_url->port, + SCRIPT_NAME => ( + $api_base_url->path eq '/' + ? '' + : $api_base_url->path + ), + PATH_INFO => $method->path, + REQUEST_URI => '', + QUERY_STRING => '', + SERVER_PROTOCOL => $api_base_url->scheme, + HTTP_USER_AGENT => $self->api_useragent->agent, + 'spore.expected' => [ $method->expected ], + 'spore.authentication' => $method->authentication, + 'spore.params' => $params, + 'spore.payload' => $payload, + 'spore.errors' => *STDERR, + 'spore.url_scheme' => $api_base_url->scheme, + }; + + my $response = $self->http_request($env); + my $code = $response->status; + + die $response if ( $method->has_expected + && !$method->find_expected_code( sub { /$code/ } ) ); + + $response; + }; + $args{body} = $code; + + $class->SUPER::wrap(%args); +} + +1; + +=head1 SYNOPSIS + +=head1 DESCRIPTION + diff --git a/lib/Net/HTTP/Spore/Meta/Method/Spore.pm b/lib/Net/HTTP/Spore/Meta/Method/Spore.pm new file mode 100644 index 0000000..4c6fe71 --- /dev/null +++ b/lib/Net/HTTP/Spore/Meta/Method/Spore.pm @@ -0,0 +1,113 @@ +package Net::HTTP::Spore::Meta::Method::Spore; + +# ABSTRACT: declare API method + +use Moose::Role; +use Net::HTTP::API::Error; +use Net::HTTP::Spore::Meta::Method; +use MooseX::Types::Moose qw/Str ArrayRef/; + +has local_spore_methods => ( + traits => ['Array'], + is => 'rw', + isa => ArrayRef [Str], + required => 1, + default => sub { [] }, + auto_deref => 1, + handles => { + _find_spore_method_by_name => 'first', + _add_spore_method => 'push', + get_all_spore_methods => 'elements', + }, +); + +sub find_spore_method_by_name { + my ($meta, $name) = @_; + my $method_name = $meta->_find_spore_method_by_name(sub {/^$name$/}); + return unless $method_name; + my $method = $meta->find_method_by_name($method_name); + if ($method->isa('Class::MOP::Method::Wrapped')) { + return $method->get_original_method; + } + else { + return $method; + } +} + +sub remove_spore_method { + my ($meta, $name) = @_; + my @methods = grep { !/$name/ } $meta->get_all_spore_methods; + $meta->local_spore_methods(\@methods); + $meta->remove_method($name); +} + +before add_spore_method => sub { + my ($meta, $name) = @_; + if ($meta->_find_spore_method_by_name(sub {/^$name$/})) { + die Net::HTTP::API::Error->new( + reason => "method '$name' is already declared in " . $meta->name); + } +}; + +sub add_spore_method { + my ($meta, $name, %options) = @_; + + my $code = delete $options{code}; + + $meta->add_method( + $name, + Net::HTTP::Spore::Meta::Method->wrap( + name => $name, + package_name => $meta->name, + body => $code, + %options + ), + ); + $meta->_add_spore_method($name); +} + +after add_spore_method => sub { + my ($meta, $name) = @_; + $meta->add_before_method_modifier( + $name, + sub { + my $self = shift; + die Net::HTTP::API::Error->new( + reason => "'api_base_url' have not been defined") + unless $self->api_base_url; + } + ); +}; + +1; + +=head1 SYNOPSIS + + my $api_client = MyAPI->new; + + my @methods = $api_client->meta->get_all_api_methods(); + + my $method = $api_client->meta->find_spore_method_by_name('users'); + + $api_client->meta->remove_spore_method($method); + + $api_client->meta->add_spore_method('users', sub {...}, + description => 'this method does...',); + +=head1 DESCRIPTION + +=method get_all_spore_methods + +Return a list of net api methods + +=method find_spore_method_by_name + +Return a net api method + +=method remove_spore_method + +Remove a net api method + +=method add_spore_method + +Add a net api method -- cgit v1.2.3