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
@@ -1,69 +0,0 @@
|
|
1
|
-
|
2
|
-
begin
|
3
|
-
require 'beanstalk-client'
|
4
|
-
rescue LoadError
|
5
|
-
fail "You should install the gem 'beanstalk-client' to use Cod::Channel::Beanstalk."
|
6
|
-
end
|
7
|
-
|
8
|
-
module Cod
|
9
|
-
# Also see Channel::Base for more documentation.
|
10
|
-
#
|
11
|
-
class Channel::Beanstalk < Channel::Base
|
12
|
-
NONBLOCK_TIMEOUT = 0.01
|
13
|
-
|
14
|
-
# Connection instance that is in use for this channel.
|
15
|
-
attr_reader :connection
|
16
|
-
|
17
|
-
# Name of the queue on the beanstalk server
|
18
|
-
attr_reader :tube_name
|
19
|
-
|
20
|
-
def initialize(connection, name)
|
21
|
-
@connection = connection
|
22
|
-
@tube_name = name.freeze
|
23
|
-
@serializer = ObjectIO::Serializer.new
|
24
|
-
end
|
25
|
-
|
26
|
-
def initialize_copy(from)
|
27
|
-
@connection = from.connection.dup
|
28
|
-
@tube_name = from.tube_name
|
29
|
-
end
|
30
|
-
|
31
|
-
def put(message)
|
32
|
-
buffer = @serializer.serialize(message)
|
33
|
-
connection.put(tube_name, buffer)
|
34
|
-
end
|
35
|
-
|
36
|
-
def waiting?
|
37
|
-
connection.waiting?(tube_name)
|
38
|
-
end
|
39
|
-
|
40
|
-
def get(opts={})
|
41
|
-
message = connection.get(tube_name,
|
42
|
-
:timeout => opts[:timeout])
|
43
|
-
return @serializer.deserialize(nil, message)
|
44
|
-
rescue Beanstalk::TimedOut
|
45
|
-
raise Channel::TimeoutError, "No messages waiting in #{tube_name}."
|
46
|
-
end
|
47
|
-
|
48
|
-
def close
|
49
|
-
connection.close
|
50
|
-
end
|
51
|
-
|
52
|
-
def identifier
|
53
|
-
identifier_class.new(connection.url, tube_name)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
class Channel::Beanstalk::Identifier
|
58
|
-
def initialize(url, tube_name)
|
59
|
-
@url, @tube_name = url, tube_name
|
60
|
-
end
|
61
|
-
|
62
|
-
def resolve(ctxt=nil)
|
63
|
-
raise NotImplementedError, "Explicit context not yet implemented." \
|
64
|
-
if ctxt
|
65
|
-
|
66
|
-
Cod.beanstalk(@url, @tube_name)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
data/lib/cod/channel/pipe.rb
DELETED
@@ -1,137 +0,0 @@
|
|
1
|
-
module Cod
|
2
|
-
# A channel that uses IO.pipe as its transport mechanism. This means that
|
3
|
-
# you can only communicate within a single process hierarchy using this,
|
4
|
-
# since the file descriptors are not visible to the outside world.
|
5
|
-
#
|
6
|
-
# Also see Channel::Base for more documentation.
|
7
|
-
#
|
8
|
-
class Channel::Pipe < Channel::Base
|
9
|
-
# A tuple storing the read and the write end of a IO.pipe.
|
10
|
-
#
|
11
|
-
Fds = Struct.new(:r, :w)
|
12
|
-
|
13
|
-
attr_reader :fds
|
14
|
-
|
15
|
-
def initialize(name=nil)
|
16
|
-
@fds = Fds.new(*IO.pipe)
|
17
|
-
|
18
|
-
init_in_and_out
|
19
|
-
end
|
20
|
-
|
21
|
-
def initialize_copy(old)
|
22
|
-
old_fds = old.fds
|
23
|
-
|
24
|
-
raise ArgumentError,
|
25
|
-
"Dupping a pipe channel only makes sense if it is still unused." \
|
26
|
-
unless old_fds.r && old_fds.w
|
27
|
-
|
28
|
-
@fds = Fds.new(
|
29
|
-
old_fds.r.dup,
|
30
|
-
old_fds.w.dup)
|
31
|
-
|
32
|
-
init_in_and_out
|
33
|
-
end
|
34
|
-
|
35
|
-
def put(message)
|
36
|
-
close_read
|
37
|
-
|
38
|
-
unless fds.w
|
39
|
-
direction_error 'Cannot put data to pipe. Already closed that end?'
|
40
|
-
end
|
41
|
-
|
42
|
-
@out.put(message)
|
43
|
-
rescue Errno::EPIPE
|
44
|
-
direction_error "You should #dup before writing; Looks like no other copy exists currently."
|
45
|
-
end
|
46
|
-
|
47
|
-
def waiting?
|
48
|
-
@in.waiting?
|
49
|
-
rescue EOFError
|
50
|
-
# We've just hit end of file in the pipe. That means that all write
|
51
|
-
# ends have been closed.
|
52
|
-
@in.queued?
|
53
|
-
end
|
54
|
-
|
55
|
-
def get(opts={})
|
56
|
-
close_write
|
57
|
-
|
58
|
-
unless fds.r
|
59
|
-
direction_error 'Cannot get data from pipe. Already closed that end?'
|
60
|
-
end
|
61
|
-
|
62
|
-
@in.get(opts)
|
63
|
-
rescue EOFError
|
64
|
-
# We've just hit end of file in the pipe. That means that all write
|
65
|
-
# ends have been closed.
|
66
|
-
communication_error "All write ends for this pipe have been closed. "+
|
67
|
-
"Further #get's would block forever." \
|
68
|
-
unless @in.queued?
|
69
|
-
|
70
|
-
# rescue Errno::EPIPE
|
71
|
-
# direction_error 'Cannot get data from pipe. Already closed that end?'
|
72
|
-
end
|
73
|
-
|
74
|
-
def close
|
75
|
-
close_write
|
76
|
-
close_read
|
77
|
-
end
|
78
|
-
|
79
|
-
private
|
80
|
-
def init_in_and_out
|
81
|
-
serializer = ObjectIO::Serializer.new
|
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 }
|
86
|
-
end
|
87
|
-
|
88
|
-
def close_write
|
89
|
-
return unless fds.w
|
90
|
-
fds.w.close
|
91
|
-
|
92
|
-
fds.w = nil
|
93
|
-
@out = nil
|
94
|
-
end
|
95
|
-
|
96
|
-
def close_read
|
97
|
-
return unless fds.r
|
98
|
-
fds.r.close
|
99
|
-
|
100
|
-
fds.r = nil
|
101
|
-
@in = nil
|
102
|
-
end
|
103
|
-
|
104
|
-
# # Tries hard to empty the pipe and to store incoming messages in
|
105
|
-
# # @waiting_messages.
|
106
|
-
# #
|
107
|
-
# def process_inbound_nonblock
|
108
|
-
# buffer = fds.r.read_nonblock(1024*1024*1024)
|
109
|
-
#
|
110
|
-
# while buffer.size > 0
|
111
|
-
# @waiting_messages << transport_unpack(buffer)
|
112
|
-
# end
|
113
|
-
rescue EOFError
|
114
|
-
# We've just hit end of file in the pipe. That means that all write
|
115
|
-
# ends have been closed.
|
116
|
-
communication_error "All write ends for this pipe have been closed. "+
|
117
|
-
"Further #get's would block forever." \
|
118
|
-
unless queued?
|
119
|
-
# rescue Errno::EAGAIN
|
120
|
-
# # Catch and ignore this: fds.r is not ready and read would block.
|
121
|
-
# end
|
122
|
-
end
|
123
|
-
|
124
|
-
class Channel::Pipe::Identifier
|
125
|
-
def initialize(channel)
|
126
|
-
@objid = channel.object_id
|
127
|
-
end
|
128
|
-
|
129
|
-
def resolve
|
130
|
-
ObjectSpace._id2ref(@objid).dup
|
131
|
-
rescue RangeError
|
132
|
-
raise Cod::InvalidIdentifier,
|
133
|
-
"Could not reference channel. Either it was garbage collected "+
|
134
|
-
"or it never existed in this process."
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
data/lib/cod/channel/tcp.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'socket'
|
2
|
-
|
3
|
-
module Cod
|
4
|
-
module Channel::TCP
|
5
|
-
def split_uri(uri)
|
6
|
-
colon = uri.index(':')
|
7
|
-
raise ArgumentError,
|
8
|
-
"TCP points must include a port number, #{uri.inspect} does not." \
|
9
|
-
unless colon
|
10
|
-
|
11
|
-
[
|
12
|
-
colon == 0 ? nil : uri[0...colon],
|
13
|
-
Integer(uri[colon+1..-1])]
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
require 'cod/channel/tcp'
|
2
|
-
|
3
|
-
module Cod
|
4
|
-
# A channel based on a tcp connection.
|
5
|
-
#
|
6
|
-
# Also see Channel::Base for more documentation.
|
7
|
-
#
|
8
|
-
class Channel::TCPConnection < Channel::Base
|
9
|
-
include Channel::TCP
|
10
|
-
|
11
|
-
# A <host, port> tuple: The target of this connection.
|
12
|
-
#
|
13
|
-
attr_reader :destination
|
14
|
-
|
15
|
-
attr_reader :connection_pool
|
16
|
-
|
17
|
-
def initialize(destination_or_connection)
|
18
|
-
if destination_or_connection.respond_to?(:to_str)
|
19
|
-
@destination = split_uri(destination_or_connection)
|
20
|
-
@connection = nil
|
21
|
-
else
|
22
|
-
@destination = nil
|
23
|
-
@connection = destination_or_connection
|
24
|
-
end
|
25
|
-
|
26
|
-
serializer = ObjectIO::Serializer.new
|
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))
|
32
|
-
end
|
33
|
-
|
34
|
-
def connected?
|
35
|
-
waiting?
|
36
|
-
connection_pool.size > 0
|
37
|
-
end
|
38
|
-
|
39
|
-
def identifier
|
40
|
-
Identifier.new(destination)
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
def connect
|
45
|
-
if destination
|
46
|
-
TCPSocket.new(*destination)
|
47
|
-
else
|
48
|
-
@connection
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
class Channel::TCPConnection::Identifier
|
54
|
-
def initialize(destination)
|
55
|
-
@destination = destination
|
56
|
-
end
|
57
|
-
|
58
|
-
def resolve
|
59
|
-
# If we've been sent to our own server end, assume the role of the
|
60
|
-
# socket on that side. This is achieved by inserting the self into
|
61
|
-
# the stream of deserialized objects and having the transformer
|
62
|
-
# (attached to the serializer, see ObjectIO::Serializer) transform
|
63
|
-
# it into a valid connection object.
|
64
|
-
self
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
@@ -1,84 +0,0 @@
|
|
1
|
-
require 'cod/channel/tcp'
|
2
|
-
|
3
|
-
module Cod
|
4
|
-
# A channel based on a passive tcp server (listening).
|
5
|
-
#
|
6
|
-
# Also see Channel::Base for more documentation.
|
7
|
-
#
|
8
|
-
class Channel::TCPServer < Channel::Base
|
9
|
-
include Channel::TCP
|
10
|
-
|
11
|
-
# The <host, port> tuple this server is bound to.
|
12
|
-
attr_reader :bind_to
|
13
|
-
|
14
|
-
# The actual TCPServer instance
|
15
|
-
attr_reader :server
|
16
|
-
|
17
|
-
# Sockets that are open to clients
|
18
|
-
attr_reader :connections
|
19
|
-
|
20
|
-
def initialize(bind_to)
|
21
|
-
@bind_to = split_uri(bind_to)
|
22
|
-
@server = TCPServer.new(*@bind_to)
|
23
|
-
|
24
|
-
connection_pool = ObjectIO::Connection::Pool.new { accept_connections(server) }
|
25
|
-
serializer = ObjectIO::Serializer.new(self)
|
26
|
-
|
27
|
-
super(
|
28
|
-
ObjectIO::Reader.new(serializer, connection_pool),
|
29
|
-
nil)
|
30
|
-
end
|
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
|
-
#
|
37
|
-
def put(message)
|
38
|
-
communication_error "You cannot write to the server directly, transmit a "
|
39
|
-
"channel to the server instead."
|
40
|
-
end
|
41
|
-
|
42
|
-
def close
|
43
|
-
super
|
44
|
-
|
45
|
-
server.close if server
|
46
|
-
@server = nil
|
47
|
-
end
|
48
|
-
|
49
|
-
def transform(socket, obj)
|
50
|
-
if obj.kind_of?(Channel::TCPConnection::Identifier)
|
51
|
-
# We've been sent 'a' tcp channel. Assume that it's our own client end
|
52
|
-
# that we've been sent and turn it into a channel that communicates
|
53
|
-
# back there.
|
54
|
-
return Channel::TCPConnection.new(socket)
|
55
|
-
end
|
56
|
-
|
57
|
-
return obj
|
58
|
-
end
|
59
|
-
|
60
|
-
def identifier
|
61
|
-
communication_error "TCP server channels cannot be transmitted."
|
62
|
-
end
|
63
|
-
|
64
|
-
private
|
65
|
-
|
66
|
-
# Accept all pending connects and stores them in the connections array.
|
67
|
-
#
|
68
|
-
def accept_connections(server)
|
69
|
-
connections = []
|
70
|
-
loop do
|
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
|
79
|
-
end
|
80
|
-
|
81
|
-
fail "NOTREACHED: return should be from loop."
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
data/lib/cod/client.rb
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
|
2
|
-
module Cod
|
3
|
-
# A client that consumes a service (Cod::Service).
|
4
|
-
#
|
5
|
-
class Client
|
6
|
-
attr_reader :incoming
|
7
|
-
attr_reader :outgoing
|
8
|
-
|
9
|
-
# Create a new client and tie it to an answer channel. The answer channel
|
10
|
-
# will often be anonymous - no one except the service needs to write
|
11
|
-
# there.
|
12
|
-
#
|
13
|
-
def initialize(requests, answers, timeout=1)
|
14
|
-
@timeout = timeout
|
15
|
-
@incoming = answers
|
16
|
-
@outgoing = requests
|
17
|
-
@request_id = 0
|
18
|
-
end
|
19
|
-
|
20
|
-
# Calls the service in a synchronous fashion. Returns the message the
|
21
|
-
# server sends back.
|
22
|
-
#
|
23
|
-
def call(message=nil)
|
24
|
-
expected_id = next_request_id
|
25
|
-
outgoing.put envelope(expected_id, message, incoming, true)
|
26
|
-
|
27
|
-
start_time = Time.now
|
28
|
-
loop do
|
29
|
-
received_id, answer = incoming.get(:timeout => @timeout)
|
30
|
-
return answer if received_id == expected_id
|
31
|
-
|
32
|
-
# We're receiving answers with request_ids that are outside the
|
33
|
-
# window that we would expect. Something is seriously amiss.
|
34
|
-
raise Cod::Channel::CommunicationError,
|
35
|
-
"Missed request." unless earlier?(expected_id, received_id)
|
36
|
-
|
37
|
-
# We've been waiting (and consuming answers) for too long - overall
|
38
|
-
# timeout has elapsed.
|
39
|
-
raise Cod::Channel::TimeoutError,
|
40
|
-
"Timed out while waiting for service request answer." \
|
41
|
-
if (Time.now-start_time) > @timeout
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# This sends the server a message without waiting for an answer. The
|
46
|
-
# server will throw away the answer produced.
|
47
|
-
#
|
48
|
-
def notify(message=nil)
|
49
|
-
outgoing.put envelope(next_request_id, message, incoming, false)
|
50
|
-
nil
|
51
|
-
end
|
52
|
-
|
53
|
-
# Closes all resources that are held in the client.
|
54
|
-
#
|
55
|
-
def close
|
56
|
-
incoming.close
|
57
|
-
outgoing.close
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
# Creates a message to send to the service.
|
63
|
-
#
|
64
|
-
def envelope(id, message, incoming_channel, needs_answer)
|
65
|
-
[id, message, incoming_channel, needs_answer]
|
66
|
-
end
|
67
|
-
|
68
|
-
# Returns a sequence of request ids.
|
69
|
-
#
|
70
|
-
def next_request_id
|
71
|
-
@request_id += 1
|
72
|
-
end
|
73
|
-
|
74
|
-
# True if the received request id answers a request that has been
|
75
|
-
# earlier than the expected request id.
|
76
|
-
#
|
77
|
-
def earlier?(expected, received)
|
78
|
-
expected > received
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|