rbpl 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +7 -0
- data/lib/perl/engine.pl +46 -0
- data/lib/perl/engine.rb +129 -0
- data/lib/perl.rb +2 -0
- metadata +58 -0
data/Rakefile
ADDED
data/lib/perl/engine.pl
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/perl
|
2
|
+
|
3
|
+
use strict;
|
4
|
+
use YAML::Syck;
|
5
|
+
|
6
|
+
$| =1; # autoflush
|
7
|
+
|
8
|
+
our $data;
|
9
|
+
our $data_length;
|
10
|
+
our %SESSION;
|
11
|
+
our %_USER_METHODS;
|
12
|
+
|
13
|
+
sub eval_and_respond {
|
14
|
+
my ($block) = @_;
|
15
|
+
my $result = &$block();
|
16
|
+
|
17
|
+
my $packed_result = Dump($@ ?
|
18
|
+
{ status => "error", error => $@ } :
|
19
|
+
{ status => "ok", result => $result });
|
20
|
+
print pack("L", length($packed_result)).$packed_result;
|
21
|
+
$result;
|
22
|
+
}
|
23
|
+
|
24
|
+
while(1) {
|
25
|
+
|
26
|
+
exit 0 if eof(STDIN); # Wait for data or exit if the parent closes the pipe
|
27
|
+
|
28
|
+
read(STDIN, $data_length, 4) == 4 or die "Can not read \$data_length from stdin";
|
29
|
+
$data_length = unpack("L", $data_length);
|
30
|
+
|
31
|
+
read(STDIN, $data, $data_length) == $data_length or die "Can not read \$data from stdin";
|
32
|
+
$data = Load($data);
|
33
|
+
|
34
|
+
if($data->{request} eq "eval") {
|
35
|
+
eval_and_respond(sub { eval($data->{code}); });
|
36
|
+
} elsif($data->{request} eq "define_method") {
|
37
|
+
eval_and_respond(sub {
|
38
|
+
$_USER_METHODS{$data->{method_name}} = eval("sub { ".$data->{body}." }");
|
39
|
+
});
|
40
|
+
} elsif($data->{request} eq "invoke_method") {
|
41
|
+
eval_and_respond(sub {
|
42
|
+
my $arguments = $data->{arguments};
|
43
|
+
$_USER_METHODS{$data->{method_name}}(@$arguments);
|
44
|
+
});
|
45
|
+
}
|
46
|
+
}
|
data/lib/perl/engine.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
|
2
|
+
require 'open3'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
class Perl
|
6
|
+
|
7
|
+
# Base for every exception raised by Perl::Engine
|
8
|
+
class EngineError < Exception; end
|
9
|
+
|
10
|
+
# Raised when a request is to be sent but the engine is stopped
|
11
|
+
class EngineNotRunningError < EngineError; end
|
12
|
+
|
13
|
+
# Raised when a request has been sent but an error ocurred when the response is read
|
14
|
+
class EngineReadingError < EngineError; end
|
15
|
+
|
16
|
+
# Raised when an evaluated code has errors
|
17
|
+
class EngineCodeError < EngineError; end
|
18
|
+
|
19
|
+
# Manages an instance of the Perl interpreter. With #eval method you can execute Perl code and get the result in a Ruby object.
|
20
|
+
# Objects are serialized using YAML, both in the Ruby and Perl side.
|
21
|
+
class Engine
|
22
|
+
|
23
|
+
PerlFile = File.join(File.dirname(__FILE__), "engine.pl")
|
24
|
+
|
25
|
+
# Creates a new instance of the Perl interpreter.
|
26
|
+
def initialize
|
27
|
+
writer = IO.pipe
|
28
|
+
reader = IO.pipe
|
29
|
+
|
30
|
+
@pid = fork do
|
31
|
+
writer[1].close
|
32
|
+
STDIN.reopen(writer[0])
|
33
|
+
writer[0].close
|
34
|
+
|
35
|
+
reader[0].close
|
36
|
+
STDOUT.reopen(reader[1])
|
37
|
+
reader[1].close
|
38
|
+
|
39
|
+
exec "perl", PerlFile
|
40
|
+
end
|
41
|
+
|
42
|
+
reader[1].close
|
43
|
+
writer[0].close
|
44
|
+
|
45
|
+
@stdin = reader[0]
|
46
|
+
@stdout = writer[1]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Check if the Perl interpreter is still alive
|
50
|
+
def running?
|
51
|
+
return false unless @stdin and @stdout
|
52
|
+
|
53
|
+
if IO.select [@stdin], [], [], 0
|
54
|
+
return false if @stdin.eof?
|
55
|
+
end
|
56
|
+
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
# Stop the Perl interpreter
|
61
|
+
def stop!
|
62
|
+
return false unless running?
|
63
|
+
|
64
|
+
@stdin.close
|
65
|
+
@stdout.close
|
66
|
+
@stdin = @stdout = nil
|
67
|
+
|
68
|
+
Process.kill "TERM", @pid
|
69
|
+
Process.wait @pid
|
70
|
+
|
71
|
+
true
|
72
|
+
end
|
73
|
+
|
74
|
+
def request(request, options = {}) # :nodoc:
|
75
|
+
raise EngineNotRunningError, "Engine is not running" unless @stdout and @stdin
|
76
|
+
|
77
|
+
generic_response = options.delete(:generic_response)
|
78
|
+
|
79
|
+
data = { "request" => request.to_s }.merge!(options).to_yaml
|
80
|
+
@stdout.print([data.length].pack("L") + data)
|
81
|
+
|
82
|
+
data_length = @stdin.read(4).to_s
|
83
|
+
raise EngineReadingError, "Can not read data" if data_length.length != 4
|
84
|
+
|
85
|
+
data_length = data_length.unpack("L").first
|
86
|
+
data = @stdin.read(data_length)
|
87
|
+
raise EngineReadingError, "Can not read data" if data.length != data_length
|
88
|
+
|
89
|
+
response = YAML.load data
|
90
|
+
|
91
|
+
if generic_response
|
92
|
+
case response["status"]
|
93
|
+
when "ok"
|
94
|
+
response["result"]
|
95
|
+
when "error"
|
96
|
+
raise EngineCodeError, response["error"].chomp
|
97
|
+
end
|
98
|
+
else
|
99
|
+
response
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Executes the given code in the Perl interpreter.
|
104
|
+
# EngineNotRunningError will be raised if the interpreter is stopped.
|
105
|
+
#
|
106
|
+
# The result will be returned in a Ruby object, using YAML to transport
|
107
|
+
# it. If an error is produced in the Perl interpreter the EngineCodeError
|
108
|
+
# exception will be raised, with the error message generated by Perl.
|
109
|
+
def eval(code)
|
110
|
+
request :eval, "code" => code, :generic_response => true
|
111
|
+
end
|
112
|
+
|
113
|
+
# Defines a method in the Perl interpreter.
|
114
|
+
#
|
115
|
+
# You can access to the arguments using the normal Perl syntax:
|
116
|
+
# engine.define_method "add_two_values", "$_[0] + $_[1]"
|
117
|
+
def define_method(method_name, body)
|
118
|
+
request "define_method", "name" => method_name.to_s, "body" => body.to_s, :generic_response => true
|
119
|
+
end
|
120
|
+
|
121
|
+
# Invokes a method previously defined by #define_method
|
122
|
+
#
|
123
|
+
# Objects are passed to Perl using YAML. Everything that can be understood by YAML in both sides can be used as an argument.
|
124
|
+
def invoke_method(method_name, *arguments)
|
125
|
+
request "invoke_method", "name" => method_name.to_s, "arguments" => arguments, :generic_response => true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
data/lib/perl.rb
ADDED
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rbpl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ayose Cazorla
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-06-13 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Loads a Perl interpreter and evaluates inside it code generated in the Ruby side
|
17
|
+
email: setepo@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/perl/engine.pl
|
26
|
+
- lib/perl/engine.rb
|
27
|
+
- lib/perl.rb
|
28
|
+
- Rakefile
|
29
|
+
has_rdoc: true
|
30
|
+
homepage: http://github.com/setepo/rbpl
|
31
|
+
licenses: []
|
32
|
+
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: "0"
|
43
|
+
version:
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
requirements: []
|
51
|
+
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 1.3.5
|
54
|
+
signing_key:
|
55
|
+
specification_version: 3
|
56
|
+
summary: Execute Perl code from a Ruby script
|
57
|
+
test_files: []
|
58
|
+
|