summary refs log tree commit diff
diff options
context:
space:
mode:
authorfranck cuny <franck@lumberjaph.net>2010-10-25 17:23:14 +0200
committerfranck cuny <franck@lumberjaph.net>2010-10-25 17:23:14 +0200
commit6c5dba1f8d4d012cf4a9d1e10fd20094a737b071 (patch)
tree47c8ba1b25126557e3504bd1d600a335ad87ecc0
parentupdate tests to use the dummy api, and replace api_base_url with base_url (diff)
downloadnet-http-spore-6c5dba1f8d4d012cf4a9d1e10fd20094a737b071.tar.gz
api spec can be modularized:
* add two new methods: new_from_specs and new_from_strings
  theses two methods can receive more than one description to create a client
* add tests and api desc
-rw-r--r--lib/Net/HTTP/Spore.pm108
-rw-r--r--t/specs/api2.json20
-rw-r--r--t/spore/01_new_from_string.t68
3 files changed, 155 insertions, 41 deletions
diff --git a/lib/Net/HTTP/Spore.pm b/lib/Net/HTTP/Spore.pm
index 34f4740..03606a0 100644
--- a/lib/Net/HTTP/Spore.pm
+++ b/lib/Net/HTTP/Spore.pm
@@ -8,70 +8,124 @@ use IO::All;
 use JSON;
 use Carp;
 use Try::Tiny;
+use Scalar::Util;
 
 use Net::HTTP::Spore::Core;
 
 our $VERSION = 0.01;
 
