zactor 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,42 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+
14
+ # jeweler generated
15
+ pkg
16
+
17
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
18
+ #
19
+ # * Create a file at ~/.gitignore
20
+ # * Include files you want ignored
21
+ # * Run: git config --global core.excludesfile ~/.gitignore
22
+ #
23
+ # After doing this, these files will be ignored in all your git projects,
24
+ # saving you from having to 'pollute' every project you touch with them
25
+ #
26
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
27
+ #
28
+ # For MacOS:
29
+ #
30
+ #.DS_Store
31
+ #
32
+ # For TextMate
33
+ #*.tmproj
34
+ #tmtags
35
+ #
36
+ # For emacs:
37
+ #*~
38
+ #\#*
39
+ #.\#*
40
+ #
41
+ # For vim:
42
+ #*.swp
data/.rake_tasks~ ADDED
@@ -0,0 +1,19 @@
1
+ build
2
+ console[script]
3
+ gemcutter:release
4
+ gemspec
5
+ gemspec:debug
6
+ gemspec:generate
7
+ gemspec:release
8
+ gemspec:validate
9
+ git:release
10
+ install
11
+ rcov
12
+ release
13
+ spec
14
+ version
15
+ version:bump:major
16
+ version:bump:minor
17
+ version:bump:patch
18
+ version:write
19
+ yard
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ -m markdown -e ruby-interface/yard
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ gem "rspec", ">= 2"
5
+ gem "yard", "~> 0.6.0"
6
+ gem "bundler", "~> 1.0.0"
7
+ gem "rcov", ">= 0"
8
+ gem "bluecloth"
9
+ gem "rr"
10
+ gem "guard-rspec"
11
+ gem "growl"
12
+ gem "ruby-debug19"
13
+ gem "em-spec", :git => "https://github.com/mloughran/em-spec.git", :branch => 'rspec2'
14
+ end
15
+
16
+
17
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,94 @@
1
+ GIT
2
+ remote: https://github.com/mloughran/em-spec.git
3
+ revision: fc888b289a424fa93848ffdac3dc4b24fdd34950
4
+ branch: rspec2
5
+ specs:
6
+ em-spec (0.2.2)
7
+
8
+ PATH
9
+ remote: .
10
+ specs:
11
+ zactor (0.0.4)
12
+ activesupport (> 0.1)
13
+ bson (> 0.1)
14
+ bson_ext (> 0.1)
15
+ em-zeromq (> 0.1)
16
+ ffi (> 0.1)
17
+ ffi-rzmq (> 0.1)
18
+ ruby-interface (> 0)
19
+
20
+ GEM
21
+ remote: http://rubygems.org/
22
+ specs:
23
+ activesupport (3.0.7)
24
+ archive-tar-minitar (0.5.2)
25
+ bluecloth (2.1.0)
26
+ bson (1.2.4)
27
+ bson_ext (1.2.4)
28
+ columnize (0.3.2)
29
+ configuration (1.2.0)
30
+ diff-lcs (1.1.2)
31
+ em-zeromq (0.2.1)
32
+ eventmachine (>= 1.0.0.beta.3)
33
+ ffi-rzmq (>= 0.7.2)
34
+ eventmachine (1.0.0.beta.3)
35
+ ffi (1.0.7)
36
+ rake (>= 0.8.7)
37
+ ffi-rzmq (0.7.2)
38
+ growl (1.0.3)
39
+ guard (0.3.0)
40
+ open_gem (~> 1.4.2)
41
+ thor (~> 0.14.6)
42
+ guard-rspec (0.2.0)
43
+ guard (>= 0.2.2)
44
+ i18n (0.5.0)
45
+ launchy (0.3.7)
46
+ configuration (>= 0.0.5)
47
+ rake (>= 0.8.1)
48
+ linecache19 (0.5.11)
49
+ ruby_core_source (>= 0.1.4)
50
+ open_gem (1.4.2)
51
+ launchy (~> 0.3.5)
52
+ rake (0.8.7)
53
+ rcov (0.9.9)
54
+ rr (1.0.2)
55
+ rspec (2.5.0)
56
+ rspec-core (~> 2.5.0)
57
+ rspec-expectations (~> 2.5.0)
58
+ rspec-mocks (~> 2.5.0)
59
+ rspec-core (2.5.1)
60
+ rspec-expectations (2.5.0)
61
+ diff-lcs (~> 1.1.2)
62
+ rspec-mocks (2.5.0)
63
+ ruby-debug-base19 (0.11.24)
64
+ columnize (>= 0.3.1)
65
+ linecache19 (>= 0.5.11)
66
+ ruby_core_source (>= 0.1.4)
67
+ ruby-debug19 (0.11.6)
68
+ columnize (>= 0.3.1)
69
+ linecache19 (>= 0.5.11)
70
+ ruby-debug-base19 (>= 0.11.19)
71
+ ruby-interface (0.0.2)
72
+ activesupport (> 0.1)
73
+ i18n (> 0.1)
74
+ ruby-interface
75
+ ruby_core_source (0.1.4)
76
+ archive-tar-minitar (>= 0.5.2)
77
+ thor (0.14.6)
78
+ yard (0.6.5)
79
+
80
+ PLATFORMS
81
+ ruby
82
+
83
+ DEPENDENCIES
84
+ bluecloth
85
+ bundler (~> 1.0.0)
86
+ em-spec!
87
+ growl
88
+ guard-rspec
89
+ rcov
90
+ rr
91
+ rspec (>= 2)
92
+ ruby-debug19
93
+ yard (~> 0.6.0)
94
+ zactor!
data/Guardfile ADDED
@@ -0,0 +1,12 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(/^spec\/(.*)_spec.rb/)
6
+ watch(/^lib\/(.*)\.rb/) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch(/^spec\/spec_helper.rb/) { "spec" }
8
+
9
+ # watch(/^lib\/zactor.rb/) { "spec/lib/actor_spec.rb" }
10
+
11
+ watch(/^app\/(.*)\.rb/) { |m| "spec/app/#{m[1]}_spec.rb" }
12
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Andrew Rudenko
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,167 @@
1
+ Что это
2
+ =======
3
+ Zactor позволяет любой руби-объект научить общаться с другими объектами посредством отправки и получения сообщений. При этом неважно где именно расположен объект, в том же процессе, в соседнем, или на другой физической машине.
4
+
5
+ Zactor использует zmq как бекенд для сообщений, eventmachine и em-zeromq для асинхронной работы с zmq, RubyInterface для описания себя, BSON для сериализации данных.
6
+
7
+ Каждый zactor-объект имеет свой identity, который генерируется автоматически, либо задается вручную и постоянен. Совокупность identity, host и port на которых был рожден этот объект, это достаточная информация для того что бы отправить этому объекту сообщение из другого объекта. Выглядит примерно так:
8
+
9
+ zactor.actor =>
10
+ {"identity"=>"actor.2154247120-0.0.0.0:8000", "host"=>"0.0.0.0:8000"}
11
+
12
+ Ипользование
13
+ ============
14
+
15
+ Для начала нужно стартануть Zactor.
16
+
17
+ Zactor.start 8000
18
+
19
+ Процесс забиндится на 0.0.0.0:8000, через эту точку будет происходить общение между zactor-процессами. Стартоваться должен в запущенном EM-контексте.
20
+
21
+ В каждый zactor-активный класс нужно делать include Zactor, после чего у класса и его экземпляров для доступа к функциями Zactor появится метод zactor. После создания объекта нужно выполнить zactor.init
22
+
23
+ class A
24
+ include Zactor
25
+
26
+ def initialize
27
+ zactor.init
28
+ end
29
+ end
30
+
31
+ Для отправки сообщений другому объекту нам нужно знать его идентификатор. Идентификатор можно получить тремя способами:
32
+
33
+ * Непосрдественной передачей. При инициализации или в любом другом месте, это исключительно внутренняя логика приложения. Идентификатор объекта можно получить вызвав zactor.actor
34
+ * При получении сообщения. В сообщении всегда содержится информация об отправителе
35
+ * Если объект имеет заранее известный identity, то мы можем получить его полный идентификатор вызвав Zactor.get_actor с identity и хостом, на котором он запущен
36
+
37
+ actor = Zactor.get_actor "broker", :host => "0.0.0.0:8001"
38
+
39
+ Получив идентификатор можно отправлять ему сообщения
40
+
41
+ zactor.send_request actor, :show_me, :boobs
42
+
43
+
44
+ Каждый класс может определять какие именно ивенты он может получать и что с ними делать
45
+
46
+ include Zactor
47
+
48
+ zactor do
49
+ event(:show_me) do |o, msg, what|
50
+ case what
51
+ when :boobs
52
+ do_it
53
+ else
54
+ do_smth_else
55
+ end
56
+ end
57
+ end
58
+
59
+ Рассмотрим пример банального ping-pong
60
+
61
+ class A
62
+ include Zactor
63
+
64
+ def initialize
65
+ zactor.init
66
+ ping Zactor.get_actor("b")
67
+ end
68
+
69
+ def ping(actor)
70
+ puts "Ping!"
71
+ zactor.send_request actor, :ping do |res|
72
+ puts res
73
+ end
74
+ end
75
+ end
76
+
77
+
78
+ class B
79
+ include Zactor
80
+
81
+ zactor do
82
+ identity "b"
83
+
84
+ event(:ping) do |o, msg|
85
+ msg.reply "Pong!"
86
+ end
87
+ end
88
+
89
+ def initialize
90
+ zactor.init
91
+ end
92
+
93
+ end
94
+
95
+ EM.run do
96
+ Zactor.start 8000
97
+
98
+ a = A.new
99
+ b = B.new
100
+ end
101
+
102
+ A посылает сообщение :ping для B, а B отвечает "Pong!"
103
+
104
+ В коллбэк определенный в event передается объект получившый сообщение, объект сообщения ({Zactor::Message}) и далее переданные в запросе аргументы (если они есть). У {Zactor::Message} есть два основных метода: sender, возвращающий идентификатор отправителя и reply, который посылает ответ на запрос.
105
+
106
+ Важный момент, identity должно задаваться ДО zactor.init и после этого не может меняться.
107
+
108
+ ZMQ
109
+ ===
110
+
111
+ При Zactor.start стартует брокер, по одному на каждый процесс, через него проходят все сообщения данного процесса, принимает сообщения через SUB-сокет, отправляет через PUB. SUB подписан на все сообщения. Каждый zactor-объект создает по паре сокетов, PUB подключается к SUB-брокера, а SUB к PUB-брокера. SUB подписывается на сообщения содержащие его identity.
112
+
113
+ ![ZMQ](images/zmq1.png)
114
+
115
+ Рассмотрим жизнь сообщения на примере с ping-ping. В случае с b в том же процессе:
116
+
117
+ <div class=wsd wsd_style="default"><pre>
118
+ A[PUB]->Broker[SUB]: Посылаем запрос :ping
119
+ Broker[SUB]->Broker[PUB]: Перебрасываем запрос в PUB сокет
120
+ Broker[PUB]->B[SUB]: Передаем получателю сообщение
121
+ B[PUB]->Broker[SUB]: Отправляем ответ "Pong!"
122
+ Broker[SUB]->Broker[PUB]: Перебрасываем запрос в PUB сокет
123
+ Broker[PUB]->A[SUB]: Отправитель получает ответ
124
+ </pre></div><script type="text/javascript" src="http://www.websequencediagrams.com/service.js"></script>
125
+
126
+ В случае с b в другом процессе:
127
+
128
+ <div class=wsd wsd_style="default"><pre>
129
+ A[PUB for App2]->App2 Broker[SUB]: Посылаем запрос :ping
130
+ App2 Broker[SUB]->App2 Broker[PUB]: Перебрасываем запрос в PUB сокет
131
+ App2 Broker[PUB]->B[SUB]: Передаем получателю сообщение
132
+ B[PUB for App1]->App1 Broker[SUB]: Отправляем ответ "Pong!"
133
+ App1 Broker[SUB]->App1 Broker[PUB]: Перебрасываем запрос в PUB сокет
134
+ App1 Broker[PUB]->A[SUB]: Отправитель получает ответ
135
+ </pre></div><script type="text/javascript" src="http://www.websequencediagrams.com/service.js"></script>
136
+
137
+ Балансировка
138
+ ============
139
+
140
+ Так как это ZMQ, мы можем очень просто изменить тип получения сообщения. Например, добавив балансер. Теперь можно запускать процессы со ссылкой на этот балансер.
141
+
142
+ Zactor.start :balancer => "0.0.0.0:4000"
143
+
144
+ У нас получится примерно следующая схема:
145
+
146
+ ![ZMQ](images/zmq2.png)
147
+
148
+ Теперь наш ping можно отправлять в балансер, а отвечать будет один из подключенных воркеров.
149
+
150
+ ping Zactor.get_actor("b", :host => "0.0.0.0:4000")
151
+
152
+
153
+ Протокол обмена
154
+ ===============
155
+
156
+
157
+ Perfomance
158
+ ==========
159
+
160
+ А хрен его знает, толком не мерялось ничего :)
161
+
162
+ TODO
163
+ ====
164
+
165
+ * Сделать событие отваливания объектов. Наверное, что-то вроде простого аналога link в эрланге.
166
+ * Добавить таймауты для запросов с коллбэками. Сейчас они будут висеть бесконечно и засрут память.
167
+ * Доступ к отправителю в колллбэке запроса. В случае с балансировкой он будет не тем же, кому мы посылали сообщение
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ Bundler::GemHelper.install_tasks
13
+
14
+ require 'rspec/core'
15
+ require 'rspec/core/rake_task'
16
+ RSpec::Core::RakeTask.new(:spec) do |spec|
17
+ spec.pattern = FileList['spec/**/*_spec.rb']
18
+ end
19
+
20
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
21
+ spec.pattern = 'spec/**/*_spec.rb'
22
+ spec.rcov = true
23
+ end
24
+
25
+ task :default => :spec
26
+
27
+ require 'yard'
28
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.3
@@ -0,0 +1,88 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # ruby client.rb SELF_PORT SERVER LOGIN
4
+ # ruby client.rb 6001 0.0.0.0:8000 lolo
5
+ require 'bundler'
6
+ ENV['BUNDLE_GEMFILE'] = File.join(File.dirname(__FILE__), '..', '..', 'Gemfile')
7
+
8
+ Bundler.setup(:default)
9
+
10
+ require 'zactor'
11
+ class Client
12
+ include Zactor
13
+
14
+ zactor do
15
+ event(:message) do |o, msg, text|
16
+ puts text
17
+ end
18
+ end
19
+
20
+ def initialize(login)
21
+ @login = login
22
+ @persons = {}
23
+ end
24
+
25
+ def start(server)
26
+ zactor.init
27
+ @server = Zactor.get_actor("server", :host => server)
28
+ connect
29
+ end
30
+
31
+ def connect
32
+ puts "Подключаемся"
33
+ zactor.send_request(@server, :new_client, @login) do
34
+ puts "Поключились!"
35
+ zactor.link(@server) { connect }
36
+ end.timeout(5) { puts "Проблемы с подключением..." }
37
+ end
38
+
39
+ def send_message(text)
40
+ if text =~ /(\w+) -> (.+)/
41
+ send_personal($1, $2)
42
+ else
43
+ zactor.send_request(@server, :message, text)
44
+ end
45
+ end
46
+
47
+ def send_personal(login, text)
48
+ if client = @persons[login]
49
+ zactor.send_request(client, :message, "(personally) #{@login}:" + "#{text}")
50
+ else
51
+ zactor.send_request(@server, :client_request, login) do |res, client|
52
+ case res
53
+ when :ok
54
+ @persons[login] = client
55
+ zactor.link(client) { @persons.delete login }
56
+ zactor.send_request(client, :message, "(personally) #{@login}:" + "#{text}")
57
+ else
58
+ puts "Ошибка отправки сообщения"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def stop
65
+ zactor.finish
66
+ EM.stop
67
+ end
68
+
69
+ module KeyboardInput
70
+ include EM::Protocols::LineText2
71
+ attr_accessor :client
72
+ def receive_line(data)
73
+ client.send_message data
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ client = Client.new ARGV[2]
80
+
81
+ Signal.trap('INT') { client.stop }
82
+ Signal.trap('TERM') { client.stop }
83
+
84
+ EM.run do
85
+ Zactor.start ARGV[0]#, :debug => true
86
+ client.start ARGV[1]
87
+ EM.open_keyboard(Client::KeyboardInput) { |c| c.client = client }
88
+ end
@@ -0,0 +1,59 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # ruby server.rb PORT
4
+ # ruby server.rb 8000
5
+ require 'bundler'
6
+ ENV['BUNDLE_GEMFILE'] = File.join(File.dirname(__FILE__), '..', '..', 'Gemfile')
7
+ Bundler.setup(:default)
8
+
9
+ require 'zactor'
10
+
11
+ class Server
12
+ include Zactor
13
+
14
+ zactor do
15
+ identity "server"
16
+
17
+ event(:new_client) do |o, msg, login|
18
+ o.new_client msg.sender, login
19
+ msg.reply :ok
20
+ end
21
+
22
+ event(:client_request) do |o, msg, login|
23
+ if client = o.clients.detect { |k, v| v == login }
24
+ msg.reply :ok, client.first
25
+ else
26
+ msg.reply :error
27
+ end
28
+ end
29
+
30
+ event(:message) do |o, msg, text|
31
+ o.send_message msg.sender, text
32
+ end
33
+ end
34
+
35
+ attr_accessor :clients
36
+ def initialize
37
+ zactor.init
38
+ @clients = {}
39
+ end
40
+
41
+ def new_client(client, login)
42
+ @clients[client] = login
43
+ send_message client, "присоединился"
44
+ zactor.link client do
45
+ send_message client, "отсоединился"
46
+ clients.delete client
47
+ end
48
+ end
49
+
50
+ def send_message(from, message)
51
+ (clients.keys - [from]).each { |c| zactor.send_request c, :message, "#{clients[from]}: #{message}"}
52
+ end
53
+ end
54
+
55
+ EM.run do
56
+ Zactor.start ARGV[0]#, :debug => true
57
+ Server.new
58
+ puts "Server started"
59
+ end
@@ -0,0 +1,50 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+
4
+ ENV['BUNDLE_GEMFILE'] = File.join(File.dirname(__FILE__), '..', '..', 'Gemfile')
5
+
6
+ Bundler.setup(:default, :development)
7
+
8
+ require 'zactor'
9
+ require 'eventmachine'
10
+
11
+ class A
12
+ include Zactor
13
+
14
+ def initialize
15
+ zactor.init
16
+ ping Zactor.get_actor("b")
17
+ end
18
+
19
+ def ping(actor)
20
+ puts "Ping!"
21
+ zactor.send_request actor, :ping do |res|
22
+ puts res
23
+ end
24
+ end
25
+ end
26
+
27
+
28
+ class B
29
+ include Zactor
30
+
31
+ zactor do
32
+ identity "b"
33
+
34
+ event(:ping) do |o, msg|
35
+ msg.reply "Pong!"
36
+ end
37
+ end
38
+
39
+ def initialize
40
+ zactor.init
41
+ end
42
+
43
+ end
44
+
45
+ EM.run do
46
+ Zactor.start 8000
47
+
48
+ a = A.new
49
+ b = B.new
50
+ end
@@ -0,0 +1,19 @@
1
+ module Zactor
2
+ class ActorPub
3
+ include ZMQMEssages
4
+
5
+ attr_accessor :actor
6
+ def initialize(actor, endpoint)
7
+ Zactor.logger.debug "ZactorPub (#{actor.actor}): starting"
8
+ @actor = actor
9
+ @connection = Zactor.zmq.connect ZMQ::PUB, endpoint, self
10
+ @socket = @connection.socket
11
+ end
12
+
13
+ def close
14
+ @connection.unbind
15
+ rescue
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,46 @@
1
+ module Zactor
2
+ class ActorSub
3
+ attr_accessor :actor
4
+ def initialize(actor)
5
+ Zactor.logger.debug "ZactorSub (#{actor.actor}): starting"
6
+ @actor = actor
7
+ @connection = Zactor.zmq.connect ZMQ::SUB, "inproc://zactor_broker_pub", self
8
+ @connection.subscribe actor.actor['identity']
9
+ end
10
+
11
+ def on_readable(socket, messages)
12
+ @connection.notify_readable = true
13
+ Zactor.logger.debug "ZactorSub for #{actor.actor}: Messages!"
14
+ to = messages.shift
15
+ sender = messages.shift
16
+ case messages.shift.copy_out_string
17
+ when "reply"
18
+ reply messages
19
+ when "request"
20
+ request sender, messages
21
+ end
22
+ end
23
+
24
+ def request(sender_mes, messages)
25
+ Zactor.logger.debug "ZactorSub for #{actor.actor}: request!"
26
+ sender = BSON.deserialize(sender_mes.copy_out_string)
27
+ callback_id = messages[0].copy_out_string
28
+ event = messages[1].copy_out_string
29
+ args = BSON.deserialize(messages[2].copy_out_string)['args']
30
+ actor.receive_request sender, event, callback_id, *args
31
+ end
32
+
33
+ def reply(messages)
34
+ Zactor.logger.debug "ZactorSub for #{actor.actor}: reply!"
35
+ callback_id = messages[0].copy_out_string
36
+ if callback_id != ''
37
+ actor.receive_reply callback_id, *BSON.deserialize(messages[1].copy_out_string)['args']
38
+ end
39
+ end
40
+
41
+ def close
42
+ @connection.unbind
43
+ rescue
44
+ end
45
+ end
46
+ end