cod 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,18 +1,3 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem 'rake'
4
- gem 'beanstalk-client'
5
-
6
- group :test do
7
- gem 'rspec'
8
- gem 'flexmock'
9
-
10
- gem 'guard'
11
- gem 'guard-rspec'
12
-
13
- gem 'rdoc'
14
- gem 'sdoc'
15
-
16
- gem 'growl'
17
- gem 'rb-fsevent'
18
- end
3
+ gemspec
data/HISTORY.txt CHANGED
@@ -1,3 +1,15 @@
1
+ = 0.4.1 / 23Jan2012
2
+ + Cod.stdio returns a channel that is linked to this process' stdin and
3
+ stdout.
4
+
5
+ + Cod.process spawns a process and allows communicating with that process
6
+ via stdin/stdout.
7
+
8
+ + Cod.bidir_pipe spawns two Cod.pipe's, one for writing to the other end,
9
+ one for reading from it. (see above...)
10
+
11
+ + Small fixes, preparation for connection robustness
12
+
1
13
  == 0.4.0 / 29Nov2011
2
14
 
3
15
  * A complete rewrite, focusing on lean implementation and logical/useful
data/README CHANGED
@@ -1,9 +1,9 @@
1
+ Some people, when confronted with a problem, think "I know, I'll use
2
+ multithreading". Nothhw tpe yawrve o oblems.[1]
1
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
+ Don't use threads, use processes. This is the toolkit for interprocess
5
+ communication that makes it real simple. A good place to start is the
6
+ documentation for the Cod module.
7
7
 
8
8
  SYNOPSIS
9
9
 
@@ -29,4 +29,6 @@ that we had before, but some things have been designed more logically.
29
29
 
30
30
  At version 0.4.0
31
31
 
32
- (c) 2011 Kaspar Schiess
32
+ (c) 2011 Kaspar Schiess
33
+
34
+ [1] https://twitter.com/rogerbraun/status/160813717502705664
data/Rakefile CHANGED
@@ -19,7 +19,7 @@ require 'sdoc'
19
19
 
20
20
  # Generate documentation
21
21
  RDoc::Task.new do |rdoc|
22
- rdoc.title = "parslet - construction of parsers made easy"
22
+ rdoc.title = "cod - IPC made really simple."
23
23
  rdoc.options << '--line-numbers'
24
24
  rdoc.options << '--fmt' << 'shtml' # explictly set shtml generator
25
25
  rdoc.template = 'direct' # lighter template used on railsapi.com
@@ -0,0 +1,2 @@
1
+ Shows that you can use netcat as a proxy between Cod.process and
2
+ Cod.tcp_server...
@@ -0,0 +1,25 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__) + "/../../lib")
2
+ require 'cod'
3
+
4
+ fork do
5
+ server = Cod.tcp_server('localhost:12345')
6
+
7
+ request, channel = server.get_ext
8
+ case request
9
+ when :get_time
10
+ channel.put Time.now
11
+ else
12
+ fail
13
+ end
14
+ end
15
+
16
+ fork do
17
+ sleep 1
18
+ process = Cod.process('nc localhost 12345')
19
+ channel = process.channel
20
+
21
+ channel.put :get_time
22
+ puts channel.get
23
+ end
24
+
25
+ Process.waitall
@@ -0,0 +1,3 @@
1
+ A demonstration of what it takes to use googles protocol buffers (protobuf) on
2
+ the wire. You will need the ruby-protocol-buffers gem for this to work.
3
+
@@ -0,0 +1,27 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__) + "/../../lib")
2
+ require 'cod'
3
+ require 'cod/protocol_buffers_serializer'
4
+
5
+ require 'protocol_buffers'
6
+ require 'protocol_buffers/compiler'
7
+
8
+
9
+ ProtocolBuffers::Compiler.compile_and_load_string <<-EOS
10
+ message Foo {
11
+ required string bar = 1;
12
+ };
13
+ EOS
14
+
15
+ pipe = Cod.pipe(Cod::ProtocolBuffersSerializer.new)
16
+
17
+ child_pid = fork do
18
+ pipe.put Foo.new(:bar => 'bar')
19
+ pipe.put Foo.new(:bar => 'baz')
20
+ end
21
+
22
+ begin
23
+ p pipe.get
24
+ p pipe.get
25
+ ensure
26
+ Process.wait(child_pid)
27
+ end
data/lib/cod.rb CHANGED
@@ -26,11 +26,20 @@ module Cod
26
26
  # Creates a pipe connection that is visible to this process and its
