iprocess 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gemtest ADDED
File without changes
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ .baretest_*
2
+ docs/*
3
+ doc/*
4
+ .yardoc/*
5
+ .DS_Store
6
+ *.gem
7
+ *.rbc
8
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - rbx-19mode
5
+ - ruby-head
6
+
7
+ notifications:
8
+ irc: "irc.freenode.org#flowof.info"
9
+ recipients:
10
+ - rob@flowof.info
data/.yardopts ADDED
@@ -0,0 +1,8 @@
1
+ -m markdown
2
+ --no-private
3
+ --hide-void-return
4
+ --no-cache
5
+ -
6
+ LICENSE.txt
7
+ ChangeLog
8
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ # This file is for Travis (travis-ci.org).
2
+ source :rubygems
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (C) 2011 by Robert Gleeson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
20
+
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ __OVERVIEW__
2
+
3
+
4
+ | Project | IProcess
5
+ |:----------------|:--------------------------------------------------
6
+ | Homepage | https://github.com/robgleeson/iprocess
7
+ | Documentation | http://rubydoc.info/gems/iprocess/frames
8
+ | Author | Rob Gleeson
9
+
10
+
11
+ __DESCRIPTION__
12
+
13
+ IProcess, short for _Inter Process Communication(IPC) Process_, is a
14
+ Domain Specific Language(DSL) that can be used to share Ruby objects
15
+ between processes on UNIX-like operating systems. The README covers the
16
+ basics, and there is the API documentation for everything else.
17
+
18
+ This project was formerly known as 'Barney'.
19
+
20
+ __WHY?__
21
+
22
+ I wanted to be able to:
23
+
24
+ * Spawn a subprocess, apply restrictions to that subprocess, then collect
25
+ and send Ruby objects back to the parent process.
26
+ (that was for a IRC bot evaluating Ruby code).
27
+
28
+ * Spawn multiple parallel jobs, then collect and send back Ruby objects to the
29
+ parent process.
30
+
31
+
32
+ __EXAMPLES__
33
+
34
+ __1.__
35
+
36
+ Sequential in nature (each subprocess must finish before another can execute):
37
+
38
+ name = "rob"
39
+
40
+ IProcess.new do
41
+ share :name
42
+
43
+ fork do
44
+ name.capitalize!
45
+ end
46
+ end
47
+
48
+ p name # => "Rob"
49
+
50
+ __2.__
51
+
52
+ A subprocess is spawned 5 times, in parallel:
53
+
54
+ workload = IProcess::Job.spawn(5) do
55
+ # Replace this with heavily CPU-bound code ;-)
56
+ 1 + 1
57
+ end
58
+
59
+ p workload # [2, 2, 2, 2, 2]
60
+
61
+ __PLATFORM SUPPORT__
62
+
63
+ _supported_
64
+
65
+ * Rubinius (1.9 mode)
66
+ * CRuby (1.9)
67
+
68
+ _unsupported_
69
+
70
+ * CRuby 1.8
71
+ * MacRuby
72
+ * JRuby
73
+
74
+ __INSTALL__
75
+
76
+ gem install iprocess
77
+
78
+ __LICENSE__
79
+
80
+
81
+ See LICENSE.txt
82
+
83
+
84
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ desc 'Run test suite.'
4
+ task :test do
5
+ $LOAD_PATH.unshift './lib'
6
+ require './test/setup'
7
+ end
8
+
9
+ task :default => :test
10
+
data/iprocess.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ $LOAD_PATH.unshift './lib'
2
+ require 'iprocess/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'iprocess'
6
+ s.version = IProcess::VERSION
7
+ s.authors = ["Rob Gleeson"]
8
+ s.email = 'rob@flowof.info'
9
+ s.homepage = 'https://github.com/robgleeson/iprocess'
10
+ s.summary = 'A Domain Specific Language(DSL) for sharing Ruby objects' \
11
+ 'between processes on UNIX-like operating systems.'
12
+ s.description = s.summary
13
+
14
+ s.files = `git ls-files`.each_line.map(&:chomp)
15
+ s.test_files = Dir.glob "test/**/*.rb"
16
+
17
+ s.platform = Gem::Platform::RUBY
18
+ s.require_path = 'lib'
19
+ s.rubyforge_project = '[none]'
20
+ s.required_rubygems_version = '>= 1.3.6'
21
+ s.required_ruby_version = '~> 1.9.1'
22
+
23
+ s.add_development_dependency 'yard' , '~> 0.7'
24
+ s.add_development_dependency 'redcarpet', '~> 1.17'
25
+ s.add_development_dependency 'minitest' , '~> 2.6'
26
+ s.add_development_dependency 'rake' , '~> 0.9.2'
27
+ end
@@ -0,0 +1,44 @@
1
+ class IProcess::Channel
2
+
3
+ attr_reader :name
4
+
5
+ #
6
+ # @yieldparam [IProcess::Channel] _self
7
+ # Yields self.
8
+ #
9
+ def initialize name = nil
10
+ @reader, @writer = IO.pipe
11
+ @name = name
12
+
13
+ if block_given?
14
+ yield self
15
+ end
16
+ end
17
+
18
+ #
19
+ # Write a object to the channel.
20
+ #
21
+ # @param [Object] object
22
+ # A object to add to the channel.
23
+ #
24
+ def write object
25
+ @reader.close
26
+ @writer.write Marshal.dump(object)
27
+ @writer.close
28
+ end
29
+
30
+ #
31
+ # Receive a object from the channel.
32
+ #
33
+ # @return [Object]
34
+ # The object added to the channel.
35
+ #
36
+ def recv
37
+ @writer.close
38
+ obj = Marshal.load(@reader.read)
39
+ @reader.close
40
+ obj
41
+ end
42
+
43
+ end
44
+
@@ -0,0 +1,55 @@
1
+ class IProcess::Delegate
2
+
3
+ #
4
+ # @param [IProcess] delegate
5
+ # An instance of {IProcess}.
6
+ #
7
+ def initialize delegate
8
+ @__delegate__ = delegate
9
+ end
10
+
11
+ #
12
+ # @param
13
+ # (see IProcess#share)
14
+ #
15
+ # @return
16
+ # (see IProcess#share)
17
+ #
18
+ def share *args
19
+ @__delegate__.share *args
20
+ end
21
+
22
+ #
23
+ # @param
24
+ # (see IProcess#unshare)
25
+ #
26
+ # @return
27
+ # (see IProcess#unshare)
28
+ #
29
+ def unshare *args
30
+ @__delegate__.unshare *args
31
+ end
32
+
33
+ #
34
+ # @return
35
+ # (see IProcess#variables)
36
+ #
37
+ def variables
38
+ @__delegate__.variables
39
+ end
40
+
41
+ #
42
+ # @param
43
+ # (see IProcess#fork)
44
+ #
45
+ # @raise
46
+ # (see IProcess#fork)
47
+ #
48
+ # @return
49
+ # (see IProcess#fork)
50
+ #
51
+ def fork(&block)
52
+ @__delegate__.fork(&block)
53
+ end
54
+
55
+ end
@@ -0,0 +1,70 @@
1
+ class IProcess::Job
2
+
3
+ #
4
+ # Spawn one or more jobs to be run in parallel.
5
+ #
6
+ # @param [Integer] number_of_jobs
7
+ # The number of jobs to spawn.
8
+ #
9
+ # @param [Proc] worker
10
+ # The unit of work to execute in one or more jobs.
11
+ #
12
+ # @return [Array<Object>]
13
+ # The return value of one or more workers.
14
+ #
15
+ def self.spawn number_of_jobs = 1, &worker
16
+ jobs =
17
+ Array.new(number_of_jobs) do
18
+ job = IProcess::Job.new(&worker)
19
+ job.execute
20
+ job
21
+ end
22
+
23
+ jobs.map do |job|
24
+ job.result
25
+ end
26
+ end
27
+
28
+ #
29
+ # @param [Proc] worker
30
+ # The unit of work to execute in a subprocess.
31
+ #
32
+ # @raise [ArgumentError]
33
+ # If a worker is not given.
34
+ #
35
+ # @return [IProcess::Job]
36
+ # Returns self.
37
+ #
38
+ def initialize &worker
39
+ unless block_given?
40
+ raise ArgumentError, 'No block given.'
41
+ end
42
+
43
+ @worker = worker
44
+ @channel = nil
45
+ @pid = nil
46
+ end
47
+
48
+ #
49
+ # Executes a unit of work in a subprocess.
50
+ #
51
+ # @return [Fixnum]
52
+ # The process ID of the spawned subprocess.
53
+ #
54
+ def execute
55
+ @channel = IProcess::Channel.new
56
+ @pid = fork { @channel.write(@worker.call) }
57
+ end
58
+
59
+ #
60
+ # @return [Object]
61
+ # Returns the return value of the unit of work.
62
+ #
63
+ def result
64
+ Process.wait(@pid)
65
+ @channel.recv
66
+ end
67
+
68
+ end
69
+
70
+
@@ -0,0 +1,3 @@
1
+ class IProcess
2
+ VERSION = '1.0.0'
3
+ end
data/lib/iprocess.rb ADDED
@@ -0,0 +1,97 @@
1
+ class IProcess
2
+
3
+ #
4
+ # @return [IProcess]
5
+ #
6
+ def initialize &block
7
+ @variables = SortedSet.new
8
+
9
+ if block_given?
10
+ @scope = block.binding
11
+ IProcess::Delegate.new(self).instance_eval(&block)
12
+ else
13
+ @scope = nil
14
+ end
15
+ end
16
+
17
+ #
18
+ # @return [Array<Symbol>]
19
+ # Returns a list of shared variables.
20
+ #
21
+ def variables
22
+ @variables.to_a
23
+ end
24
+
25
+ #
26
+ # Marks a variable or constant to be shared between two processes.
27
+ #
28
+ # @param [Array<#to_sym>] variables
29
+ # Accepts the name(s) of the variables or constants to share.
30
+ #
31
+ # @return [Array<Symbol>]
32
+ # Returns a list of all variables that are being shared.
33
+ #
34
+ def share *variables
35
+ @variables.merge variables.map(&:to_sym)
36
+ @variables.to_a
37
+ end
38
+
39
+ #
40
+ # Removes a variable or constant from being shared between two processes.
41
+ #
42
+ # @param [Array<#to_sym>] variables
43
+ # Accepts the name(s) of the variables or constants to stop sharing.
44
+ #
45
+ # @return [Array<Symbol>]
46
+ # Returns a list of the variables that are still being shared.
47
+ #
48
+ def unshare *variables
49
+ @variables.subtract variables.map(&:to_sym)
50
+ @variables.to_a
51
+ end
52
+
53
+ #
54
+ # Spawns a subprocess.
55
+ # The subprocess is waited on via Process.wait().
56
+ #
57
+ # @param [Proc] &block
58
+ # A block executed within a subprocess.
59
+ #
60
+ # @raise [ArgumentError]
61
+ # If no block is given.
62
+ #
63
+ # @return [Fixnum]
64
+ # The Process ID(PID) of the subprocess.
65
+ #
66
+ def fork &block
67
+ unless block_given?
68
+ raise ArgumentError, "No block given."
69
+ end
70
+
71
+ scope = @scope || block.binding
72
+ channels = @variables.map { |name| IProcess::Channel.new(name) }
73
+
74
+ pid = Kernel.fork do
75
+ scope.eval("self").instance_eval(&block)
76
+ channels.each do |channel|
77
+ channel.write scope.eval(channel.name.to_s)
78
+ end
79
+ end
80
+
81
+ Process.wait(pid)
82
+
83
+ channels.each do |channel|
84
+ Thread.current[:__iprocess_obj__] = channel.recv
85
+ scope.eval("#{channel.name} = Thread.current[:__iprocess_obj__]")
86
+ end
87
+
88
+ pid
89
+ end
90
+
91
+ end
92
+
93
+ require 'set'
94
+ require 'iprocess/version'
95
+ require 'iprocess/channel'
96
+ require 'iprocess/job'
97
+ require 'iprocess/delegate'
data/test/setup.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'iprocess'
2
+ require 'minitest/spec'
3
+ require 'minitest/autorun'
4
+
5
+ alias :context :describe
6
+
7
+ Dir.glob("test/*.rb").each do |test|
8
+ require "./#{test}"
9
+ end
@@ -0,0 +1,9 @@
1
+ context IProcess::Job do
2
+ context 'spawn' do
3
+ it 'must spawn two workers and return the result of each.' do
4
+ topic = IProcess::Job.spawn(2) { :ok }
5
+ topic.must_equal([:ok, :ok])
6
+ end
7
+ end
8
+ end
9
+
@@ -0,0 +1,95 @@
1
+ context IProcess do
2
+
3
+ context 'initialize' do
4
+ it 'must call block if given.' do
5
+ mock = MiniTest::Mock.new
6
+ mock.expect :ok
7
+
8
+ IProcess.new do
9
+ mock.ok
10
+ end
11
+ end
12
+ end
13
+
14
+ context 'share' do
15
+ it 'must share a variable.' do
16
+ IProcess.new do
17
+ share :a
18
+ variables.must_equal [:a]
19
+ end
20
+ end
21
+
22
+ it "must not store duplicate variables" do
23
+ IProcess.new do
24
+ share :a, :a
25
+ variables.must_equal [:a]
26
+ end
27
+ end
28
+ end
29
+
30
+ context 'unshare' do
31
+ it "must unshare a variable." do
32
+ IProcess.new do
33
+ share :a
34
+ unshare :a
35
+ variables.must_be_empty
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'fork' do
41
+ it 'must return the PID on success.' do
42
+ pid = IProcess.new.fork { }
43
+ assert_equal Fixnum, pid.class
44
+ end
45
+
46
+ it 'must raise a ArgumentError if a block is not given.' do
47
+ assert_raises ArgumentError do
48
+ IProcess.new.fork
49
+ end
50
+ end
51
+
52
+ it 'must synchronize a shared variable.' do
53
+ variable = :o_O
54
+
55
+ IProcess.new do
56
+ share :variable
57
+ fork { variable = :ok }
58
+ end
59
+
60
+ variable.must_equal(:ok)
61
+ end
62
+
63
+ it 'must synchronize two shared variables.' do
64
+ variable1 = :o_O
65
+ variable2 = :UnF
66
+
67
+ IProcess.new do
68
+ share :variable1, :variable2
69
+ fork do
70
+ variable1 = 123
71
+ variable2 = 456
72
+ end
73
+ end
74
+
75
+ variable1.must_equal(123)
76
+ variable2.must_equal(456)
77
+ end
78
+
79
+ it 'must synchronize a instance variable.' do
80
+ @ivar = :o_O
81
+
82
+ IProcess.new do
83
+ share :@ivar
84
+
85
+ fork do
86
+ @ivar = :ok
87
+ end
88
+ end
89
+
90
+ @ivar.must_equal(:ok)
91
+ end
92
+ end
93
+
94
+ end
95
+
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iprocess
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rob Gleeson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: yard
16
+ requirement: &70132214923240 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.7'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70132214923240
25
+ - !ruby/object:Gem::Dependency
26
+ name: redcarpet
27
+ requirement: &70132214921700 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '1.17'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70132214921700
36
+ - !ruby/object:Gem::Dependency
37
+ name: minitest
38
+ requirement: &70132214920640 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '2.6'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70132214920640
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: &70132214919640 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.2
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70132214919640
58
+ description: A Domain Specific Language(DSL) for sharing Ruby objectsbetween processes
59
+ on UNIX-like operating systems.
60
+ email: rob@flowof.info
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gemtest
66
+ - .gitignore
67
+ - .travis.yml
68
+ - .yardopts
69
+ - Gemfile
70
+ - LICENSE.txt
71
+ - README.md
72
+ - Rakefile
73
+ - iprocess.gemspec
74
+ - lib/iprocess.rb
75
+ - lib/iprocess/channel.rb
76
+ - lib/iprocess/delegate.rb
77
+ - lib/iprocess/job.rb
78
+ - lib/iprocess/version.rb
79
+ - test/setup.rb
80
+ - test/test_IProcess_Job_class.rb
81
+ - test/test_IProcess_class.rb
82
+ homepage: https://github.com/robgleeson/iprocess
83
+ licenses: []
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 1.9.1
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: 1.3.6
100
+ requirements: []
101
+ rubyforge_project: ! '[none]'
102
+ rubygems_version: 1.8.15
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: A Domain Specific Language(DSL) for sharing Ruby objectsbetween processes
106
+ on UNIX-like operating systems.
107
+ test_files:
108
+ - test/setup.rb
109
+ - test/test_IProcess_class.rb
110
+ - test/test_IProcess_Job_class.rb
111
+ has_rdoc: