File Coverage

blib/lib/JSON/RPC/Simple/Lite.pm
Criterion Covered Total %
statement 35 73 47.9
branch 2 14 14.2
condition n/a
subroutine 7 9 77.7
pod 1 3 33.3
total 45 99 45.4


line stmt bran cond sub pod time code
1             package JSON::RPC::Simple::Lite;
2              
3 1     1   87258 use vars '$AUTOLOAD';
  1         2  
  1         48  
4 1     1   618 use HTTP::Tiny;
  1         75733  
  1         67  
5 1     1   935 use JSON::PP;
  1         25268  
  1         111  
6 1     1   10 use Time::HiRes qw(time);
  1         2  
  1         10  
7              
8             our $VERSION = '0.1';
9              
10             sub new {
11 1     1 0 204794 my $class = shift();
12 1         4 my $url = shift();
13 1         2 my $opts = shift();
14              
15 1         7 my $attrs = {
16             'agent' => 'JSON::RPC::Simple::Lite',
17             'timeout' => 45,
18             };
19              
20 1         13 my $self = {
21             "version" => $version,
22             "api_url" => $url,
23             "opts" => $opts,
24             "http" => HTTP::Tiny->new(%$attrs),
25             "breadcrumbs" => [],
26             };
27              
28 1         142 bless $self, $class;
29              
30 1         7 return $self;
31             }
32              
33             sub _call {
34 0     0   0 my ($self,$method,@params) = @_;
35              
36 0         0 my $start = time();
37 0         0 my $url = $self->{api_url};
38 0         0 my $json = $self->create_request($method,@params);
39 0         0 my $debug = $self->{opts}->{debug};
40              
41 0 0       0 if ($debug) {
42 0         0 print "RPC URL : $url\n";
43 0         0 print "Sending : " . $json . "\n";
44             }
45              
46 0         0 my $opts = {
47             content => $json,
48             headers => { 'Content-Type' => 'application/json;charset=UTF-8' },
49             };
50              
51 0         0 my $resp = $self->{http}->post($url,$opts);
52 0         0 my $status = $resp->{status};
53 0         0 my $json_resp = $resp->{content};
54              
55 0         0 my $total_ms = int((time() - $start) * 1000);
56              
57 0         0 $self->{response} = $resp;
58              
59 0 0       0 if ($debug) {
60 0         0 print "Received : " . $json_resp . "\n";
61 0         0 print "HTTP Code: $status\n";
62 0         0 print "Query ms : $total_ms\n\n";
63             }
64              
65 0 0       0 if ($status != 200) {
66             #return undef;
67             }
68              
69 0         0 my $ret = {};
70 0         0 eval {
71 0         0 $ret = decode_json($json_resp);
72              
73 0 0       0 if ($ret->{result}) {
74 0         0 $ret = $ret->{result};
75             }
76             };
77              
78             # There was an error with decoding the JSON
79 0 0       0 if ($@) {
80 0         0 print $@;
81 0         0 return undef;
82             }
83              
84 0         0 $self->{breadcrumbs} = [];
85              
86 0         0 return $ret;
87             }
88              
89             sub create_request {
90 4     4 0 21 my ($self,$method,@params) = @_;
91              
92 4         24 my $hash = {
93             "method" => $method,
94             "version" => 1.1,
95             "id" => 1,
96             "params" => \@params,
97             };
98              
99 4         22 my $obj = JSON::PP->new();
100              
101             # If we're doing unit testing we need the JSON output to be consistent.
102             # Specifying canonical = 1 makes the JSON output in alphabetical order.
103             # This adds overhead though, so we only enable it for unit testing.
104 4 50       61 if ($ENV{'HARNESS_ACTIVE'}) {
105 4         135 my $ok = $obj->canonical(1);
106             }
107              
108 4         51 my $json = $obj->encode($hash);
109              
110 4         1185 return $json;
111             }
112              
113             sub AUTOLOAD {
114 1     1   1144 my $self = shift;
115 1         3 my $func = $AUTOLOAD;
116 1         3 my @params = @_;
117              
118             # Remove the class name, we just want the function that was called
119 1         3 my $str = __PACKAGE__ . "::";
120 1         21 $func =~ s/$str//;
121              
122 1         3 push(@{$self->{breadcrumbs}},$func);
  1         7  
123              
124             # If there are params it's the final function call
125 1 50       5 if (@params) {
126 0         0 my $method = join(".",@{$self->{breadcrumbs}});
  0         0  
127 0         0 my $ret = $self->_call($method,@params);
128              
129 0         0 return $ret;
130             }
131              
132 1         119 return $self;
133             }
134              
135             sub curl_call {
136 0     0 1   my ($self,$method,@params) = @_;
137              
138 0           my $json = $self->create_request($method,@params);
139 0           my $url = $self->{api_url};
140              
141             #curl -d '{"id":"json","method":"add","params":{"a":2,"b":3} }' -o - http://domain.com
142 0           my $curl = "curl --data '$json' $url";
143              
144 0           return $curl;
145             }
146              
147             =head1 NAME
148              
149             JSON::RPC::Simple::Lite - A simple and lite JSON-RPC client.
150              
151             =head1 DESCRIPTION
152              
153             C provides a simple interface for JSON-RPC APIs.
154             It uses C for the backend transfer and supports all the
155             interfaces that library does.
156              
157             =head1 USAGE
158              
159             JSON::RPC::Simple::Lite;
160              
161             my $api_url = "https://www.perturb.org/api/json-rpc/";
162             my $opts = { debug => 0 };
163             my $json = JSON::RPC::Simple::Lite->new($api_url, $opts);
164              
165             # Direct using _call()
166             my $resp = $json->_call($method, @params);
167              
168             # OOP style using chaining and AUTOLOAD magic
169             my $str = $json->echo_data("Hello world!");
170             my $num = $json->math->sum(1, 4);
171              
172             # Get the curl command for this call
173             my $curl_str = $json->curl_call($method, @params);
174              
175             =head1 FUNCTIONS
176              
177             =head2 _call($method, @params)
178              
179             Call the remote function C<$method> passing it C<@params>. The return value is
180             the response from the server.
181              
182             =head2 curl_call($method, @params)
183              
184             Returns a string that represents a command line Curl call of C<$method>.
185             This can be useful for debugging and testing.
186              
187             =head1 OBJECT ORIENTED INTERFACE
188              
189             C allows a pseudo OOP interface using AUTOLOAD.
190             This allows you to chain calls in different namespaces together which gets
191             mapped to the correct method name before calling.
192              
193             $json->user->email->login($user, $pass); # Maps to method 'user.email.login'
194              
195             This format can make your code cleaner and easier to read.
196              
197             B This does require that
198             your final method include B parameter. If your function does not require
199             any params pass C or use the explicit C<_call()> method.
200              
201             =head1 DEBUG
202              
203             If debug is passed in via the constructor options JSON information will be
204             printed to C.
205              
206             =head1 AUTHORS
207              
208             Scott Baker - https://www.perturb.org/
209              
210             =cut
211              
212             1;