File Coverage

blib/lib/Alien/Taco.pm
Criterion Covered Total %
statement 72 93 77.4
branch 10 22 45.4
condition 1 3 33.3
subroutine 18 23 78.2
pod 8 9 88.8
total 109 150 72.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 .
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   102632 use IPC::Open2;
  3         40463  
  3         319  
37 3     3   35 use Scalar::Util qw/blessed/;
  3         5  
  3         450  
38              
39 3     3   2212 use Alien::Taco::Object;
  3         10  
  3         81  
40 3     3   2021 use Alien::Taco::Transport;
  3         11  
  3         90  
41              
42 3     3   18 use strict;
  3         7  
  3         6315  
43              
44             our $VERSION = '0.001';
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
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 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   115 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         12 }],
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   1558 my $self = shift;
117 4         7 my $message = shift;
118 4         6 my $xp = $self->{'xp'};
119              
120 4         15 $xp->write($message);
121              
122 4         21 my $res = $xp->read();
123 4         9 my $act = $res->{'action'};
124              
125 4 100       14 if ($act eq 'result') {
    100          
126 2 50 33     7 return @{$res->{'result'}}
  0         0  
127             if wantarray and 'ARRAY' eq ref $res->{'result'};
128              
129 2         12 return $res->{'result'};
130             }
131             elsif ($act eq 'exception') {
132 1         10 die 'received exception: ' . $res->{'message'};
133             }
134             else {
135 1         12 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 and I
154             must be given separately.
155              
156             =cut
157              
158             sub call_class_method {
159 1     1 1 15 my $self = shift;
160 1         3 my $class = shift;
161 1         2 my $name = shift;
162 1         4 my %opts = @_;
163              
164 1 0       14 return $self->_interact({
    50          
165             action => 'call_class_method',
166             class => $class,
167             name => $name,
168             args => $opts{'args'},
169             kwargs => $opts{'kwargs'},
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 and I
180             must be given separately.
181              
182             =cut
183              
184             sub call_function {
185 1     1 1 1585 my $self = shift;
186 1         2 my $name = shift;
187 1         4 my %opts = @_;
188              
189 1 50       12 return $self->_interact({
    50          
190             action => 'call_function',
191             name => $name,
192             args => $opts{'args'},
193             kwargs => $opts{'kwargs'},
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         1 my $name = shift;
206 1         4 my %opts = @_;
207              
208 1 50       11 return $self->_interact({
    50          
209             action => 'call_method',
210             number => $number,
211             name => $name,
212             args => $opts{'args'},
213             kwargs => $opts{'kwargs'},
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 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 892 my $self = shift;
228 1         2 my $class = shift;
229 1         4 my %opts = @_;
230              
231 1         8 return $self->_interact({
232             action => 'construct_object',
233             class => $class,
234             args => $opts{'args'},
235             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   5 my $self = shift;
245 2         3 my $number = shift;
246              
247 2         11 $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         2 my $name = shift;
261              
262 1         6 return $self->_interact({
263             action => 'get_attribute',
264             number => $number,
265             name => $name,
266             });
267             }
268              
269             =item get_value('variable_name')
270              
271             Request the value of the given variable.
272              
273             =cut
274              
275             sub get_value {
276 1     1 1 677 my $self = shift;
277 1         3 my $name = shift;
278              
279 1         5 return $self->_interact({
280             action => 'get_value',
281             name => $name,
282             });
283             }
284              
285             =item import_module('Module::Name', [args => \@args], [kwargs => \%kwargs])
286              
287             Instruct the server to load the specified module. The interpretation
288             of the arguments depends on the language of the Taco server implementation.
289              
290             =cut
291              
292             sub import_module {
293 1     1 0 665 my $self = shift;
294 1         3 my $name = shift;
295 1         4 my %opts = @_;
296              
297 1         8 $self->_interact({
298             action => 'import_module',
299             name => $name,
300             args => $opts{'args'},
301             kwargs => $opts{'kwargs'},
302             });
303             }
304              
305             # _set_attribute($number, 'attribute_name', $value)
306             #
307             # Internal method invoked by Alien::Taco::Object instances.
308              
309             sub _set_attribute {
310 1     1   3 my $self = shift;
311 1         2 my $number = shift;
312 1         3 my $name = shift;
313 1         1 my $value = shift;
314              
315 1         7 $self->_interact({
316             action => 'set_attribute',
317             number => $number,
318             name => $name,
319             value => $value,
320             });
321             }
322              
323             =item set_value('attribute_name', $value)
324              
325             Set the value of the given variable.
326              
327             =cut
328              
329             sub set_value {
330 1     1 1 834 my $self = shift;
331 1         3 my $name = shift;
332 1         2 my $value = shift;
333              
334 1         6 $self->_interact({
335             action => 'set_value',
336             name => $name,
337             value => $value,
338             });
339             }
340              
341             =back
342              
343             =head2 Convenience Methods
344              
345             The methods in this section additional methods for convenience.
346              
347             =over 4
348              
349             =item function('function_name')
350              
351             Return a subroutine reference which calls the given function
352             with plain arguments only. The following example is equivalent
353             to that given in the L.
354              
355             my $sleep = $taco->function('CORE::sleep');
356             $sleep->(10);
357              
358             =cut
359              
360             sub function {
361 0     0 1   my $self = shift;
362 0           my $name = shift;
363              
364             return sub {
365 0     0     $self->call_function($name, args => \@_);
366 0           };
367             }
368              
369             =item constructor('ClassName')
370              
371             Return a subroutine reference to call the constructor for the
372             specified class, with plain arguments. For example, to
373             allow multiple L objects to be constructed easily,
374             a constructor can be used:
375              
376             my $datetime = $taco->constructor('DateTime');
377             my $afd = $datetime->(year => 2000, month => 4, day => 1);
378              
379             This is equivalent to calling the L
380             method:
381              
382             my $afd = $taco->construct_object('DateTime',
383             kwargs => {year => 2000, month => 4, day => 1});
384              
385             =cut
386              
387             sub constructor {
388 0     0 1   my $self = shift;
389 0           my $class = shift;
390              
391             return sub {
392 0     0     $self->construct_object($class, args => \@_);
393 0           };
394             }
395              
396             1;
397              
398             __END__