File Coverage

lib/AnyEvent/RPC.pm
Criterion Covered Total %
statement 12 75 16.0
branch 0 38 0.0
condition 0 23 0.0
subroutine 4 13 30.7
pod 0 6 0.0
total 16 155 10.3


line stmt bran cond sub pod time code
1             package AnyEvent::RPC;
2              
3 1     1   27758 use 5.006000;
  1         3  
  1         32  
4 1     1   6 use common::sense 2;
  1         14  
  1         7  
5             m{
6             use strict;
7             use warnings;
8             }; # Until cpants will know it make strict
9 1     1   64 use Carp;
  1         10  
  1         562  
10             =head1 NAME
11              
12             AnyEvent::RPC - Abstract framework for Asyncronous RPC clients
13              
14             =cut
15              
16             our $VERSION = '0.05';
17              
18             =head1 SYNOPSIS
19              
20             use AnyEvent::RPC;
21            
22             my $rpc = AnyEvent::RPC->new(
23             host => 'your.api.host',
24             port => 8080,
25             base => '/api/rest/',
26              
27             type => 'REST', # or type => '+AnyEvent::RPC::Enc::REST',
28             )
29            
30             $rpc->req( # will be called as GET http://your.api.host:8080/api/rest/method/path/args?query=param
31             call => [ method => qw(path args)],
32             query => { query => 'param' },
33             cb => sub { # ( response, code, error )
34             if (my $response = shift) {
35             #
36             } else {
37             my ($code,$err) = @_;
38             }
39             },
40             );
41              
42             =cut
43              
44 0     0 0   sub ua { shift->{ua} }
45 0     0 0   sub encoder { shift->{encoder} }
46              
47             sub new {
48 0     0 0   my $pkg = shift;
49 0           my $self = bless {}, $pkg;
50 0           $self->init(@_);
51 0           $self->components;
52 0           $self;
53             }
54              
55             sub init {
56 0     0 0   my $self = shift;
57 0     0     local $SIG{__WARN__} = sub { local $_ = shift; s{\n$}{};carp $_ };
  0            
  0            
  0            
58 0           my %args = (
59             base => '/',
60             @_
61             );
62 0           @$self{keys %args} = values %args;
63 0   0       $self->{$_} or croak "$_ not defined" for qw(host);
64 0   0       $self->{useragent} ||= 'AnyEvent::RPC/'.$AnyEvent::RPC::VERSION;
65 0           return;
66             }
67              
68             sub components {
69 0     0 0   my $self = shift;
70 0           my $package = ref $self;
71 0 0         unless ( ref $self->{encoder} ) {
72 0           $self->{encoder} = $self->_load(
73             '::Enc', $self->{encoder}, 'REST',
74             debug => $self->{debug},
75             );
76             }
77 0 0         unless ( ref $self->{ua} ) {
78             $self->{ua} = $self->_load(
79             '::UA', $self->{ua}, '',
80 1   0 1   6 ua => $self->{useragent} || $package.'/'.( do{ no strict 'refs'; ${$package.'::VERSION'} } || $VERSION ),
  1         2  
  1         997  
  0            
81             timeout => $self->{timeout},
82             debug => $self->{debug},
83             );
84             }
85 0           return;
86             }
87              
88             sub _load {
89 0     0     my $pkg = shift;
90 0           my ($suffix,$req,$default,@args) = @_;
91 0           my $prefix = __PACKAGE__.$suffix;
92 0 0         if (defined $req) {
93 0 0         $req =~ s{^\+}{} or $req = $prefix.'::'.$req;
94             } else {
95 0 0         $req = $prefix.($default ? '::'.$default : '' );
96             }
97             eval {
98 0 0         $req->can('new')
99             or require join '/', split '::', $req.'.pm';
100 0           1;
101             }
102 0 0         or do {
103 0           croak "Can't load $req: $@\n";
104             };
105 0 0         return $req->new(@args) or croak "$req not created";
106             }
107              
108             sub req {
109 0     0 0   my $self = shift;
110 0           my %args = @_;
111 0 0         croak("req have no cb and useragent is async") unless $args{cb};
112             #my ( $methodname, @params ) = @{ $args{call} };
113             #my $uri = "$url#$methodname";
114 0           my %req = $self->encoder->request( $self, %args );
115             #warn "request: ".dumper(\%req) if $args{debug} or $self->{debug} > 2;
116              
117             #my $start = time;
118 0           my @data;
119             #warn "Call $body";
120 0           $self->ua->call(
121             ($args{method} || $req{method} || 'POST') => $req{uri},
122             headers => {
123 0           exists $req{headers} ? ( %{$req{headers}} ) : (),
124             exists $args{headers} ? ( %{$args{headers}} ) : (),
125             },
126             exists $req{body} ? (body => $req{body}) : (),
127             cb => sub {
128 0     0     my $res = shift;
129 0           my @rv = $self->encoder->decode($self, $res);
130 0           $args{cb}(@rv);
131 0           return;
132 0           my @data;
133             {
134 0           ( my $status = $res->status_line )=~ s/:?\s*$//s;
  0            
135 0 0 0       $res->code == 200 or #$args{cb}(undef);
136             @data =
137             (rpcfault( $res->code, "Call to $req{uri} failed: $status" ))
138             and last;
139 0           my $text = $res->content;
140 0 0 0       length($text) and $text =~ /^\s*<\?xml/s or @data =
      0        
141             ({fault=>{ faultCode => 499, faultString => "Call to $req{uri} failed: Response is not an XML: \"$text\"" }})
142             and last;
143 0 0 0       eval {
144 0           @data = $self->encoder->decode( $text );
145 0           1;
146             } or @data =
147             ({fault=>{ faultCode => 499, faultString => "Call to $req{uri} failed: Bad Response: $@, \"$text\"" }})
148             and last;
149             }
150             #warn "Have data @data";
151 0 0         if ($args{cb}) {{
  0            
152             #local $faultCode = $data[0]{fault}{faultCode} if ref $data[0] eq 'HASH' and exists $data[0]{fault};
153 0           $args{cb}(@data);
154 0           return;
155             }}
156             },
157 0 0 0       );
    0          
    0          
158 0 0 0       $args{cb} and defined wantarray and carp "Useless use of return value for ".__PACKAGE__."->call(cb)";
159 0 0         return if $args{cb};
160             #if ( ref $data[0] eq 'HASH' and exists $data[0]{fault} ) {
161             # $faultCode = $data[0]{fault}{faultCode};
162             # croak( "Remote Error [$data[0]{fault}{faultCode}]: ".$data[0]{fault}{faultString} );
163             #}
164 0 0         return @data == 1 ? $data[0] : @data;
165             }
166              
167              
168             =head1 AUTHOR
169              
170             Mons Anderson, C<< >>
171              
172             =head1 COPYRIGHT & LICENSE
173              
174             Copyright 2009 Mons Anderson, all rights reserved.
175              
176             This program is free software; you can redistribute it and/or modify it
177             under the same terms as Perl itself.
178              
179             =cut
180              
181             1; # End of AnyEvent::RPC