zactor 0.0.4
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/.document +5 -0
- data/.gitignore +42 -0
- data/.rake_tasks~ +19 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +94 -0
- data/Guardfile +12 -0
- data/LICENSE.txt +20 -0
- data/README.md +167 -0
- data/Rakefile +28 -0
- data/VERSION +1 -0
- data/examples/chat/client.rb +88 -0
- data/examples/chat/server.rb +59 -0
- data/examples/ping/inproc.rb +50 -0
- data/lib/zactor/actor_pub.rb +19 -0
- data/lib/zactor/actor_sub.rb +46 -0
- data/lib/zactor/broker.rb +71 -0
- data/lib/zactor/log_subscriber.rb +25 -0
- data/lib/zactor/message.rb +23 -0
- data/lib/zactor/version.rb +3 -0
- data/lib/zactor.rb +263 -0
- data/spec/lib/actor_spec.rb +136 -0
- data/spec/lib/exchange_spec.rb +81 -0
- data/spec/lib/link_spec.rb +67 -0
- data/spec/lib/zactor_spec.rb +40 -0
- data/spec/spec_helper.rb +13 -0
- data/zactor.gemspec +28 -0
- metadata +192 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
module Zactor
|
2
|
+
class BrokerIn
|
3
|
+
attr_accessor :broker, :params
|
4
|
+
def initialize(broker, params = {})
|
5
|
+
@broker = broker
|
6
|
+
@params = params
|
7
|
+
init_connection
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_readable(socket, messages)
|
11
|
+
Zactor.logger.debug "Broker: messages"
|
12
|
+
@broker.pub.request messages
|
13
|
+
end
|
14
|
+
|
15
|
+
def close
|
16
|
+
@connection.unbind
|
17
|
+
rescue
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class BrokerPub
|
22
|
+
include ZMQMEssages
|
23
|
+
def initialize(broker)
|
24
|
+
@broker = broker
|
25
|
+
@connection = Zactor.zmq.bind ZMQ::PUB, "inproc://zactor_broker_pub", self
|
26
|
+
@socket = @connection.socket
|
27
|
+
end
|
28
|
+
|
29
|
+
def request(messages)
|
30
|
+
Zactor.logger.debug { "Broker: request #{messages.map(&:copy_out_string).inspect}" }
|
31
|
+
send_messages messages
|
32
|
+
end
|
33
|
+
|
34
|
+
def close
|
35
|
+
@connection.unbind
|
36
|
+
rescue
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class BrokerSub < BrokerIn
|
41
|
+
def init_connection
|
42
|
+
Zactor.logger.info "Starting sub broker tcp://0.0.0.0:#{Zactor.broker_port}"
|
43
|
+
@connection = Zactor.zmq.bind ZMQ::SUB, "tcp://0.0.0.0:#{Zactor.broker_port}", self
|
44
|
+
@connection.bind "inproc://zactor_broker_sub"
|
45
|
+
@connection.subscribe ''
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class BrokerPull < BrokerIn
|
50
|
+
def init_connection
|
51
|
+
Zactor.logger.info "Starting pull broker tcp://#{params[:host]}"
|
52
|
+
@connection = Zactor.zmq.connect ZMQ::PULL, "tcp://#{params[:host]}", self
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Broker
|
57
|
+
attr_accessor :sub, :pub
|
58
|
+
def initialize(params = {})
|
59
|
+
Zactor.logger.info "Broker: starting"
|
60
|
+
@pub = BrokerPub.new self
|
61
|
+
@subs = []
|
62
|
+
@subs << BrokerSub.new(self)
|
63
|
+
@subs << BrokerPull.new(self, :host => params[:balancer]) if params[:balancer]
|
64
|
+
end
|
65
|
+
|
66
|
+
def finish
|
67
|
+
@pub.close
|
68
|
+
@subs.each(&:close)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'active_support/log_subscriber'
|
3
|
+
module Zactor
|
4
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
5
|
+
def logger
|
6
|
+
Zactor.logger
|
7
|
+
end
|
8
|
+
|
9
|
+
def merge(event)
|
10
|
+
payload = event.payload
|
11
|
+
|
12
|
+
message = "[Zactor](%.0fms) sending request '#{payload[:event]}' to '#{payload[:actor]}'' with params #{paylaod[:args].inspect}" % event.duration
|
13
|
+
debug message
|
14
|
+
end
|
15
|
+
|
16
|
+
def send_to(event)
|
17
|
+
payload = event.payload
|
18
|
+
|
19
|
+
message = "[Zactor] sending messages to '#{payload[:actor]}'" % event.duration
|
20
|
+
debug message
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Zactor::LogSubscriber.attach_to :zactor
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Zactor
|
2
|
+
class Message
|
3
|
+
attr_accessor :actor, :params
|
4
|
+
attr_accessor :callback_id, :args
|
5
|
+
def initialize(actor, params = {})
|
6
|
+
@actor = actor
|
7
|
+
@params = params
|
8
|
+
end
|
9
|
+
|
10
|
+
def sender
|
11
|
+
params[:sender]
|
12
|
+
end
|
13
|
+
|
14
|
+
def args
|
15
|
+
params[:args]
|
16
|
+
end
|
17
|
+
|
18
|
+
def reply(*args)
|
19
|
+
return false unless params[:callback_id]
|
20
|
+
actor.send_reply sender, params[:callback_id], *args
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/zactor.rb
ADDED
@@ -0,0 +1,263 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'ruby_interface'
|
3
|
+
require 'active_support/all'
|
4
|
+
require 'ffi-rzmq'
|
5
|
+
require 'em-zeromq'
|
6
|
+
require 'bson'
|
7
|
+
|
8
|
+
module Zactor
|
9
|
+
extend ActiveSupport::Autoload
|
10
|
+
|
11
|
+
autoload :Broker
|
12
|
+
autoload :ActorSub
|
13
|
+
autoload :ActorPub
|
14
|
+
autoload :Message
|
15
|
+
require 'zactor/log_subscriber'
|
16
|
+
|
17
|
+
mattr_accessor :stub
|
18
|
+
mattr_accessor :zmq, :logger
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def broker
|
22
|
+
@broker
|
23
|
+
end
|
24
|
+
|
25
|
+
def broker_port
|
26
|
+
@broker_port
|
27
|
+
end
|
28
|
+
|
29
|
+
def host
|
30
|
+
@host
|
31
|
+
end
|
32
|
+
|
33
|
+
def start(broker_port, params = {})
|
34
|
+
self.zmq ||= EM::ZeroMQ::Context.new(1)
|
35
|
+
self.logger ||= Logger.new(STDOUT).tap { |o| o.level = params[:debug] ? Logger::DEBUG : Logger::INFO }
|
36
|
+
@host = "#{params.delete(:host) || '0.0.0.0'}:#{broker_port}"
|
37
|
+
@broker_port = broker_port
|
38
|
+
@params = params
|
39
|
+
|
40
|
+
logger.info "Starting Zactor"
|
41
|
+
@broker = Broker.new :balancer => params[:balancer]
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_actor(pid, options = {})
|
45
|
+
h = options[:host] || host
|
46
|
+
{ 'identity' => pid, 'host' => h }
|
47
|
+
end
|
48
|
+
|
49
|
+
def register(zactor)
|
50
|
+
@zactors ||= {}
|
51
|
+
@zactors[zactor] = true
|
52
|
+
end
|
53
|
+
|
54
|
+
def deregister(zactor)
|
55
|
+
@zactors.delete zactor
|
56
|
+
end
|
57
|
+
|
58
|
+
def finish
|
59
|
+
@zactors.keys.each(&:finish)
|
60
|
+
@broker.finish
|
61
|
+
end
|
62
|
+
|
63
|
+
def clear
|
64
|
+
@zactors = {}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
module ZMQMEssages
|
70
|
+
class ZMQMessage
|
71
|
+
attr_accessor :res
|
72
|
+
def initialize(&blk)
|
73
|
+
@res = []
|
74
|
+
blk.call self
|
75
|
+
end
|
76
|
+
|
77
|
+
def str(str)
|
78
|
+
@res << ZMQ::Message.new.tap { |o| o.copy_in_string str.to_s }
|
79
|
+
end
|
80
|
+
|
81
|
+
def mes(message)
|
82
|
+
@res << message
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def send_messages(messages)
|
87
|
+
last = messages[-1]
|
88
|
+
messages[0...-1].each do |mes|
|
89
|
+
sent = @socket.send mes, ZMQ::NOBLOCK | ZMQ::SNDMORE
|
90
|
+
break unless sent
|
91
|
+
end
|
92
|
+
sent = @socket.send last, ZMQ::NOBLOCK
|
93
|
+
unless sent
|
94
|
+
Zactor.logger.info "[Zactor] Error while sending messages"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def messages(&blk)
|
99
|
+
ZMQMessage.new(&blk).res
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
extend RubyInterface
|
105
|
+
interface :zactor do
|
106
|
+
interfaced do
|
107
|
+
self.zactor do
|
108
|
+
event(:finish) { |o| o.finish }
|
109
|
+
event(:link) do |o, msg|
|
110
|
+
o.zactor.linked msg
|
111
|
+
# msg.reply :ok
|
112
|
+
end
|
113
|
+
event(:link_ping) do |o, msg|
|
114
|
+
msg.reply :pong
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
include ZMQMEssages
|
120
|
+
|
121
|
+
class_attribute :identity_val
|
122
|
+
class_attribute :events
|
123
|
+
self.events = {}
|
124
|
+
class << self
|
125
|
+
def identity(val)
|
126
|
+
self.identity_val = val
|
127
|
+
end
|
128
|
+
|
129
|
+
def event(name, &clb)
|
130
|
+
self.events = self.events.merge name.to_sym => clb
|
131
|
+
end
|
132
|
+
end
|
133
|
+
attr_accessor :actor, :identity
|
134
|
+
attr_accessor :pubs
|
135
|
+
|
136
|
+
# Инициализация, подписывается на сообщения для себя, создает сокет для отправки локальных сообщений
|
137
|
+
def init
|
138
|
+
@actor = Zactor.get_actor @identity || self.class.identity_val || "actor.#{owner.object_id}-#{Zactor.host}"
|
139
|
+
Zactor.register self
|
140
|
+
return if Zactor.stub
|
141
|
+
@sub = make_sub
|
142
|
+
@callbacks, @timeouts = {}, {}
|
143
|
+
@pubs = {}
|
144
|
+
@pubs["0.0.0.0:#{Zactor.broker_port}"] = make_pub "inproc://zactor_broker_sub"
|
145
|
+
end
|
146
|
+
|
147
|
+
# Закрываем все соедниения и чистим сисьему. Обязательно нужно делать, когда объект перестает существовать
|
148
|
+
def finish
|
149
|
+
Zactor.deregister self
|
150
|
+
return if Zactor.stub
|
151
|
+
if @linked
|
152
|
+
@linked.each do |link|
|
153
|
+
link.reply :finish
|
154
|
+
end
|
155
|
+
end
|
156
|
+
@finished = true
|
157
|
+
@sub.close
|
158
|
+
@pubs.values.each(&:close)
|
159
|
+
end
|
160
|
+
|
161
|
+
def make_pub(endpoint)
|
162
|
+
Zactor::ActorPub.new self, endpoint
|
163
|
+
end
|
164
|
+
|
165
|
+
def make_sub
|
166
|
+
Zactor::ActorSub.new self
|
167
|
+
end
|
168
|
+
|
169
|
+
def send_request(actor, event, *args, &clb)
|
170
|
+
return if @finished || Zactor.stub
|
171
|
+
@callbacks[clb.object_id.to_s] = clb if clb
|
172
|
+
send_to actor, messages { |m|
|
173
|
+
m.str 'request'
|
174
|
+
m.str "#{clb ? clb.object_id : ''}"
|
175
|
+
m.str event
|
176
|
+
m.str BSON.serialize({ 'args' => args })
|
177
|
+
}
|
178
|
+
@last_callback = clb.object_id.to_s
|
179
|
+
self
|
180
|
+
end
|
181
|
+
|
182
|
+
def timeout(secs, &clb)
|
183
|
+
raise "Only for requests" unless @last_callback
|
184
|
+
@timeouts[@last_callback] = EM.add_timer(secs, &clb)
|
185
|
+
end
|
186
|
+
|
187
|
+
def send_reply(actor, callback_id, *args)
|
188
|
+
return if @finished || Zactor.stub
|
189
|
+
Zactor.logger.debug "Zactor: send reply"
|
190
|
+
send_to actor, messages { |m|
|
191
|
+
m.str 'reply'
|
192
|
+
m.str callback_id
|
193
|
+
m.str BSON.serialize({ 'args' => args })
|
194
|
+
}
|
195
|
+
end
|
196
|
+
|
197
|
+
def send_to(actor, mes = [])
|
198
|
+
pub = @pubs[actor['host']] ||= make_pub("tcp://#{actor['host']}")
|
199
|
+
pub.send_messages(messages { |m|
|
200
|
+
m.str actor['identity']
|
201
|
+
m.str bson_actor
|
202
|
+
} + mes)
|
203
|
+
end
|
204
|
+
|
205
|
+
def link(actor, &clb)
|
206
|
+
Zactor.logger.debug { "Zactor: link #{actor} with #{self.actor}"}
|
207
|
+
send_request actor, :link do
|
208
|
+
clb.call
|
209
|
+
end
|
210
|
+
link_ping actor, &clb
|
211
|
+
self
|
212
|
+
end
|
213
|
+
|
214
|
+
def link_ping(actor, &clb)
|
215
|
+
EM.add_timer(5) do
|
216
|
+
unless @finished
|
217
|
+
send_request(actor, :link_ping) do
|
218
|
+
link_ping actor, &clb
|
219
|
+
end.timeout(5) do
|
220
|
+
clb.call
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def linked(msg)
|
227
|
+
Zactor.logger.debug { "Zactor: linked #{actor} with #{msg.sender}"}
|
228
|
+
@linked ||= []
|
229
|
+
@linked << msg
|
230
|
+
end
|
231
|
+
|
232
|
+
def receive_reply(callback_id, *args)
|
233
|
+
Zactor.logger.debug "Zactor: receive reply"
|
234
|
+
if (callback = @callbacks[callback_id])
|
235
|
+
if timeout = @timeouts[callback_id]
|
236
|
+
EM.cancel_timer timeout
|
237
|
+
end
|
238
|
+
callback.call(*args)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def receive_request(sender, event_name, callback_id, *args)
|
243
|
+
Zactor.logger.debug "Zactor: receive request"
|
244
|
+
mes = Message.new self, :sender => sender, :callback_id => callback_id, :args => args
|
245
|
+
if (event = self.class.events[event_name.to_sym])
|
246
|
+
event.call owner, mes, *args
|
247
|
+
else
|
248
|
+
raise "Undefined event #{event_name}"
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def bson_actor
|
253
|
+
@bson_actor ||= BSON.serialize(@actor).to_s
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
def inspect
|
258
|
+
"<#Zactor Interface for #{owner.inspect}"
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "Zactor actor" do
|
6
|
+
class A
|
7
|
+
include Zactor
|
8
|
+
end
|
9
|
+
before do
|
10
|
+
stub(Zactor::Broker).new
|
11
|
+
Zactor.start 8000
|
12
|
+
end
|
13
|
+
describe "init" do
|
14
|
+
let(:actor) { A.new.zactor }
|
15
|
+
let(:sub) { Object.new }
|
16
|
+
let(:local_pub) { Object.new }
|
17
|
+
before do
|
18
|
+
stub(actor).make_sub { sub }
|
19
|
+
stub(actor).make_pub { local_pub }
|
20
|
+
end
|
21
|
+
it "должен зарегистрировать себя" do
|
22
|
+
mock(Zactor).register actor
|
23
|
+
actor.init
|
24
|
+
end
|
25
|
+
|
26
|
+
it "должен создать sub сокет" do
|
27
|
+
mock(actor).make_sub { sub }
|
28
|
+
actor.init
|
29
|
+
actor.instance_eval { @sub }.should eq(sub)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "должен создать pub сокет для локальных вызовов" do
|
33
|
+
mock(actor).make_pub("inproc://zactor_broker_sub") { local_pub }
|
34
|
+
actor.init
|
35
|
+
actor.instance_eval { @pubs['0.0.0.0:8000'] }.should eq(local_pub)
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "identity" do
|
39
|
+
it "по-умолчанию" do
|
40
|
+
actor.init
|
41
|
+
actor.actor.should eq({ 'identity' => "actor.#{actor.owner.object_id}-0.0.0.0:8000", 'host' => '0.0.0.0:8000' })
|
42
|
+
end
|
43
|
+
|
44
|
+
it "с глобальным указанием" do
|
45
|
+
A.zactor.identity "a"
|
46
|
+
actor.init
|
47
|
+
actor.actor.should eq({ 'identity' => "a", 'host' => '0.0.0.0:8000' })
|
48
|
+
end
|
49
|
+
|
50
|
+
it "с указанием для этого объекта" do
|
51
|
+
actor.identity = "b"
|
52
|
+
actor.init
|
53
|
+
actor.actor.should eq({ 'identity' => "b", 'host' => '0.0.0.0:8000' })
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "after init" do
|
58
|
+
include Zactor::ZMQMEssages
|
59
|
+
|
60
|
+
def assert_messages(got, expected)
|
61
|
+
got.map(&:copy_out_string).should eq(expected.map(&:copy_out_string))
|
62
|
+
end
|
63
|
+
|
64
|
+
before do
|
65
|
+
actor.init
|
66
|
+
end
|
67
|
+
describe "send_to" do
|
68
|
+
it "должен отправить в локальный pub для локального объекта" do
|
69
|
+
mes = messages { |m| }
|
70
|
+
stub(local_pub).send_messages do |mes|
|
71
|
+
assert_messages mes, messages { |m|
|
72
|
+
m.str('b')
|
73
|
+
m.str(actor.bson_actor)
|
74
|
+
}
|
75
|
+
end
|
76
|
+
actor.send_to Zactor.get_actor('b')
|
77
|
+
end
|
78
|
+
|
79
|
+
it "должен отправить в удаленный pub для удаленного объекта, перед этим создав его" do
|
80
|
+
remote_pub = Object.new
|
81
|
+
mock(actor).make_pub("tcp://192.168.1.1:3000") { remote_pub }
|
82
|
+
stub(remote_pub).send_messages do |mes|
|
83
|
+
assert_messages mes, messages { |m| m.str('b'); m.str(actor.bson_actor) }
|
84
|
+
end
|
85
|
+
actor.send_to Zactor.get_actor('b', :host => '192.168.1.1:3000')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "send_request" do
|
90
|
+
it "должен отправлять сообщение типа request" do
|
91
|
+
stub(local_pub).send_messages do |mes|
|
92
|
+
assert_messages mes, messages { |m|
|
93
|
+
m.str('b')
|
94
|
+
m.str(actor.bson_actor)
|
95
|
+
|
96
|
+
m.str 'request'
|
97
|
+
m.str ""
|
98
|
+
m.str "show"
|
99
|
+
m.str BSON.serialize({ 'args' => ['foo', :bar] })
|
100
|
+
}
|
101
|
+
end
|
102
|
+
actor.send_request Zactor.get_actor('b'), :show, 'foo', :bar
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "send_reply" do
|
107
|
+
it "должен отправлять сообщение типа reply" do
|
108
|
+
stub(local_pub).send_messages do |mes|
|
109
|
+
assert_messages mes, messages { |m|
|
110
|
+
m.str('b')
|
111
|
+
m.str(actor.bson_actor)
|
112
|
+
|
113
|
+
m.str 'reply'
|
114
|
+
m.str 5
|
115
|
+
m.str BSON.serialize({ 'args' => ['foo', :bar] })
|
116
|
+
}
|
117
|
+
end
|
118
|
+
actor.send_reply Zactor.get_actor('b'), 5, 'foo', :bar
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "receive_reply" do
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "receive_request" do
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "finish" do
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require "em-spec/rspec"
|
5
|
+
|
6
|
+
describe "Zactor" do
|
7
|
+
include EM::SpecHelper
|
8
|
+
module Exchange
|
9
|
+
class A
|
10
|
+
include Zactor
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
zactor.init
|
14
|
+
end
|
15
|
+
|
16
|
+
def ping(actor = Zactor.get_actor("b"))
|
17
|
+
zactor.send_request actor, :ping do |res|
|
18
|
+
reply
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def reply
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class B
|
28
|
+
include Zactor
|
29
|
+
|
30
|
+
zactor do
|
31
|
+
identity "b"
|
32
|
+
|
33
|
+
event(:ping) do |o, msg|
|
34
|
+
o.receive
|
35
|
+
msg.reply "Pong!"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
zactor.init
|
41
|
+
end
|
42
|
+
|
43
|
+
def receive
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
after do
|
50
|
+
Zactor.clear
|
51
|
+
Zactor.finish
|
52
|
+
end
|
53
|
+
|
54
|
+
it "B должен получить сообщение" do
|
55
|
+
em do
|
56
|
+
Zactor.start 8000, :debug => true
|
57
|
+
Exchange::A.new.ping
|
58
|
+
b = Exchange::B.new
|
59
|
+
mock.proxy(b).receive { done }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "A должен получить ответ" do
|
64
|
+
em do
|
65
|
+
Zactor.start 8000, :debug => true
|
66
|
+
a = Exchange::A.new
|
67
|
+
a.ping
|
68
|
+
Exchange::B.new
|
69
|
+
mock.proxy(a).reply { done }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "Если задан таймаут, то он должен срабатывать" do
|
74
|
+
em(7) do
|
75
|
+
Zactor.start 8000, :debug => true
|
76
|
+
a = Exchange::A.new.ping.timeout(5) do
|
77
|
+
done
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require "em-spec/rspec"
|
5
|
+
|
6
|
+
describe "Zactor" do
|
7
|
+
include EM::SpecHelper
|
8
|
+
module Link
|
9
|
+
class A
|
10
|
+
include Zactor
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
zactor.init
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class B
|
18
|
+
include Zactor
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
zactor.init
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
after do
|
27
|
+
puts "FINISH ZACTOR"
|
28
|
+
Zactor.finish
|
29
|
+
end
|
30
|
+
|
31
|
+
it "A должен уведомляться о смерти B" do
|
32
|
+
em do
|
33
|
+
Zactor.start 8000, :debug => true
|
34
|
+
a = Link::A.new
|
35
|
+
b = Link::B.new
|
36
|
+
a.zactor.link b.zactor.actor do
|
37
|
+
done
|
38
|
+
end
|
39
|
+
EM.add_timer(0.5) do
|
40
|
+
b.zactor.finish
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# it "A должен уведомляться о смерти B в случае удаления объекта гарбэйдж коллектором" do
|
46
|
+
#
|
47
|
+
# end
|
48
|
+
|
49
|
+
it "A должен уведомляться о смерти B, даже если B закончил выполнение неожиданно" do
|
50
|
+
em(12) do
|
51
|
+
EM.add_timer(0.5) do #FIXME ZMQ-сокетам нужно время чтобы закрыться. Нужно придумать, что с этим делать
|
52
|
+
Zactor.start 8000, :debug => true
|
53
|
+
a = Link::A.new
|
54
|
+
b = Link::B.new
|
55
|
+
a.zactor.link b.zactor.actor do
|
56
|
+
done
|
57
|
+
end
|
58
|
+
EM.add_timer(0.5) do
|
59
|
+
b.zactor.instance_eval do
|
60
|
+
@linked = []
|
61
|
+
end
|
62
|
+
b.zactor.finish
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "Zactor" do
|
6
|
+
describe "start" do
|
7
|
+
before do
|
8
|
+
stub(Zactor::Broker).new
|
9
|
+
end
|
10
|
+
it "должен выставлять хост по-умолчанию 0.0.0.0" do
|
11
|
+
Zactor.start 8000
|
12
|
+
Zactor.host.should eq('0.0.0.0:8000')
|
13
|
+
end
|
14
|
+
|
15
|
+
it "должен выставлять хост с учетом переданного" do
|
16
|
+
Zactor.start 8000, :host => '192.168.1.1'
|
17
|
+
Zactor.host.should eq('192.168.1.1:8000')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "должен создавать новый брокер с указаным балансером" do
|
21
|
+
mock(Zactor::Broker).new :balancer => '0.0.0.0:4000'
|
22
|
+
Zactor.start 8000, :balancer => '0.0.0.0:4000'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "get_actor" do
|
27
|
+
before do
|
28
|
+
stub(Zactor::Broker).new
|
29
|
+
Zactor.start 8000
|
30
|
+
end
|
31
|
+
it "в качестве хоста по-умолчанию ставит себя же" do
|
32
|
+
Zactor.get_actor("actor1").should eq({ 'identity' => 'actor1', 'host' => '0.0.0.0:8000' })
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
it "в качестве хоста ставит переданный" do
|
37
|
+
Zactor.get_actor("actor1", :host => '192.168.1.1:3000').should eq({ 'identity' => 'actor1', 'host' => '192.168.1.1:3000' })
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|