27
27
  # children. (see Cod::Pipe)
28
28
  #
29
- def pipe
30
- Cod::Pipe.new
29
+ def pipe(serializer=nil, pipe_pair=nil)
30
+ Cod::Pipe.new(serializer)
31
31
  end
32
32
  module_function :pipe
33
33
 
34
+ # Creates two channels based on Cod.pipe (unidirectional IO.pipe) and links
35
+ # things up so that you communication is bidirectional. Writes go to
36
+ # #out and reads come from #in.
37
+ #
38
+ def bidir_pipe(serializer=nil, pipe_pair=nil)
39
+ Cod::BidirPipe.new(serializer, pipe_pair)
40
+ end
41
+ module_function :bidir_pipe
42
+
34
43
  # Creates a tcp connection to the destination and returns a channel for it.
35
44
  # (see Cod::TcpClient)
36
45
  #
@@ -57,6 +66,25 @@ module Cod
57
66
  end
58
67
  module_function :beanstalk
59
68
 
69
+ # Runs a command via Process.spawn, then links a channel to the commands
70
+ # stdout and stdin. Returns the commands pid and the channel.
71
+ #
72
+ # Example:
73
+ # pid, channel = Cod.process('cat')
74
+ def process(command, serializer=nil)
75
+ Cod::Process.new(command, serializer)
76
+ end
77
+ module_function :process
78
+
79
+ # Links a process' stdin and stdout up with a pipe. This means that the
80
+ # pipes #put method will print to stdout, and the #get method will read from
81
+ # stdin.
82
+ #
83
+ def stdio(serializer=nil)
84
+ Cod::Pipe.new(serializer, [$stdin, $stdout])
85
+ end
86
+ module_function :stdio
87
+
60
88
  # Indicates that the given channel is write only. This gets raised on
61
89
  # operations like #put.
62
90
  #
@@ -79,11 +107,16 @@ end
79
107
  require 'cod/select_group'
80
108
  require 'cod/select'
81
109
 
110
+ require 'cod/iopair'
111
+
82
112
  require 'cod/channel'
83
113
 
84
114
  require 'cod/simple_serializer'
85
115
 
86
116
  require 'cod/pipe'
117
+ require 'cod/bidir_pipe'
118
+
119
+ require 'cod/process'
87
120
 
88
121
  require 'cod/tcp_client'
89
122
  require 'cod/tcp_server'
@@ -11,13 +11,27 @@ module Cod::Beanstalk
11
11
  class Channel < Cod::Channel
12
12
  JOB_PRIORITY = 0
13
13
 
14
+ # Which tube this channel is connected to
15
+ attr_reader :tube_name
16
+ # Beanstalkd server this channel is connected to
17
+ attr_reader :server_url
18
+
14
19
  def initialize(tube_name, server_url)
20
+ super()
15
21
  @tube_name, @server_url = tube_name, server_url
16
22
 
17
23
  @body_serializer = Cod::SimpleSerializer.new
18
24
  @transport = connection(server_url, tube_name)
19
25
  end
20
26
 
27
+ # Allow #dup on beanstalkd channels, resulting in a _new_ connection to
28
+ # the beanstalkd server.
29
+ #
30
+ def initialize_copy(other)
31
+ super(other)
32
+ initialize(other.tube_name, other.server_url)
33
+ end
34
+
21
35
  def put(msg)
22
36
  pri = JOB_PRIORITY
23
37
  delay = 0
