zactor 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|