+# XXX should we let the possibility to override this super class, or add
+# another superclasses?
+
 sub new_from_string {
     my ($class, $string, %args) = @_;
 
-    my $spec;
+    my $spore_class =
+      Class::MOP::Class->create_anon_class(
+        superclasses => ['Net::HTTP::Spore::Core'] );
 
-    try {
-        $spec = JSON::decode_json($string);
-    }catch{
-        Carp::confess("unable to parse JSON spec: ".$_);
-    };
+    my $spore_object = _attach_spec_to_class($string, \%args, $spore_class);
+
+    return $spore_object;
+}
+
+sub new_from_strings {
+    my $class = shift;
 
-    my ( $spore_class, $spore_object );
-    # XXX should we let the possibility to override this super class, or add
-    # another superclasses?
+    my $opts;
+    if (ref ($_[-1]) eq 'HASH') {
+        $opts = pop @_;
+    }
+    my @strings = @_;
 
-    $spore_class =
+    my $spore_class =
       Class::MOP::Class->create_anon_class(
         superclasses => ['Net::HTTP::Spore::Core'] );
 
+    my $spore_object = undef;
+    foreach my $string (@strings) {
+        $spore_object = _attach_spec_to_class($string, $opts, $spore_class, $spore_object);
+    }
+    return $spore_object;
+}
+
+sub new_from_spec {
+    my ( $class, $spec_file, %args ) = @_;
+
+    Carp::confess("specification file is missing") unless $spec_file;
+
+    my $content = _read_spec($spec_file);
+
+    $class->new_from_string( $content, %args );
+}
+
+sub new_from_specs {
+    my $class = shift;
+
+    my $opts;
+    if (ref ($_[-1]) eq 'HASH') {
+        $opts = pop @_;
+    }
+    my @specs = @_;
+
+    my @strings;
+    foreach my $spec (@specs) {
+        push @strings,_read_spec($spec);
+    }
+
+    $class->new_from_strings(@strings, $opts);
+}
+
+sub _attach_spec_to_class {
+    my ( $string, $opts, $class, $object ) = @_;
+
+    my $spec;
+    try {
+        $spec = JSON::decode_json($string);
+    }
+    catch {
+        Carp::confess( "unable to parse JSON spec: " . $_ );
+    };
+
     try {
         my $base_url;
-        if ( $spec->{base_url} && !$args{base_url} ) {
-            $args{base_url} = $spec->{base_url};
+        if ( $spec->{base_url} && !$opts->{base_url} ) {
+            $opts->{base_url} = $spec->{base_url};
         }
-        elsif ( !$args{base_url} ) {
+        elsif ( !$opts->{base_url} ) {
             die "base_url is missing!";
         }
 
         if ( $spec->{formats} ) {
-            $args{formats} = $spec->{formats};
+            $opts->{formats} = $spec->{formats};
         }
 
         if ( $spec->{authentication} ) {
-            $args{authentication} = $spec->{authentication};
+            $opts->{authentication} = $spec->{authentication};
         }
 
-        $spore_object = $spore_class->new_object(%args);
-        $spore_object = _add_methods( $spore_object, $spec->{methods} );
-
+        if ( !$object ) {
+            $object = $class->new_object(%$opts);
+        }
+        $object = _add_methods( $object, $spec->{methods} );
     }
     catch {
         Carp::confess( "unable to create new Net::HTTP::Spore object: " . $_ );
     };
 
-    return $spore_object;
+    return $object;
 }
 
-sub new_from_spec {
-    my ( $class, $spec_file, %args ) = @_;
-
-    Carp::confess("specification file is missing") unless $spec_file;
+sub _read_spec {
+    my $spec_file = shift;
 
-    my ( $content, $spec );
+    my $content;
 
     if ( $spec_file =~ m!^http(s)?://! ) {
         my $uri = URI->new($spec_file);
-        my $req = HTTP::Request->new(GET => $spec_file);
+        my $req = HTTP::Request->new( GET => $spec_file );
         my $ua  = LWP::UserAgent->new();
-        my $res = $ua->request( $req );
+        my $res = $ua->request($req);
         $content = $res->content;
     }
     else {
@@ -81,7 +135,7 @@ sub new_from_spec {
         $content < io($spec_file);
     }
 
-    $class->new_from_string( $content, %args );
+    return $content;
 }
 
 sub _add_methods {
diff --git a/t/specs/api2.json b/t/specs/api2.json
new file mode 100644
index 0000000..7341405
--- /dev/null
+++ b/t/specs/api2.json
@@ -0,0 +1,20 @@
+{
+    "name": "Test API",
+    "methods" : {
+        "get_projects" : {
+            "authentication": true,
+            "path" : "/projects/show",
+            "method" : "GET",
+            "optional_params" : [
+                "name"
+            ]
+        },
+        "get_project_info" : {
+            "required_params" : [
+                "project"
+            ],
+            "path" : "/project/show",
+            "method" : "GET"
+        }
+    }
+}
diff --git a/t/spore/01_new_from_string.t b/t/spore/01_new_from_string.t
index e9d9357..6a21994 100644
--- a/t/spore/01_new_from_string.t
+++ b/t/spore/01_new_from_string.t
@@ -3,18 +3,39 @@ use warnings;
 use Test::More;
 use Test::Exception;
 
-plan tests => 14;
+plan tests => 27;
 
+use JSON;
 use IO::All;
 use Net::HTTP::Spore;
 
 my $api_spec = 't/specs/api.json';
+my $api2_spec = 't/specs/api2.json';
+
 my %args = ( base_url => 'http://localhost/', );
 
 my $github_spec =
   "http://github.com/franckcuny/spore/raw/master/services/github.json";
 
-my $content < io($api_spec);
+my $api_ok = {
+    base_url => "http://services.org/restapi",
+    methods  => { get_info => { method => 'GET', path => '/show' } },
+};
+
+my $second_api = {
+    base_url => "http://services.org/restapi",
+    methods  => { list_users => { method => 'GET', path => '/users' } },
+};
+
+my $api_without_path = {
+    base_url => "http://services.org/restapi",
+    methods  => { get_info => { method => 'GET' } },
+};
+
+my $api_without_method = {
+    base_url => "http://services.org/restapi",
+    methods  => { get_info => { method => 'PET', path => '/show' } },
+};
 
 dies_ok { Net::HTTP::Spore->new_from_spec };
 like $@, qr/specification file is missing/;
@@ -22,31 +43,50 @@ like $@, qr/specification file is missing/;
 dies_ok { Net::HTTP::Spore->new_from_spec( "/foo/bar/baz", ) };
 like $@, qr/does not exists/;
 
-dies_ok { Net::HTTP::Spore->new_from_spec( $api_spec, ) };
+dies_ok { Net::HTTP::Spore->new_from_spec( $api_spec ) };
 like $@, qr/base_url is missing/;
 
 ok my $client = Net::HTTP::Spore->new_from_spec( $api_spec, %args );
-ok $client = Net::HTTP::Spore->new_from_string( $content, %args );
+ok $client =
+  Net::HTTP::Spore->new_from_string( JSON::encode_json($api_ok), %args );
+ok $client->meta->_find_spore_method_by_name(sub{/^get_info$/});
 
 SKIP: {
-    skip "require RUN_HTTP_TEST", 1 unless $ENV{RUN_HTTP_TEST};
+    skip "require RUN_HTTP_TEST", 2 unless $ENV{RUN_HTTP_TEST};
     ok $client = Net::HTTP::Spore->new_from_spec( $github_spec, %args );
+    ok $client->meta->_find_spore_method_by_name(sub{/^user_search$/});
 }
 
 dies_ok {
-    Net::HTTP::Spore->new_from_string(
-'{"base_url" : "http://services.org/restapi/","methods" : { "get_info" : { "method" : "GET" } } }'
-    );
+    Net::HTTP::Spore->new_from_string( JSON::encode_json($api_without_path) );
 };
 like $@, qr/Attribute \(path\) is required/;
 
 dies_ok {
-    Net::HTTP::Spore->new_from_string(
-'{"base_url" : "http://services.org/restapi/","methods" : { "get_info" : { "method" : "PET", "path":"/info" } } }'
-    );
+    Net::HTTP::Spore->new_from_string(JSON::encode_json($api_without_method));
 };
 like $@, qr/Attribute \(method\) does not pass the type constraint/;
 
-ok $client = Net::HTTP::Spore->new_from_string(
-'{"base_url" : "http://services.org/restapi/","methods" : { "get_info" : { "path" : "/show", "method" : "GET" } } }'
-);
+ok $client = Net::HTTP::Spore->new_from_string(JSON::encode_json($api_ok));
+ok $client->meta->_find_spore_method_by_name(sub{/^get_info$/});
+
+dies_ok {
+    Net::HTTP::Spore->new_from_strings('/a/b/c', '/a/b/c');
+};
+
+for ( {}, { base_url => 'http://localhost/api' } ) {
+    ok $client = Net::HTTP::Spore->new_from_strings( JSON::encode_json($api_ok),
+        JSON::encode_json($second_api), $_ );
+    ok $client->meta->_find_spore_method_by_name( sub { /^get_info$/ } );
+    ok $client->meta->_find_spore_method_by_name( sub { /^list_users$/ } );
+}
+
+dies_ok {
+    $client = Net::HTTP::Spore->new_from_specs($api_spec, $api2_spec);
+};
+like $@, qr/base_url is missing/;
+
+ok $client =
+  Net::HTTP::Spore->new_from_specs( $api_spec, $api2_spec,
+    { base_url => 'http://localhost' } );
+