File Coverage

blib/lib/Alien/Taco.pm
Criterion Covered Total %
statement 81 102 79.4
branch 10 22 45.4
condition 1 3 33.3
subroutine 20 25 80.0
pod 8 11 72.7
total 120 163 73.6


line stmt bran cond sub pod time code
1             # Taco Perl client module.
2             # Copyright (C) 2013-2014 Graham Bell
3             #
4             # This program is free software: you can redistribute it and/or modify
5             # it under the terms of the GNU General Public License as published by
6             # the Free Software Foundation, either version 3 of the License, or
7             # (at your option) any later version.
8             #
9             # This program is distributed in the hope that it will be useful,
10             # but WITHOUT ANY WARRANTY; without even the implied warranty of
11             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12             # GNU General Public License for more details.
13             #
14             # You should have received a copy of the GNU General Public License
15             # along with this program. If not, see <http://www.gnu.org/licenses/>.
16              
17             =head1 NAME
18              
19             Alien::Taco - Taco Perl client module
20              
21             =head1 SYNOPSIS
22              
23             use Alien::Taco;
24              
25             my $taco = new Alien::Taco(lang => 'perl');
26             $taco->call_function('CORE::sleep', args => [10]);
27              
28             =head1 DESCRIPTION
29              
30             This is the Taco client module for Perl.
31              
32             =cut
33              
34             package Alien::Taco;
35              
36 3     3   217624 use IPC::Open2;
  3         14251  
  3         197  
37 3     3   23 use Scalar::Util qw/blessed/;
  3         6  
  3         206  
38              
39 3     3   1335 use Alien::Taco::Object;
  3         15  
  3         97  
40 3     3   1286 use Alien::Taco::Transport;
  3         9  
  3         113  
41              
42 3     3   20 use strict;
  3         6  
  3         2652  
