cod 0.4.0 → 0.4.1

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 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