cod 0.3.1 → 0.4.0
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/Gemfile +1 -1
- data/HISTORY.txt +5 -1
- data/README +12 -13
- data/Rakefile +7 -1
- data/examples/{ping.rb → ping_pong/ping.rb} +1 -1
- data/examples/{pong.rb → ping_pong/pong.rb} +1 -1
- data/examples/{presence_client.rb → presence/client.rb} +0 -0
- data/examples/{presence_server.rb → presence/server.rb} +0 -0
- data/examples/queue/README +9 -0
- data/examples/queue/client.rb +31 -0
- data/examples/queue/queue.rb +51 -0
- data/examples/queue/send.rb +9 -0
- data/examples/service.rb +2 -2
- data/examples/tcp.rb +1 -1
- data/lib/cod.rb +47 -82
- data/lib/cod/beanstalk.rb +7 -0
- data/lib/cod/beanstalk/channel.rb +170 -0
- data/lib/cod/beanstalk/serializer.rb +80 -0
- data/lib/cod/beanstalk/service.rb +53 -0
- data/lib/cod/channel.rb +54 -12
- data/lib/cod/pipe.rb +188 -0
- data/lib/cod/select.rb +47 -0
- data/lib/cod/select_group.rb +87 -0
- data/lib/cod/service.rb +55 -42
- data/lib/cod/simple_serializer.rb +19 -0
- data/lib/cod/tcp_client.rb +202 -0
- data/lib/cod/tcp_server.rb +124 -0
- data/lib/cod/work_queue.rb +129 -0
- metadata +31 -45
- data/examples/pubsub/README +0 -12
- data/examples/pubsub/client.rb +0 -13
- data/examples/pubsub/directory.rb +0 -13
- data/examples/service_directory.rb +0 -32
- data/lib/cod/channel/base.rb +0 -185
- data/lib/cod/channel/beanstalk.rb +0 -69
- data/lib/cod/channel/pipe.rb +0 -137
- data/lib/cod/channel/tcp.rb +0 -16
- data/lib/cod/channel/tcpconnection.rb +0 -67
- data/lib/cod/channel/tcpserver.rb +0 -84
- data/lib/cod/client.rb +0 -81
- data/lib/cod/connection/beanstalk.rb +0 -77
- data/lib/cod/directory.rb +0 -98
- data/lib/cod/directory/countdown.rb +0 -31
- data/lib/cod/directory/subscription.rb +0 -59
- data/lib/cod/object_io.rb +0 -6
- data/lib/cod/objectio/connection.rb +0 -106
- data/lib/cod/objectio/reader.rb +0 -98
- data/lib/cod/objectio/serializer.rb +0 -26
- data/lib/cod/objectio/writer.rb +0 -27
- data/lib/cod/topic.rb +0 -95
data/Gemfile
CHANGED
data/HISTORY.txt
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
== 0.4.0 / 29Nov2011
|
1
2
|
|
2
|
-
|
3
|
+
* A complete rewrite, focusing on lean implementation and logical/useful
|
4
|
+
error conditions.
|
5
|
+
|
6
|
+
== 0.3.1 / 25Aug2011
|
3
7
|
|
4
8
|
+ Large improvements in resilience to crashes, reconnects are now attempted
|
5
9
|
but can be improved further.
|
data/README
CHANGED
@@ -1,33 +1,32 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
2
|
+
IPC for babies and those who want to become. Another thin API layer over what
|
3
|
+
Ruby offers for IO.pipe, TCP sockets and FIFOs: Makes sending and receiving
|
4
|
+
of Ruby objects a breeze.
|
5
|
+
|
6
|
+
A good place to start is the documentation for the Cod module.
|
4
7
|
|
5
8
|
SYNOPSIS
|
6
9
|
|
7
10
|
# Cod's basic elements are channels, unidirectional communication links.
|
8
11
|
pipe = Cod.pipe
|
9
|
-
beanstalk = Cod.beanstalk('localhost:11300', 'a_channel')
|
10
12
|
|
11
13
|
# You can use those either directly:
|
12
14
|
pipe.put :some_ruby_object # Process A
|
13
15
|
pipe.get # => :some_ruby_object # Process B
|
14
16
|
|
15
17
|
# Or use them as bricks for more:
|
16
|
-
service =
|
17
|
-
client
|
18
|
+
service = beanstalk.service
|
19
|
+
client = beanstalk.client(pipe)
|
18
20
|
|
19
21
|
service.one { |msg| :response } # Process A
|
20
22
|
client.call :ruby_object # => :response # Process B
|
21
|
-
|
22
|
-
# And more: Publish/Subscribe, easy construction of more advanced
|
23
|
-
# distributed communication.
|
24
|
-
|
23
|
+
|
25
24
|
STATUS
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
Complete rewrite of the code: Did away with some of the complexities that
|
27
|
+
stemmed from early design work. The functionality that is there is much like
|
28
|
+
that we had before, but some things have been designed more logically.
|
30
29
|
|
31
|
-
At version 0.
|
30
|
+
At version 0.4.0
|
32
31
|
|
33
32
|
(c) 2011 Kaspar Schiess
|
data/Rakefile
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'psych'
|
2
1
|
require "rubygems"
|
3
2
|
require "rdoc/task"
|
4
3
|
require 'rspec/core/rake_task'
|
@@ -9,6 +8,13 @@ RSpec::Core::RakeTask.new
|
|
9
8
|
|
10
9
|
task :default => :spec
|
11
10
|
|
11
|
+
task :stats do
|
12
|
+
%w(lib spec).each do |path|
|
13
|
+
printf "%10s:", path
|
14
|
+
system %Q(find #{path} -name "*.rb" | xargs wc -l | grep total)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
12
18
|
require 'sdoc'
|
13
19
|
|
14
20
|
# Generate documentation
|
File without changes
|
File without changes
|
@@ -0,0 +1,9 @@
|
|
1
|
+
A small queue like example.
|
2
|
+
|
3
|
+
run ruby send.rb N to send N messages through queue.rb to client.rb.
|
4
|
+
Client will print statistics (calls per second) every 100 messages.
|
5
|
+
|
6
|
+
Problems:
|
7
|
+
- queue.rb seems to always consume 100% CPU
|
8
|
+
- number of messages per second is way low
|
9
|
+
- some exceptions are still not handled correctly
|
@@ -0,0 +1,31 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + "/../../lib")
|
2
|
+
require 'cod'
|
3
|
+
|
4
|
+
queue = Cod.tcp('localhost:12345')
|
5
|
+
|
6
|
+
queue.put [:join, queue]
|
7
|
+
|
8
|
+
class CallCounter
|
9
|
+
attr_reader :n
|
10
|
+
def initialize
|
11
|
+
@n = 0
|
12
|
+
@last_lap = [0, Time.now]
|
13
|
+
end
|
14
|
+
def inc
|
15
|
+
@n += 1
|
16
|
+
end
|
17
|
+
def calls_per_sec
|
18
|
+
ln, ll = @last_lap
|
19
|
+
@last_lap = [@n, Time.now]
|
20
|
+
|
21
|
+
(@n - ln) / (Time.now - ll)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
cc = CallCounter.new
|
26
|
+
loop do
|
27
|
+
wi = queue.get
|
28
|
+
|
29
|
+
cc.inc
|
30
|
+
puts cc.calls_per_sec if cc.n%100==0
|
31
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + "/../../lib")
|
2
|
+
require 'cod'
|
3
|
+
|
4
|
+
class Queue
|
5
|
+
def initialize(url)
|
6
|
+
@url = url
|
7
|
+
connect
|
8
|
+
end
|
9
|
+
|
10
|
+
def connect
|
11
|
+
@clients = []
|
12
|
+
@server = Cod.tcpserver(@url)
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
loop do
|
17
|
+
handle_commands
|
18
|
+
|
19
|
+
check_connections
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def handle_commands
|
24
|
+
while @server.waiting?
|
25
|
+
cmd, *rest = @server.get
|
26
|
+
|
27
|
+
dispatch_command cmd, rest
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def dispatch_command(cmd, rest)
|
32
|
+
self.send("cmd_#{cmd}", *rest)
|
33
|
+
end
|
34
|
+
|
35
|
+
def cmd_join(connection)
|
36
|
+
puts "Join at #{Time.now}."
|
37
|
+
@clients << connection
|
38
|
+
end
|
39
|
+
|
40
|
+
def cmd_work_item
|
41
|
+
@clients.each do |client|
|
42
|
+
client.put :work_item
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def check_connections
|
47
|
+
@clients.keep_if { |client| client.connected? }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Queue.new('localhost:12345').run
|
data/examples/service.rb
CHANGED
@@ -9,7 +9,7 @@ service_channel = Cod.pipe
|
|
9
9
|
answer_channel = Cod.pipe
|
10
10
|
|
11
11
|
child_pid = fork do
|
12
|
-
service = Cod
|
12
|
+
service = Cod.service(service_channel)
|
13
13
|
service.one { |call|
|
14
14
|
puts "Service got called with #{call.inspect}"
|
15
15
|
time = Time.now
|
@@ -17,7 +17,7 @@ child_pid = fork do
|
|
17
17
|
time }
|
18
18
|
end
|
19
19
|
|
20
|
-
client = Cod
|
20
|
+
client = Cod.client(service_channel, answer_channel)
|
21
21
|
puts "Calling service..."
|
22
22
|
answer = client.call('42')
|
23
23
|
|
data/examples/tcp.rb
CHANGED
data/lib/cod.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'uuid'
|
2
|
-
|
3
1
|
# The core concept of Cod are 'channels'. (Cod::Channel::Base) You can create
|
4
2
|
# such channels on top of the various transport layers. Once you have such a
|
5
3
|
# channel, you #put messages into it and you #get messages out of it. Messages
|
@@ -8,10 +6,7 @@ require 'uuid'
|
|
8
6
|
#
|
9
7
|
# Cod also brings a few abstractions layered on top of channels: You can use
|
10
8
|
# channels to present 'services' (Cod::Service) to the network: A service is a
|
11
|
-
# simple one or two way RPC call. (one way = asynchronous)
|
12
|
-
# channels to run a 'directory' (Cod::Directory) where processes subscribe to
|
13
|
-
# information using a filter. They then get information that matches their
|
14
|
-
# filter written to their inbound channel. (also called pub/sub)
|
9
|
+
# simple one or two way RPC call. (one way = asynchronous)
|
15
10
|
#
|
16
11
|
# Cod channels are serializable whereever possible. If you want to tell
|
17
12
|
# somebody where to write his answers and/or questions to, send him the
|
@@ -28,101 +23,71 @@ require 'uuid'
|
|
28
23
|
# most of the tricky stuff!
|
29
24
|
#
|
30
25
|
module Cod
|
31
|
-
#
|
32
|
-
#
|
33
|
-
# object instance. (Because it might have been garbage collected or other
|
34
|
-
# such reasons)
|
35
|
-
#
|
36
|
-
class InvalidIdentifier < StandardError; end
|
37
|
-
|
38
|
-
# Creates a beanstalkd based channel. Messages are written to a tube and
|
39
|
-
# read from it. This channel is read/write. Multiple readers will obtain
|
40
|
-
# messages in a round-robin fashion from the beanstalk server.
|
41
|
-
#
|
42
|
-
# Returns an instance of the Cod::Channel::Beanstalk class.
|
26
|
+
# Creates a pipe connection that is visible to this process and its
|
27
|
+
# children. (see Cod::Pipe)
|
43
28
|
#
|
44
|
-
|
45
|
-
|
46
|
-
#
|
47
|
-
def beanstalk(url, name)
|
48
|
-
Cod::Channel::Beanstalk.new(
|
49
|
-
Connection::Beanstalk.new(url),
|
50
|
-
name)
|
51
|
-
end
|
52
|
-
module_function :beanstalk
|
53
|
-
|
54
|
-
# Creates a IO.pipe based channel. Messages are written to one end of the
|
55
|
-
# pipe and come out on the other end. This channel can have only one reader,
|
56
|
-
# but of course multiple writers. Also, once you either write or read from
|
57
|
-
# such a channel, it will not be available for the other operation anymore.
|
58
|
-
#
|
59
|
-
# A common trick is to #dup the channel before using it to either read or
|
60
|
-
# write, so that the copy can still be used for both operations.
|
61
|
-
#
|
62
|
-
# Note that Cod.pipe channels are usable from process childs (#fork) as
|
63
|
-
# well. As such, they are ideally suited for process control.
|
64
|
-
#
|
65
|
-
# Returns an instance of the Cod::Channel::Pipe class.
|
66
|
-
#
|
67
|
-
# Example:
|
68
|
-
# chan = Cod.pipe
|
69
|
-
#
|
70
|
-
def pipe(name=nil)
|
71
|
-
Cod::Channel::Pipe.new(name)
|
29
|
+
def pipe
|
30
|
+
Cod::Pipe.new
|
72
31
|
end
|
73
32
|
module_function :pipe
|
74
33
|
|
75
|
-
# Creates a tcp connection to the destination and returns a channel for it.
|
34
|
+
# Creates a tcp connection to the destination and returns a channel for it.
|
35
|
+
# (see Cod::TcpClient)
|
76
36
|
#
|
77
|
-
def tcp(destination)
|
78
|
-
Cod::
|
37
|
+
def tcp(destination, serializer=nil)
|
38
|
+
Cod::TcpClient.new(
|
39
|
+
destination,
|
40
|
+
serializer || SimpleSerializer.new)
|
79
41
|
end
|
80
42
|
module_function :tcp
|
81
43
|
|
82
|
-
# Creates a tcp listener on bind_to and returns a channel for it.
|
44
|
+
# Creates a tcp listener on bind_to and returns a channel for it. (see
|
45
|
+
# Cod::TcpServer)
|
83
46
|
#
|
84
|
-
def
|
85
|
-
Cod::
|
47
|
+
def tcp_server(bind_to)
|
48
|
+
Cod::TcpServer.new(bind_to)
|
86
49
|
end
|
87
|
-
module_function :
|
88
|
-
|
89
|
-
#
|
90
|
-
#
|
50
|
+
module_function :tcp_server
|
51
|
+
|
52
|
+
# Creates a channel based on the beanstalkd messaging queue. (see
|
53
|
+
# Cod::Beanstalk::Channel)
|
91
54
|
#
|
92
|
-
|
55
|
+
def beanstalk(tube_name, server=nil)
|
56
|
+
Cod::Beanstalk::Channel.new(tube_name, server||'localhost:11300')
|
57
|
+
end
|
58
|
+
module_function :beanstalk
|
59
|
+
|
60
|
+
# Indicates that the given channel is write only. This gets raised on
|
61
|
+
# operations like #put.
|
62
|
+
#
|
63
|
+
class WriteOnlyChannel < StandardError
|
64
|
+
def initialize
|
65
|
+
super("This channel is write only, attempted read operation.")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Indicates that the channel is read only. This gets raised on operations
|
70
|
+
# like #get.
|
93
71
|
#
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
def uuid_generator
|
98
|
-
pid, generator = Thread.current[:_cod_uuid_generator]
|
99
|
-
|
100
|
-
if pid && Process.pid == pid
|
101
|
-
return generator
|
72
|
+
class ReadOnlyChannel < StandardError
|
73
|
+
def initialize
|
74
|
+
super("This channel is read only, attempted write operation.")
|
102
75
|
end
|
103
|
-
|
104
|
-
pid, generator = Thread.current[:_cod_uuid_generator] = [Process.pid, UUID.new]
|
105
|
-
return generator
|
106
76
|
end
|
107
|
-
module_function :uuid, :uuid_generator
|
108
77
|
end
|
109
78
|
|
110
|
-
|
111
|
-
require 'cod/
|
112
|
-
|
113
|
-
require 'cod/object_io'
|
79
|
+
require 'cod/select_group'
|
80
|
+
require 'cod/select'
|
114
81
|
|
115
82
|
require 'cod/channel'
|
116
|
-
require 'cod/channel/base'
|
117
|
-
require 'cod/channel/pipe'
|
118
|
-
require 'cod/channel/beanstalk'
|
119
|
-
require 'cod/channel/tcpconnection'
|
120
|
-
require 'cod/channel/tcpserver'
|
121
83
|
|
122
|
-
require 'cod/
|
84
|
+
require 'cod/simple_serializer'
|
85
|
+
|
86
|
+
require 'cod/pipe'
|
87
|
+
|
88
|
+
require 'cod/tcp_client'
|
89
|
+
require 'cod/tcp_server'
|
123
90
|
|
124
91
|
require 'cod/service'
|
92
|
+
require 'cod/beanstalk'
|
125
93
|
|
126
|
-
require 'cod/directory'
|
127
|
-
require 'cod/directory/subscription'
|
128
|
-
require 'cod/topic'
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module Cod::Beanstalk
|
2
|
+
|
3
|
+
# NOTE: Beanstalk channels cannot currently be used in Cod.select. This is
|
4
|
+
# due to limitations inherent in the beanstalkd protocol. We'll probably
|
5
|
+
# try to get a patch into beanstalkd to change this.
|
6
|
+
#
|
7
|
+
# NOTE: If you embed a beanstalk channel into one of your messages, you will
|
8
|
+
# get a channel that connects to the same server and the same tube on the
|
9
|
+
# other end. This behaviour is useful for Cod::Service.
|
10
|
+
#
|
11
|
+
class Channel < Cod::Channel
|
12
|
+
JOB_PRIORITY = 0
|
13
|
+
|
14
|
+
def initialize(tube_name, server_url)
|
15
|
+
@tube_name, @server_url = tube_name, server_url
|
16
|
+
|
17
|
+
@body_serializer = Cod::SimpleSerializer.new
|
18
|
+
@transport = connection(server_url, tube_name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def put(msg)
|
22
|
+
pri = JOB_PRIORITY
|
23
|
+
delay = 0
|
24
|
+
ttr = 120
|
25
|
+
body = @body_serializer.en(msg)
|
26
|
+
|
27
|
+
answer, *rest = @transport.interact([:put, pri, delay, ttr, body])
|
28
|
+
fail "#put fails, #{answer.inspect}" unless answer == :inserted
|
29
|
+
end
|
30
|
+
|
31
|
+
def get
|
32
|
+
id, msg = bs_reserve
|
33
|
+
|
34
|
+
# We delete the job immediately, since #get should be definitive.
|
35
|
+
bs_delete(id)
|
36
|
+
|
37
|
+
deserialize(msg)
|
38
|
+
end
|
39
|
+
|
40
|
+
def close
|
41
|
+
@transport.close
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_read_fds
|
45
|
+
fail "Cod.select not supported with beanstalkd channels.\n"+
|
46
|
+
"To support this, we will have to extend the beanstalkd protocol."
|
47
|
+
end
|
48
|
+
|
49
|
+
# --------------------------------------------------------- service/client
|
50
|
+
def service
|
51
|
+
Service.new(self)
|
52
|
+
end
|
53
|
+
def client(answers_to)
|
54
|
+
Service::Client.new(self, answers_to)
|
55
|
+
end
|
56
|
+
|
57
|
+
# -------------------------------------------------------- queue interface
|
58
|
+
def try_get
|
59
|
+
fail "No block given to #try_get" unless block_given?
|
60
|
+
|
61
|
+
id, msg = bs_reserve
|
62
|
+
control = Control.new(id, self)
|
63
|
+
|
64
|
+
begin
|
65
|
+
retval = yield(deserialize(msg), control)
|
66
|
+
rescue Exception
|
67
|
+
control.release unless control.command_given?
|
68
|
+
raise
|
69
|
+
ensure
|
70
|
+
control.delete unless control.command_given?
|
71
|
+
end
|
72
|
+
|
73
|
+
return retval
|
74
|
+
end
|
75
|
+
|
76
|
+
# Holds a message id of a reserved message. Allows several commands to be
|
77
|
+
# executed on the message. See #try_get.
|
78
|
+
class Control # :nodoc:
|
79
|
+
attr_reader :msg_id
|
80
|
+
|
81
|
+
def initialize(msg_id, channel)
|
82
|
+
@msg_id = msg_id
|
83
|
+
@channel = channel
|
84
|
+
@command_given = false
|
85
|
+
end
|
86
|
+
|
87
|
+
def command_given?
|
88
|
+
@command_given
|
89
|
+
end
|
90
|
+
|
91
|
+
def delete
|
92
|
+
@command_given = true
|
93
|
+
@channel.bs_delete(@msg_id)
|
94
|
+
end
|
95
|
+
def release
|
96
|
+
@command_given = true
|
97
|
+
@channel.bs_release(@msg_id)
|
98
|
+
end
|
99
|
+
def release_with_delay(seconds)
|
100
|
+
fail ArgumentError, "Only integer number of seconds are allowed." \
|
101
|
+
unless seconds.floor == seconds
|
102
|
+
|
103
|
+
@command_given = true
|
104
|
+
@channel.bs_release_with_delay(@msg_id, seconds)
|
105
|
+
end
|
106
|
+
def bury
|
107
|
+
@command_given = true
|
108
|
+
@channel.bs_bury(@msg_id)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# ---------------------------------------------------------- serialization
|
113
|
+
def _dump(level) # :nodoc:
|
114
|
+
Marshal.dump(
|
115
|
+
[@tube_name, @server_url])
|
116
|
+
end
|
117
|
+
def self._load(str) # :nodoc:
|
118
|
+
tube_name, server_url = Marshal.load(str)
|
119
|
+
Cod.beanstalk(tube_name, server_url)
|
120
|
+
end
|
121
|
+
|
122
|
+
# ----------------------------------------------------- beanstalk commands
|
123
|
+
def bs_delete(msg_id) # :nodoc:
|
124
|
+
bs_command([:delete, msg_id], :deleted)
|
125
|
+
end
|
126
|
+
def bs_release(msg_id) # :nodoc:
|
127
|
+
bs_command([:release, msg_id, JOB_PRIORITY, 0], :released)
|
128
|
+
end
|
129
|
+
def bs_release_with_delay(msg_id, seconds) # :nodoc:
|
130
|
+
bs_command([:release, msg_id, JOB_PRIORITY, seconds], :released)
|
131
|
+
end
|
132
|
+
def bs_bury(msg_id)
|
133
|
+
# NOTE: Why I need to assign a priority when burying I fail to
|
134
|
+
# understand. Like a priority for rapture?
|
135
|
+
bs_command([:bury, msg_id, JOB_PRIORITY], :buried)
|
136
|
+
end
|
137
|
+
private
|
138
|
+
def bs_reserve
|
139
|
+
answer, *rest = bs_command([:reserve], :reserved)
|
140
|
+
rest
|
141
|
+
end
|
142
|
+
def bs_command(cmd, good_answer)
|
143
|
+
answer, *rest = @transport.interact(cmd)
|
144
|
+
fail "#{cmd.first.inspect} fails, #{answer.inspect}" \
|
145
|
+
unless answer == good_answer
|
146
|
+
[answer, *rest]
|
147
|
+
end
|
148
|
+
|
149
|
+
def deserialize(msg)
|
150
|
+
@body_serializer.de(StringIO.new(msg))
|
151
|
+
end
|
152
|
+
|
153
|
+
def connection(server_url, tube_name)
|
154
|
+
conn = Cod.tcp(server_url, Serializer.new)
|
155
|
+
|
156
|
+
begin
|
157
|
+
answer, *rest = conn.interact([:use, tube_name])
|
158
|
+
fail "#init_tube fails, #{answer.inspect}" unless answer == :using
|
159
|
+
|
160
|
+
answer, *rest = conn.interact([:watch, tube_name])
|
161
|
+
fail "#init_tube fails, #{answer.inspect}" unless answer == :watching
|
162
|
+
rescue
|
163
|
+
conn.close
|
164
|
+
raise
|
165
|
+
end
|
166
|
+
|
167
|
+
conn
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|