43              
44             our $VERSION = '0.003';
45              
46             =head1 METHODS
47              
48             =head2 Constructor
49              
50             =over 4
51              
52             =item new(lang => 'language' | script => 'server_script')
53              
54             Connect to a Taco server instance. The server script can either
55             be specified explicitly, or the language can be given. In that
56             case the server script will be assumed to be named taco-I<language>
57             and installed in your executable search path (C<$PATH>).
58              
59             The server script will be launched in a subprocess, and a
60             L<Alien::Taco::Transport> object will be attached to it.
61              
62             =cut
63              
64             sub new {
65 0     0 1 0 my $class = shift;
66 0         0 my %opts = @_;
67              
68 0         0 my $serv = undef;
69 0 0       0 if (exists $opts{'script'}) {
    0          
70 0         0 $serv = $opts{'script'};
71             }
72             elsif (exists $opts{'lang'}) {
73 0         0 $serv = 'taco-' . $opts{'lang'};
74             }
75             else {
76 0         0 die 'languange or script not specified';
77             }
78              
79 0         0 my ($serv_in, $serv_out);
80 0         0 my $pid = open2($serv_out, $serv_in, $serv);
81              
82 0         0 my $self = bless {}, $class;
83              
84 0         0 $self->{'xp'} = $self->_construct_transport($serv_out, $serv_in);
85              
86 0         0 return $self;
87             }
88              
89             # _construct_transport()
90              
91             sub _construct_transport {
92 1     1   219 my $self = shift;
93 1         2 my $in = shift;
94 1         2 my $out = shift;
95              
96             return new Alien::Taco::Transport(
97             in => $in,
98             out => $out,
99             filter_single => ['_Taco_Object_' => sub {
100 1     1   5 return new Alien::Taco::Object($self, shift);
101 1         11 }],
102             );
103             }
104              
105             # _interact(\%message)
106             #
107             # General interaction method. This is the internal method used to
108             # implement the main Taco methods.
109             #
110             # The given message is filtered for objects and then sent using the
111             # Alien::Taco::Transport. If the response is a result then it is
112             # returned. If the response is an exception, then an exception is
113             # raised.
114              
115             sub _interact {
116 4     4   1731 my $self = shift;
117 4         6 my $message = shift;
118 4         8 my $xp = $self->{'xp'};
119              
120 4         16 $xp->write($message);
121              
122 4         21 my $res = $xp->read();
123 4         9 my $act = $res->{'action'};
124              
125 4 100       21 if ($act eq 'result') {
    100          
126 0         0 return @{$res->{'result'}}
127 2 50 33     6 if wantarray and 'ARRAY' eq ref $res->{'result'};
128              
129 2         9 return $res->{'result'};
130             }
131             elsif ($act eq 'exception') {
132 1         11 die 'received exception: ' . $res->{'message'};
133             }
134             else {
135 1         15 die 'received unknown action: ' . $act;
136             }
137             }
138              
139             =back
140              
141             =head2 Taco Methods
142              
143             The methods in this section allow the corresponding Taco actions to be sent.
144              
145             =over 4
146              
147             =item call_class_method('class_name', 'function_name',
148             [args => \@args], [kwargs => \%kwargs])
149              
150             Invoke a class method call within the Taco server script, returning the
151             result of that method. The context (void / scalar / list)
152             is detected and sent as a parameter. Since Perl subroutine arguments
153             are expanded into a list, the I<arguments> and I<keyword arguments>
154             must be given separately.
155              
156             =cut
157              
158             sub call_class_method {
159 1     1 1 13 my $self = shift;
160 1         2 my $class = shift;
161 1         2 my $name = shift;
162 1         17 my %opts = @_;
163              
164             return $self->_interact({
165             action => 'call_class_method',
166             class => $class,
167             name => $name,
168             args => $opts{'args'},
169 1 0       11 kwargs => $opts{'kwargs'},
    50          
170             context => (defined wantarray ? (wantarray?'list':'scalar') : 'void'),
171             });
172             }
173              
174             =item call_function('function_name', [args => \@args], [kwargs => \%kwargs])
175              
176             Invoke a function call within the Taco server script, returning the
177             result of that function. The context (void / scalar / list)
178             is detected and sent as a parameter. Since Perl subroutine arguments
179             are expanded into a list, the I<arguments> and I<keyword arguments>
180             must be given separately.
181              
182             =cut
183              
184             sub call_function {
185 1     1 1 1235 my $self = shift;
186 1         2 my $name = shift;
187 1         3 my %opts = @_;
188              
189             return $self->_interact({
190             action => 'call_function',
191             name => $name,
192             args => $opts{'args'},
193 1 50       9 kwargs => $opts{'kwargs'},
    50          
194             context => (defined wantarray ? (wantarray?'list':'scalar') : 'void'),
195             });
196             }
197              
198             # _call_method($number, 'method', [args => \@args], [kwargs => \%kwargs])
199             #
200             # Internal method invoked by Alien::Taco::Object instances.
201              
202             sub _call_method {
203 1     1   3 my $self = shift;
204 1         2 my $number = shift;
205 1         3 my $name = shift;
206 1         6 my %opts = @_;
207              
208             return $self->_interact({
209             action => 'call_method',
210             number => $number,
211             name => $name,
212             args => $opts{'args'},
213 1 50       9 kwargs => $opts{'kwargs'},
    50          
214             context => (defined wantarray ? (wantarray?'list':'scalar') : 'void'),
215             });
216             }
217              
218             =item construct_object('class', [args => \@args], [kwargs => \%kwargs])
219              
220             Invoke an object constructor. If successful, this should return
221             an L<Alien::Taco::Object> instance which references the new object.
222             The given arguments are passed to the object constructor.
223              
224             =cut
225              
226             sub construct_object {
227 1     1 1 762 my $self = shift;
228 1         3 my $class = shift;
229 1         3 my %opts = @_;
230              
231             return $self->_interact({
232             action => 'construct_object',
233             class => $class,
234             args => $opts{'args'},
235 1         6 kwargs => $opts{'kwargs'},
236             });
237             }
238              
239             # _destroy_object($number)
240             #
241             # Internal method invoked by Alien::Taco::Object instances.
242              
243             sub _destroy_object {
244 2     2   4 my $self = shift;
245 2         4 my $number = shift;
246              
247 2         8 $self->_interact({
248             action => 'destroy_object',
249             number => $number,
250             });
251             }
252              
253             # _get_attribute($number, 'attribute_name')
254             #
255             # Internal method invoked by Alien::Taco::Object instances.
256              
257             sub _get_attribute {
258 1     1   2 my $self = shift;
259 1         2 my $number = shift;
260 1         1 my $name = shift;
261              
262 1         5 return $self->_interact({
263             action => 'get_attribute',
264             number => $number,
265             name => $name,
266             });
267             }
268              
269             =item get_class_attribute('Class::Name', 'attribute_name')
270              
271             Request the value of a static attribute of a class.
272              
273             =cut
274              
275             sub get_class_attribute {
276 1     1 0 567 my $self = shift;
277 1         2 my $class = shift;
278 1         1 my $name = shift;
279              
280 1         6 return $self->_interact({
281             action => 'get_class_attribute',
282             class => $class,
283             name => $name,
284             });
285             }
286              
287             =item get_value('variable_name')
288              
289             Request the value of the given variable.
290              
291             =cut
292              
293             sub get_value {
294 1     1 1 564 my $self = shift;
295 1         3 my $name = shift;
296              
297 1         4 return $self->_interact({
298             action => 'get_value',
299             name => $name,
300             });
301             }
302              
303             =item import_module('Module::Name', [args => \@args], [kwargs => \%kwargs])
304              
305             Instruct the server to load the specified module. The interpretation
306             of the arguments depends on the language of the Taco server implementation.
307              
308             =cut
309              
310             sub import_module {
311 1     1 0 600 my $self = shift;
312 1         2 my $name = shift;
313 1         4 my %opts = @_;
314              
315             $self->_interact({
316             action => 'import_module',
317             name => $name,
318             args => $opts{'args'},
319 1         6 kwargs => $opts{'kwargs'},
320             });
321             }
322              
323             # _set_attribute($number, 'attribute_name', $value)
324             #
325             # Internal method invoked by Alien::Taco::Object instances.
326              
327             sub _set_attribute {
328 1     1   3 my $self = shift;
329 1         2 my $number = shift;
330 1         2 my $name = shift;
331 1         2 my $value = shift;
332              
333 1         5 $self->_interact({
334             action => 'set_attribute',
335             number => $number,
336             name => $name,
337             value => $value,
338             });
339             }
340              
341             =item set_class_attribute('Class::Name', 'attribute_name', $value)
342              
343             Set the value of a static attribute of a class.
344              
345             =cut
346              
347             sub set_class_attribute {
348 1     1 0 590 my $self = shift;
349 1         5 my $class = shift;
350 1         1 my $name = shift;
351 1         2 my $value = shift;
352              
353 1         6 $self->_interact({
354             action => 'set_class_attribute',
355             class => $class,
356             name => $name,
357             value => $value,
358             });
359             }
360              
361             =item set_value('attribute_name', $value)
362              
363             Set the value of the given variable.
364              
365             =cut
366              
367             sub set_value {
368 1     1 1 587 my $self = shift;
369 1         2 my $name = shift;
370 1         2 my $value = shift;
371              
372 1         6 $self->_interact({
373             action => 'set_value',
374             name => $name,
375             value => $value,
376             });
377             }
378              
379             =back
380              
381             =head2 Convenience Methods
382              
383             The methods in this section additional methods for convenience.
384              
385             =over 4
386              
387             =item function('function_name')
388              
389             Return a subroutine reference which calls the given function
390             with plain arguments only. The following example is equivalent
391             to that given in the L</SYNOPSIS>.
392              
393             my $sleep = $taco->function('CORE::sleep');
394             $sleep->(10);
395              
396             =cut
397              
398             sub function {
399 0     0 1   my $self = shift;
400 0           my $name = shift;
401              
402             return sub {
403 0     0     $self->call_function($name, args => \@_);
404 0           };
405             }
406              
407             =item constructor('ClassName')
408              
409             Return a subroutine reference to call the constructor for the
410             specified class, with plain arguments. For example, to
411             allow multiple L<DateTime> objects to be constructed easily,
412             a constructor can be used:
413              
414             my $datetime = $taco->constructor('DateTime');
415             my $afd = $datetime->(year => 2000, month => 4, day => 1);
416              
417             This is equivalent to calling the L</construct_object>
418             method:
419              
420             my $afd = $taco->construct_object('DateTime',
421             kwargs => {year => 2000, month => 4, day => 1});
422              
423             =cut
424              
425             sub constructor {
426 0     0 1   my $self = shift;
427 0           my $class = shift;
428              
429             return sub {
430 0     0     $self->construct_object($class, args => \@_);
431 0           };
432             }
433              
434             1;
435              
436             __END__
437              
438             =back
439              
440             =cut