File Coverage

blib/lib/WebService/Simple.pm
Criterion Covered Total %
statement 153 181 84.5
branch 44 76 57.8
condition 18 43 41.8
subroutine 25 29 86.2
pod 8 9 88.8
total 248 338 73.3


line stmt bran cond sub pod time code
1             package WebService::Simple;
2 8     8   180876 use strict;
  8         1046  
  8         180  
3 8     8   36 use warnings;
  8         12  
  8         229  
4 8     8   34 use base qw(LWP::UserAgent Class::Data::ConfigHash);
  8         21  
  8         7745  
5 8     8   695658 use Class::Inspector;
  8         26388  
  8         206  
6 8     8   6270 use Data::Dumper ();
  8         47936  
  8         177  
7 8     8   48 use Digest::MD5 ();
  8         15  
  8         124  
8 8     8   36 use URI::Escape;
  8         17  
  8         491  
9 8     8   5558 use URI::QueryParam;
  8         4677  
  8         213  
10 8     8   38 use HTTP::Message;
  8         14  
  8         172  
11 8     8   5666 use Hash::MultiValue;
  8         18065  
  8         223  
12 8     8   4373 use WebService::Simple::Response;
  8         20  
  8         201  
13 8     8   5338 use UNIVERSAL::require;
  8         10695  
  8         70  
