cod 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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