rbpl 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+
2
+ task :test do
3
+ require 'rake/runtest'
4
+ $LOAD_PATH << "lib"
5
+ require 'perl'
6
+ Rake.run_tests 'tests/test_*.rb'
7
+ end
@@ -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
+ }
@@ -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
@@ -0,0 +1,2 @@
1
+
2
+ require 'perl/engine'
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
+