14              
15             our $VERSION = '0.25';
16              
17             __PACKAGE__->config(
18             base_url => '',
19             response_parser => { module => "XML::Simple" },
20             );
21              
22             sub new {
23 12     12 1 10538 my $class = shift;
24 12         47 my %args = @_;
25             my $base_url = delete $args{base_url}
26             || $class->config->{base_url}
27 12   0     55 || Carp::croak("base_url is required");
28 12         88 $base_url = URI->new($base_url);
29 12   100     57929 my $basic_params = delete $args{params} || delete $args{param} || {};
30 12   50     78 my $debug = delete $args{debug} || 0;
31              
32             my $response_parser = delete $args{response_parser}
33 12   66     141 || $class->config->{response_parser};
34 12 50 33     350 if ( !$response_parser
35 12         278 || !eval { $response_parser->isa('WebService::Simple::Parser') } )
36             {
37 12   33     39 my $config = $response_parser || $class->config->{response_parser};
38 12 100       42 if ( !ref $config ) {
39 3         10 $config = { module => $config };
40             }
41 12         28 my $module = $config->{module};
42 12 50       50 if ( $module !~ s/^\+// ) {
43 12         36 $module = __PACKAGE__ . "::Parser::$module";
44             }
45 12 100       105 if ( !Class::Inspector->loaded($module) ) {
46 8 50       749 $module->require or die;
47             }
48 12 50       251 $response_parser = $module->new( %{ $config->{args} || {} } );
  12         148  
49             }
50              
51 12         34 my $cache = delete $args{cache};
52 12 50 33     58 if ( !$cache || ref $cache eq 'HASH' ) {
53 12 50       125 my $config = ref $cache eq 'HASH' ? $cache : $class->config->{cache};
54 12 50       346 if ($config) {
55 0 0       0 if ( !ref $config ) {
56 0         0 $config = { module => $config };
57             }
58              
59 0         0 my $module = $config->{module};
60 0 0       0 if ( !Class::Inspector->loaded($module) ) {
61 0 0       0 $module->require or die;
62             }
63             $cache =
64             $module->new( $config->{hashref_args}
65             ? $config->{args}
66 0 0       0 : %{ $config->{args} } );
  0         0  
67             }
68             }
69              
70 12         97 my $self = $class->SUPER::new(%args);
71 12         31 $self->{base_url} = $base_url;
72 12         40 $self->{basic_params} = $basic_params;
73 12         24 $self->{response_parser} = $response_parser;
74 12         26 $self->{cache} = $cache;
75 12         22 $self->{compression} = delete $args{compression};
76 12         22 $self->{content_type} = delete $args{content_type};
77 12         26 $self->{croak} = delete $args{croak};
78 12         26 $self->{debug} = $debug;
79              
80 12 100 66     53 if($self->{content_type} && $self->{content_type} eq 'application/json'){
81 1         5 $self->__init_request_parser_json();
82             }else{
83 11     0   56 $self->{request_parser} = sub { return \$_[0] };
  0         0  
84             }
85              
86 12         44 return $self;
87             }
88              
89 12     12   864 sub _agent { "libwww-perl/$LWP::VERSION+". __PACKAGE__ .'/'.$VERSION }
90              
91 34     34 1 72 sub base_url { $_[0]->{base_url} }
92 34     34 1 201 sub basic_params { $_[0]->{basic_params} }
93 37     37 1 150 sub response_parser { $_[0]->{response_parser} }
94 0     0 0 0 sub request_parser { $_[0]->{request_parser} }
95 28     28 1 49 sub cache { $_[0]->{cache} }
96              
97             sub __cache_get {
98 14     14   17 my $self = shift;
99 14         28 my $cache = $self->cache;
100 14 50       43 return unless $cache;
101              
102 0         0 my $key = $self->__cache_key(shift);
103 0         0 return $cache->get( $key, @_ );
104             }
105              
106             sub __cache_set {
107 14     14   15 my $self = shift;
108 14         28 my $cache = $self->cache;
109 14 50       33 return unless $cache;
110              
111 0         0 my $key = $self->__cache_key(shift);
112 0         0 return $cache->set( $key, @_ );
113             }
114              
115             sub __cache_remove {
116 0     0   0 my $self = shift;
117 0         0 my $cache = $self->cache;
118 0 0       0 return unless $cache;
119              
120 0         0 my $key = $self->__cache_key(shift);
121 0         0 return $cache->remove( $key, @_ );
122             }
123              
124             sub __cache_key {
125 0     0   0 my $self = shift;
126 0         0 local $Data::Dumper::Indent = 1;
127 0         0 local $Data::Dumper::Terse = 1;
128 0         0 local $Data::Dumper::Sortkeys = 1;
129 0         0 return Digest::MD5::md5_hex( Data::Dumper::Dumper( $_[0] ) );
130             }
131              
132             sub __init_request_parser_json {
133 2     2   3 my $self = shift;
134 2         816 require WebService::Simple::Parser::JSON;
135 2         11 my $json_parser = WebService::Simple::Parser::JSON->new();
136 2     2   10 $self->{request_parser_json} = sub { $json_parser->parse_request(@_); }
137 2         10 }
138              
139             sub request_url {
140 35     35 1 1115 my $self = shift;
141 35         116 my %args = @_;
142            
143 35 100       281 my $uri = ref($args{url}) =~ m/^URI/ ? $args{url}->clone() : URI->new($args{url});
144 35 100       468 if ( my $extra_path = $args{extra_path} ) {
145 13         77 $extra_path =~ s!^/!!;
146 13         46 $uri->path( $uri->path . $extra_path );
147             }
148 35 100       573 if($args{params}) {
149 33 100       84 if(ref $args{params} eq 'Hash::MultiValue') {
150 32         107 for my $key ($args{params}->keys) {
151 34         801 $uri->query_param_append($key, $args{params}->get($key));
152             }
153             }else{
154 1         3 $uri->query_form(%{$args{params}});
  1         9  
155             }
156             }
157 35         2354 return $uri;
158             }
159              
160             sub get {
161 14     14 1 11733 my $self = shift;
162 14         24 my ($url, $extra) = ("", {});
163              
164 14 100       35 if ( ref $_[0] eq 'HASH' ) {
165 6         11 $extra = shift @_;
166             }
167             else {
168 8         12 $url = shift @_;
169 8 100       20 if ( ref $_[0] eq 'HASH' ) {
170 4         13 $extra = shift @_;
171             }
172             }
173              
174             my $uri = $self->request_url(
175             url => $self->base_url,
176             extra_path => $url,
177 14         35 params => Hash::MultiValue->new(%{$self->basic_params}, %$extra),
  14         28  
178             );
179              
180 14 50       43 warn "Request URL is $uri\n" if $self->{debug};
181              
182 14         165 my @headers = @_;
183 14 50 33     42 unless(defined($self->{compression}) && !$self->{compression}){
184 14         35 my $can_accept = HTTP::Message::decodable();
185 14         92277 push @headers, ('Accept-Encoding' => $can_accept) ;
186             }
187              
188 14         17 my $response;
189 14         47 $response = $self->__cache_get( [ $uri, @headers ] );
190 14 50       33 if ($response) {
191 0 0       0 if ($response->isa('WebService::Simple::Response')) { # backward compatibility
192 0         0 return $response;
193             } else {
194 0         0 return WebService::Simple::Response->new_from_response(
195             response => $response,
196             parser => $self->response_parser
197             );
198             }
199             }
200              
201 14         52 $response = $self->SUPER::get( $uri, @headers );
202              
203 14 50       10932 if ( $response->is_success ) {
204 14         112 $self->__cache_set( [ $uri, @headers ], $response );
205 14         34 $response = WebService::Simple::Response->new_from_response(
206             response => $response,
207             parser => $self->response_parser
208             );
209             }else{
210 0 0 0     0 Carp::croak("request to $uri failed") unless defined($self->{croak}) && !$self->{croak};
211             }
212              
213 14         85 return $response;
214             }
215              
216             sub post {
217 20     20 1 14476 my $self = shift;
218 20         44 my ( $url, $extra ) = ( '', {} );
219              
220 20 100       78 if ( ref $_[0] eq 'HASH' ) { # post(\%arg [, @header ])
    100          
    100          
221 10         19 $extra = shift @_;
222             }
223             elsif ( ref $_[1] eq 'HASH' ) { # post($url, \%arg [, @header ])
224 5         9 $url = shift @_;
225 5         10 $extra = shift @_;
226             }
227             elsif ( @_ % 2 ) { # post($url [, @header ])
228 2         6 $url = shift @_;
229             }
230              
231 20         47 my @headers = @_;
232 20 50 33     61 unless(defined($self->{compression}) && !$self->{compression}){
233 20         57 my $can_accept = HTTP::Message::decodable();
234 20         54363 push @headers, ('Accept-Encoding' => $can_accept) ;
235             }
236 20         66 my %headers = @headers;
237              
238             # Content-Type tells us where "extra params" go: form-urlencoded -> $uri / json/xml -> $content
239 20         26 my ($uri,$response);
240            
241 20 100 66     129 if( ($self->{content_type} && $self->{content_type} eq 'application/json') || ($headers{'Content-Type'} && $headers{'Content-Type'} eq 'application/json') ){
      66        
      66        
242 2 100       7 $self->__init_request_parser_json() unless $self->{request_parser_json};
243              
244 2         7 $uri = $self->request_url(
245             url => $self->base_url,
246             extra_path => $url,
247             # params => Hash::MultiValue->new(%{$self->basic_params}, %$extra), # this'll go to content
248             );
249             # $uri->query_form(undef); # we'll leave all params on the url
250              
251 2         11 my $req = HTTP::Request->new(POST => $uri, \@headers);
252 2         250 $req->content_type('application/json');
253 2         61 $req->content($self->{request_parser_json}->({ %{$self->basic_params}, %$extra }));
  2         5  
254 2         38 $response = $self->SUPER::request($req);
255             }else{
256             $uri = $self->request_url(
257             url => $self->base_url,
258             extra_path => $url,
259 18         47 params => Hash::MultiValue->new(%{$self->basic_params}, %$extra),
  18         39  
260             );
261 18         55 my $content = $uri->query_form_hash();
262 18         923 $uri->query_form(undef);
263              
264 18 50       520 push(@headers, 'Content-Type' => $self->{content_type}) if $self->{content_type};
265              
266 18         66 $response = $self->SUPER::post( $uri, $content, @headers );
267             }
268              
269 20 50       17535 if ( $response->is_success ) {
270 20         154 $response = WebService::Simple::Response->new_from_response(
271             response => $response,
272             parser => $self->response_parser
273             );
274             }else{
275 0 0 0     0 Carp::croak("request to $uri failed") unless defined($self->{croak}) && !$self->{croak};
276             }
277              
278 20         117 return $response;
279             }
280              
281             1;
282              
283             __END__