File Coverage

blib/lib/Perinci/Access.pm
Criterion Covered Total %
statement 17 66 25.7
branch 0 18 0.0
condition 0 32 0.0
subroutine 6 10 60.0
pod 3 3 100.0
total 26 129 20.1


line stmt bran cond sub pod time code
1             package Perinci::Access;
2              
3             our $DATE = '2017-07-10'; # DATE
4             our $VERSION = '0.45'; # VERSION
5              
6 1     1   55564 use 5.010001;
  1         4  
7 1     1   6 use strict;
  1         2  
  1         27  
8 1     1   6 use warnings;
  1         2  
  1         24  
9 1     1   2851 use Log::ger;
  1         118  
  1         8  
10              
11 1     1   1119 use Scalar::Util qw(blessed);
  1         2  
  1         43  
12 1     1   327 use URI::Split qw(uri_split uri_join);
  1         1979  
  1         467  
13              
14             our $Log_Request = $ENV{LOG_RIAP_REQUEST} // 0;
15             our $Log_Response = $ENV{LOG_RIAP_RESPONSE} // 0;
16              
17             sub new {
18 0     0 1   my ($class, %opts) = @_;
19              
20 0   0       $opts{riap_version} //= 1.1;
21 0   0       $opts{handlers} //= {};
22 0   0       $opts{handlers}{''} //= 'Perinci::Access::Schemeless';
23 0   0       $opts{handlers}{pl} //= 'Perinci::Access::Perl';
24 0   0       $opts{handlers}{http} //= 'Perinci::Access::HTTP::Client';
25 0   0       $opts{handlers}{https} //= 'Perinci::Access::HTTP::Client';
26 0   0       $opts{handlers}{'riap+tcp'} //= 'Perinci::Access::Simple::Client';
27 0   0       $opts{handlers}{'riap+unix'} //= 'Perinci::Access::Simple::Client';
28 0   0       $opts{handlers}{'riap+pipe'} //= 'Perinci::Access::Simple::Client';
29              
30 0   0       $opts{_handler_objs} //= {};
31 0           bless \%opts, $class;
32             }
33              
34             sub _request_or_parse_url {
35 0     0     my $self = shift;
36 0           my $which = shift;
37              
38 0           my ($action, $uri, $extra, $copts);
39 0 0         if ($which eq 'request') {
40 0           ($action, $uri, $extra, $copts) = @_;
41             } else {
42 0           ($uri, $copts) = @_;
43             }
44              
45 0           my ($sch, $auth, $path, $query, $frag) = uri_split($uri);
46 0   0       $sch //= "";
47 0 0         die "Can't handle scheme '$sch' in URL" unless $self->{handlers}{$sch};
48              
49             # convert riap://perl/Foo/Bar to pl:/Foo/Bar/ as Perl only accepts pl
50 0 0         if ($sch eq 'riap') {
51 0   0       $auth //= '';
52 0 0         die "Unsupported auth '$auth' in riap: scheme, ".
53             "only 'perl' is supported" unless $auth eq 'perl';
54 0           $sch = 'pl';
55 0           $auth = undef;
56 0           $uri = uri_join($sch, $auth, $path, $query, $frag);
57             }
58              
59 0 0         unless ($self->{_handler_objs}{$sch}) {
60 0 0         if (blessed($self->{handlers}{$sch})) {
61 0           $self->{_handler_objs}{$sch} = $self->{handlers}{$sch};
62             } else {
63 0           my $modp = $self->{handlers}{$sch};
64 0           $modp =~ s!::!/!g; $modp .= ".pm";
  0            
65 0           require $modp;
66             #$log->tracef("TMP: Creating Riap client object for schema %s with args %s", $sch, $self->{handler_args});
67             $self->{_handler_objs}{$sch} = $self->{handlers}{$sch}->new(
68             riap_version => $self->{riap_version},
69 0   0       %{ $self->{handler_args} // {}});
  0            
70             }
71             }
72              
73 0           my $res;
74 0 0         if ($which eq 'request') {
75 0 0 0       if ($Log_Request && log_is_trace()) {
76             log_trace(
77             "Riap request (%s): %s -> %s (%s)",
78 0           ref($self->{_handler_objs}{$sch}),
79             $action, $uri, $extra, $copts);
80             }
81 0           $res = $self->{_handler_objs}{$sch}->request(
82             $action, $uri, $extra, $copts);
83 0 0 0       if ($Log_Response && log_is_trace()) {
84 0           log_trace("Riap response: %s", $res);
85             }
86             } else {
87 0           $res = $self->{_handler_objs}{$sch}->parse_url($uri, $copts);
88             }
89 0           $res;
90             }
91              
92             sub request {
93 0     0 1   my $self = shift;
94 0           $self->_request_or_parse_url('request', @_);
95             }
96              
97             sub parse_url {
98 0     0 1   my $self = shift;
99 0           $self->_request_or_parse_url('parse_url', @_);
100             }
101              
102             1;
103             # ABSTRACT: Wrapper for Perinci Riap clients
104              
105             __END__
106              
107             =pod
108              
109             =encoding UTF-8
110              
111             =head1 NAME
112              
113             Perinci::Access - Wrapper for Perinci Riap clients
114              
115             =head1 VERSION
116              
117             This document describes version 0.45 of Perinci::Access (from Perl distribution Perinci-Access), released on 2017-07-10.
118              
119             =head1 SYNOPSIS
120              
121             use Perinci::Access;
122              
123             my $pa = Perinci::Access->new;
124             my $res;
125              
126             ### launching Riap request
127              
128             # use Perinci::Access::Perl
129             $res = $pa->request(call => "pl:/Mod/SubMod/func");
130              
131             # use Perinci::Access::Schemeless
132             $res = $pa->request(call => "/Mod/SubMod/func");
133              
134             # use Perinci::Access::HTTP::Client
135             $res = $pa->request(info => "http://example.com/Sub/ModSub/func",
136             {uri=>'/Sub/ModSub/func'});
137              
138             # use Perinci::Access::Simple::Client
139             $res = $pa->request(meta => "riap+tcp://localhost:7001/Sub/ModSub/");
140              
141             # dies, unknown scheme
142             $res = $pa->request(call => "baz://example.com/Sub/ModSub/");
143              
144             ### parse URI
145              
146             $res = $pa->parse_url("/Foo/bar"); # {proto=>'pl', path=>"/Foo/bar"}
147             $res = $pa->parse_url("pl:/Foo/bar"); # ditto
148             $res = $pa->parse_url("riap+unix:/var/run/apid.sock//Foo/bar"); # {proto=>'riap+unix', path=>"/Foo/bar", unix_sock_path=>"/var/run/apid.sock"}
149             $res = $pa->parse_url("riap+tcp://localhost:7001/Sub/ModSub/"); # {proto=>'riap+tcp', path=>"/Sub/ModSub/", host=>"localhost", port=>7001}
150             $res = $pa->parse_url("http://cpanlists.org/api/"); # {proto=>'http', path=>"/App/cpanlists/Server/"} # will perform an 'info' Riap request to the server first
151              
152             =head1 DESCRIPTION
153              
154             This module provides a convenient wrapper to select appropriate Riap client
155             (Perinci::Access::*) objects based on URI scheme.
156              
157             /Foo/Bar/ -> Perinci::Access::Schemeless
158             pl:/Foo/Bar -> Perinci::Access::Perl
159             riap://perl/Foo/Bar/ -> Perinci::Access::Perl (converted to pl:/Foo/Bar/)
160             http://... -> Perinci::Access::HTTP::Client
161             https://... -> Perinci::Access::HTTP::Client
162             riap+tcp://... -> Perinci::Access::Simple::Client
163             riap+unix://... -> Perinci::Access::Simple::Client
164             riap+pipe://... -> Perinci::Access::Simple::Client
165              
166             For more details on each scheme, please consult the appropriate module.
167              
168             You can customize or add supported schemes by providing class name or object to
169             the B<handlers> attribute (see its documentation for more details).
170              
171             =head1 VARIABLES
172              
173             =head2 $Log_Request (BOOL)
174              
175             Whether to log every Riap request. Default is from environment variable
176             LOG_RIAP_REQUEST, or false. Logging is done with L<Log::ger> at trace level.
177              
178             =head2 $Log_Response (BOOL)
179              
180             Whether to log every Riap response. Default is from environment variable
181             LOG_RIAP_RESPONSE, or false. Logging is done with L<Log::ger> at trace level.
182              
183             =head1 METHODS
184              
185             =head2 new(%opts) -> OBJ
186              
187             Create new instance. Known options:
188              
189             =over 4
190              
191             =item * handlers => HASH
192              
193             A mapping of scheme names and class names or objects. If values are class names,
194             they will be require'd and instantiated. The default is:
195              
196             {
197             '' => 'Perinci::Access::Schemeless',
198             pl => 'Perinci::Access::Perl',
199             http => 'Perinci::Access::HTTP::Client',
200             https => 'Perinci::Access::HTTP::Client',
201             'riap+tcp' => 'Perinci::Access::Simple::Client',
202             'riap+unix' => 'Perinci::Access::Simple::Client',
203             'riap+pipe' => 'Perinci::Access::Simple::Client',
204             }
205              
206             Objects can be given instead of class names. This is used if you need to pass
207             special options when instantiating the class.
208              
209             =item * handler_args => HASH
210              
211             Arguments to pass to handler objects' constructors.
212              
213             =back
214              
215             =head2 $pa->request($action, $server_url[, \%extra_keys[, \%client_opts]]) -> RESP
216              
217             Send Riap request to Riap server. Pass the request to the appropriate Riap
218             client (as configured in C<handlers> constructor options). RESP is the enveloped
219             result.
220              
221             C<%extra_keys> is optional, containing Riap request keys (the C<action> request
222             key is taken from C<$action>).
223              
224             C<%client_opts> is optional, containing Riap-client-specific options. For
225             example, to pass HTTP credentials to C<Perinci::Access::HTTP::Client>, you can
226             do:
227              
228             $pa->request(call => 'http://example.com/Foo/bar', {args=>{a=>1}},
229             {user=>'admin', password=>'secret'});
230              
231             =head2 $pa->parse_url($server_url[, \%client_opts]) => HASH
232              
233             Parse C<$server_url> into its components. Will be done by respective subclasses.
234             Die on failure (e.g. invalid URL). Return a hash on success, containing at least
235             these keys:
236              
237             =over
238              
239             =item * proto => STR
240              
241             =item * path => STR
242              
243             Code entity path. Most URL schemes include the code entity path as part of the
244             URL, e.g. C<pl>, C<riap+unix>, C<riap+tcp>, or C<riap+pipe>. Some do not, e.g.
245             C<http> and C<https>. For the latter case, an C<info> Riap request will be sent
246             to the server first to find out the code entity path .
247              
248             =back
249              
250             Subclasses will add other appropriate keys.
251              
252             =head1 ENVIRONMENT
253              
254             LOG_RIAP_REQUEST
255              
256             LOG_RIAP_RESPONSE
257              
258             =head1 HOMEPAGE
259              
260             Please visit the project's homepage at L<https://metacpan.org/release/Perinci-Access>.
261              
262             =head1 SOURCE
263              
264             Source repository is at L<https://github.com/perlancar/perl-Perinci-Access>.
265              
266             =head1 BUGS
267              
268             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-Access>
269              
270             When submitting a bug or request, please include a test-file or a
271             patch to an existing test-file that illustrates the bug or desired
272             feature.
273              
274             =head1 SEE ALSO
275              
276             L<Perinci::Access::Schemeless>
277              
278             L<Perinci::Access::Perl>
279              
280             L<Perinci::Access::HTTP::Client>
281              
282             L<Perinci::Access::Simple::Client>
283              
284             =head1 AUTHOR
285              
286             perlancar <perlancar@cpan.org>
287              
288             =head1 COPYRIGHT AND LICENSE
289              
290             This software is copyright (c) 2017, 2015, 2014, 2013, 2012 by perlancar@cpan.org.
291              
292             This is free software; you can redistribute it and/or modify it under
293             the same terms as the Perl 5 programming language system itself.
294              
295             =cut