collective 0.2.0
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/.autotest +13 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/Gemfile +16 -0
- data/Guardfile +6 -0
- data/README +7 -0
- data/Rakefile +11 -0
- data/bin/collective +37 -0
- data/collective.gemspec +23 -0
- data/demo/demo +36 -0
- data/demo/demo.rb +30 -0
- data/demo/demo3 +36 -0
- data/demo/job1.rb +31 -0
- data/demo/job2.rb +42 -0
- data/demo/job3.rb +44 -0
- data/demo/populate.rb +22 -0
- data/lib/collective.rb +52 -0
- data/lib/collective/checker.rb +51 -0
- data/lib/collective/configuration.rb +219 -0
- data/lib/collective/idler.rb +81 -0
- data/lib/collective/key.rb +48 -0
- data/lib/collective/lifecycle_observer.rb +25 -0
- data/lib/collective/log.rb +29 -0
- data/lib/collective/messager.rb +218 -0
- data/lib/collective/mocks/storage.rb +108 -0
- data/lib/collective/monitor.rb +58 -0
- data/lib/collective/policy.rb +60 -0
- data/lib/collective/pool.rb +180 -0
- data/lib/collective/redis/storage.rb +142 -0
- data/lib/collective/registry.rb +123 -0
- data/lib/collective/squiggly.rb +20 -0
- data/lib/collective/utilities/airbrake_observer.rb +26 -0
- data/lib/collective/utilities/hoptoad_observer.rb +26 -0
- data/lib/collective/utilities/log_observer.rb +40 -0
- data/lib/collective/utilities/observeable.rb +18 -0
- data/lib/collective/utilities/observer_base.rb +59 -0
- data/lib/collective/utilities/process.rb +82 -0
- data/lib/collective/utilities/signal_hook.rb +47 -0
- data/lib/collective/utilities/storage_base.rb +41 -0
- data/lib/collective/version.rb +3 -0
- data/lib/collective/worker.rb +161 -0
- data/spec/checker_spec.rb +20 -0
- data/spec/configuration_spec.rb +24 -0
- data/spec/helper.rb +33 -0
- data/spec/idler_spec.rb +58 -0
- data/spec/key_spec.rb +41 -0
- data/spec/messager_spec.rb +131 -0
- data/spec/mocks/storage_spec.rb +108 -0
- data/spec/monitor_spec.rb +15 -0
- data/spec/policy_spec.rb +43 -0
- data/spec/pool_spec.rb +119 -0
- data/spec/redis/storage_spec.rb +133 -0
- data/spec/registry_spec.rb +52 -0
- data/spec/support/jobs.rb +58 -0
- data/spec/support/redis.rb +22 -0
- data/spec/support/timing.rb +32 -0
- data/spec/utilities/observer_base_spec.rb +50 -0
- data/spec/utilities/process_spec.rb +17 -0
- data/spec/worker_spec.rb +121 -0
- data/unused/times.rb +45 -0
- metadata +148 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
class Collective::Utilities::StorageBase
|
4
|
+
|
5
|
+
module Resolver
|
6
|
+
# resolution is as so:
|
7
|
+
# nil, :mock => :mock
|
8
|
+
# :redis => redis://127.0.0.1:6379/1
|
9
|
+
# string => CLASS.new
|
10
|
+
# CLASS => CLASS.new
|
11
|
+
# PROC => yield (recursive)
|
12
|
+
# [ ARRAY ] => first, *args (recursive)
|
13
|
+
def resolve( storage, *args )
|
14
|
+
storage ||= :mock
|
15
|
+
case
|
16
|
+
when storage.respond_to?(:call)
|
17
|
+
resolve(storage.call(*args))
|
18
|
+
else
|
19
|
+
case storage
|
20
|
+
when :mock
|
21
|
+
resolve( Collective::Mocks::Storage, *args )
|
22
|
+
when :redis
|
23
|
+
resolve( Collective::Redis::Storage, *args )
|
24
|
+
when Class
|
25
|
+
storage.new(*args)
|
26
|
+
when String
|
27
|
+
resolve( Collective.resolve_class(storage), *args )
|
28
|
+
when Array
|
29
|
+
args = storage.dup + args
|
30
|
+
storage = args.shift
|
31
|
+
resolve( storage, *args )
|
32
|
+
else
|
33
|
+
return storage
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end # resolve
|
37
|
+
end # Resolver
|
38
|
+
|
39
|
+
extend Resolver
|
40
|
+
|
41
|
+
end # Collective::Utilities::StorageBase
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
=begin
|
4
|
+
|
5
|
+
A Worker is a forked process which runs jobs.
|
6
|
+
Jobs are short lived and run repeatedly.
|
7
|
+
|
8
|
+
=end
|
9
|
+
|
10
|
+
class Collective::Worker
|
11
|
+
|
12
|
+
include Collective::Utilities::Observeable
|
13
|
+
|
14
|
+
# forks a new process
|
15
|
+
# creates a new instance of the job class
|
16
|
+
# runs a loop which calls the job
|
17
|
+
def self.spawn( prototype_job, options = {} )
|
18
|
+
policy = options[:policy] || Collective::Policy.resolve
|
19
|
+
name = options[:name] || policy.name || prototype_job.to_s
|
20
|
+
storage = policy.storage
|
21
|
+
registry = options[:registry] || Collective::Registry.new( name, storage )
|
22
|
+
|
23
|
+
foptions = { stdout: "/tmp/debug.log" }
|
24
|
+
Collective::Utilities::Process.fork_and_detach( foptions ) do
|
25
|
+
if after_forks = policy.after_forks then
|
26
|
+
after_forks.each { |af| af.call }
|
27
|
+
end
|
28
|
+
# $0 = "$0 #{name}"
|
29
|
+
worker = new( prototype_job, options )
|
30
|
+
trap("TERM") { worker.quit! }
|
31
|
+
worker.run
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
attr :job
|
36
|
+
attr :name
|
37
|
+
attr :policy
|
38
|
+
attr :registry
|
39
|
+
attr :state
|
40
|
+
attr :storage
|
41
|
+
attr :worker_expire
|
42
|
+
attr :worker_jobs
|
43
|
+
|
44
|
+
# @param options[:name] is optional
|
45
|
+
# @param options[:policy] is optional
|
46
|
+
# @param options[:registry] is optional
|
47
|
+
def initialize( prototype_job, options = {} )
|
48
|
+
@policy = options[:policy] || Collective::Policy.resolve
|
49
|
+
@name = options[:name] || policy.name || prototype_job.to_s
|
50
|
+
@storage = policy.storage
|
51
|
+
@registry = options[:registry] || Collective::Registry.new( name, storage )
|
52
|
+
@job = Collective::Idler.new( resolve_job( prototype_job ), min_sleep: policy.worker_idle_min_sleep, max_sleep: policy.worker_idle_max_sleep )
|
53
|
+
|
54
|
+
# type checks
|
55
|
+
policy.pool_min_workers
|
56
|
+
registry.workers
|
57
|
+
|
58
|
+
# post-fork processing
|
59
|
+
storage.reconnect_after_fork
|
60
|
+
registry.reconnect_after_fork
|
61
|
+
|
62
|
+
# set up observers
|
63
|
+
policy.observers.each do |observer|
|
64
|
+
o = Collective::Utilities::ObserverBase.resolve(observer)
|
65
|
+
add_observer(o)
|
66
|
+
end
|
67
|
+
|
68
|
+
# manage the registry via an observer
|
69
|
+
add_observer( Collective::LifecycleObserver.new( key, registry ) )
|
70
|
+
end
|
71
|
+
|
72
|
+
def run()
|
73
|
+
@state = :running
|
74
|
+
@worker_jobs = 0
|
75
|
+
@worker_expire = Time.now + policy.worker_max_lifetime
|
76
|
+
|
77
|
+
context = { worker: self }
|
78
|
+
with_start_and_stop do
|
79
|
+
while running? do
|
80
|
+
with_quitting_checks do
|
81
|
+
with_heartbeat do
|
82
|
+
job.call(context)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def quit!()
|
90
|
+
@state = :quitting
|
91
|
+
end
|
92
|
+
|
93
|
+
def running?
|
94
|
+
state == :running
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_s
|
98
|
+
%Q[Worker(#{key})]
|
99
|
+
end
|
100
|
+
|
101
|
+
# the key is a constant string which uniquely identifies this worker
|
102
|
+
# WARNING this would be invalidated if we forked or set this before forking
|
103
|
+
def key
|
104
|
+
@key ||= Collective::Key.new( name, Process.pid )
|
105
|
+
end
|
106
|
+
|
107
|
+
def mq
|
108
|
+
@mq ||= Collective::Messager.new( storage, my_address: key )
|
109
|
+
end
|
110
|
+
|
111
|
+
# ----------------------------------------------------------------------------
|
112
|
+
protected
|
113
|
+
# ----------------------------------------------------------------------------
|
114
|
+
|
115
|
+
def with_start_and_stop(&block)
|
116
|
+
notify :worker_started
|
117
|
+
begin
|
118
|
+
yield
|
119
|
+
ensure
|
120
|
+
notify :worker_stopped
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def with_quitting_checks(&block)
|
125
|
+
yield
|
126
|
+
ensure
|
127
|
+
@worker_jobs += 1
|
128
|
+
quit! if policy.worker_max_jobs <= worker_jobs
|
129
|
+
quit! if worker_expire <= Time.now
|
130
|
+
end
|
131
|
+
|
132
|
+
def with_heartbeat(&block)
|
133
|
+
begin
|
134
|
+
yield
|
135
|
+
rescue => x
|
136
|
+
notify :job_error, x
|
137
|
+
ensure
|
138
|
+
notify :worker_heartbeat
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def resolve_job( job_factory )
|
143
|
+
raise Collective::ConfigurationError if ! job_factory
|
144
|
+
|
145
|
+
case
|
146
|
+
when job_factory.respond_to?(:call)
|
147
|
+
job_factory
|
148
|
+
when job_factory.respond_to?(:new)
|
149
|
+
context = { worker: self }
|
150
|
+
resolve_job(job_factory.new(context))
|
151
|
+
else
|
152
|
+
case job_factory
|
153
|
+
when String, Symbol
|
154
|
+
resolve_job(Collective.resolve_class(job_factory.to_s))
|
155
|
+
else
|
156
|
+
raise Collective::ConfigurationError, "Unknown kind of job #{job_factory.inspect}"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end # Collective::Worker
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "helper"
|
4
|
+
require "collective/checker"
|
5
|
+
|
6
|
+
describe Checker do
|
7
|
+
|
8
|
+
it "does something" do
|
9
|
+
activity_count = 192
|
10
|
+
last_time = Time.now - 3600
|
11
|
+
c = Checker.new activity_count, last_time
|
12
|
+
|
13
|
+
c.check?.should_not be_false
|
14
|
+
c.estimated_delay.should be >= 1.0
|
15
|
+
c.estimated_delay.should be < 8.0
|
16
|
+
|
17
|
+
c.checked 12
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "helper"
|
4
|
+
|
5
|
+
describe Collective::Configuration do
|
6
|
+
|
7
|
+
it "should parse command-line switches" do
|
8
|
+
c = Collective::Configuration.parse %w(--dry-run --env the_env --name a_name --chdir .)
|
9
|
+
c.env.should eq("the_env")
|
10
|
+
c.name.should eq("a_name")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should parse the DSL" do
|
14
|
+
script = <<-EOT.gsub(/^ +/,'')
|
15
|
+
set_env "the_env"
|
16
|
+
set_name "a_name"
|
17
|
+
chdir "."
|
18
|
+
EOT
|
19
|
+
c = Collective::Configuration.parse ["--dry-run", "--script", script]
|
20
|
+
c.env.should eq("the_env")
|
21
|
+
c.name.should eq("a_name")
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
=begin
|
4
|
+
@see https://www.relishapp.com/rspec/rspec-core/docs/hooks/around-hooks
|
5
|
+
@see https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/be-matchers
|
6
|
+
@see https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/equality-matchers
|
7
|
+
@see https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/operator-matchers
|
8
|
+
@see https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/predicate-matchers
|
9
|
+
=end
|
10
|
+
|
11
|
+
require "bundler/setup" # set up gem paths
|
12
|
+
require "ruby-debug" # because sometimes you need it
|
13
|
+
|
14
|
+
#require "simplecov" # code coverage
|
15
|
+
#SimpleCov.start # must be loaded before our own code
|
16
|
+
|
17
|
+
require "collective" # load this gem
|
18
|
+
require "support/jobs" # simple helpers for testing
|
19
|
+
require "support/redis" # simple helpers for testing
|
20
|
+
require "support/timing" # simple helpers for testing
|
21
|
+
|
22
|
+
RSpec.configure do |spec|
|
23
|
+
# @see https://www.relishapp.com/rspec/rspec-core/docs/helper-methods/define-helper-methods-in-a-module
|
24
|
+
spec.include RedisClient, redis: true
|
25
|
+
spec.include Timing, time: true
|
26
|
+
spec.include Collective::Idler::Utilities
|
27
|
+
|
28
|
+
# nuke the Redis database around each run
|
29
|
+
# @see https://www.relishapp.com/rspec/rspec-core/docs/hooks/around-hooks
|
30
|
+
spec.around( :each, redis: true ) do |example|
|
31
|
+
with_clean_redis { example.run }
|
32
|
+
end
|
33
|
+
end
|
data/spec/idler_spec.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "helper"
|
4
|
+
|
5
|
+
describe Collective::Idler do
|
6
|
+
|
7
|
+
it "should accept a proc" do
|
8
|
+
idler = Collective::Idler.new() { true }
|
9
|
+
expect { idler.call }.to_not raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should accept a lambda" do
|
13
|
+
job = ->() { true }
|
14
|
+
idler = Collective::Idler.new(job)
|
15
|
+
expect { idler.call }.to_not raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should accept an object with an interface" do
|
19
|
+
idler = Collective::Idler.new( TrueJob.new )
|
20
|
+
expect { idler.call }.to_not raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should refuse non-callable jobs" do
|
24
|
+
fake_job = Object.new
|
25
|
+
expect { Collective::Idler.new(fake_job) }.to raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "when dealing with sleep times", time: true do
|
29
|
+
|
30
|
+
it "should not run idle tasks too much" do
|
31
|
+
count = 0
|
32
|
+
Collective::Idler.wait_until { count += 1; false }
|
33
|
+
count.should be <= 10
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should sleep after a failure" do
|
37
|
+
job = ->() { false }
|
38
|
+
idler = Collective::Idler.new( job, min_sleep: 0.125 )
|
39
|
+
time { idler.call }.should be >= 0.125
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should not sleep after a success" do
|
43
|
+
result = false
|
44
|
+
job = ->() { result }
|
45
|
+
idler = Collective::Idler.new( job, min_sleep: 0.125 )
|
46
|
+
idler.call
|
47
|
+
time { result = true; idler.call }.should be < 0.1
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should not sleep too long" do
|
51
|
+
job = ->() { false }
|
52
|
+
idler = Collective::Idler.new( job, min_sleep: 0.125 )
|
53
|
+
time { idler.call }.should be < 0.25
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/spec/key_spec.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "helper"
|
4
|
+
|
5
|
+
describe Collective::Key do
|
6
|
+
|
7
|
+
it "can make keys" do
|
8
|
+
name = "processor"
|
9
|
+
pid = 1234
|
10
|
+
host = "foo.example.com"
|
11
|
+
Collective::Key.new( name, pid, host ).to_s.should eq("processor-1234@foo.example.com")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can parse keys" do
|
15
|
+
Collective::Key.parse("processor-1234@foo.example.com").should eq(Collective::Key.new( "processor", "1234", "foo.example.com" ))
|
16
|
+
Collective::Key.parse("test-job-1234@foo.example.com").should eq(Collective::Key.new( "test-job", "1234", "foo.example.com" ))
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can compare to another key" do
|
20
|
+
key1 = Collective::Key.new "Alpha", 1234, "example.com"
|
21
|
+
key2 = Collective::Key.new "Alpha", 1234, "example.com"
|
22
|
+
key1.should eq(key2)
|
23
|
+
|
24
|
+
key1 = Collective::Key.new "Alpha", 1234, "example.com"
|
25
|
+
key2 = Collective::Key.new "Alpha", "1234", "example.com"
|
26
|
+
key1.should eq(key2)
|
27
|
+
|
28
|
+
key1 = Collective::Key.new "Alpha", 1234, "example.com"
|
29
|
+
key2 = Collective::Key.new "Beta", 1234, "example.com"
|
30
|
+
key1.should_not eq(key2)
|
31
|
+
|
32
|
+
key1 = Collective::Key.new "Alpha", 1234, "example.com"
|
33
|
+
key2 = Collective::Key.new "Alpha", 2345, "example.com"
|
34
|
+
key1.should_not eq(key2)
|
35
|
+
|
36
|
+
key1 = Collective::Key.new "Alpha", 1234, "example.com"
|
37
|
+
key2 = Collective::Key.new "Alpha", 1234, "example.org"
|
38
|
+
key1.should_not eq(key2)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "helper"
|
4
|
+
|
5
|
+
describe Collective::Messager, redis: true do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@a = "me@example.com"
|
9
|
+
@b = "you@example.com"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "can take to_address initially or per message" do
|
13
|
+
storage = Collective::Mocks::Storage.new
|
14
|
+
|
15
|
+
a = Collective::Messager.new( storage, my_address: @a )
|
16
|
+
expect { a.send "Hello" }.to raise_exception
|
17
|
+
expect { a.send "Hello", to: @b }.to_not raise_exception
|
18
|
+
|
19
|
+
b = Collective::Messager.new( storage, my_address: @a, to_address: @b )
|
20
|
+
expect { b.send "Hello" }.to_not raise_exception
|
21
|
+
end
|
22
|
+
|
23
|
+
it "the message id varies with the source, content and timestamp" do
|
24
|
+
storage = Collective::Mocks::Storage.new
|
25
|
+
a = Collective::Messager.new( storage, my_address: @a, to_address: @b )
|
26
|
+
b = Collective::Messager.new( storage, my_address: @b, to_address: @a )
|
27
|
+
now = 1234567890
|
28
|
+
|
29
|
+
id1 = a.send "Hello", at: now
|
30
|
+
id3 = a.send "Hello", at: now+1
|
31
|
+
id4 = a.send "Goodbye", at: now
|
32
|
+
id5 = b.send "Hello", at: now
|
33
|
+
|
34
|
+
id3.should_not eq(id1)
|
35
|
+
id4.should_not eq(id1)
|
36
|
+
id5.should_not eq(id1)
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "sending and receiving messages" do
|
40
|
+
|
41
|
+
it "can match against a string" do
|
42
|
+
storage = Collective::Mocks::Storage.new
|
43
|
+
a = Collective::Messager.new( storage, my_address: @a )
|
44
|
+
b = Collective::Messager.new( storage, my_address: @b )
|
45
|
+
b.expect("Hello") { |message| false }
|
46
|
+
a.send "Hello", to: @b
|
47
|
+
expect { b.receive }.to_not raise_exception
|
48
|
+
end
|
49
|
+
|
50
|
+
it "can match against a regexp" do
|
51
|
+
storage = Collective::Mocks::Storage.new
|
52
|
+
a = Collective::Messager.new( storage, my_address: @a )
|
53
|
+
b = Collective::Messager.new( storage, my_address: @b )
|
54
|
+
b.expect(/ello/) { |message| false }
|
55
|
+
a.send "Hello", to: @b
|
56
|
+
expect { b.receive }.to_not raise_exception
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns true if it got a message" do
|
60
|
+
storage = Collective::Mocks::Storage.new
|
61
|
+
a = Collective::Messager.new( storage, my_address: @a )
|
62
|
+
b = Collective::Messager.new( storage, my_address: @b )
|
63
|
+
b.expect(//) { |message| false }
|
64
|
+
a.receive.should eq(false)
|
65
|
+
a.send "Hello", to: @b
|
66
|
+
b.receive.should eq(true)
|
67
|
+
b.receive.should eq(false)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "can send and receive messages" do
|
71
|
+
storage = Collective::Mocks::Storage.new
|
72
|
+
a = Collective::Messager.new( storage, my_address: @a )
|
73
|
+
b = Collective::Messager.new( storage, my_address: @b )
|
74
|
+
|
75
|
+
callback = double("callback")
|
76
|
+
callback.should_receive(:call).with(anything)
|
77
|
+
|
78
|
+
reply_to_id = nil
|
79
|
+
|
80
|
+
b.expect("Hello") { |message|
|
81
|
+
message.body.should eq("Hello")
|
82
|
+
message.from.should eq(@a)
|
83
|
+
reply_to_id = message.id
|
84
|
+
b.reply( "Goodbye", to: message )
|
85
|
+
}
|
86
|
+
|
87
|
+
a.expect("Goodbye") { |message|
|
88
|
+
message.body.should eq("Goodbye")
|
89
|
+
message.from.should eq(@b)
|
90
|
+
message.id.should_not be_nil
|
91
|
+
message.reply_to_id.should eq(reply_to_id)
|
92
|
+
callback.call(message)
|
93
|
+
}
|
94
|
+
|
95
|
+
a.send "Hello", to: @b
|
96
|
+
b.receive
|
97
|
+
a.receive
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "when working with multiple processes", redis: true do
|
103
|
+
|
104
|
+
it "can send a message between processes" do
|
105
|
+
storage = Collective::Redis::Storage.new(redis)
|
106
|
+
me = Collective::Messager.new( storage, my_address: @a )
|
107
|
+
|
108
|
+
ok = false
|
109
|
+
me.expect("Goodbye") do |message|
|
110
|
+
ok = true
|
111
|
+
end
|
112
|
+
|
113
|
+
Collective::Utilities::Process.fork_and_detach do
|
114
|
+
redis.client.disconnect
|
115
|
+
me = Collective::Messager.new( storage, my_address: @b )
|
116
|
+
ok = false
|
117
|
+
me.expect("Hello") do |message|
|
118
|
+
me.reply "Goodbye", to: message
|
119
|
+
ok = true
|
120
|
+
end
|
121
|
+
wait_until { me.receive; ok }
|
122
|
+
end
|
123
|
+
|
124
|
+
me.send "Hello", to: @b
|
125
|
+
wait_until { me.receive }
|
126
|
+
ok.should be_true
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|