File Coverage

blib/lib/Valence.pm
Criterion Covered Total %
statement 25 27 92.5
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 34 36 94.4


line stmt bran cond sub pod time code
1             package Valence;
2              
3 2     2   30656 use common::sense;
  2         7  
  2         11  
4              
5 2     2   858 use AnyEvent;
  2         3405  
  2         36  
6 2     2   961 use AnyEvent::Util;
  2         16294  
  2         166  
7 2     2   1255 use AnyEvent::Handle;
  2         10287  
  2         66  
8 2     2   1206 use Callback::Frame;
  2         2016  
  2         114  
9 2     2   1029 use JSON::XS;
  2         11283  
  2         121  
10 2     2   11 use File::Spec;
  2         2  
  2         28  
11 2     2   812 use File::ShareDir;
  2         8726  
  2         108  
12              
13 2     2   1776 use Alien::Electron;
  0            
  0            
14             use Valence::Object;
15              
16              
17             our $VERSION = '0.100';
18              
19              
20             sub new {
21             my ($class, %args) = @_;
22              
23             my $electron_binary = $args{electron_binary} ||
24             $ENV{ELECTRON_BINARY} ||
25             $Alien::Electron::electron_binary;
26             debug(1, sub { "Electron binary location: $electron_binary" });
27              
28             my $valence_dir = $args{valence_dir} ||
29             $ENV{VALENCE_DIR} ||
30             File::ShareDir::dist_dir('Valence');
31             debug(1, sub { "Valence JS directory: $valence_dir" });
32              
33             my $self = {
34             next_object_id => 1,
35             object_map => {},
36              
37             next_callback_id => 1,
38             callback_map => {},
39             };
40              
41             bless $self, $class;
42              
43             my ($fh1, $fh2) = portable_socketpair();
44              
45             $self->{cv} = run_cmd [ $electron_binary, $valence_dir ],
46             close_all => 1,
47             '>' => $fh2,
48             '<' => $fh2,
49             '2>' => $ENV{VALENCE_DEBUG} >= 2 ? \*STDERR : File::Spec->devnull(),
50             '$$' => \$self->{pid};
51              
52             close $fh2;
53              
54             $self->{fh} = $fh1;
55              
56             $self->{hdl} = AnyEvent::Handle->new(fh => $self->{fh});
57              
58             my $line_handler; $line_handler = sub {
59             my ($hdl, $line) = @_;
60              
61             my $msg = eval { decode_json($line) };
62              
63             if ($@) {
64             warn "error decoding JSON from electron: $@: $line";
65             } else {
66             debug(1, sub { "<<<<<<<<<<<<<<<<< Message from electron" }, $msg, 1);
67              
68             $self->_handle_msg($msg);
69             }
70              
71             $self->{hdl}->push_read(line => $line_handler);
72             };
73              
74             $self->{hdl}->push_read(line => $line_handler);
75              
76             return $self;
77             }
78              
79              
80              
81             sub _handle_msg {
82             my ($self, $msg) = @_;
83              
84             if ($msg->{cmd} eq 'cb') {
85             $self->{callback_map}->{$msg->{cb}}->(@{ $msg->{args} });
86             } else {
87             warn "unknown cmd: '$msg->{cmd}'";
88             }
89             }
90              
91              
92             sub run {
93             my ($self) = @_;
94              
95             $self->{cv} = AE::cv;
96              
97             $self->{cv}->recv;
98             }
99              
100              
101              
102              
103             sub _send {
104             my ($self, $msg) = @_;
105              
106             debug(1, sub { "Sending to electron >>>>>>>>>>>>>>>>>" }, $msg);
107              
108             $self->{hdl}->push_write(json => $msg);
109              
110             $self->{hdl}->push_write("\n");
111             }
112              
113              
114             sub _call_method {
115             my ($self, $msg) = @_;
116              
117             ## Manipulate arguments
118              
119             for (my $i=0; $i < @{ $msg->{args} }; $i++) {
120             if (ref $msg->{args}->[$i] eq 'CODE') {
121             my $callback_id = $self->{next_callback_id}++;
122              
123             push @{ $msg->{args_cb} }, [$i, $callback_id];
124              
125             $self->{callback_map}->{$callback_id} = $msg->{args}->[$i];
126              
127             $msg->{args}->[$i] = undef;
128             }
129             }
130              
131             ## Send msg
132              
133             $msg->{cmd} = 'call';
134              
135             my $obj = Valence::Object->_valence_new(valence => $self);
136              
137             $msg->{save} = $obj->{id};
138              
139             $self->_send($msg);
140              
141             return $obj;
142             }
143              
144              
145             sub _get_attr {
146             my ($self, $msg) = @_;
147              
148             ## Send msg
149              
150             $msg->{cmd} = 'attr';
151              
152             my $obj = Valence::Object->_valence_new(valence => $self);
153              
154             $msg->{save} = $obj->{id};
155              
156             $self->_send($msg);
157              
158             return $obj;
159             }
160              
161              
162             sub require {
163             my ($self) = shift;
164              
165             return $self->_call_method({ method => 'require', args => \@_, });
166             }
167              
168              
169              
170              
171             my $pretty_js_ctx;
172              
173             sub debug {
174             my ($level, $msg_cb, $to_dump, $indent) = @_;
175              
176             return if $level > $ENV{VALENCE_DEBUG};
177              
178             $pretty_js_ctx ||= JSON::XS->new->pretty->canonical;
179              
180             my $out = "\n" . $msg_cb->() . "\n";
181              
182             $out .= $pretty_js_ctx->encode($to_dump) . "\n" if $to_dump;
183              
184             $out =~ s/\n/\n /g if $indent;
185              
186             print STDERR $out;
187             }
188              
189              
190              
191             sub DESTROY {
192             my ($self) = @_;
193              
194             kill 'KILL', $self->{pid};
195             }
196              
197              
198             1;
199              
200              
201              
202             __END__