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.
Files changed (50) hide show
  1. data/Gemfile +1 -1
  2. data/HISTORY.txt +5 -1
  3. data/README +12 -13
  4. data/Rakefile +7 -1
  5. data/examples/{ping.rb → ping_pong/ping.rb} +1 -1
  6. data/examples/{pong.rb → ping_pong/pong.rb} +1 -1
  7. data/examples/{presence_client.rb → presence/client.rb} +0 -0
  8. data/examples/{presence_server.rb → presence/server.rb} +0 -0
  9. data/examples/queue/README +9 -0
  10. data/examples/queue/client.rb +31 -0
  11. data/examples/queue/queue.rb +51 -0
  12. data/examples/queue/send.rb +9 -0
  13. data/examples/service.rb +2 -2
  14. data/examples/tcp.rb +1 -1
  15. data/lib/cod.rb +47 -82
  16. data/lib/cod/beanstalk.rb +7 -0
  17. data/lib/cod/beanstalk/channel.rb +170 -0
  18. data/lib/cod/beanstalk/serializer.rb +80 -0
  19. data/lib/cod/beanstalk/service.rb +53 -0
  20. data/lib/cod/channel.rb +54 -12
  21. data/lib/cod/pipe.rb +188 -0
  22. data/lib/cod/select.rb +47 -0
  23. data/lib/cod/select_group.rb +87 -0
  24. data/lib/cod/service.rb +55 -42
  25. data/lib/cod/simple_serializer.rb +19 -0
  26. data/lib/cod/tcp_client.rb +202 -0
  27. data/lib/cod/tcp_server.rb +124 -0
  28. data/lib/cod/work_queue.rb +129 -0
  29. metadata +31 -45
  30. data/examples/pubsub/README +0 -12
  31. data/examples/pubsub/client.rb +0 -13
  32. data/examples/pubsub/directory.rb +0 -13
  33. data/examples/service_directory.rb +0 -32
  34. data/lib/cod/channel/base.rb +0 -185
  35. data/lib/cod/channel/beanstalk.rb +0 -69
  36. data/lib/cod/channel/pipe.rb +0 -137
  37. data/lib/cod/channel/tcp.rb +0 -16
  38. data/lib/cod/channel/tcpconnection.rb +0 -67
  39. data/lib/cod/channel/tcpserver.rb +0 -84
  40. data/lib/cod/client.rb +0 -81
  41. data/lib/cod/connection/beanstalk.rb +0 -77
  42. data/lib/cod/directory.rb +0 -98
  43. data/lib/cod/directory/countdown.rb +0 -31
  44. data/lib/cod/directory/subscription.rb +0 -59
  45. data/lib/cod/object_io.rb +0 -6
  46. data/lib/cod/objectio/connection.rb +0 -106
  47. data/lib/cod/objectio/reader.rb +0 -98
  48. data/lib/cod/objectio/serializer.rb +0 -26
  49. data/lib/cod/objectio/writer.rb +0 -27
  50. 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
@@ -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
@@ -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