File Coverage

blib/lib/Thrust.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             package Thrust;
2              
3 2     2   68864 use common::sense;
  2         14  
  2         15  
4              
5             our $VERSION = '0.200';
6              
7 2     2   1494 use AnyEvent;
  2         5340  
  2         73  
8 2     2   1416 use AnyEvent::Util;
  2         23853  
  2         239  
9 2     2   1903 use AnyEvent::Handle;
  2         15678  
  2         119  
10 2     2   1650 use JSON::XS;
  2         19100  
  2         168  
11 2     2   1219 use File::ShareDir;
  2         13626  
  2         143  
12 2     2   19 use Scalar::Util;
  2         2  
  2         91  
13 2     2   3108 use Alien::Thrust;
  0            
  0            
14              
15             use Thrust::Window;
16              
17              
18              
19             our $THRUST_BOUNDARY = "\n--(Foo)++__THRUST_SHELL_BOUNDARY__++(Bar)--\n";
20              
21              
22              
23              
24             my $js;
25              
26             sub debug {
27             my ($level, $msg_cb, $to_dump, $indent) = @_;
28              
29             return if $level > $ENV{THRUST_DEBUG};
30              
31             $js ||= JSON::XS->new->pretty->canonical;
32              
33             my $out = "\n" . $msg_cb->() . "\n";
34              
35             $out .= $js->encode($to_dump) . "\n" if $to_dump;
36              
37             $out =~ s/\n/\n /g if $indent;
38              
39             print STDERR $out;
40             }
41              
42              
43              
44             sub new {
45             my ($class, %args) = @_;
46              
47             my $self = {
48             action_id => 10, # start at 10 to make groking protocol a bit easier from debug dumps
49             };
50              
51             bless $self, $class;
52              
53             my ($fh1, $fh2) = portable_socketpair();
54              
55             $self->{cv} = run_cmd [ $Alien::Thrust::thrust_shell_binary ],
56             close_all => 1,
57             '>' => $fh2,
58             '<' => $fh2,
59             '2>' => $ENV{THRUST_DEBUG} >= 2 ? \*STDERR : '/dev/null', ## FIXME: /dev/null not portable
60             '$$' => \$self->{pid};
61              
62             close $fh2;
63              
64             $self->{fh} = $fh1;
65              
66             $self->{hdl} = AnyEvent::Handle->new(fh => $self->{fh});
67              
68             my $line_handler; $line_handler = sub {
69             my ($hdl, $line) = @_;
70              
71             my $msg = eval { decode_json($line) };
72              
73             if (defined $msg) {
74              
75             debug(1, sub { "<<<<<<<<<<<<<<<<< Message from thrust shell" }, $msg, 1);
76              
77             if ($msg->{_action} eq 'reply') {
78             my $action_cb = $self->{actions}->{$msg->{_id}};
79             if ($action_cb) {
80             $action_cb->($msg);
81             } else {
82             warn "reply to unknown request";
83             }
84             } elsif ($msg->{_action} eq 'event') {
85             my $window = $self->{windows}->{$msg->{_target}};
86              
87             if ($window) {
88             $window->_trigger($msg->{_type}, $msg->{_event});
89             }
90             }
91             }
92              
93             $self->{hdl}->push_read(line => $line_handler);
94             };
95              
96             $self->{hdl}->push_read(line => $line_handler);
97              
98             return $self;
99             }
100              
101             sub run {
102             my ($self) = @_;
103              
104             $self->{cv} = AE::cv;
105              
106             $self->{cv}->recv;
107             }
108              
109             sub do_action {
110             my ($self, $params, $cb) = @_;
111              
112             my $action_id = $self->{action_id}++;
113              
114             $params->{_id} = $action_id;
115              
116             debug(1, sub { "Sending to thrust shell >>>>>>>>>>>>>>>>>" }, $params);
117              
118             $self->{hdl}->push_write(json => $params);
119              
120             $self->{hdl}->push_write($THRUST_BOUNDARY);
121              
122             $self->{actions}->{$action_id} = sub {
123             delete $self->{actions}->{$action_id};
124             $cb->($_[0]->{_result});
125             };
126             }
127              
128             sub window {
129             my ($self, %args) = @_;
130              
131             $self = Thrust->new if !ref $self; ## in case you forget the ->new in one-liners
132              
133             my $window = { thrust => $self, };
134             bless $window, 'Thrust::Window';
135              
136             $self->do_action({ '_action' => 'create', '_type' => 'window', '_args' => \%args, }, sub {
137             my $id = $_[0]->{_target};
138             $window->{target} = $id;
139             $self->{windows}->{$id} = $window;
140             Scalar::Util::weaken $self->{windows}->{$id};
141             $window->_trigger_event('ready');
142             });
143              
144             return $window;
145             }
146              
147              
148              
149             sub DESTROY {
150             my ($self) = @_;
151              
152             kill 'KILL', $self->{pid};
153             }
154              
155              
156             1;
157              
158              
159             __END__