ruby_process 0.0.9 → 0.0.13
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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/README.md +98 -0
- data/Rakefile +2 -2
- data/RubyProcess.gemspec +88 -0
- data/VERSION +1 -1
- data/cmds/blocks.rb +18 -18
- data/cmds/marshal.rb +2 -2
- data/cmds/new.rb +13 -13
- data/cmds/numeric.rb +4 -4
- data/cmds/static.rb +10 -10
- data/cmds/str_eval.rb +4 -4
- data/cmds/system.rb +11 -11
- data/examples/example_csv.rb +13 -0
- data/examples/example_file_write.rb +2 -2
- data/examples/example_knj_db_dump.rb +7 -7
- data/examples/example_strscan.rb +3 -3
- data/include/args_handeling.rb +21 -21
- data/lib/ruby_process.rb +111 -105
- data/lib/{ruby_process_cproxy.rb → ruby_process/class_proxy.rb} +35 -35
- data/lib/{ruby_process_proxyobj.rb → ruby_process/proxy_object.rb} +18 -18
- data/ruby_process.gemspec +17 -10
- data/scripts/ruby_process_script.rb +7 -7
- data/shippable.yml +7 -0
- data/spec/class_proxy_spec.rb +95 -0
- data/spec/hard_load_spec.rb +13 -17
- data/spec/leaks_spec.rb +7 -7
- data/spec/ruby_process_spec.rb +75 -77
- data/spec/spec_helper.rb +3 -1
- metadata +60 -60
- data/README.rdoc +0 -19
- data/spec/cproxy_spec.rb +0 -101
@@ -1,52 +1,52 @@
|
|
1
|
-
#This class is used to seamlessly use leaky classes without working through '
|
1
|
+
#This class is used to seamlessly use leaky classes without working through 'RubyProcess'.
|
2
2
|
#===Examples
|
3
|
-
#
|
3
|
+
# RubyProcess::ClassProxy.run do |data|
|
4
4
|
# data[:subproc].static(:Object, :require, "rubygems")
|
5
5
|
# data[:subproc].static(:Object, :require, "rexml/document")
|
6
|
-
#
|
7
|
-
# doc =
|
6
|
+
#
|
7
|
+
# doc = RubyProcess::ClassProxy::REXML::Document.new("test")
|
8
8
|
# strio = StringIO.new
|
9
9
|
# doc.write(strio)
|
10
10
|
# puts strio.string #=> "<test/>"
|
11
11
|
# raise "REXML shouldnt be defined?" if Kernel.const_defined?(:REXML)
|
12
|
-
class
|
12
|
+
class RubyProcess::ClassProxy
|
13
13
|
#Lock is used to to create new Ruby-process-instances and not doing double-counts.
|
14
14
|
@@lock = Mutex.new
|
15
|
-
|
15
|
+
|
16
16
|
#Counts how many instances are using the Cproxy-module. This way it can be safely unset once no-body is using it again.
|
17
17
|
@@instances = 0
|
18
|
-
|
19
|
-
#This variable will hold the '
|
18
|
+
|
19
|
+
#This variable will hold the 'RubyProcess'-object where sub-objects will be created.
|
20
20
|
@@subproc = nil
|
21
|
-
|
21
|
+
|
22
22
|
#All use should go through this method to automatically destroy sub-processes and keep track of ressources.
|
23
23
|
def self.run
|
24
24
|
#Increase count of instances that are using Cproxy and set the subproc-object if not already set.
|
25
25
|
@@lock.synchronize do
|
26
26
|
#Check if the sub-process is alive.
|
27
|
-
if @@subproc
|
27
|
+
if @@subproc && (!@@subproc.alive? || @@subproc.destroyed?)
|
28
28
|
raise "Cant destroy sub-process because instances are running: '#{@@instances}'." if @@instances > 0
|
29
29
|
@@subproc.destroy
|
30
30
|
@@subproc = nil
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
#Start a new subprocess if none is defined and active.
|
34
|
-
|
35
|
-
subproc =
|
34
|
+
unless @@subproc
|
35
|
+
subproc = RubyProcess.new(title: "ruby_process_cproxy", debug: false)
|
36
36
|
subproc.spawn_process
|
37
37
|
@@subproc = subproc
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
@@instances += 1
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
begin
|
44
|
-
yield(:
|
44
|
+
yield(subproc: @@subproc)
|
45
45
|
raise "'run'-caller destroyed sub-process. This shouldn't happen." if @@subproc.destroyed?
|
46
46
|
ensure
|
47
47
|
@@lock.synchronize do
|
48
48
|
@@instances -= 1
|
49
|
-
|
49
|
+
|
50
50
|
if @@instances <= 0
|
51
51
|
begin
|
52
52
|
@@subproc.destroy
|
@@ -58,49 +58,49 @@ class Ruby_process::Cproxy
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
61
|
-
|
62
|
-
#Returns the '
|
61
|
+
|
62
|
+
#Returns the 'RubyProcess'-object or raises an error if it has not been set.
|
63
63
|
def self.subproc
|
64
|
-
raise "CProxy process not set for some reason?"
|
64
|
+
raise "CProxy process not set for some reason?" unless @@subproc
|
65
65
|
return @@subproc
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
68
|
#Destroy all loaded sub-process-constants.
|
69
69
|
def self.destroy_loaded_constants
|
70
70
|
self.constants.each do |constant|
|
71
71
|
self.__send__(:remove_const, constant)
|
72
72
|
end
|
73
73
|
end
|
74
|
-
|
75
|
-
#Creates the new constant under the '
|
74
|
+
|
75
|
+
#Creates the new constant under the 'RubyProcess::ClassProxy'-namespace.
|
76
76
|
def self.const_missing(name)
|
77
|
-
|
78
|
-
raise "Still not created on const: '#{name}'."
|
77
|
+
RubyProcess::ClassProxy.load_class(self, name) unless const_defined?(name)
|
78
|
+
raise "Still not created on const: '#{name}'." unless const_defined?(name)
|
79
79
|
return self.const_get(name)
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
#Loads a new class to the given constants namespace for recursive creating of missing classes.
|
83
83
|
def self.load_class(const, name)
|
84
84
|
const.const_set(name, Class.new{
|
85
85
|
#Use 'const_missing' to auto-create missing sub-constants recursivly.
|
86
86
|
def self.const_missing(name)
|
87
|
-
|
88
|
-
raise "Still not created on const: '#{name}'."
|
87
|
+
RubyProcess::ClassProxy.load_class(self, name) unless const_defined?(name)
|
88
|
+
raise "Still not created on const: '#{name}'." unless self.const_defined?(name)
|
89
89
|
return self.const_get(name)
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
#Manipulate 'new'-method return proxy-objects instead of real objects.
|
93
93
|
def self.new(*args, &blk)
|
94
|
-
name_match = self.name.to_s.match(/^
|
94
|
+
name_match = self.name.to_s.match(/^RubyProcess::ClassProxy::(.+)$/)
|
95
95
|
class_name = name_match[1]
|
96
|
-
return
|
96
|
+
return RubyProcess::ClassProxy.subproc.new(class_name, *args, &blk)
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
def self.method_missing(method_name, *args, &blk)
|
100
|
-
name_match = self.name.to_s.match(/^
|
100
|
+
name_match = self.name.to_s.match(/^RubyProcess::ClassProxy::(.+)$/)
|
101
101
|
class_name = name_match[1]
|
102
|
-
return
|
102
|
+
return RubyProcess::ClassProxy.subproc.static(class_name, method_name, *args, &blk)
|
103
103
|
end
|
104
104
|
})
|
105
105
|
end
|
106
|
-
end
|
106
|
+
end
|
@@ -1,37 +1,37 @@
|
|
1
1
|
#This class handels the calling of methods on objects in the other process seamlessly.
|
2
|
-
class
|
2
|
+
class RubyProcess::ProxyObject
|
3
3
|
#Hash that contains various information about the proxyobj.
|
4
4
|
attr_reader :__rp_rp, :__rp_id, :__rp_pid
|
5
|
-
|
6
|
-
#Constructor. This should not be called manually but through a running '
|
5
|
+
|
6
|
+
#Constructor. This should not be called manually but through a running 'RubyProcess'.
|
7
7
|
#===Examples
|
8
|
-
# proxy_obj = rp.new(:String, "Kasper") #=> <
|
9
|
-
# proxy_obj = rp.static(:File, :open, "/tmp/somefile") #=> <
|
8
|
+
# proxy_obj = rp.new(:String, "Kasper") #=> <RubyProcess::ProxyObject>
|
9
|
+
# proxy_obj = rp.static(:File, :open, "/tmp/somefile") #=> <RubyProcess::ProxyObject>
|
10
10
|
def initialize(rp, id, pid)
|
11
11
|
@__rp_rp, @__rp_id, @__rp_pid = rp, id, pid
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
#Returns the object as the real object transfered by using the marshal-lib.
|
15
15
|
#===Examples
|
16
|
-
# str = rp.new(:String, "Kasper") #=> <
|
16
|
+
# str = rp.new(:String, "Kasper") #=> <RubyProcess::ProxyObject>
|
17
17
|
# str.__rp_marshal #=> "Kasper"
|
18
18
|
def __rp_marshal
|
19
|
-
return Marshal.load(@__rp_rp.send(:
|
19
|
+
return Marshal.load(@__rp_rp.send(cmd: :obj_marshal, id: @__rp_id))
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
#Unsets all data on the object.
|
23
23
|
def __rp_destroy
|
24
24
|
@__rp_id = nil, @__rp_rp = nil, @__rp_pid = nil
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
#Overwrite certain convert methods.
|
28
28
|
RUBY_METHODS = [:to_i, :to_s, :to_str, :to_f]
|
29
29
|
RUBY_METHODS.each do |method_name|
|
30
30
|
define_method(method_name) do |*args, &blk|
|
31
|
-
return @__rp_rp.send(:
|
31
|
+
return @__rp_rp.send(cmd: :obj_method, id: @__rp_id, method: method_name, args: args, &blk).__rp_marshal
|
32
32
|
end
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
#Overwrite certain methods.
|
36
36
|
PROXY_METHODS = [:send]
|
37
37
|
PROXY_METHODS.each do |method_name|
|
@@ -39,17 +39,17 @@ class Ruby_process::Proxyobj
|
|
39
39
|
self.method_missing(method_name, *args, &blk)
|
40
40
|
end
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
#Proxies all calls to the process-object.
|
44
44
|
#===Examples
|
45
|
-
# str = rp.new(:String, "Kasper") #=> <
|
46
|
-
# length_int = str.length #=> <
|
45
|
+
# str = rp.new(:String, "Kasper") #=> <RubyProcess::ProxyObject::1>
|
46
|
+
# length_int = str.length #=> <RubyProcess::ProxyObject::2>
|
47
47
|
# length_int.__rp_marshal #=> 6
|
48
48
|
def method_missing(method, *args, &block)
|
49
49
|
debug "Method-missing-args-before: #{args} (#{@__rp_pid})\n" if @debug
|
50
50
|
real_args = @__rp_rp.parse_args(args)
|
51
51
|
debug "Method-missing-args-after: #{real_args}\n" if @debug
|
52
|
-
|
53
|
-
return @__rp_rp.send(:
|
52
|
+
|
53
|
+
return @__rp_rp.send(cmd: :obj_method, id: @__rp_id, method: method, args: real_args, &block)
|
54
54
|
end
|
55
|
-
end
|
55
|
+
end
|
data/ruby_process.gemspec
CHANGED
@@ -2,27 +2,30 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: ruby_process 0.0.13 ruby lib
|
5
6
|
|
6
7
|
Gem::Specification.new do |s|
|
7
8
|
s.name = "ruby_process"
|
8
|
-
s.version = "0.0.
|
9
|
+
s.version = "0.0.13"
|
9
10
|
|
10
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
11
13
|
s.authors = ["Kasper Johansen"]
|
12
|
-
s.date = "
|
14
|
+
s.date = "2015-04-06"
|
13
15
|
s.description = "A framework for spawning and communicating with other Ruby-processes"
|
14
16
|
s.email = "k@spernj.org"
|
15
17
|
s.extra_rdoc_files = [
|
16
18
|
"LICENSE.txt",
|
17
|
-
"README.
|
19
|
+
"README.md"
|
18
20
|
]
|
19
21
|
s.files = [
|
20
22
|
".document",
|
21
23
|
".rspec",
|
22
24
|
"Gemfile",
|
23
25
|
"LICENSE.txt",
|
24
|
-
"README.
|
26
|
+
"README.md",
|
25
27
|
"Rakefile",
|
28
|
+
"RubyProcess.gemspec",
|
26
29
|
"VERSION",
|
27
30
|
"cmds/blocks.rb",
|
28
31
|
"cmds/marshal.rb",
|
@@ -31,16 +34,18 @@ Gem::Specification.new do |s|
|
|
31
34
|
"cmds/static.rb",
|
32
35
|
"cmds/str_eval.rb",
|
33
36
|
"cmds/system.rb",
|
37
|
+
"examples/example_csv.rb",
|
34
38
|
"examples/example_file_write.rb",
|
35
39
|
"examples/example_knj_db_dump.rb",
|
36
40
|
"examples/example_strscan.rb",
|
37
41
|
"include/args_handeling.rb",
|
38
42
|
"lib/ruby_process.rb",
|
39
|
-
"lib/
|
40
|
-
"lib/
|
43
|
+
"lib/ruby_process/class_proxy.rb",
|
44
|
+
"lib/ruby_process/proxy_object.rb",
|
41
45
|
"ruby_process.gemspec",
|
42
46
|
"scripts/ruby_process_script.rb",
|
43
|
-
"
|
47
|
+
"shippable.yml",
|
48
|
+
"spec/class_proxy_spec.rb",
|
44
49
|
"spec/hard_load_spec.rb",
|
45
50
|
"spec/leaks_spec.rb",
|
46
51
|
"spec/ruby_process_spec.rb",
|
@@ -48,16 +53,16 @@ Gem::Specification.new do |s|
|
|
48
53
|
]
|
49
54
|
s.homepage = "http://github.com/kaspernj/ruby_process"
|
50
55
|
s.licenses = ["MIT"]
|
51
|
-
s.
|
52
|
-
s.rubygems_version = "1.8.23"
|
56
|
+
s.rubygems_version = "2.4.0"
|
53
57
|
s.summary = "A framework for spawning and communicating with other Ruby-processes"
|
54
58
|
|
55
59
|
if s.respond_to? :specification_version then
|
56
|
-
s.specification_version =
|
60
|
+
s.specification_version = 4
|
57
61
|
|
58
62
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
59
63
|
s.add_runtime_dependency(%q<wref>, [">= 0"])
|
60
64
|
s.add_runtime_dependency(%q<tsafe>, [">= 0"])
|
65
|
+
s.add_runtime_dependency(%q<string-cases>, [">= 0"])
|
61
66
|
s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
|
62
67
|
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
63
68
|
s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
|
@@ -65,6 +70,7 @@ Gem::Specification.new do |s|
|
|
65
70
|
else
|
66
71
|
s.add_dependency(%q<wref>, [">= 0"])
|
67
72
|
s.add_dependency(%q<tsafe>, [">= 0"])
|
73
|
+
s.add_dependency(%q<string-cases>, [">= 0"])
|
68
74
|
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
69
75
|
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
70
76
|
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
@@ -73,6 +79,7 @@ Gem::Specification.new do |s|
|
|
73
79
|
else
|
74
80
|
s.add_dependency(%q<wref>, [">= 0"])
|
75
81
|
s.add_dependency(%q<tsafe>, [">= 0"])
|
82
|
+
s.add_dependency(%q<string-cases>, [">= 0"])
|
76
83
|
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
77
84
|
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
78
85
|
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
@@ -25,13 +25,13 @@ end
|
|
25
25
|
debug = true if ARGV.index("--debug") != nil
|
26
26
|
raise "No PID given of parent process." if !pid
|
27
27
|
|
28
|
-
rps =
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
28
|
+
rps = RubyProcess.new(
|
29
|
+
in: $stdin,
|
30
|
+
out: $stdout,
|
31
|
+
err: $stderr,
|
32
|
+
debug: debug,
|
33
|
+
pid: pid
|
34
34
|
)
|
35
35
|
rps.listen
|
36
36
|
$stdout.puts("ruby_process_started")
|
37
|
-
rps.join
|
37
|
+
rps.join
|
data/shippable.yml
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "RubyProcess" do
|
4
|
+
it "should be able to do quick in-and-outs without leaking" do
|
5
|
+
ts = []
|
6
|
+
|
7
|
+
1.upto(2) do |tcount|
|
8
|
+
ts << Thread.new do
|
9
|
+
1.upto(10) do
|
10
|
+
RubyProcess::ClassProxy.run do |data|
|
11
|
+
sp = data[:subproc]
|
12
|
+
str = sp.new(:String, "Wee")
|
13
|
+
str.__rp_marshal.should eq "Wee"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
ts.each do |thread|
|
20
|
+
thread.join
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should be able to do basic stuff" do
|
25
|
+
require "stringio"
|
26
|
+
|
27
|
+
RubyProcess::ClassProxy.run do |data|
|
28
|
+
data[:subproc].static(:Object, :require, "rubygems")
|
29
|
+
data[:subproc].static(:Object, :require, "rexml/document")
|
30
|
+
|
31
|
+
doc = RubyProcess::ClassProxy::REXML::Document.new
|
32
|
+
doc.add_element("test")
|
33
|
+
|
34
|
+
strio = StringIO.new
|
35
|
+
doc.write(strio)
|
36
|
+
|
37
|
+
Kernel.const_defined?(:REXML).should eq false
|
38
|
+
strio.string.should eq "<test/>"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should be able to do multiple calls at once" do
|
43
|
+
ts = []
|
44
|
+
|
45
|
+
0.upto(9) do |tcount|
|
46
|
+
ts << Thread.new do
|
47
|
+
RubyProcess::ClassProxy.run do |data|
|
48
|
+
sp = data[:subproc]
|
49
|
+
sp.new(:String, "Wee")
|
50
|
+
|
51
|
+
1.upto(250) do
|
52
|
+
str = sp.new(:String, "Kasper Johansen")
|
53
|
+
|
54
|
+
str.__rp_marshal.should include "Kasper"
|
55
|
+
str << " More"
|
56
|
+
|
57
|
+
str.__rp_marshal.should include "Johansen"
|
58
|
+
str << " Even more"
|
59
|
+
|
60
|
+
str.__rp_marshal.should_not include "Christina"
|
61
|
+
str << " Much more"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
count = 0
|
68
|
+
ts.each do |t|
|
69
|
+
count += 1
|
70
|
+
#puts "Thread #{count}"
|
71
|
+
t.join
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should not leak" do
|
76
|
+
str = "kasper"
|
77
|
+
str = nil
|
78
|
+
sleep 0.1
|
79
|
+
GC.start
|
80
|
+
sleep 0.1
|
81
|
+
|
82
|
+
count_objs = 0
|
83
|
+
ObjectSpace.each_object(RubyProcess) do |obj|
|
84
|
+
count_objs += 1
|
85
|
+
end
|
86
|
+
|
87
|
+
count_proxy_objs = 0
|
88
|
+
ObjectSpace.each_object(RubyProcess::ProxyObject) do |obj|
|
89
|
+
count_proxy_objs += 1
|
90
|
+
end
|
91
|
+
|
92
|
+
count_objs.should be <= 1
|
93
|
+
RubyProcess::ClassProxy.constants.empty?.should eq true
|
94
|
+
end
|
95
|
+
end
|
data/spec/hard_load_spec.rb
CHANGED
@@ -2,38 +2,34 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
2
|
|
3
3
|
describe "RubyProcess" do
|
4
4
|
it "should be able to do basic stuff" do
|
5
|
-
|
5
|
+
RubyProcess::ClassProxy.run do |data|
|
6
6
|
sp = data[:subproc]
|
7
7
|
sp.new(:String, "Wee")
|
8
|
-
|
8
|
+
|
9
9
|
ts = []
|
10
|
-
|
10
|
+
|
11
11
|
1.upto(50) do |tcount|
|
12
12
|
ts << Thread.new do
|
13
13
|
1.upto(250) do
|
14
14
|
str = sp.new(:String, "Kasper Johansen")
|
15
|
-
|
16
|
-
|
15
|
+
|
16
|
+
str.__rp_marshal.should eq "Kasper Johansen"
|
17
17
|
str << " More"
|
18
|
-
|
19
|
-
|
18
|
+
|
19
|
+
str.__rp_marshal.should include "Johansen"
|
20
20
|
str << " Even more"
|
21
|
-
|
22
|
-
|
21
|
+
|
22
|
+
str.__rp_marshal.should_not include "Christina"
|
23
23
|
str << " Much more"
|
24
|
-
|
25
|
-
|
26
|
-
raise "Expected res2 to be true but it wasnt: '#{res2}'." if res2 != true
|
27
|
-
raise "Expected res3 to be false but it wasnt: '#{res3}'." if res3 != false
|
28
|
-
|
29
|
-
#print "."
|
24
|
+
|
25
|
+
str.__rp_marshal.should eq "Kasper Johansen More Even more Much more"
|
30
26
|
end
|
31
27
|
end
|
32
28
|
end
|
33
|
-
|
29
|
+
|
34
30
|
ts.each do |t|
|
35
31
|
t.join
|
36
32
|
end
|
37
33
|
end
|
38
34
|
end
|
39
|
-
end
|
35
|
+
end
|