zactor 0.0.4 → 0.0.5

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/lib/zactor.rb DELETED
@@ -1,263 +0,0 @@
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
@@ -1,136 +0,0 @@
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
@@ -1,81 +0,0 @@
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
@@ -1,67 +0,0 @@
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
@@ -1,40 +0,0 @@
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