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 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