cod 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/HISTORY.txt +13 -0
- data/README +3 -3
- data/Rakefile +2 -2
- data/examples/presence_client.rb +15 -0
- data/examples/presence_server.rb +30 -0
- data/examples/pubsub/README +12 -0
- data/examples/pubsub/client.rb +13 -0
- data/examples/pubsub/directory.rb +13 -0
- data/lib/cod.rb +28 -15
- data/lib/cod/channel/base.rb +28 -5
- data/lib/cod/channel/beanstalk.rb +1 -1
- data/lib/cod/channel/pipe.rb +4 -2
- data/lib/cod/channel/tcpconnection.rb +18 -24
- data/lib/cod/channel/tcpserver.rb +24 -24
- data/lib/cod/connection/beanstalk.rb +1 -1
- data/lib/cod/directory.rb +67 -13
- data/lib/cod/directory/countdown.rb +31 -0
- data/lib/cod/directory/subscription.rb +43 -5
- data/lib/cod/object_io.rb +3 -0
- data/lib/cod/objectio/connection.rb +106 -0
- data/lib/cod/objectio/reader.rb +15 -46
- data/lib/cod/objectio/writer.rb +7 -12
- data/lib/cod/topic.rb +66 -5
- metadata +26 -14
- data/lib/at_fork.rb +0 -53
- data/lib/cod/channel/abstract.rb +0 -32
- data/lib/cod/context.rb +0 -25
data/Gemfile
CHANGED
data/HISTORY.txt
CHANGED
@@ -1,4 +1,17 @@
|
|
1
1
|
|
2
|
+
== 0.3.1 / ???
|
3
|
+
|
4
|
+
+ Large improvements in resilience to crashes, reconnects are now attempted
|
5
|
+
but can be improved further.
|
6
|
+
|
7
|
+
+ Both sides of an 1:n directory:topic structure can now crash without
|
8
|
+
permanent message loss.
|
9
|
+
|
10
|
+
+ tcp channels now have a #connected? method that indicates permanent
|
11
|
+
unrecoverable disconnection.
|
12
|
+
|
13
|
+
- at_fork extension is now permanently gone.
|
14
|
+
|
2
15
|
== 0.3 / 20Jul2011
|
3
16
|
|
4
17
|
+ Cod.tcpserver and Cod.tcp channels. Allows direct connection via
|
data/README
CHANGED
@@ -19,8 +19,8 @@ SYNOPSIS
|
|
19
19
|
service.one { |msg| :response } # Process A
|
20
20
|
client.call :ruby_object # => :response # Process B
|
21
21
|
|
22
|
-
# And more: Publish/Subscribe, easy construction of more advanced
|
23
|
-
# communication.
|
22
|
+
# And more: Publish/Subscribe, easy construction of more advanced
|
23
|
+
# distributed communication.
|
24
24
|
|
25
25
|
STATUS
|
26
26
|
|
@@ -28,6 +28,6 @@ Becoming more useful by the day. Most things will work nicely already,
|
|
28
28
|
although error handling is not production quality. Toy around with it now
|
29
29
|
and give me feedback!
|
30
30
|
|
31
|
-
At version 0.3
|
31
|
+
At version 0.3.1
|
32
32
|
|
33
33
|
(c) 2011 Kaspar Schiess
|
data/Rakefile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'psych'
|
2
2
|
require "rubygems"
|
3
|
-
require "
|
3
|
+
require "rdoc/task"
|
4
4
|
require 'rspec/core/rake_task'
|
5
5
|
require 'rubygems/package_task'
|
6
6
|
|
@@ -12,7 +12,7 @@ task :default => :spec
|
|
12
12
|
require 'sdoc'
|
13
13
|
|
14
14
|
# Generate documentation
|
15
|
-
|
15
|
+
RDoc::Task.new do |rdoc|
|
16
16
|
rdoc.title = "parslet - construction of parsers made easy"
|
17
17
|
rdoc.options << '--line-numbers'
|
18
18
|
rdoc.options << '--fmt' << 'shtml' # explictly set shtml generator
|
@@ -0,0 +1,15 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
|
2
|
+
require 'cod'
|
3
|
+
|
4
|
+
|
5
|
+
raise unless ARGV.first
|
6
|
+
|
7
|
+
client = Cod.tcp('localhost:12345')
|
8
|
+
client.put [client, ARGV.first]
|
9
|
+
|
10
|
+
puts "Waiting..."
|
11
|
+
$stdin.gets
|
12
|
+
client.close
|
13
|
+
$stdin.gets
|
14
|
+
|
15
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
|
3
|
+
require 'cod'
|
4
|
+
|
5
|
+
|
6
|
+
present = {}
|
7
|
+
server = Cod.tcpserver('localhost:12345')
|
8
|
+
|
9
|
+
loop do
|
10
|
+
# Process connection requests
|
11
|
+
while server.waiting?
|
12
|
+
connection, attributes = server.get
|
13
|
+
|
14
|
+
present[connection] = attributes
|
15
|
+
end
|
16
|
+
|
17
|
+
# Check if all connections are alive
|
18
|
+
remove = []
|
19
|
+
present.each do |conn, attrs|
|
20
|
+
if conn.connected?
|
21
|
+
puts "Alive: #{attrs.inspect}"
|
22
|
+
else
|
23
|
+
puts "Dead: #{attrs.inspect}"
|
24
|
+
remove << conn
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
remove.each { |conn| present.delete(conn) }
|
29
|
+
sleep 1
|
30
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
|
2
|
+
This is an example of PUB/SUB style messaging. You should run one directory
|
3
|
+
and any number of clients. All clients should receive timestamps from the directory, once every second.
|
4
|
+
|
5
|
+
Experiments that can be made using this setup:
|
6
|
+
|
7
|
+
1) Disconnect a client, restart it. It should start receiving updates
|
8
|
+
immediately.
|
9
|
+
|
10
|
+
2) Disconnect the directory, restart it. It should start redistributing
|
11
|
+
updates to all clients within 5 seconds.
|
12
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + "/../../lib")
|
2
|
+
require 'cod'
|
3
|
+
|
4
|
+
channels = Struct.new(:directory, :answers).new(
|
5
|
+
Cod.beanstalk('localhost:11300', 'directory'),
|
6
|
+
Cod.beanstalk('localhost:11300', 'directory.'+Cod.uuid))
|
7
|
+
|
8
|
+
topic = Cod::Topic.new('', channels.directory, channels.answers, :renew => 5)
|
9
|
+
|
10
|
+
loop do
|
11
|
+
puts topic.get
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + "/../../lib")
|
2
|
+
require 'cod'
|
3
|
+
|
4
|
+
channels = Struct.new(:directory).new(
|
5
|
+
Cod.beanstalk('localhost:11300', 'directory'))
|
6
|
+
|
7
|
+
directory = Cod::Directory.new(channels.directory)
|
8
|
+
|
9
|
+
loop do
|
10
|
+
directory.publish '', Time.now
|
11
|
+
sleep 1
|
12
|
+
end
|
13
|
+
|
data/lib/cod.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'uuid'
|
2
2
|
|
3
3
|
# The core concept of Cod are 'channels'. (Cod::Channel::Base) You can create
|
4
4
|
# such channels on top of the various transport layers. Once you have such a
|
@@ -45,7 +45,9 @@ module Cod
|
|
45
45
|
# chan = Cod.beanstalk('localhost:11300', 'my_tube')
|
46
46
|
#
|
47
47
|
def beanstalk(url, name)
|
48
|
-
|
48
|
+
Cod::Channel::Beanstalk.new(
|
49
|
+
Connection::Beanstalk.new(url),
|
50
|
+
name)
|
49
51
|
end
|
50
52
|
module_function :beanstalk
|
51
53
|
|
@@ -66,31 +68,43 @@ module Cod
|
|
66
68
|
# chan = Cod.pipe
|
67
69
|
#
|
68
70
|
def pipe(name=nil)
|
69
|
-
|
71
|
+
Cod::Channel::Pipe.new(name)
|
70
72
|
end
|
71
73
|
module_function :pipe
|
72
74
|
|
75
|
+
# Creates a tcp connection to the destination and returns a channel for it.
|
76
|
+
#
|
73
77
|
def tcp(destination)
|
74
|
-
|
78
|
+
Cod::Channel::TCPConnection.new(destination)
|
75
79
|
end
|
76
80
|
module_function :tcp
|
77
81
|
|
82
|
+
# Creates a tcp listener on bind_to and returns a channel for it.
|
83
|
+
#
|
78
84
|
def tcpserver(bind_to)
|
79
|
-
|
85
|
+
Cod::Channel::TCPServer.new(bind_to)
|
80
86
|
end
|
81
87
|
module_function :tcpserver
|
82
88
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
# For testing mainly
|
89
|
+
# Returns a UUID that should be unique for this machine (based on MAC), this
|
90
|
+
# Thread and Process. Even after a fork.
|
91
|
+
#
|
92
|
+
# This is used to create identity on the network. Internal method.
|
89
93
|
#
|
90
|
-
def
|
91
|
-
|
94
|
+
def uuid
|
95
|
+
uuid_generator.generate
|
96
|
+
end
|
97
|
+
def uuid_generator
|
98
|
+
pid, generator = Thread.current[:_cod_uuid_generator]
|
99
|
+
|
100
|
+
if pid && Process.pid == pid
|
101
|
+
return generator
|
102
|
+
end
|
103
|
+
|
104
|
+
pid, generator = Thread.current[:_cod_uuid_generator] = [Process.pid, UUID.new]
|
105
|
+
return generator
|
92
106
|
end
|
93
|
-
module_function :
|
107
|
+
module_function :uuid, :uuid_generator
|
94
108
|
end
|
95
109
|
|
96
110
|
module Cod::Connection; end
|
@@ -105,7 +119,6 @@ require 'cod/channel/beanstalk'
|
|
105
119
|
require 'cod/channel/tcpconnection'
|
106
120
|
require 'cod/channel/tcpserver'
|
107
121
|
|
108
|
-
require 'cod/context'
|
109
122
|
require 'cod/client'
|
110
123
|
|
111
124
|
require 'cod/service'
|
data/lib/cod/channel/base.rb
CHANGED
@@ -40,6 +40,11 @@ module Cod
|
|
40
40
|
# channel.close
|
41
41
|
#
|
42
42
|
class Channel::Base
|
43
|
+
def initialize(reader, writer)
|
44
|
+
@reader = reader
|
45
|
+
@writer = writer
|
46
|
+
end
|
47
|
+
|
43
48
|
# Writes a Ruby object (the 'message') to the channel. This object will
|
44
49
|
# be queued in the channel and become available for #get in a FIFO manner.
|
45
50
|
#
|
@@ -51,7 +56,8 @@ module Cod
|
|
51
56
|
# chan.put :symbol
|
52
57
|
#
|
53
58
|
def put(message)
|
54
|
-
|
59
|
+
# TODO Errno::EPIPE raised after a while when the receiver goes away.
|
60
|
+
@writer.put(message)
|
55
61
|
end
|
56
62
|
|
57
63
|
# Reads a Ruby object (a message) from the channel. Some channels may not
|
@@ -60,17 +66,30 @@ module Cod
|
|
60
66
|
# <code>:timeout</code> :: Time to wait before throwing Cod::Channel::TimeoutError.
|
61
67
|
#
|
62
68
|
def get(opts={})
|
63
|
-
|
69
|
+
@reader.get(opts)
|
64
70
|
end
|
65
71
|
|
66
72
|
# Returns true if there are messages waiting in the channel.
|
67
73
|
#
|
68
74
|
def waiting?
|
75
|
+
# TODO EOFError is thrown when the other end has gone away
|
76
|
+
@reader.waiting?
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns true if the channel is connected, and false if all hope must be
|
80
|
+
# given up of reconnecting this channel.
|
81
|
+
#
|
82
|
+
def connected?
|
69
83
|
not_implemented
|
70
84
|
end
|
71
85
|
|
86
|
+
# Closes reader and writer.
|
87
|
+
#
|
72
88
|
def close
|
73
|
-
|
89
|
+
@reader.close if @reader
|
90
|
+
@writer.close if @writer
|
91
|
+
|
92
|
+
@reader = @writer = nil
|
74
93
|
end
|
75
94
|
|
76
95
|
# Returns the Identifier class below the current channel class. This is
|
@@ -154,9 +173,13 @@ module Cod
|
|
154
173
|
end
|
155
174
|
|
156
175
|
def not_implemented
|
157
|
-
|
176
|
+
trace = caller.reject {|l| l =~ %r{#{Regexp.escape(__FILE__)}}} # blatantly stolen from dependencies.rb in activesupport
|
177
|
+
exception = NotImplementedError.new(
|
158
178
|
"You called a method in Cod::Channel::Base. Missing implementation in "+
|
159
|
-
"the subclass!"
|
179
|
+
"the subclass #{self.class.name}!")
|
180
|
+
exception.set_backtrace trace
|
181
|
+
|
182
|
+
raise exception
|
160
183
|
end
|
161
184
|
end
|
162
185
|
end
|
data/lib/cod/channel/pipe.rb
CHANGED
@@ -79,8 +79,10 @@ module Cod
|
|
79
79
|
private
|
80
80
|
def init_in_and_out
|
81
81
|
serializer = ObjectIO::Serializer.new
|
82
|
-
|
83
|
-
|
82
|
+
read_pool = ObjectIO::Connection::Single.new { fds.r }
|
83
|
+
write_pool = ObjectIO::Connection::Single.new { fds.w }
|
84
|
+
@in = ObjectIO::Reader.new(serializer, read_pool)
|
85
|
+
@out = ObjectIO::Writer.new(serializer, write_pool) { fds.w }
|
84
86
|
end
|
85
87
|
|
86
88
|
def close_write
|
@@ -12,6 +12,8 @@ module Cod
|
|
12
12
|
#
|
13
13
|
attr_reader :destination
|
14
14
|
|
15
|
+
attr_reader :connection_pool
|
16
|
+
|
15
17
|
def initialize(destination_or_connection)
|
16
18
|
if destination_or_connection.respond_to?(:to_str)
|
17
19
|
@destination = split_uri(destination_or_connection)
|
@@ -20,23 +22,18 @@ module Cod
|
|
20
22
|
@destination = nil
|
21
23
|
@connection = destination_or_connection
|
22
24
|
end
|
23
|
-
|
25
|
+
|
24
26
|
serializer = ObjectIO::Serializer.new
|
25
|
-
@
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
@writer.put(message)
|
31
|
-
end
|
32
|
-
|
33
|
-
def get(opts={})
|
34
|
-
@reader.get(opts)
|
27
|
+
@connection_pool = ObjectIO::Connection::Single.new { connect }
|
28
|
+
|
29
|
+
super(
|
30
|
+
ObjectIO::Reader.new(serializer, @connection_pool),
|
31
|
+
ObjectIO::Writer.new(serializer, @connection_pool))
|
35
32
|
end
|
36
|
-
|
37
|
-
def
|
38
|
-
|
39
|
-
|
33
|
+
|
34
|
+
def connected?
|
35
|
+
waiting?
|
36
|
+
connection_pool.size > 0
|
40
37
|
end
|
41
38
|
|
42
39
|
def identifier
|
@@ -44,15 +41,12 @@ module Cod
|
|
44
41
|
end
|
45
42
|
|
46
43
|
private
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
# The other end doesn't exist as of this moment. Have the caller retry
|
54
|
-
# later on.
|
55
|
-
nil
|
44
|
+
def connect
|
45
|
+
if destination
|
46
|
+
TCPSocket.new(*destination)
|
47
|
+
else
|
48
|
+
@connection
|
49
|
+
end
|
56
50
|
end
|
57
51
|
end
|
58
52
|
|
@@ -20,37 +20,33 @@ module Cod
|
|
20
20
|
def initialize(bind_to)
|
21
21
|
@bind_to = split_uri(bind_to)
|
22
22
|
@server = TCPServer.new(*@bind_to)
|
23
|
-
|
23
|
+
|
24
|
+
connection_pool = ObjectIO::Connection::Pool.new { accept_connections(server) }
|
24
25
|
serializer = ObjectIO::Serializer.new(self)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def get(opts={})
|
31
|
-
# Read a message from the wire and transform all contained objects.
|
32
|
-
@reader.get(opts)
|
26
|
+
|
27
|
+
super(
|
28
|
+
ObjectIO::Reader.new(serializer, connection_pool),
|
29
|
+
nil)
|
33
30
|
end
|
34
|
-
|
31
|
+
|
32
|
+
# Sending a message to the server side of a socket is not supported.
|
33
|
+
# Transmit the client side channel instance to be able to write back to it
|
34
|
+
# on the server side. This overwrites #put in Base for the sole purpose of
|
35
|
+
# raising a better error.
|
36
|
+
#
|
35
37
|
def put(message)
|
36
38
|
communication_error "You cannot write to the server directly, transmit a "
|
37
39
|
"channel to the server instead."
|
38
40
|
end
|
39
41
|
|
40
|
-
def waiting?
|
41
|
-
@reader.waiting?
|
42
|
-
end
|
43
|
-
|
44
42
|
def close
|
45
|
-
|
43
|
+
super
|
44
|
+
|
46
45
|
server.close if server
|
47
|
-
|
48
46
|
@server = nil
|
49
|
-
@reader = nil
|
50
47
|
end
|
51
48
|
|
52
49
|
def transform(socket, obj)
|
53
|
-
# p [:tcp_server_deserialize, obj]
|
54
50
|
if obj.kind_of?(Channel::TCPConnection::Identifier)
|
55
51
|
# We've been sent 'a' tcp channel. Assume that it's our own client end
|
56
52
|
# that we've been sent and turn it into a channel that communicates
|
@@ -72,13 +68,17 @@ module Cod
|
|
72
68
|
def accept_connections(server)
|
73
69
|
connections = []
|
74
70
|
loop do
|
75
|
-
|
76
|
-
|
71
|
+
# Try connecting more sockets.
|
72
|
+
begin
|
73
|
+
connections << server.accept_nonblock
|
74
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR
|
75
|
+
# Means that no more connects are pending. Ignore, since this is exactly
|
76
|
+
# one of the termination conditions for this method.
|
77
|
+
return connections
|
78
|
+
end
|
77
79
|
end
|
78
|
-
|
79
|
-
|
80
|
-
# one of the termination conditions for this method.
|
81
|
-
return connections
|
80
|
+
|
81
|
+
fail "NOTREACHED: return should be from loop."
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
data/lib/cod/directory.rb
CHANGED
@@ -8,19 +8,40 @@ module Cod
|
|
8
8
|
#
|
9
9
|
attr_reader :channel
|
10
10
|
|
11
|
+
# Subscriptions this directory handles.
|
12
|
+
#
|
13
|
+
attr_reader :subscriptions
|
14
|
+
|
11
15
|
def initialize(channel)
|
12
16
|
@channel = channel
|
13
|
-
@subscriptions =
|
17
|
+
@subscriptions = Set.new
|
14
18
|
end
|
15
19
|
|
16
|
-
# Sends the message to all subscribers that listen to this topic.
|
20
|
+
# Sends the message to all subscribers that listen to this topic. Returns
|
21
|
+
# the number of subscribers this message has been sent to.
|
17
22
|
#
|
18
23
|
def publish(topic, message)
|
19
|
-
|
20
|
-
|
24
|
+
process_control_messages
|
25
|
+
|
26
|
+
n = 0
|
27
|
+
failed_subscriptions = []
|
21
28
|
for subscription in @subscriptions
|
22
|
-
|
29
|
+
begin
|
30
|
+
if subscription === topic
|
31
|
+
subscription.put message
|
32
|
+
n += 1
|
33
|
+
end
|
34
|
+
# TODO reenable this with more specific exception list.
|
35
|
+
rescue Cod::Channel::DirectionError
|
36
|
+
# Writing message failed; remove the subscription.
|
37
|
+
failed_subscriptions << subscription
|
38
|
+
end
|
23
39
|
end
|
40
|
+
|
41
|
+
process_control_messages
|
42
|
+
|
43
|
+
remove_subscriptions { |sub| failed_subscriptions.include?(sub) }
|
44
|
+
return n
|
24
45
|
end
|
25
46
|
|
26
47
|
# Closes all resources used by the directory.
|
@@ -28,17 +49,50 @@ module Cod
|
|
28
49
|
def close
|
29
50
|
channel.close
|
30
51
|
end
|
52
|
+
|
53
|
+
# Internal use: Subscribe a new topic to this directory.
|
54
|
+
#
|
55
|
+
def subscribe(subscription, status=:new)
|
56
|
+
if status == :new && subscriptions.include?(subscription)
|
57
|
+
raise "UUID collision? I already have a subscription for #{subscription.identifier}."
|
58
|
+
end
|
59
|
+
|
60
|
+
@subscriptions << subscription
|
61
|
+
end
|
31
62
|
|
32
|
-
|
33
|
-
|
34
|
-
|
63
|
+
# Internal method to process messages that are inbound on the directory
|
64
|
+
# control channel.
|
65
|
+
#
|
66
|
+
def process_control_messages(now = Time.now)
|
67
|
+
# Handle incoming messages on channel
|
35
68
|
while channel.waiting?
|
36
|
-
|
69
|
+
cmd, *rest = channel.get(timeout: 0.1)
|
70
|
+
case cmd
|
71
|
+
when :subscribe
|
72
|
+
subscription, status = *rest
|
73
|
+
subscribe subscription, status
|
74
|
+
when :ping
|
75
|
+
ping_id = rest.first
|
76
|
+
subscriptions.
|
77
|
+
find { |sub| sub.identifier == ping_id }.
|
78
|
+
ping
|
79
|
+
else
|
80
|
+
warn "Unknown command received: #{cmd.inspect} (#{rest.inspect})"
|
81
|
+
end
|
37
82
|
end
|
83
|
+
|
84
|
+
# Remove all stale subscriptions
|
85
|
+
remove_subscriptions { |sub| sub.stale?(now) }
|
86
|
+
rescue ArgumentError
|
87
|
+
# Probably we could not create a duplicate of a serialized channel.
|
88
|
+
# Ignore this round of subscriptions.
|
38
89
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
90
|
+
|
91
|
+
private
|
92
|
+
def remove_subscriptions(&block)
|
93
|
+
@subscriptions.delete_if(&block)
|
42
94
|
end
|
43
95
|
end
|
44
|
-
end
|
96
|
+
end
|
97
|
+
|
98
|
+
require 'cod/directory/countdown'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Cod::Directory
|
2
|
+
class Countdown
|
3
|
+
attr_reader :run_time
|
4
|
+
|
5
|
+
def initialize(run_time=30*60, now = Time.now)
|
6
|
+
@run_time = run_time
|
7
|
+
start(now); stop
|
8
|
+
end
|
9
|
+
|
10
|
+
def elapsed?(now = Time.now)
|
11
|
+
if running?
|
12
|
+
return (now - @started_at) > @run_time
|
13
|
+
else
|
14
|
+
return (@stopped_at - @started_at) > @run_time
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def running?
|
19
|
+
@started_at && !@stopped_at
|
20
|
+
end
|
21
|
+
|
22
|
+
def start(now = Time.now)
|
23
|
+
@started_at = now
|
24
|
+
@stopped_at = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def stop(now = Time.now)
|
28
|
+
@stopped_at = now
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,21 +1,59 @@
|
|
1
|
-
|
2
|
-
# Represents a subscription to a directory.
|
1
|
+
class Cod::Directory
|
2
|
+
# Represents a subscription to a directory. The subscription is what links
|
3
|
+
# the topic to the directory. It carries the topic id as identifier; this is
|
4
|
+
# what gives the subscription identity. If two subscriptions in a system
|
5
|
+
# have the same identifier, they link to the same topic instance and should
|
6
|
+
# not be sent the same message twice.
|
3
7
|
#
|
4
|
-
class
|
8
|
+
class Subscription
|
5
9
|
attr_reader :matcher
|
6
10
|
attr_reader :channel
|
11
|
+
attr_reader :countdown
|
7
12
|
|
8
|
-
def initialize(matcher, channel)
|
13
|
+
def initialize(matcher, channel, topic_id)
|
9
14
|
@matcher = matcher
|
10
15
|
@channel = channel
|
16
|
+
@countdown = Countdown.new
|
17
|
+
@identifier = topic_id
|
11
18
|
end
|
12
19
|
|
13
20
|
def ===(other)
|
14
21
|
matcher === other
|
15
22
|
end
|
16
23
|
|
24
|
+
def identifier
|
25
|
+
@identifier
|
26
|
+
end
|
27
|
+
def eql?(other)
|
28
|
+
hash == other.hash &&
|
29
|
+
identifier == other.identifier
|
30
|
+
end
|
31
|
+
alias == eql?
|
32
|
+
def hash
|
33
|
+
identifier.hash
|
34
|
+
end
|
35
|
+
|
17
36
|
def put(msg)
|
18
|
-
|
37
|
+
countdown.start
|
38
|
+
|
39
|
+
# Envelope the message to send this subscription id along
|
40
|
+
channel.put [identifier, msg]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Is this subscription stale? Staleness is determined by an internal
|
44
|
+
# countdown since last #put operation.
|
45
|
+
#
|
46
|
+
def stale?(now=Time.now)
|
47
|
+
countdown.running? && countdown.elapsed?(now)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Tells the subscription that the other end has sent back a ping. This
|
51
|
+
# always marks the subscription alive.
|
52
|
+
#
|
53
|
+
def ping(now= Time.now)
|
54
|
+
# Stop the countdown where it is. If it has elapsed?, the subscription
|
55
|
+
# will be marked as stale?
|
56
|
+
countdown.stop(now)
|
19
57
|
end
|
20
58
|
end
|
21
59
|
end
|
data/lib/cod/object_io.rb
CHANGED
@@ -0,0 +1,106 @@
|
|
1
|
+
module Cod::ObjectIO::Connection
|
2
|
+
class Pool
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :connect_action
|
6
|
+
attr_reader :connections
|
7
|
+
|
8
|
+
# Example:
|
9
|
+
# Connection::Single.new { TCPSocket.new('...') }
|
10
|
+
# Connection::Pool.new { socket.accept }
|
11
|
+
#
|
12
|
+
def initialize(&connect_action)
|
13
|
+
raise ArgumentError unless connect_action
|
14
|
+
|
15
|
+
@connect_action = connect_action
|
16
|
+
@connections = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def size
|
20
|
+
@connections.size
|
21
|
+
end
|
22
|
+
|
23
|
+
def report_failed(connection)
|
24
|
+
@connections.delete_if { |e| e == connection }
|
25
|
+
end
|
26
|
+
|
27
|
+
def accept
|
28
|
+
@connections += call_connect
|
29
|
+
end
|
30
|
+
|
31
|
+
def each(&block)
|
32
|
+
@connections.each(&block)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Closes all connections in this pool.
|
36
|
+
#
|
37
|
+
def close
|
38
|
+
self.each do |connection|
|
39
|
+
begin
|
40
|
+
connection.close
|
41
|
+
rescue IOError
|
42
|
+
# Maybe someone else that shares this connection closed it already?
|
43
|
+
# DO NOTHING
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
private
|
48
|
+
# Calls the connect_action block and normalizes the result to be either
|
49
|
+
# an array or nil.
|
50
|
+
#
|
51
|
+
def call_connect
|
52
|
+
result = connect_action[]
|
53
|
+
if result
|
54
|
+
return [result].flatten
|
55
|
+
end
|
56
|
+
|
57
|
+
return nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Implements a single connection that is retried on failure for a number
|
62
|
+
# of times.
|
63
|
+
#
|
64
|
+
class Single < Pool
|
65
|
+
MAX_FAILURES = 10
|
66
|
+
|
67
|
+
def initialize(&connect_action)
|
68
|
+
super
|
69
|
+
|
70
|
+
@connected = false
|
71
|
+
@failures = 0
|
72
|
+
end
|
73
|
+
|
74
|
+
def report_failed(connection)
|
75
|
+
super
|
76
|
+
|
77
|
+
@failures += 1
|
78
|
+
@connected = false
|
79
|
+
end
|
80
|
+
|
81
|
+
def accept
|
82
|
+
return if @connected
|
83
|
+
|
84
|
+
# How many times have we reconnected? Maybe just give up.
|
85
|
+
permanent_connection_error if @failures >= MAX_FAILURES
|
86
|
+
|
87
|
+
# Try and make a new connection: Returns it on success.
|
88
|
+
new_connection = call_connect
|
89
|
+
if new_connection
|
90
|
+
@connected = true
|
91
|
+
@connections += new_connection
|
92
|
+
return
|
93
|
+
end
|
94
|
+
|
95
|
+
# No new connection could be made. Count this as a failure.
|
96
|
+
@failures += 1
|
97
|
+
return
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def permanent_connection_error
|
102
|
+
raise Cod::Channel::CommunicationError,
|
103
|
+
"Permanent connection failure: Giving up."
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/lib/cod/objectio/reader.rb
CHANGED
@@ -3,55 +3,25 @@ module Cod::ObjectIO
|
|
3
3
|
#
|
4
4
|
class Reader
|
5
5
|
attr_reader :waiting_messages
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :pool
|
7
7
|
|
8
8
|
# Initializes an object reader that reads from one or several IO objects.
|
9
|
-
# You can either pass the io object in the constructor (io) or you can
|
10
|
-
# provide the instance with a block that is called each time a read is
|
11
|
-
# attempted. The block should return an array of IO objects to also read
|
12
|
-
# from.
|
13
9
|
#
|
14
10
|
# Example:
|
15
|
-
#
|
11
|
+
# connection = Connection::Pool.new { make_new_connection }
|
12
|
+
# reader = Reader.new(serializer, connection)
|
16
13
|
#
|
17
|
-
def initialize(serializer,
|
14
|
+
def initialize(serializer, conn_pool)
|
18
15
|
@serializer = serializer
|
19
16
|
@waiting_messages = []
|
20
|
-
@
|
21
|
-
@registered_ios = Set.new
|
22
|
-
|
23
|
-
register io if io
|
17
|
+
@pool = conn_pool
|
24
18
|
end
|
25
19
|
|
26
|
-
# Called before each attempt to read from the wire. This should return
|
27
|
-
# the IO objects that need to be considered when reading.
|
28
|
-
#
|
29
|
-
def establish
|
30
|
-
sockets = @establish_block && @establish_block.call(@io) ||
|
31
|
-
nil
|
32
|
-
|
33
|
-
[sockets].flatten
|
34
|
-
end
|
35
|
-
|
36
|
-
def register(ios)
|
37
|
-
return unless ios
|
38
|
-
ios.each do |io|
|
39
|
-
registered_ios << io
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def unregister(ios)
|
44
|
-
ios.each do |io|
|
45
|
-
registered_ios.delete(io)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
20
|
def get(opts={})
|
50
21
|
return waiting_messages.shift if queued?
|
51
22
|
|
52
23
|
start_time = Time.now
|
53
24
|
loop do
|
54
|
-
# p [:looping, opts]
|
55
25
|
read_from_wire opts
|
56
26
|
|
57
27
|
# Early return in case we have a message waiting
|
@@ -69,6 +39,8 @@ module Cod::ObjectIO
|
|
69
39
|
def waiting?
|
70
40
|
read_from_wire
|
71
41
|
queued?
|
42
|
+
rescue Cod::Channel::CommunicationError
|
43
|
+
queued?
|
72
44
|
end
|
73
45
|
|
74
46
|
def queued?
|
@@ -76,7 +48,7 @@ module Cod::ObjectIO
|
|
76
48
|
end
|
77
49
|
|
78
50
|
def close
|
79
|
-
@
|
51
|
+
@pool.close
|
80
52
|
end
|
81
53
|
|
82
54
|
private
|
@@ -85,13 +57,10 @@ module Cod::ObjectIO
|
|
85
57
|
#
|
86
58
|
def read_from_wire(opts={})
|
87
59
|
# Establish new connections and register them
|
88
|
-
|
60
|
+
@pool.accept
|
89
61
|
|
90
|
-
#
|
91
|
-
|
92
|
-
|
93
|
-
# Read all ready sockets
|
94
|
-
process_nonblock(ready_read) if ready_read
|
62
|
+
# Process all waiting data
|
63
|
+
process_nonblock(@pool.connections)
|
95
64
|
end
|
96
65
|
|
97
66
|
# Reads all data waiting in each io in the ios array.
|
@@ -111,11 +80,11 @@ module Cod::ObjectIO
|
|
111
80
|
while not sio.eof?
|
112
81
|
waiting_messages << deserialize(io, sio)
|
113
82
|
end
|
83
|
+
rescue Errno::EAGAIN
|
84
|
+
# read failed because there was no data. This is expected.
|
85
|
+
return
|
114
86
|
rescue EOFError
|
115
|
-
|
116
|
-
# We will need to reconnect this. If possible.
|
117
|
-
registered_ios.delete(io)
|
118
|
-
raise
|
87
|
+
@pool.report_failed(io)
|
119
88
|
end
|
120
89
|
|
121
90
|
# Deserializes a message (in message format, string) into the object that
|
data/lib/cod/objectio/writer.rb
CHANGED
@@ -2,29 +2,24 @@ module Cod::ObjectIO
|
|
2
2
|
# Writes objects to an IO stream.
|
3
3
|
#
|
4
4
|
class Writer
|
5
|
-
def initialize(serializer,
|
5
|
+
def initialize(serializer, pool)
|
6
6
|
@serializer = serializer
|
7
|
-
@
|
8
|
-
@reconnect_block = block
|
7
|
+
@pool = pool
|
9
8
|
end
|
10
9
|
|
11
10
|
def put(message)
|
12
|
-
|
11
|
+
@pool.accept
|
13
12
|
|
14
|
-
@
|
13
|
+
@pool.each do |connection|
|
14
|
+
connection.write(serialize(message))
|
15
|
+
end
|
15
16
|
end
|
16
17
|
|
17
18
|
def close
|
18
|
-
@
|
19
|
+
@pool.close
|
19
20
|
end
|
20
21
|
|
21
22
|
private
|
22
|
-
def attempt_reconnect
|
23
|
-
if @reconnect_block
|
24
|
-
@io = @reconnect_block[]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
23
|
def serialize(message)
|
29
24
|
@serializer.serialize(message)
|
30
25
|
end
|
data/lib/cod/topic.rb
CHANGED
@@ -4,9 +4,33 @@ module Cod
|
|
4
4
|
class Topic
|
5
5
|
attr_reader :answers, :directory
|
6
6
|
attr_reader :match_expr
|
7
|
-
|
7
|
+
attr_reader :identifier
|
8
|
+
attr_reader :subscription
|
9
|
+
attr_reader :renew_countdown
|
10
|
+
|
11
|
+
# Creates a topic that subscribes to a part of a directory. The match_expr
|
12
|
+
# decides which messages get forwarded to this topic, it limits the
|
13
|
+
# topic to a subset of the messages in the directory.
|
14
|
+
#
|
15
|
+
# Parameters:
|
16
|
+
# match_expr :: Topic to subscribe
|
17
|
+
# directory_channel :: Directory channel
|
18
|
+
# answer_channel :: Where the messages for this topic get sent
|
19
|
+
# opts :: See below
|
20
|
+
#
|
21
|
+
# Available options are:
|
22
|
+
# :renew :: Renew the subscription every n seconds.
|
23
|
+
#
|
24
|
+
def initialize(match_expr, directory_channel, answer_channel, opts={})
|
8
25
|
@directory, @answers = directory_channel, answer_channel
|
9
26
|
@match_expr = match_expr
|
27
|
+
@identifier = Cod.uuid
|
28
|
+
@subscription = Directory::Subscription.new(
|
29
|
+
match_expr, answers, @identifier)
|
30
|
+
|
31
|
+
# Default is to renew subscriptions every 30 minutes
|
32
|
+
@renew_countdown = Directory::Countdown.new(opts[:renew] || 30*60)
|
33
|
+
renew_countdown.start
|
10
34
|
|
11
35
|
subscribe
|
12
36
|
end
|
@@ -14,14 +38,51 @@ module Cod
|
|
14
38
|
# Subscribes this topic to the directory's messages. This gets called upon
|
15
39
|
# initialization and must not be called again.
|
16
40
|
#
|
17
|
-
def subscribe
|
18
|
-
directory.put
|
41
|
+
def subscribe(status=:new)
|
42
|
+
directory.put [
|
43
|
+
:subscribe, subscription, status]
|
44
|
+
|
45
|
+
# Start counting down to next subscription renewal
|
46
|
+
renew_countdown.start
|
47
|
+
end
|
48
|
+
def renew_subscription
|
49
|
+
subscribe(:refresh)
|
50
|
+
end
|
51
|
+
def renewal_needed?
|
52
|
+
renew_countdown.elapsed?
|
19
53
|
end
|
20
54
|
|
21
55
|
# Reads the next message from the directory that matches this topic.
|
22
56
|
#
|
23
|
-
def get
|
24
|
-
|
57
|
+
def get(opts={})
|
58
|
+
# Read one message from the channel
|
59
|
+
subscription_id, message = next_message(opts)
|
60
|
+
# Answer back with a ping (so the directory knows we're still there)
|
61
|
+
directory.put [:ping, subscription_id]
|
62
|
+
|
63
|
+
return message
|
64
|
+
end
|
65
|
+
def next_message(opts)
|
66
|
+
if t=opts[:timeout]
|
67
|
+
timeout_at = Time.now + t
|
68
|
+
timeout = [t, renew_countdown.run_time].min
|
69
|
+
else
|
70
|
+
timeout_at = nil
|
71
|
+
timeout = renew_countdown.run_time
|
72
|
+
end
|
73
|
+
|
74
|
+
loop do
|
75
|
+
renew_subscription if renewal_needed?
|
76
|
+
|
77
|
+
begin
|
78
|
+
return answers.get(opts.merge(:timeout => timeout))
|
79
|
+
rescue Cod::Channel::TimeoutError
|
80
|
+
raise if timeout_at && Time.now > timeout_at
|
81
|
+
# DO NOTHING
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
fail "NOT REACHED"
|
25
86
|
end
|
26
87
|
|
27
88
|
# Closes all resources used by the topic.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cod
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,12 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-07-20 00:00:00.
|
13
|
-
default_executable:
|
12
|
+
date: 2011-07-20 00:00:00.000000000Z
|
14
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: uuid
|
16
|
+
requirement: &70169583690420 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70169583690420
|
15
25
|
- !ruby/object:Gem::Dependency
|
16
26
|
name: rspec
|
17
|
-
requirement: &
|
27
|
+
requirement: &70169583689920 !ruby/object:Gem::Requirement
|
18
28
|
none: false
|
19
29
|
requirements:
|
20
30
|
- - ! '>='
|
@@ -22,10 +32,10 @@ dependencies:
|
|
22
32
|
version: '0'
|
23
33
|
type: :development
|
24
34
|
prerelease: false
|
25
|
-
version_requirements: *
|
35
|
+
version_requirements: *70169583689920
|
26
36
|
- !ruby/object:Gem::Dependency
|
27
37
|
name: flexmock
|
28
|
-
requirement: &
|
38
|
+
requirement: &70169583689340 !ruby/object:Gem::Requirement
|
29
39
|
none: false
|
30
40
|
requirements:
|
31
41
|
- - ! '>='
|
@@ -33,10 +43,10 @@ dependencies:
|
|
33
43
|
version: '0'
|
34
44
|
type: :development
|
35
45
|
prerelease: false
|
36
|
-
version_requirements: *
|
46
|
+
version_requirements: *70169583689340
|
37
47
|
- !ruby/object:Gem::Dependency
|
38
48
|
name: sdoc
|
39
|
-
requirement: &
|
49
|
+
requirement: &70169583688800 !ruby/object:Gem::Requirement
|
40
50
|
none: false
|
41
51
|
requirements:
|
42
52
|
- - ! '>='
|
@@ -44,7 +54,7 @@ dependencies:
|
|
44
54
|
version: '0'
|
45
55
|
type: :development
|
46
56
|
prerelease: false
|
47
|
-
version_requirements: *
|
57
|
+
version_requirements: *70169583688800
|
48
58
|
description:
|
49
59
|
email: kaspar.schiess@absurd.li
|
50
60
|
executables: []
|
@@ -57,8 +67,6 @@ files:
|
|
57
67
|
- LICENSE
|
58
68
|
- Rakefile
|
59
69
|
- README
|
60
|
-
- lib/at_fork.rb
|
61
|
-
- lib/cod/channel/abstract.rb
|
62
70
|
- lib/cod/channel/base.rb
|
63
71
|
- lib/cod/channel/beanstalk.rb
|
64
72
|
- lib/cod/channel/pipe.rb
|
@@ -68,7 +76,7 @@ files:
|
|
68
76
|
- lib/cod/channel.rb
|
69
77
|
- lib/cod/client.rb
|
70
78
|
- lib/cod/connection/beanstalk.rb
|
71
|
-
- lib/cod/
|
79
|
+
- lib/cod/directory/countdown.rb
|
72
80
|
- lib/cod/directory/subscription.rb
|
73
81
|
- lib/cod/directory.rb
|
74
82
|
- lib/cod/object_io.rb
|
@@ -83,10 +91,14 @@ files:
|
|
83
91
|
- examples/master_child.rb
|
84
92
|
- examples/ping.rb
|
85
93
|
- examples/pong.rb
|
94
|
+
- examples/presence_client.rb
|
95
|
+
- examples/presence_server.rb
|
96
|
+
- examples/pubsub/client.rb
|
97
|
+
- examples/pubsub/directory.rb
|
98
|
+
- examples/pubsub/README
|
86
99
|
- examples/service.rb
|
87
100
|
- examples/service_directory.rb
|
88
101
|
- examples/tcp.rb
|
89
|
-
has_rdoc: true
|
90
102
|
homepage: http://kschiess.github.com/cod
|
91
103
|
licenses: []
|
92
104
|
post_install_message:
|
@@ -109,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
121
|
version: '0'
|
110
122
|
requirements: []
|
111
123
|
rubyforge_project:
|
112
|
-
rubygems_version: 1.6
|
124
|
+
rubygems_version: 1.8.6
|
113
125
|
signing_key:
|
114
126
|
specification_version: 3
|
115
127
|
summary: Really simple IPC.
|
data/lib/at_fork.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
# Extends the Kernel module with an at_fork method for installing at_fork
|
2
|
-
# handlers.
|
3
|
-
#
|
4
|
-
# NOTE: at_fork handlers are executed in the thread that does the forking and
|
5
|
-
# that survives the process fork.
|
6
|
-
#
|
7
|
-
# Usage:
|
8
|
-
#
|
9
|
-
# at_fork do
|
10
|
-
# # Do something on fork (in the forking process)
|
11
|
-
# end
|
12
|
-
# at_fork(:child) { ... } # do something in the forked process
|
13
|
-
# at_fork(:parent) { ... } # do something in the forking process
|
14
|
-
#
|
15
|
-
module Kernel
|
16
|
-
# Child at_fork handlers
|
17
|
-
#
|
18
|
-
def self.at_fork_child
|
19
|
-
@at_fork_child ||= []
|
20
|
-
end
|
21
|
-
|
22
|
-
# Parent at_fork handlers
|
23
|
-
#
|
24
|
-
def self.at_fork_parent
|
25
|
-
@at_fork_parent ||= []
|
26
|
-
end
|
27
|
-
|
28
|
-
def at_fork(type=:parent,&block)
|
29
|
-
raise ArgumentError, "Must provide a handler block." unless block
|
30
|
-
|
31
|
-
handler_array = (type == :child) ?
|
32
|
-
Kernel.at_fork_child : Kernel.at_fork_parent
|
33
|
-
|
34
|
-
handler_array << block
|
35
|
-
end
|
36
|
-
|
37
|
-
def fork_with_at_fork(&block)
|
38
|
-
Kernel.at_fork_parent.each(&:call)
|
39
|
-
|
40
|
-
fork_without_at_fork do
|
41
|
-
# From this point on, operation is single threaded.
|
42
|
-
|
43
|
-
Kernel.at_fork_child.each(&:call)
|
44
|
-
|
45
|
-
Kernel.at_fork_parent.replace([])
|
46
|
-
Kernel.at_fork_child.replace([])
|
47
|
-
|
48
|
-
block.call
|
49
|
-
end
|
50
|
-
end
|
51
|
-
alias fork_without_at_fork fork
|
52
|
-
alias fork fork_with_at_fork
|
53
|
-
end
|
data/lib/cod/channel/abstract.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
module Cod
|
2
|
-
# This is mostly documentation: Use it as a template for new channels.
|
3
|
-
class Channel::Abstract < Channel::Base
|
4
|
-
def initialize(destination)
|
5
|
-
not_implemented
|
6
|
-
end
|
7
|
-
|
8
|
-
def initialize_copy(from)
|
9
|
-
not_implemented
|
10
|
-
end
|
11
|
-
|
12
|
-
def put(message)
|
13
|
-
not_implemented
|
14
|
-
end
|
15
|
-
|
16
|
-
def get(opts={})
|
17
|
-
not_implemented
|
18
|
-
end
|
19
|
-
|
20
|
-
def waiting?
|
21
|
-
not_implemented
|
22
|
-
end
|
23
|
-
|
24
|
-
def close
|
25
|
-
not_implemented
|
26
|
-
end
|
27
|
-
|
28
|
-
def identifier
|
29
|
-
not_implemented
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
data/lib/cod/context.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'weakref'
|
2
|
-
|
3
|
-
module Cod
|
4
|
-
# Context will allow to produce channels retaining some state. Until now,
|
5
|
-
# this hasn't been neccessary.
|
6
|
-
#
|
7
|
-
class Context
|
8
|
-
def pipe(name=nil)
|
9
|
-
Cod::Channel::Pipe.new(name)
|
10
|
-
end
|
11
|
-
|
12
|
-
def beanstalk(url, name=nil)
|
13
|
-
Cod::Channel::Beanstalk.new(
|
14
|
-
Connection::Beanstalk.new(url), name)
|
15
|
-
end
|
16
|
-
|
17
|
-
def tcp(destination)
|
18
|
-
Cod::Channel::TCPConnection.new(destination)
|
19
|
-
end
|
20
|
-
|
21
|
-
def tcpserver(bind_to)
|
22
|
-
Cod::Channel::TCPServer.new(bind_to)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|