@@ -0,0 +1,32 @@
1
+ module Cod
2
+ class BidirPipe < Channel
3
+ # The Cod::Pipe instance we're currently #put'ting to.
4
+ attr_reader :w
5
+ # The Cod::Pipe instance we're currently #get'ting from.
6
+ attr_reader :r
7
+
8
+ def initialize(serializer=nil, pipe_pair=nil)
9
+ @serializer = serializer || SimpleSerializer.new
10
+ @r, @w = pipe_pair || [Cod.pipe(@serializer), Cod.pipe(@serializer)]
11
+ end
12
+
13
+ def put(msg)
14
+ w.put(msg)
15
+ end
16
+
17
+ def get
18
+ r.get
19
+ end
20
+
21
+ def close
22
+ r.close
23
+ w.close
24
+ end
25
+
26
+ # Swaps the end of this pipe around.
27
+ #
28
+ def swap!
29
+ @r, @w = w, r
30
+ end
31
+ end
32
+ end
data/lib/cod/iopair.rb ADDED
@@ -0,0 +1,40 @@
1
+ module Cod
2
+ IOPair = Struct.new(:r, :w) do
3
+ def initialize(r=nil, w=nil)
4
+ if r && w
5
+ super(r, w)
6
+ else
7
+ super(*IO.pipe)
8
+ end
9
+ end
10
+
11
+ # Performs a deep copy of the structure.
12
+ def initialize_copy(other)
13
+ super
14
+ self.r = other.r.dup if other.r
15
+ self.w = other.w.dup if other.w
16
+ end
17
+ def write(buf)
18
+ close_r
19
+ raise Cod::ReadOnlyChannel unless w
20
+ w.write(buf)
21
+ end
22
+ def read(serializer)
23
+ close_w
24
+ raise Cod::WriteOnlyChannel unless r
25
+ serializer.de(r)
26
+ end
27
+ def close
28
+ close_r
29
+ close_w
30
+ end
31
+ def close_r
32
+ r.close if r
33
+ self.r = nil
34
+ end
35
+ def close_w
36
+ w.close if w
37
+ self.w = nil
38
+ end
39
+ end
40
+ end
data/lib/cod/pipe.rb CHANGED
@@ -12,36 +12,10 @@ module Cod
12
12
  # create the channel before forking the child, since master and child will
13
13
  # share all objects that were available before the fork.
14
14
  #
15
- class Pipe
15
+ class Pipe < Channel
16
16
  attr_reader :pipe
17
17
  attr_reader :serializer
18
18
 
19
- IOPair = Struct.new(:r, :w) do
20
- # Performs a deep copy of the structure.
21
- def initialize_copy(other)
22
- super
23
- self.r = other.r.dup if other.r
24
- self.w = other.w.dup if other.w
25
- end
26
- def write(buf)
27
- close_r
28
- raise Cod::ReadOnlyChannel unless w
29
- w.write(buf)
30
- end
31
- def close
32
- close_r
33
- close_w
34
- end
35
- def close_r
36
- r.close if r
37
- self.r = nil
38
- end
39
- def close_w
40
- w.close if w
41
- self.w = nil
42
- end
43
- end
44
-
45
19
  # A few methods that a pipe split must answer to. The split itself is
46
20
  # basically an array instance; these methods add some calling safety and
47
21
  # convenience.
@@ -51,10 +25,10 @@ module Cod
51
25
  def write; last; end
52
26
  end
53
27
 
54
- def initialize(serializer=nil)
55
- super
28
+ def initialize(serializer=nil, pipe_pair=nil)
29
+ super()
56
30
  @serializer = serializer || SimpleSerializer.new
57
- @pipe = IOPair.new(*IO.pipe)
31
+ @pipe = IOPair.new(*pipe_pair)
58
32
  end
59
33
 
60
34
  # Creates a copy of this pipe channel. This performs a shallow #dup except
@@ -148,13 +122,13 @@ module Cod
148
122
  not result.nil?
149
123
  end
150
124
  def to_read_fds
151
- pipe.r
125
+ r
152
126
  end
153
127
 
154
128
  # Returns true if you can read from this pipe.
155
129
  #
156
130
  def can_read?
157
- not pipe.r.nil?
131
+ not r.nil?
158
132
  end
159
133
 
160
134
  # Returns true if you can write to this pipe.
@@ -163,6 +137,20 @@ module Cod
163
137
  not pipe.w.nil?
164
138
  end
165
139
 
140
+ # ------------------------------------------------------- internal helpers
141
+
142
+ # Returns the read end of the pipe
143
+ #
144
+ def r
145
+ pipe.r
146
+ end
147
+
148
+ # Returns the write end of the pipe
149
+ #
150
+ def w
151
+ pipe.w
152
+ end
153
+
166
154
  # --------------------------------------------------------- service/client
167
155
 
168
156
  def service
