From 3a1b11f0a8684c7d50d5ccae126f9027b526f10a Mon Sep 17 00:00:00 2001 From: franck cuny Date: Fri, 4 Dec 2009 09:51:31 +0100 Subject: move meta class and method to new file, add meta to handle tests --- lib/MooseX/Net/API/Meta/Class.pm | 34 ++++++++++++++++++++++++++++++++++ lib/MooseX/Net/API/Meta/Method.pm | 18 ++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 lib/MooseX/Net/API/Meta/Class.pm create mode 100644 lib/MooseX/Net/API/Meta/Method.pm (limited to 'lib') diff --git a/lib/MooseX/Net/API/Meta/Class.pm b/lib/MooseX/Net/API/Meta/Class.pm new file mode 100644 index 0000000..80075f8 --- /dev/null +++ b/lib/MooseX/Net/API/Meta/Class.pm @@ -0,0 +1,34 @@ +package MooseX::Net::API::Meta::Class; + +use Moose::Role; +use Moose::Meta::Class; +use MooseX::Types::Moose qw(Str ArrayRef ClassName Object); + +has local_api_methods => ( + traits => ['Array'], + is => 'ro', + isa => ArrayRef [Str], + required => 1, + default => sub { [] }, + auto_deref => 1, + handles => { '_add_api_method' => 'push' }, +); +has local_api_test_methods => ( + traits => ['Array'], + is => 'ro', + isa => ArrayRef [Str], + required => 1, + default => sub { [] }, + auto_deref => 1, + handles => { '_add_api_test_method' => 'push' }, +); + +sub _build_meta_class { + my $self = shift; + return Moose::Meta::Class->create_anon_class( + superclasses => [ $self->method_metaclass ], + cache => 1, + ); +} + +1; diff --git a/lib/MooseX/Net/API/Meta/Method.pm b/lib/MooseX/Net/API/Meta/Method.pm new file mode 100644 index 0000000..74f9a07 --- /dev/null +++ b/lib/MooseX/Net/API/Meta/Method.pm @@ -0,0 +1,18 @@ +package MooseX::Net::API::Meta::Method; + +use Moose; +extends 'Moose::Meta::Method'; + +has description => ( is => 'ro', isa => 'Str' ); +has path => ( is => 'ro', isa => 'Str', required => 1 ); +has method => ( is => 'ro', isa => 'Str', required => 1 ); +has params => ( is => 'ro', isa => 'ArrayRef', required => 0 ); +has required => ( is => 'ro', isa => 'ArrayRef', required => 0 ); + +sub new { + my $class = shift; + my %args = @_; + $class->SUPER::wrap(@_); +} + +1; -- cgit 1.4.1 From 598767795c68b6f4f2fdf3d159ca2e29f1ac44db Mon Sep 17 00:00:00 2001 From: franck cuny Date: Fri, 4 Dec 2009 09:51:51 +0100 Subject: remove meta class and method --- lib/MooseX/Net/API.pm | 141 ++++++++++++++++++++++++++++---------------------- 1 file changed, 79 insertions(+), 62 deletions(-) (limited to 'lib') diff --git a/lib/MooseX/Net/API.pm b/lib/MooseX/Net/API.pm index e1ab774..4e1ae5e 100644 --- a/lib/MooseX/Net/API.pm +++ b/lib/MooseX/Net/API.pm @@ -1,13 +1,17 @@ package MooseX::Net::API; -use Carp; use URI; -use HTTP::Request; +use Carp; use Try::Tiny; +use HTTP::Request; + +use Moose; use Moose::Exporter; use MooseX::Net::API::Error; -use MooseX::Net::API::Role::Deserialize; +use MooseX::Net::API::Meta::Class; +use MooseX::Net::API::Meta::Method; use MooseX::Net::API::Role::Serialize; +use MooseX::Net::API::Role::Deserialize; our $VERSION = '0.01'; @@ -30,6 +34,16 @@ my $reverse_content_type = { Moose::Exporter->setup_import_methods( with_caller => [qw/net_api_method net_api_declare/], ); +sub init_meta { + my ( $me, %options ) = @_; + + my $for = $options{for_class}; + Moose::Util::MetaRole::apply_metaclass_roles( + for_class => $for, + metaclass_roles => ['MooseX::Net::API::Meta::Class'], + ); +} + my ( $do_auth, $auth_method, $deserialize_method ); sub net_api_declare { @@ -39,18 +53,13 @@ sub net_api_declare { my $class = Moose::Meta::Class->initialize($caller); - if ( !$options{base_url} ) { - croak "base_url is missing in your api declaration"; - } - else { - $class->add_attribute( - 'api_base_url', - is => 'ro', - isa => 'Str', - lazy => 1, - default => delete $options{base_url} - ); - } + $class->add_attribute( + 'api_base_url', + is => 'rw', + isa => 'Str', + lazy => 1, + default => delete $options{base_url} || '', + ); if ( !$options{format} ) { croak "format is missing in your api declaration"; @@ -180,37 +189,8 @@ sub net_api_method { my $format = $self->api_format(); $url .= "." . $format if ( $self->api_format_mode() eq 'append' ); my $uri = URI->new($url); + my $res = _request( $self, $format, \%options, $uri, \%args ); - my $req; - my $method = $options{method}; - if ( $method =~ /^(?:GET|DELETE)$/ || $options{params_in_url} ) { - $uri->query_form(%args); - $req = HTTP::Request->new( $method => $uri ); - } - elsif ( $method =~ /^(?:POST|PUT)$/ ) { - $req = HTTP::Request->new( $method => $uri ); - # XXX GNI - use JSON::XS; - $req->content( encode_json \%args ); - } - else { - croak "$method is not defined"; - } - - # XXX check presence content type - $req->header( 'Content-Type' => $list_content_type->{$format} ) - if $self->api_format_mode eq 'content-type'; - - if ($do_auth) { - if ($auth_method) { - $req = $self->$auth_method($req); - } - else { - $req = _do_authentication( $self, $req ); - } - } - - my $res = $self->useragent->request($req); my $content_type = $res->headers->{"content-type"}; $content_type =~ s/(;.+)$//; @@ -249,6 +229,7 @@ sub net_api_method { %options, ), ); + $class->_add_api_method($name); } sub _add_useragent { @@ -277,6 +258,41 @@ sub _add_useragent { ); } +sub _request { + my ( $self, $format, $options, $uri, $args ) = @_; + + my $req; + my $method = $options->{method}; + if ( $method =~ /^(?:GET|DELETE)$/ || $options->{params_in_url} ) { + $uri->query_form(%$args); + $req = HTTP::Request->new( $method => $uri ); + } + elsif ( $method =~ /^(?:POST|PUT)$/ ) { + $req = HTTP::Request->new( $method => $uri ); + + # XXX proper serialisation + use JSON::XS; + $req->content( encode_json $args ); + } + else { + croak "$method is not defined"; + } + + $req->header( 'Content-Type' => $list_content_type->{$format} ) + if $self->api_format_mode eq 'content-type'; + + if ($do_auth) { + if ($auth_method) { + $req = $self->$auth_method($req); + } + else { + $req = _do_authentication( $self, $req ); + } + } + + return $self->useragent->request($req); +} + sub _do_authentication { my ( $caller, $req ) = @_; $req->headers->authorization_basic( $caller->api_username, @@ -305,23 +321,6 @@ sub _do_deserialization { } } -package MooseX::Net::API::Meta::Method; - -use Moose; -extends 'Moose::Meta::Method'; - -has description => ( is => 'ro', isa => 'Str' ); -has path => ( is => 'ro', isa => 'Str', required => 1 ); -has method => ( is => 'ro', isa => 'Str', required => 1 ); -has params => ( is => 'ro', isa => 'ArrayRef', required => 0 ); -has required => ( is => 'ro', isa => 'ArrayRef', required => 0 ); - -sub new { - my $class = shift; - my %args = @_; - $class->SUPER::wrap(@_); -} - 1; __END__ @@ -338,6 +337,7 @@ MooseX::Net::API - Easily create client for net API # we declare an API, the base_url is http://exemple.com/api # the format is json and it will be happened to the query + # You can set base_url later, calling $my_obj->api_base_url('http://..') net_api_declare my_api => ( base_url => 'http://exemple.com/api', format => 'json', @@ -426,6 +426,23 @@ HTTP method (GET, POST, PUT, DELETE) path of the query. +If you defined your path and params like this + + net_api_method user_comments => ( + ... + path => '/user/$user/list/$date/', + params => [qw/user date foo bar/], + ... + ); + +and you call + + $obj->user_comments(user => 'franck', date => 'today', foo => 1, bar => 2); + +the url generetad will look like + + /user/franck/list/today/?foo=1&bar=2 + =item B [arrayref] list of params. -- cgit 1.4.1 From 4ac321283d9b600f8595e334c8c6ce2ed203e922 Mon Sep 17 00:00:00 2001 From: franck cuny Date: Fri, 4 Dec 2009 09:52:06 +0100 Subject: add basic tests --- lib/MooseX/Net/API/Test.pm | 120 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/MooseX/Net/API/Test.pm b/lib/MooseX/Net/API/Test.pm index 2f2e428..e991b7f 100644 --- a/lib/MooseX/Net/API/Test.pm +++ b/lib/MooseX/Net/API/Test.pm @@ -1,13 +1,131 @@ package MooseX::Net::API::Test; +use lib ('t/lib'); +use Try::Tiny; + +use Test::More; +use Moose; use Moose::Exporter; +use MooseX::Net::API::Meta::Class; +use MooseX::Net::API::Meta::Method; + +Moose::Exporter->setup_import_methods( + with_caller => [qw/test_api_method test_api_declare run/] ); + +my $api_to_test; + +sub init_meta { + my ( $me, %options ) = @_; + + my $for = $options{for_class}; + Moose::Util::MetaRole::apply_metaclass_roles( + for_class => $for, + metaclass_roles => ['MooseX::Net::API::Meta::Class'], + ); +} + +my $list_content_type = { + 'json' => 'application/json', + 'yaml' => 'text/x-yaml', + 'xml' => 'text/xml', +}; + +my $tests_count = 0; + +sub test_api_declare { + my $caller = shift; + my $name = shift; + my %options = @_; + + unless ( Class::MOP::is_class_loaded($name) ) { + Class::MOP::load_class($name); + } -Moose::Exporter->setup_import_methods( with_caller => [qw/test_api_method/] ); + $api_to_test = $name; + + if ( $options{catalyst} ) { + my $app = $options{catalyst_app_name}; + + Class::MOP::load_class("HTTP::Request"); + Class::MOP::load_class("Catalyst::Test"); + + Catalyst::Test->import($app); + + my $res = __PACKAGE__->meta->remove_method('_request'); + MooseX::Net::API->meta->add_method( + '_request' => sub { + my ( $class, $format, $options, $uri, $args ) = @_; + my $method = $options->{method}; + + my $res; + if ( $method =~ /^(?:GET|DELETE)$/ + || $options->{params_in_url} ) + { + $uri->query_form(%$args); + my $req = HTTP::Request->new( $method => $uri ); + $req->header( + 'Content-Type' => $list_content_type->{$format} ); + $res = request($req); + } + else { + my $req = HTTP::Request->new( $method => $uri ); + $req->header( + 'Content-Type' => $list_content_type->{$format} ); + $req->header( 'Content' => Dump $args); + $res = request($req); + } + return $res; + } + ); + } +} sub test_api_method { my $caller = shift; my $name = shift; my %options = @_; + + my $meta = $api_to_test->meta; + my $method = $meta->find_method_by_name($name); + + if ( !$method ) { + die "method $name does not exists\n"; + } + + my $class = Moose::Meta::Class->initialize($caller); + foreach my $test_name ( keys %{ $options{tests} } ) { + foreach my $test ( @{ $options{tests}{$test_name} } ) { + __PACKAGE__->meta->add_method( + $test_name => sub { + my $res = $method->execute( $api_to_test->new ); + if (ref $test eq 'HASH') { + my $action = $test->{test}; + my $result = $test->{expected}; + # XXX sucky sucky sucky + if ( $action eq 'is_deeply' ) { + is_deeply( $res, $result ); + } + }else{ + if ($test eq 'ok') { + ok $res; + } + } + } + ); + $class->_add_api_test_method($test_name); + } + } +} + +sub run { + my $caller = shift; + + my $class = Moose::Meta::Class->initialize($caller); + my @test_methods = $class->local_api_test_methods(); + foreach my $m (@test_methods) { + my $method = __PACKAGE__->meta->find_method_by_name($m); + $method->execute(); + } } 1; -- cgit 1.4.1