ruby-perl 0.99.15j
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +1 -0
- data/.rspec +0 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +49 -0
- data/README.md +123 -0
- data/Rakefile +5 -0
- data/autotest/discover.rb +1 -0
- data/bin/rperl +23 -0
- data/examples/hello.pl +1 -0
- data/examples/hello.rb +9 -0
- data/examples/hello_block.rb +5 -0
- data/examples/hello_here.rb +5 -0
- data/examples/passenger/config.ru +3 -0
- data/examples/passenger/log/.keep +0 -0
- data/examples/passenger/public/.keep +0 -0
- data/examples/passenger/tmp/.keep +0 -0
- data/examples/passenger/webapp.psgi +16 -0
- data/examples/perl.ru +4 -0
- data/examples/webapp.psgi +16 -0
- data/lib/perl.rb +43 -0
- data/lib/perl/common.rb +64 -0
- data/lib/perl/ext/hash.rb +10 -0
- data/lib/perl/ext/object.rb +30 -0
- data/lib/perl/ext/string.rb +10 -0
- data/lib/perl/ffi_lib.rb +65 -0
- data/lib/perl/internal.rb +73 -0
- data/lib/perl/interpreter.rb +89 -0
- data/lib/perl/rack.rb +45 -0
- data/lib/perl/shell.rb +28 -0
- data/lib/perl/stack.rb +99 -0
- data/lib/perl/stack/function.rb +67 -0
- data/lib/perl/value.rb +4 -0
- data/lib/perl/value/array.rb +54 -0
- data/lib/perl/value/hash.rb +73 -0
- data/lib/perl/value/scalar.rb +130 -0
- data/spec/ext/hash_spec.rb +24 -0
- data/spec/ext/object_spec.rb +40 -0
- data/spec/ext/string_spec.rb +37 -0
- data/spec/hash_value_spec.rb +47 -0
- data/spec/interpreter_spec.rb +220 -0
- data/spec/scalar_value_spec.rb +82 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/perl_value_helpers.rb +13 -0
- metadata +109 -0
data/.autotest
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "autotest/bundler"
|
data/.rspec
ADDED
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
ZenTest (4.5.0)
|
5
|
+
autotest (4.4.6)
|
6
|
+
ZenTest (>= 4.4.1)
|
7
|
+
daemon_controller (0.2.6)
|
8
|
+
daemons (1.1.0)
|
9
|
+
diff-lcs (1.1.2)
|
10
|
+
eventmachine (0.12.10)
|
11
|
+
fastthread (1.0.7)
|
12
|
+
ffi (1.0.6)
|
13
|
+
rake (>= 0.8.7)
|
14
|
+
file-tail (1.0.5)
|
15
|
+
spruz (>= 0.1.0)
|
16
|
+
passenger (3.0.5)
|
17
|
+
daemon_controller (>= 0.2.5)
|
18
|
+
fastthread (>= 1.0.1)
|
19
|
+
file-tail
|
20
|
+
rack
|
21
|
+
rake (>= 0.8.1)
|
22
|
+
rack (1.2.1)
|
23
|
+
rake (0.8.7)
|
24
|
+
rspec (2.0.1)
|
25
|
+
rspec-core (~> 2.0.1)
|
26
|
+
rspec-expectations (~> 2.0.1)
|
27
|
+
rspec-mocks (~> 2.0.1)
|
28
|
+
rspec-core (2.0.1)
|
29
|
+
rspec-expectations (2.0.1)
|
30
|
+
diff-lcs (>= 1.1.2)
|
31
|
+
rspec-mocks (2.0.1)
|
32
|
+
rspec-core (~> 2.0.1)
|
33
|
+
rspec-expectations (~> 2.0.1)
|
34
|
+
spruz (0.2.5)
|
35
|
+
thin (1.2.8)
|
36
|
+
daemons (>= 1.0.9)
|
37
|
+
eventmachine (>= 0.12.6)
|
38
|
+
rack (>= 1.0.0)
|
39
|
+
|
40
|
+
PLATFORMS
|
41
|
+
ruby
|
42
|
+
|
43
|
+
DEPENDENCIES
|
44
|
+
autotest
|
45
|
+
ffi
|
46
|
+
passenger
|
47
|
+
rack
|
48
|
+
rspec
|
49
|
+
thin
|
data/README.md
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
ruby-perl
|
2
|
+
=========
|
3
|
+
|
4
|
+
Ruby and Perl, sitting in a tree K-I-S-S-I-N-G
|
5
|
+
|
6
|
+
For rubyists:
|
7
|
+
-------------
|
8
|
+
|
9
|
+
ruby-perl lets you evaluate and run Perl code within the same binary, without
|
10
|
+
any heavy-weight forking of sub-processes. Enjoy the compactness, robustness
|
11
|
+
and maintainability of Perl!
|
12
|
+
|
13
|
+
For perlists:
|
14
|
+
-------------
|
15
|
+
|
16
|
+
Run your Perl application over industry-standard, enterprise-grade MRI Ruby,
|
17
|
+
Rack and Passenger!
|
18
|
+
|
19
|
+
Getting started
|
20
|
+
===============
|
21
|
+
|
22
|
+
On the command line
|
23
|
+
-------------------
|
24
|
+
|
25
|
+
Install the gem:
|
26
|
+
|
27
|
+
gem install ruby-perl
|
28
|
+
|
29
|
+
Run a Perl program:
|
30
|
+
|
31
|
+
rperl examples/hello.pl
|
32
|
+
|
33
|
+
Or play around in the Perl interactive shell:
|
34
|
+
|
35
|
+
rperl -de 0
|
36
|
+
|
37
|
+
Web applications
|
38
|
+
----------------
|
39
|
+
|
40
|
+
You can run your Perl webapp with Rack using the provided Rack adapter:
|
41
|
+
|
42
|
+
thin -R examples/perl.ru
|
43
|
+
|
44
|
+
ruby-perl supports [PSGI](http://search.cpan.org/~miyagawa/PSGI-1.03/PSGI.pod)
|
45
|
+
(Perl Web Server Gateway Interface Specification); as such it can run any
|
46
|
+
webapp written in any conforming framework, such as
|
47
|
+
[Mason](http://www.masonhq.com/).
|
48
|
+
|
49
|
+
However, for the time you have to provide your own Rackup file. Luckily a
|
50
|
+
typical Rackup file for ruby-perl is fairly simple (just take a look at
|
51
|
+
[examples/perl.ru](examples/perl.ru) in the repository), and it boils down to:
|
52
|
+
|
53
|
+
run Perl::Rack.new("examples/webapp.psgi")
|
54
|
+
|
55
|
+
ruby-perl has been developed with [Thin](http://code.macournoyer.com/thin/)
|
56
|
+
in mind, but since [Phusion Passenger](http://www.modrails.com/) is 100%
|
57
|
+
compatible with Rack, you can use it too: just stick a config.ru file in your
|
58
|
+
document root (together with the mandatory public/ and tmp/ subdirectories)
|
59
|
+
and you are ready to go. Read The Fine [Passenger Manual](http://www.modrails.com/documentation/Users%20guide%20Apache.html#_deploying_a_rack_based_ruby_application)
|
60
|
+
for more info.
|
61
|
+
|
62
|
+
In your own application: evaluating Perl code
|
63
|
+
---------------------------------------------
|
64
|
+
|
65
|
+
You can embed Perl code directly in your Ruby application in a nice and
|
66
|
+
friendly way:
|
67
|
+
|
68
|
+
require 'perl'
|
69
|
+
|
70
|
+
def foo(arg)
|
71
|
+
Perl.run %Q{$_=#{arg};(1x$_)!~/^1?$|^(11+?)\\1+$/&&print"$_\n"}
|
72
|
+
end
|
73
|
+
|
74
|
+
foo(42)
|
75
|
+
foo(13)
|
76
|
+
|
77
|
+
For additional eye-candy, more styles are supported:
|
78
|
+
|
79
|
+
Perl <<-PERL
|
80
|
+
return 0 if $_[0] =~
|
81
|
+
/(?:[\.\-\_]{2,})|(?:@[\.\-\_])|(?:[\.\-\_]@)|(?:\A\.)/;
|
82
|
+
return 1 if $_[0] =~
|
83
|
+
/^[\w\.\-\_]+\@\[?[\w\.\-\_]+\.(?:[\w\.\-\_]{2,}|[0-9])\]?$/;
|
84
|
+
return 0;
|
85
|
+
PERL
|
86
|
+
|
87
|
+
or:
|
88
|
+
|
89
|
+
Perl do
|
90
|
+
run <<-PERL
|
91
|
+
my @sorted_ips = #sort by ip
|
92
|
+
map substr($_, 4) =>
|
93
|
+
sort map pack('C4' =>
|
94
|
+
split /\./)
|
95
|
+
. $_ => (@unsorted_ips);
|
96
|
+
PERL
|
97
|
+
end
|
98
|
+
|
99
|
+
Embedded Perl however has a some pretty big limitation, the biggest being
|
100
|
+
that you cannot easily pass data from Ruby to Perl and vice-versa, except
|
101
|
+
by string interpolation as shown in the first example.
|
102
|
+
|
103
|
+
In your own application: invoking Perl code
|
104
|
+
-------------------------------------------
|
105
|
+
|
106
|
+
ruby-perl lets you invoke arbitrary Perl code you have loaded or evaluated.
|
107
|
+
In other words, you can implement some functionality in Perl and seamlessly
|
108
|
+
call it from Ruby:
|
109
|
+
|
110
|
+
require 'perl'
|
111
|
+
|
112
|
+
Perl do
|
113
|
+
@func = eval %Q{sub { $arg = shift; print "You said: $arg\n"; };}
|
114
|
+
|
115
|
+
...
|
116
|
+
|
117
|
+
call @func, "42", :scalar
|
118
|
+
end
|
119
|
+
|
120
|
+
In the previous snippet we first define a Perl `sub` and we assign it to
|
121
|
+
the `@func` instance variable; we then call it passing a String. For all
|
122
|
+
intents and purposes, `@func` is now a lambda you can pass around and call,
|
123
|
+
only it's implemented in Perl.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
data/bin/rperl
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# This allows us to run easily from a git checkout without install.
|
4
|
+
def fallback_load_path(path)
|
5
|
+
retried = false
|
6
|
+
begin
|
7
|
+
yield
|
8
|
+
rescue LoadError
|
9
|
+
unless retried
|
10
|
+
$: << path
|
11
|
+
retried = true
|
12
|
+
retry
|
13
|
+
end
|
14
|
+
raise
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
fallback_load_path(File.join(File.dirname(__FILE__), '..', 'lib')) do
|
19
|
+
require 'perl'
|
20
|
+
require 'perl/shell'
|
21
|
+
end
|
22
|
+
|
23
|
+
Perl::Shell.run
|
data/examples/hello.pl
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
''=~('(?{'.('_/)@^['^'/]@.*{').'"'.('/+<][}}{|'^']^^${;),^').',$/})')
|
data/examples/hello.rb
ADDED
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,16 @@
|
|
1
|
+
my $app = sub {
|
2
|
+
print STDERR "just in app\n";
|
3
|
+
my $env = shift;
|
4
|
+
my @ret;
|
5
|
+
|
6
|
+
print STDERR "\$env = '$env'";
|
7
|
+
while (my ($k, $v) = each %$env) {
|
8
|
+
push(@ret, "<p>key: $k, value: $v.</p>\n");
|
9
|
+
}
|
10
|
+
|
11
|
+
return [
|
12
|
+
'200',
|
13
|
+
[ 'Content-Type' => 'text/plain' ],
|
14
|
+
\@ret, # or IO::Handle-like object
|
15
|
+
];
|
16
|
+
}
|
data/examples/perl.ru
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
my $app = sub {
|
2
|
+
print STDERR "just in app\n";
|
3
|
+
my $env = shift;
|
4
|
+
my @ret;
|
5
|
+
|
6
|
+
print STDERR "\$env = '$env'";
|
7
|
+
while (my ($k, $v) = each %$env) {
|
8
|
+
push(@ret, "<p>key: $k, value: $v.</p>\n");
|
9
|
+
}
|
10
|
+
|
11
|
+
return [
|
12
|
+
'200',
|
13
|
+
[ 'Content-Type' => 'text/plain' ],
|
14
|
+
\@ret, # or IO::Handle-like object
|
15
|
+
];
|
16
|
+
}
|
data/lib/perl.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
require 'perl/ffi_lib'
|
5
|
+
require 'perl/common'
|
6
|
+
require 'perl/interpreter'
|
7
|
+
|
8
|
+
module Perl
|
9
|
+
include Perl::FFILib
|
10
|
+
extend Perl::Common
|
11
|
+
|
12
|
+
@initialized = false
|
13
|
+
@mutex = Mutex.new
|
14
|
+
|
15
|
+
def setup
|
16
|
+
@mutex.synchronize do
|
17
|
+
return if @initialized
|
18
|
+
|
19
|
+
argc, argv = argv_to_ffi
|
20
|
+
|
21
|
+
Perl.Perl_sys_init3(argc, argv, nil)
|
22
|
+
|
23
|
+
at_exit { shutdown }
|
24
|
+
@initialized = true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
module_function :setup
|
28
|
+
|
29
|
+
def shutdown
|
30
|
+
Perl.Perl_sys_term
|
31
|
+
@initialized = false
|
32
|
+
end
|
33
|
+
module_function :shutdown
|
34
|
+
|
35
|
+
def run(args)
|
36
|
+
Interpreter.new.eval(args)
|
37
|
+
end
|
38
|
+
module_function :run
|
39
|
+
end
|
40
|
+
|
41
|
+
require 'perl/ext/hash'
|
42
|
+
require 'perl/ext/object'
|
43
|
+
require 'perl/ext/string'
|
data/lib/perl/common.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
module Perl
|
2
|
+
module Common
|
3
|
+
PERL_EXIT_EXPECTED = 0x01
|
4
|
+
PERL_EXIT_DESTRUCT_END = 0x02
|
5
|
+
|
6
|
+
def start
|
7
|
+
Perl.setup
|
8
|
+
argc, argv = embedded_argv_to_ffi
|
9
|
+
|
10
|
+
@my_perl = Perl.perl_alloc
|
11
|
+
Perl.perl_construct(@my_perl)
|
12
|
+
|
13
|
+
Perl.curinterp[:Iexit_flags] |= PERL_EXIT_DESTRUCT_END
|
14
|
+
|
15
|
+
Perl.perl_parse(@my_perl, nil, argc, argv, nil)
|
16
|
+
Perl.perl_run(@my_perl)
|
17
|
+
end
|
18
|
+
|
19
|
+
def stop
|
20
|
+
Perl.perl_destruct(@my_perl)
|
21
|
+
Perl.perl_free(@my_perl)
|
22
|
+
|
23
|
+
@my_perl = nil
|
24
|
+
Perl.PL_curinterp = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Returns a C-style tuple of <argc,argv> corresponding to the real
|
29
|
+
# arguments the application was invoked with.
|
30
|
+
#
|
31
|
+
def argv_to_ffi
|
32
|
+
array_to_ffi(ARGV)
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Returns a C-style tuple of <argc,argv> suitable for running an
|
37
|
+
# embedded Perl interpreter.
|
38
|
+
#
|
39
|
+
def embedded_argv_to_ffi
|
40
|
+
array_to_ffi(%w[-e 0])
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
def array_to_ffi(array)
|
45
|
+
strptrs = [].tap do |ptrs|
|
46
|
+
ptrs << FFI::MemoryPointer.from_string("") # XXX
|
47
|
+
array.each do |arg|
|
48
|
+
ptrs << FFI::MemoryPointer.from_string(arg)
|
49
|
+
end
|
50
|
+
ptrs << nil
|
51
|
+
end
|
52
|
+
|
53
|
+
[strptrs.length-1, array_ptr(strptrs)]
|
54
|
+
end
|
55
|
+
|
56
|
+
def array_ptr(list)
|
57
|
+
FFI::MemoryPointer.new(:pointer, list.length).tap do |argv|
|
58
|
+
list.each_with_index do |p, i|
|
59
|
+
argv[i].put_pointer(0, p)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'perl/interpreter'
|
2
|
+
|
3
|
+
class Object
|
4
|
+
#
|
5
|
+
# Evaluate Perl code.
|
6
|
+
#
|
7
|
+
# You can provide the code as a String:
|
8
|
+
#
|
9
|
+
# Perl 'print STDERR "hello world\n"'
|
10
|
+
#
|
11
|
+
# An alternative is to pass a block, which will be evaluated in the context
|
12
|
+
# of a Perl::Interpreter instance. This means all Perl::Interpreter methods
|
13
|
+
# will be available, so you can write:
|
14
|
+
#
|
15
|
+
# Perl do
|
16
|
+
# load 'test.pl'
|
17
|
+
# eval 'sub { ... }'
|
18
|
+
# run 'print "hi there!\n";'
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
def Perl(args = nil, &block)
|
22
|
+
@_perl ||= Perl::Interpreter.new
|
23
|
+
|
24
|
+
if args
|
25
|
+
@_perl.eval(args)
|
26
|
+
else
|
27
|
+
@_perl.instance_eval(&block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|