@@ -182,7 +170,7 @@ module Cod
182
170
  private
183
171
  def deserialize_one
184
172
  # Now deserialize one message from the buffer in io
185
- serializer.de(pipe.r)
173
+ pipe.read(serializer)
186
174
  end
187
175
  end
188
176
  end
@@ -0,0 +1,34 @@
1
+ module Cod
2
+ class Process
3
+ attr_reader :pid
4
+
5
+ def initialize(command, serializer=nil)
6
+ @serializer = serializer || SimpleSerializer.new
7
+
8
+ run(command)
9
+ end
10
+
11
+ def run(command)
12
+ @pipe = Cod.bidir_pipe(@serializer)
13
+
14
+ @pid = ::Process.spawn(command,
15
+ :in => @pipe.w.r,
16
+ :out => @pipe.r.w)
17
+ end
18
+
19
+ def channel
20
+ @pipe
21
+ end
22
+
23
+ def kill
24
+ ::Process.kill :TERM, @pid
25
+ end
26
+ def terminate
27
+ @pipe.w.close
28
+ end
29
+
30
+ def wait
31
+ ::Process.wait(@pid)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,59 @@
1
+ # Install the ruby-protocol-buffers gem for this to work.
2
+ require 'protocol_buffers'
3
+
4
+ module Cod
5
+ # Serializes a protocol buffer (googles protobufs) message to the wire and
6
+ # reads it back. Protobufs are not self-delimited, this is why we store the
7
+ # messages in the following on-wire format:
8
+ #
9
+ # LEN(class) BYTES(class) LEN(message) BYTES(message)
10
+ #
11
+ # where LEN is a varint representation of the length of the contained item
12
+ # and BYTES are the binary bytes of said item.
13
+ #
14
+ # This is not the most space efficient manner of representing things on the
15
+ # wire. It also assumes that you have defined the message classes on both
16
+ # sides (client and server).
17
+ #
18
+ # For applications where this is a problem, you can always use this
19
+ # implementation as a guide for your own implementation. For example,
20
+ # message polymorphism could be coded as a single byte on the wire, allowing
21
+ # for one of 255 messages to be sent each time. For really efficient
22
+ # transfers, you could even send a fixed amount of bytes and one message,
23
+ # getting the most out of protobufs.
24
+ #
25
+ # Please see examples/protocol-buffers/master_child.rb for information
26
+ # on how to use this.
27
+ #
28
+ class ProtocolBuffersSerializer
29
+ Varint = ProtocolBuffers::Varint
30
+
31
+ def en(obj)
32
+ sio = ProtocolBuffers.bin_sio
33
+
34
+ # Assuming that obj is a protocol buffers message object, this should
35
+ # work:
36
+ klass_name = obj.class.name
37
+ buffer = obj.to_s
38
+
39
+ Varint.encode(sio, klass_name.size)
40
+ sio.write(klass_name)
41
+
42
+ Varint.encode(sio, buffer.size)
43
+ sio.write(buffer)
44
+
45
+ sio.string
46
+ end
47
+
48
+ def de(io)
49
+ klass_size = Varint.decode(io)
50
+ klass_name = io.read(klass_size)
51
+
52
+ klass = self.class.const_get(klass_name)
53
+
54
+ msg_size = Varint.decode(io)
55
+ limited_io = LimitedIO.new(io, msg_size)
56
+ klass.parse(limited_io)
57
+ end
58
+ end
59
+ end
@@ -1,4 +1,33 @@
1
- module Cod
1
+ require 'socket'
2
+
3
+ module Cod
4
+ # A tcp server channel. Messages are read from any of the connected sockets
5
+ # in a round robin fashion.
6
+ #
7
+ # Synopsis:
8
+ # server = Cod.tcp_server('localhost:12345')
9
+ # server.get # 'a message'
10
+ # msg, chan = server.get_ext
11
+ #
12
+ # There is no implementation of #put that would broadcast back to all
13
+ # connected sockets, this is up to you to implement. Instead, you can use
14
+ # one of two ways to obtain a channel for talking back to a specific client:
15
+ #
16
+ # Using #get_ext:
17
+ # msg, chan = server.get_ext
18
+ #
19
+ # chan is a two way connected channel to the specific client that has opened
20
+ # its communication with msg.
21
+ #
22
+ # Using plain #get:
23
+ # # on the client:
24
+ # client.put [client, :msg]
25
+ # # on the server
26
+ # chan, msg = server.get
27
+ #
28
+ # This means that you can transmit the client channel through the connection
29
+ # as part of the message you send.
30
+ #
2
31
  class TcpServer
