File Coverage

blib/lib/Plient.pm
Criterion Covered Total %
statement 51 142 35.9
branch 20 98 20.4
condition 7 34 20.5
subroutine 10 16 62.5
pod 2 7 28.5
total 90 297 30.3


line stmt bran cond sub pod time code
1             package Plient;
2              
3 5     5   118406 use warnings;
  5         13  
  5         176  
4 5     5   29 use strict;
  5         8  
  5         158  
5 5     5   36 use Carp;
  5         8  
  5         576  
6             our $VERSION = '0.03';
7 5     5   3842 use File::Spec::Functions;
  5         4030  
  5         421  
8 5     5   28 use base 'Exporter';
  5         9  
  5         11875  
9             our @EXPORT = 'plient';
10             our @EXPORT_OK = 'plient_support';
11             our $bundle_mode = $ENV{PLIENT_BUNDLE_MODE};
12              
13             sub plient {
14 0     0 1 0 my ( $method, $uri, $args ) = @_;
15 0 0 0     0 if ( $args && ref $args ne 'HASH' ) {
16 0         0 warn 'invalid args: should be a hashref';
17 0         0 return;
18             }
19 0   0     0 $args ||= {};
20 0 0       0 return unless $uri;
21 0         0 $uri =~ s/^\s+//;
22              
23             # XXX TODO move this $uri tweak thing to HTTP part
24             # http://localhost:5000 => http://localhost:5000/
25 0 0       0 $uri .= '/' if $uri =~ m{^https?://[^/]+$};
26              
27             # generate both body_hash and body_array to make handlers' life easier
28 0 0       0 if ( $args->{body} ) {
29 0 0       0 if ( ref $args->{body} eq 'HASH' ) {
    0          
30 0         0 $args->{body_hash} = $args->{body};
31 0         0 $args->{body_array} = [];
32 0         0 for my $k ( keys %{ $args->{body} } ) {
  0         0  
33 0 0       0 if ( ref $args->{body}{$k} eq 'ARRAY' ) {
34 0         0 push @{ $args->{body_array} }, $k, $_
  0         0  
35 0         0 for @{ $args->{body}{$k} };
36             }
37             else {
38 0         0 push @{ $args->{body_array} }, $k, $args->{body}{$k};
  0         0  
39             }
40             }
41             }
42             elsif ( ref $args->{body} eq 'ARRAY' ) {
43 0         0 $args->{body_array} = $args->{body};
44 0         0 $args->{body_hash} = {};
45 0         0 for ( my $i = 0 ; $i < $#{ $args->{body} } ; $i += 2 ) {
  0         0  
46 0         0 my $key = $args->{body}[$i];
47 0 0       0 my $value =
48             defined $args->{body}[ $i + 1 ]
49             ? $args->{body}[ $i + 1 ]
50             : '';
51 0 0       0 if ( exists $args->{body_hash}{$key} ) {
52 0 0       0 if ( ref $args->{body_hash}{$key} eq 'ARRAY' ) {
53 0         0 push @{ $args->{body_hash}{$key} }, $value;
  0         0  
54             }
55             else {
56 0         0 $args->{body_hash}{$key} =
57             [ $args->{body_hash}{$key}, $value ];
58             }
59             }
60             }
61             }
62             else {
63 0         0 die 'invalid body args, should be either hashref or arrayref';
64             }
65             }
66              
67 0         0 my $sub = dispatch( $method, $uri, $args );
68 0 0       0 if ( $sub ) {
69 0 0       0 if ( $args->{output_file} ) {
70 0 0       0 open my $fh, '>', $args->{output_file} or die $!;
71 0         0 print $fh $sub->();
72 0         0 close $fh;
73 0         0 return 1;
74             }
75             else {
76 0         0 return $sub->();
77             }
78             }
79             else {
80 0         0 warn "failed to $method on $uri";
81 0         0 return;
82             }
83              
84             }
85              
86             sub _extract_protocol {
87 0 0 0 0   0 shift if $_[0] && $_[0] eq __PACKAGE__;
88 0         0 my $uri = shift;
89 0 0       0 return unless $uri;
90 0 0       0 if ( $uri =~ /^http:/i ) {
    0          
    0          
91 0         0 return 'http';
92             }
93             elsif ( $uri =~ /^https:/i ) {
94 0         0 return 'https';
95             }
96             elsif ( $uri =~ /^file:/i ) {
97 0         0 return 'file';
98             }
99             else {
100 0         0 warn "unsupported uri: $uri";
101 0         0 return;
102             }
103             }
104              
105             sub _dispatch_protocol {
106 1 50 33 1   7 shift if $_[0] && $_[0] eq __PACKAGE__;
107 1         2 my $protocol = shift;
108 1 50       5 return unless $protocol;
109 1 50       3 if ( $protocol eq 'file' ) {
    0          
    0          
110 1 50       782 require Plient::Protocol::File unless $bundle_mode;
111 1         6 return 'Plient::Protocol::File';
112             }
113             elsif ( $protocol eq 'http' ) {
114 0 0       0 require Plient::Protocol::HTTP unless $bundle_mode;
115 0         0 return 'Plient::Protocol::HTTP';
116             }
117             elsif ( $protocol eq 'https' ) {
118 0 0       0 require Plient::Protocol::HTTPS unless $bundle_mode;
119 0         0 return 'Plient::Protocol::HTTPS';
120             }
121             else {
122 0         0 warn "unsupported protocol: $protocol";
123 0         0 return;
124             }
125             }
126              
127              
128             sub plient_support {
129 1 50 33 1 1 24 shift if $_[0] && $_[0] eq __PACKAGE__;
130 1         4 my ( $protocol, $method, $args ) = @_;
131 1 50       5 return unless $protocol;
132 1   50     4 $method ||= 'get';
133 1   50     7 $args ||= {};
134 1         5 my $class = _dispatch_protocol( lc $protocol );
135 1 50       5 return unless $class;
136 1         12 return $class->support_method( $method, { %$args, check_only => 1 } );
137             }
138              
139             sub dispatch {
140 0     0 0 0 my ( $method, $uri, $args ) = @_;
141 0         0 $method = lc $method;
142 0   0     0 $method ||= 'get'; # people use get most of the time.
143 0         0 my $class = _dispatch_protocol( _extract_protocol($uri) );
144 0 0       0 return unless $class;
145              
146 0 0       0 if ( my $sub = $class->support_method( $method, $args ) ) {
147 0     0   0 return sub { $sub->( $uri, $args ) };
  0         0  
148             }
149             else {
150 0         0 warn "unsupported method: $method";
151 0         0 return;
152             }
153             }
154              
155             my %all_handlers;
156             my $found_handlers;
157             sub all_handlers {
158 3 100   3 0 38 return keys %all_handlers if $found_handlers;
159 1         5 @all_handlers{keys %all_handlers, find_handlers()} = ();
160 1         7 keys %all_handlers;
161             }
162              
163             # to include handlers not in @INC.
164             sub _add_handlers {
165 3 50 33 3   829 shift if $_[0] && $_[0] eq __PACKAGE__;
166 3         9 for my $handler (@_) {
167 3 50       8 next unless $handler;
168 3 100 66     83 if ( $handler->can('support_protocol')
169             && $handler->can('support_method') )
170             {
171 2         8 $all_handlers{$handler} = ();
172             }
173             else {
174 1         154 warn "$handler is not a valid Plient handler";
175             }
176             }
177              
178 3         12 return keys %all_handlers;
179             }
180              
181             sub handlers {
182 0 0 0 0 0 0 shift if $_[0] && $_[0] eq __PACKAGE__;
183 0 0       0 if ( my $protocol = lc shift ) {
184 0         0 my %map =
185 0         0 map { $_ => 1 }
186 0         0 grep { $_->may_support_protocol($protocol) } all_handlers();
187 0         0 my @handlers;
188 0         0 my $preference = handler_preference($protocol);
189 0 0       0 if ($preference) {
190 0 0       0 @handlers =
191 0 0       0 map { /^Plient::Handler::/ ? $_ : "Plient::Handler::$_" }
192             grep {
193 0         0 $_ =~ /::/
194             ? delete $map{$_}
195             : delete $map{"Plient::Handler::$_"}
196             } @$preference;
197             }
198 0 0       0 push @handlers, keys %map unless $ENV{PLIENT_HANDLER_PREFERENCE_STRICT};
199 0         0 return @handlers;
200             }
201             else {
202             # fallback to return all the handlers
203 0         0 return keys %all_handlers;
204             }
205             }
206              
207             sub find_handlers {
208 1     1 0 2 $found_handlers = 1;
209 1 50       6 return if $bundle_mode;
210 1         2 my @hd;
211 1         3 for my $inc (@INC) {
212 11         45 my $handler_dir = catdir( $inc, 'Plient', 'Handler' );
213 11 100       268 if ( -e $handler_dir ) {
214 3 50       113 if ( opendir my $dh, $handler_dir ) {
215 21 100       132 push @hd,
216 3         73 map { /(\w+)\.pm/ ? "Plient::Handler::$1" : () } readdir $dh;
217             }
218             else {
219 0         0 warn "can't read $handler_dir";
220             }
221             }
222             }
223              
224 1 50 0     2 @hd = grep { eval "require $_" or warn "failed to require $_" and 0 } @hd;
  15         668  
225              
226 1         12 @hd;
227             }
228              
229             my %handler_preference = (
230             http => [qw/curl wget HTTPTiny HTTPLite LWP/],
231             https => [qw/curl wget HTTPTiny LWP/],
232             );
233             if ( my $env = $ENV{PLIENT_HANDLER_PREFERENCE} ) {
234             my %entry = map { split /:/, $_, 2 } split /;/, $env;
235             %entry = map { $_ => [ split /,/, $entry{$_} || '' ] } keys %entry;
236             for my $p ( keys %entry ) {
237             $handler_preference{$p} = $entry{$p};
238             }
239             }
240              
241             sub handler_preference {
242 0 0 0 0 0   shift if $_[0] && $_[0] eq __PACKAGE__;
243 0           my ( $protocol, $handlers ) = @_;
244 0           $protocol = lc $protocol;
245 0 0         if ($handlers) {
246 0 0         if ( ref $handlers eq 'ARRAY' ) {
247 0           return $handler_preference{ $protocol } = $handlers;
248             }
249             else {
250 0           warn "handlers shold be an arrayref";
251 0           return;
252             }
253             }
254             else {
255 0           return $handler_preference{ $protocol };
256             }
257             }
258              
259              
260             1;
261              
262             __END__