Release commit for 0.002003
[scpubgit/Object-Remote.git] / lib / Object / Remote / Role / Connector / PerlInterpreter.pm
1 package Object::Remote::Role::Connector::PerlInterpreter;
2
3 use IPC::Open2;
4 use IO::Handle;
5 use Object::Remote::ModuleSender;
6 use Object::Remote::Handle;
7 use Object::Remote::Future;
8 use Scalar::Util qw(blessed);
9 use Moo::Role;
10
11 with 'Object::Remote::Role::Connector';
12
13 has module_sender => (is => 'lazy');
14
15 sub _build_module_sender {
16   my ($hook) =
17     grep {blessed($_) && $_->isa('Object::Remote::ModuleLoader::Hook') }
18       @INC;
19   return $hook ? $hook->sender : Object::Remote::ModuleSender->new;
20 }
21
22 has perl_command => (is => 'lazy');
23
24 sub _build_perl_command { [ 'perl', '-' ] }
25
26 around connect => sub {
27   my ($orig, $self) = (shift, shift);
28   my $f = $self->$start::start($orig => @_);
29   return future {
30     $f->on_done(sub {
31       my ($conn) = $f->get;
32       Object::Remote::Handle->new(
33         connection => $conn,
34         class => 'Object::Remote::ModuleLoader',
35         args => { module_sender => $self->module_sender }
36       )->disarm_free;
37       require Object::Remote::Prompt;
38       Object::Remote::Prompt::maybe_set_prompt_command_on($conn);
39     });
40     $f;
41   } 2;
42 };
43
44 sub final_perl_command { shift->perl_command }
45
46 sub _start_perl {
47   my $self = shift;
48   my $pid = open2(
49     my $foreign_stdout,
50     my $foreign_stdin,
51     @{$self->final_perl_command},
52   ) or die "Failed to run perl at '$_[0]': $!";
53   return ($foreign_stdin, $foreign_stdout, $pid);
54 }
55
56 sub _open2_for {
57   my $self = shift;
58   my ($foreign_stdin, $foreign_stdout, $pid) = $self->_start_perl(@_);
59   my $to_send = $self->fatnode_text;
60   Object::Remote->current_loop
61                 ->watch_io(
62                     handle => $foreign_stdin,
63                     on_write_ready => sub {
64                       my $len = syswrite($foreign_stdin, $to_send, 4096);
65                       if (defined $len) {
66                         substr($to_send, 0, $len) = '';
67                       }
68                       # if the stdin went away, we'll never get Shere
69                       # so it's not a big deal to simply give up on !defined
70                       if (!defined($len) or 0 == length($to_send)) {
71                         Object::Remote->current_loop
72                                       ->unwatch_io(
73                                           handle => $foreign_stdin,
74                                           on_write_ready => 1
75                                         );
76                       }
77                     }
78                   );
79   return ($foreign_stdin, $foreign_stdout, $pid);
80 }
81
82 sub fatnode_text {
83   my ($self) = @_;
84   require Object::Remote::FatNode;
85   my $text = '';
86   $text .= 'BEGIN { $ENV{OBJECT_REMOTE_DEBUG} = 1 }'."\n"
87     if $ENV{OBJECT_REMOTE_DEBUG};
88   $text .= <<'END';
89 $INC{'Object/Remote/FatNode.pm'} = __FILE__;
90 $Object::Remote::FatNode::DATA = <<'ENDFAT';
91 END
92   $text .= do { no warnings 'once'; $Object::Remote::FatNode::DATA };
93   $text .= "ENDFAT\n";
94   $text .= <<'END';
95 eval $Object::Remote::FatNode::DATA;
96 END
97   $text .= "__END__\n";
98   return $text;
99 }
100
101 1;