3
32
  def initialize(bind_to)
4
33
  @socket = TCPServer.new(*bind_to.split(':'))
data/lib/tcp_proxy.rb ADDED
@@ -0,0 +1,132 @@
1
+ class TCPProxy
2
+ attr_reader :connections
3
+
4
+ def initialize(host, from_port, to_port)
5
+ @ins = TCPServer.new(host, from_port)
6
+
7
+ @host = host
8
+ @from_port, @to_port = from_port, to_port
9
+
10
+ # Active connections and mutex to protect access to it.
11
+ @connections = []
12
+ @connections_m = Mutex.new
13
+
14
+ # Are we currently accepting new connections?
15
+ @accept_new = true
16
+
17
+ @thread = Thread.start(&method(:thread_main))
18
+ end
19
+
20
+ def close
21
+ @shutdown = true
22
+
23
+ @thread.join
24
+ @thread = nil
25
+
26
+ # Since the thread is stopped now, we can be sure no new connections are
27
+ # accepted. This is why we access the collection without locking.
28
+ @connections.each do |connection|
29
+ connection.close
30
+ end
31
+ @ins.close
32
+ end
33
+
34
+ def block
35
+ @accept_new = false
36
+ end
37
+ def allow
38
+ @accept_new = true
39
+ end
40
+
41
+ def drop_all
42
+ # Copy the connections and then empty the collection
43
+ connections = @connections_m.synchronize {
44
+ @connections.tap {
45
+ @connections = [] } }
46
+
47
+ connections.each do |conn|
48
+ conn.close
49
+ end
50
+ end
51
+
52
+ # Inside the background thread ----------------------------------------
53
+
54
+ def thread_main
55
+ loop do
56
+ accept_connections if @accept_new
57
+
58
+ forward_data
59
+
60
+ break if @shutdown
61
+ end
62
+ rescue Exception => ex
63
+ p [:uncaught, ex]
64
+ ex.backtrace.each do |line|
65
+ puts line
66
+ end
67
+ raise
68
+ end
69
+
70
+ class Connection
71
+ def initialize(in_sock, out_sock)
72
+ @m = Mutex.new
73
+ @in_sock, @out_sock = in_sock, out_sock
74
+ end
75
+
76
+ def close
77
+ @m.synchronize {
78
+ @in_sock.close; @in_sock = nil
79
+ @out_sock.close; @out_sock = nil }
80
+ end
81
+
82
+ def pump_synchronized(n=10)
83
+ @m.synchronize {
84
+ return unless @in_sock && @out_sock
85
+ pump(n) }
86
+ end
87
+
88
+ def pump(n)
89
+ while n>0
90
+ available_sockets = [@in_sock, @out_sock]
91
+ ready_sockets, (*) = IO.select(available_sockets, nil, nil, 0)
92
+
93
+ break unless ready_sockets && !ready_sockets.empty?
94
+
95
+ ready_sockets.each do |socket|
96
+ buf = socket.read_nonblock(16*1024)
97
+
98
+ if socket == @in_sock
99
+ puts "--> #{buf.size}"
100
+ @out_sock.write(buf)
101
+ else
102
+ puts "<-- #{buf.size}"
103
+ @in_sock.write(buf)
104
+ end
105
+ end
106
+
107
+ n -= 1
108
+ end
109
+ rescue Errno::EAGAIN
110
+ # Read would block, attempt later
111
+ end
112
+ end
113
+
114
+ def accept_connections
115
+ loop do
116
+ in_sock = @ins.accept_nonblock
117
+ out_sock = TCPSocket.new(@host, @to_port)
118
+
119
+ @connections_m.synchronize {
120
+ @connections << Connection.new(in_sock, out_sock) }
121
+ end
122
+ rescue Errno::EAGAIN
123
+ # No more connections pending, stop accepting new connections
124
+ end
125
+
126
+ def forward_data
127
+ connections = @connections_m.synchronize { @connections.dup }
128
+ connections.each do |conn|
129
+ conn.pump_synchronized
130
+ end
131
+ end
132
+ end
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.4.0
4
+ version: 0.4.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-29 00:00:00.000000000Z
12
+ date: 2012-01-23 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &70299431388260 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70299431388260
14
25
  - !ruby/object:Gem::Dependency
