File Coverage

blib/lib/JSON/RPC2/AnyEvent/Client.pm
Criterion Covered Total %
statement 76 102 74.5
branch 15 28 53.5
condition 0 11 0.0
subroutine 18 26 69.2
pod 1 1 100.0
total 110 168 65.4


line stmt bran cond sub pod time code
1             package JSON::RPC2::AnyEvent::Client;
2 3     3   308633 use 5.008005;
  3         20  
3 3     3   12 use strict;
  3         5  
  3         45  
4 3     3   10 use warnings;
  3         5  
  3         59  
5              
6 3     3   1451 use utf8;
  3         34  
  3         12  
7 3     3   678 use AnyEvent::Handle;
  3         21704  
  3         67  
8 3     3   1605 use AnyEvent::HTTP;
  3         20761  
  3         184  
9 3     3   1243 use JSON::RPC2::Client;
  3         13049  
  3         91  
10 3     3   16 use Scalar::Util qw(weaken);
  3         6  
  3         3281  
11              
12             our $VERSION = "0.04";
13              
14             our $AUTOLOAD; # it's a package global
15              
16             my @remappable = qw( service named listed destroy );
17              
18             sub new {
19 2     2 1 4007647 my $class = shift;
20             my $self = bless {
21             client => JSON::RPC2::Client->new(),
22             call => 'call',
23             @_,
24             remappable => {},
25             cb => {},
26 0     0   0 on_error => sub{ warn shift . ' at ' . join(' ',caller) }
27 2         30 }, $class;
28 2 50       79 $self->{call} = 'call' if 'listed' eq $self->{call};
29 2 50       9 $self->{call} = 'call_named' if 'named' eq $self->{call};
30 2         8 for( @remappable ) {
31 8 50       22 if( exists $self->{$_} ) {
32 0         0 $self->{remappable}->{ $self->{$_} } = '__'.$_
33             } else {
34 8         26 $self->{remappable}->{ $_ } = '__'.$_
35             }
36             }
37 2 100       9 if ( $self->{url} ) {
38 1         16 $self->{request_fn} = \&JSON::RPC2::AnyEvent::Client::__request_http;
39             } else {
40 1         7 $self->__connect_tcp;
41 1         595 $self->{request_fn} = \&JSON::RPC2::AnyEvent::Client::__request_tcp;
42             }
43 2         12 $self;
44             }
45              
46             sub __connect_tcp {
47 1     1   17 my $self = shift;
48 1         6 weaken($self);
49 1 50       5 return if $self->{http};
50             $self->{handle} = new AnyEvent::Handle
51             connect => [ $self->{host}, $self->{port} ],
52             on_error => sub {
53 0   0 0   0 my $url = 'url '.($self->{host}||'').':'.($self->{port}||'').' ';
      0        
54 0         0 $self->__fail_error($url . $!);
55 0 0       0 $self->{handle}->destroy if $self->{handle}; # explicitly destroy
56             },
57             on_eof => sub {
58 0   0 0   0 my $url = 'url '.($self->{host}||'').':'.($self->{port}||'').' ';
      0        
59 0         0 $self->__fail_error("$url CONNECTION CLOSED $!");
60 0 0       0 $self->{handle}->destroy if $self->{handle}; # explicitly destroy
61 1         19 };
62             }
63              
64             sub __named {
65 0     0   0 my $self = shift;
66 0         0 $self->{call} = 'call_named';
67 0         0 $self;
68             }
69              
70             sub __listed {
71 0     0   0 my $self = shift;
72 0         0 $self->{call} = 'call';
73 0         0 $self;
74             }
75              
76             sub __service {
77 0     0   0 my $self = shift;
78 0         0 $self->{service} = shift;
79 0         0 $self;
80             }
81              
82             sub __fail_error {
83 0     0   0 my ( $self, $error ) = @_;
84 0         0 $self->{on_error}->( $error . ' at ' . join(' ', caller) );
85 0         0 foreach my $call_id ( keys %{$self->{cb}} ) {
  0         0  
86 0         0 my $cb = delete $self->{cb}->{$call_id};
87 0         0 $cb->( $error );
88             }
89             }
90              
91             sub AUTOLOAD {
92 4     4   4191 my $self = shift;
93              
94 4         9 my $method = $AUTOLOAD;
95 4         33 $method =~ s/(.*):://g;
96              
97 4 100       21 if( exists $self->{remappable}->{$method} ) {
98 2         6 $method = $self->{remappable}->{$method};
99 2         11 return $self->$method( @_ );
100             }
101              
102 2         5 my $cb = pop;
103              
104 2 50       8 $method = $self->{service} ? $self->{service} . '.' . $method : $method;
105              
106 2         4 my $call = $self->{call};
107              
108 2         21 my ( $json_request, $call_id ) = $self->{client}->$call( $method, @_ );
109              
110 2         110 $self->{cb}->{$call_id} = $cb;
111              
112 2         10 $self->{request_fn}->( $self, $json_request );
113              
114 2         807 return $call_id;
115             }
116              
117             sub __request_tcp {
118 1     1   2 my ( $self, $json_request ) = @_;
119              
120 1         7 $self->{handle}->push_write( $json_request );
121              
122             $self->{handle}->push_read( json => sub{
123 1     1   2539 my ( $handle, $hash ) = @_;
124 1         5 my ( $failed, $result, $error, $call_id ) = $self->{client}->response( $hash );
125 1 50       43 return $self->__fail_error( $failed ) if $failed;
126 1         4 $self->__do_callback( $call_id, $failed, $result, $error );
127 1         19 } );
128             }
129              
130             sub __request_http {
131 1     1   2 my ( $self, $json_request ) = @_;
132              
133             http_post $self->{url}, $json_request, sub {
134 1     1   8709 my ( $resp, $hdr ) = @_;
135              
136 1 50       7 unless ( $hdr->{Status} =~ /^2/ ) {
137 0         0 return $self->__fail_error( "$hdr->{Status} $hdr->{Reason}" );
138             }
139              
140             my ( $failed, $result, $error, $call_id ) =
141 1         8 $self->{client}->response( $resp );
142              
143 1 50       51 return $self->__fail_error( $failed ) if $failed;
144              
145 1         6 $self->__do_callback( $call_id, $failed, $result, $error );
146 1         11 };
147             }
148              
149             sub __do_callback {
150 2     2   6 my ( $self, $call_id, $failed, $result, $error ) = @_;
151 2         7 my $cb = delete $self->{cb}->{$call_id};
152 2 50       8 if( $self->{simplify_errors} ) {
153 0   0     0 my $err = $failed || $error && $error->{message};
154 0         0 $cb->( $err, $result );
155             } else {
156 2         8 $cb->( $failed, $result, $error );
157             }
158             }
159              
160             # This DESTROY-pattern originates from AnyEvent::Handle code.
161             sub DESTROY {
162 2     2   6 my ($self) = @_;
163 2 100       11 $self->{handle}->destroy() if $self->{handle};
164             }
165              
166             sub __destroy {
167 2     2   5 my ($self) = @_;
168 2         6 $self->DESTROY;
169 2         104 %$self = ();
170 2         22 bless $self, "JSON::RPC2::AnyEvent::Client::Magic::destroyed";
171             }
172              
173       0     sub JSON::RPC2::AnyEvent::Client::destroyed::AUTOLOAD {
174             #nop
175             }
176              
177             1;
178             __END__