15
26
  name: rspec
16
- requirement: &70297088779060 !ruby/object:Gem::Requirement
27
+ requirement: &70299431387140 !ruby/object:Gem::Requirement
17
28
  none: false
18
29
  requirements:
19
30
  - - ! '>='
@@ -21,10 +32,10 @@ dependencies:
21
32
  version: '0'
22
33
  type: :development
23
34
  prerelease: false
24
- version_requirements: *70297088779060
35
+ version_requirements: *70299431387140
25
36
  - !ruby/object:Gem::Dependency
26
37
  name: flexmock
27
- requirement: &70297088777820 !ruby/object:Gem::Requirement
38
+ requirement: &70299431386460 !ruby/object:Gem::Requirement
28
39
  none: false
29
40
  requirements:
30
41
  - - ! '>='
@@ -32,10 +43,21 @@ dependencies:
32
43
  version: '0'
33
44
  type: :development
34
45
  prerelease: false
35
- version_requirements: *70297088777820
46
+ version_requirements: *70299431386460
36
47
  - !ruby/object:Gem::Dependency
37
48
  name: sdoc
38
- requirement: &70297088776720 !ruby/object:Gem::Requirement
49
+ requirement: &70299431385340 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70299431385340
58
+ - !ruby/object:Gem::Dependency
59
+ name: guard
60
+ requirement: &70299431384600 !ruby/object:Gem::Requirement
39
61
  none: false
40
62
  requirements:
41
63
  - - ! '>='
@@ -43,7 +65,18 @@ dependencies:
43
65
  version: '0'
44
66
  type: :development
45
67
  prerelease: false
46
- version_requirements: *70297088776720
68
+ version_requirements: *70299431384600
69
+ - !ruby/object:Gem::Dependency
70
+ name: growl
71
+ requirement: &70299431383600 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70299431383600
47
80
  description:
48
81
  email: kaspar.schiess@absurd.li
49
82
  executables: []
@@ -60,8 +93,12 @@ files:
60
93
  - lib/cod/beanstalk/serializer.rb
61
94
  - lib/cod/beanstalk/service.rb
62
95
  - lib/cod/beanstalk.rb
96
+ - lib/cod/bidir_pipe.rb
63
97
  - lib/cod/channel.rb
98
+ - lib/cod/iopair.rb
64
99
  - lib/cod/pipe.rb
100
+ - lib/cod/process.rb
101
+ - lib/cod/protocol_buffers_serializer.rb
65
102
  - lib/cod/select.rb
66
103
  - lib/cod/select_group.rb
67
104
  - lib/cod/service.rb
@@ -70,12 +107,17 @@ files:
70
107
  - lib/cod/tcp_server.rb
71
108
  - lib/cod/work_queue.rb
72
109
  - lib/cod.rb
110
+ - lib/tcp_proxy.rb
73
111
  - examples/example_scaffold.rb
74
112
  - examples/master_child.rb
113
+ - examples/netcat/README
114
+ - examples/netcat/server.rb
75
115
  - examples/ping_pong/ping.rb
76
116
  - examples/ping_pong/pong.rb
77
117
  - examples/presence/client.rb
78
118
  - examples/presence/server.rb
119
+ - examples/protocol-buffers/master_child.rb
120
+ - examples/protocol-buffers/README
79
121
  - examples/queue/client.rb
80
122
  - examples/queue/queue.rb
81
123
  - examples/queue/README
@@ -98,13 +140,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
98
140
  version: '0'
99
141
  segments:
100
142
  - 0
101
- hash: -3574736004851783821
143
+ hash: 1455579630017282096
102
144
  required_rubygems_version: !ruby/object:Gem::Requirement
103
145
  none: false
104
146
  requirements:
105
147
  - - ! '>='
106
148
  - !ruby/object:Gem::Version
107
149
  version: '0'
150
+ segments:
151
+ - 0
152
+ hash: 1455579630017282096
108
153
  requirements: []
109
154
  rubyforge_project:
110
155
  rubygems_version: 1.8.10