cod 0.4.4 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ = 0.5.0 / 12Jun2012
2
+
3
+ + Process#wait will not throw Errno::ECHILD anymore.
4
+
1
5
  = 0.4.4 / 7Mar2012
2
6
 
3
7
  + Uses IO.select in the server as well, not polling for accept. This should
data/README CHANGED
@@ -34,7 +34,7 @@ Working transports include:
34
34
  * tcp (server and client)
35
35
  * beanstalk (connects to beanstalkd)
36
36
 
37
- At version 0.4.4
37
+ At version 0.5.0
38
38
 
39
39
  (c) 2011 Kaspar Schiess
40
40
 
@@ -0,0 +1,15 @@
1
+ Demonstrates a server that uses more than one worker process to handle a
2
+ single tcp port.
3
+
4
+ This example forks one client that repeatedly makes connections to a tcp
5
+ server and then reads the servers pid from that connection. It counts what
6
+ pid has answered how many times and prints that result in the end.
7
+
8
+ The server accepts connections, writes its pid to it and then closes the
9
+ connections. It will wait at most TIMEOUT seconds for a new connection to
10
+ be made, otherwise it will terminate. This is to ensure that the example exits
11
+ cleanly and leaves no processes laying around.
12
+
13
+ You can try (for kicks) to double/triple/n-uple the number of clients that
14
+ does this counting. Server should keep up its even load distribution up to
15
+ a high number of clients.
@@ -0,0 +1,46 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__) + "/../../lib")
2
+ $:.unshift File.expand_path(File.dirname(__FILE__)) + "/../"
3
+ require 'cod'
4
+ require 'example_scaffold'
5
+ require 'pp'
6
+ require 'timeout'
7
+
8
+ def timeout_no_exception(seconds)
9
+ timeout(seconds, &Proc.new)
10
+ false
11
+ rescue Timeout::Error
12
+ true
13
+ end
14
+
15
+ client {
16
+ answer_map = Hash.new(0)
17
+
18
+ 1000.times do
19
+ chan = Cod.tcp('localhost:32423')
20
+ chan.put :hei
21
+
22
+ pid = chan.get
23
+ answer_map[pid] += 1
24
+
25
+ chan.close
26
+ end
27
+
28
+ pp answer_map
29
+ }
30
+
31
+ server {
32
+ server = Cod.tcp_server('localhost:32423')
33
+ 8.times do
34
+ fork {
35
+ loop do
36
+ break if timeout_no_exception(1) do
37
+ m, chan = server.get_ext
38
+ chan.put Process.pid
39
+ chan.close
40
+ end
41
+ end
42
+ }
43
+ end
44
+ }
45
+
46
+ run
data/lib/cod.rb CHANGED
@@ -3,42 +3,64 @@ require 'stringio'
3
3
  # The core concept of Cod are 'channels'. (see {Cod::Channel::Base}) You can
4
4
  # create such channels on top of the various transport layers. Once you have
5
5
  # such a channel, you #put messages into it and you #get messages out of it.
6
- # Messages are retrieved in FIFO manner, making channels look like a
7
- # communication pipe most of the time.
6
+ # Messages are retrieved in FIFO manner.
7
+ #
8
+ # channel.put :test1
9
+ # channel.put :test2
10
+ # channel.get # => :test1
8
11
  #
9
12
  # Cod also brings a few abstractions layered on top of channels: You can use
10
13
  # channels to present 'services' (Cod::Service) to the network: A service is a
11
14
  # simple one or two way RPC call. (one way = asynchronous)
12
15
  #
16
+ # client = channel.client
17
+ # client.notify [:foo, :bar]
18
+ #
13
19
  # Cod channels are serializable whereever possible. If you want to tell
14
20
  # somebody where to write his answers and/or questions to, send him the
15
21
  # channel! This is really powerful and used extensively in constructing the
16
22
  # higher order primitives.
17
23
  #
24
+ # server.put [:some_request, my_channel]
25
+ # # Server will receive my_channel and be able to contact us there.
26
+ #
18
27
  # All Cod channels have a serializer. If you don't specify your own
19
28
  # serializer, they will use Marshal.dump and Marshal.load. (see
20
29
  # {Cod::SimpleSerializer}) This allows to send Ruby objects and not just
21
30
  # strings by default. If you want to, you can of course go back to very strict
22
- # wire formats, see {Cod::ProtocolBuffersSerializer} for an example of that.
31
+ # wire formats, see {Cod::ProtocolBuffersSerializer} or {Cod::LineSerializer}
32
+ # for an example of that.
33
+ #
34
+ # line_protocol_channel = Cod.pipe(Cod::LineSerializer.new)
35
+ # line_protocol_channel.put 'some_string'
23
36
  #
24
37
  # The goal of Cod is that you have to know only very few things about the
25
38
  # network (the various transports) to be able to construct complex things. It
26
- # handles reconnection and reliability for you. It also translates cryptic OS
27
- # errors into plain text messages where it can't just handle them. This should
28
- # give you a clear place to look at if things go wrong. Note that this can
29
- # only be ever as good as the sum of situations Cod has been tested in.
30
- # Contribute your observations and we'll come up with a way of dealing with
31
- # most of the tricky stuff!
39
+ # also translates cryptic OS errors into plain text messages where it can't
40
+ # just handle them. This should give you a clear place to look at if things go
41
+ # wrong. Note that this can only be ever as good as the sum of situations Cod
42
+ # has been tested in. Contribute your observations and we'll come up with a
43
+ # way of dealing with most of the tricky stuff!
32
44
  #
33
45
  # @see Cod::Channel
46
+ #
47
+ # == Types of channels in this version
48
+ #
49
+ # {Cod.pipe} :: Transports via +IO.pipe+
50
+ # {Cod.tcp} :: Transports via TCP (client)
51
+ # {Cod.tcp_server} :: Transports via TCP (as a server)
52
+ # {Cod.stdio} :: Connects to +$stdin+ and +$stdout+ (+IO.pipe+)
53
+ # {Cod.process} :: Spawn a child process and connects to that process' +$stdin+ and +$stdout+ (+IO.pipe+)
54
+ # {Cod.beanstalk} :: Transports via a tube on beanstalkd
34
55
  #
35
56
  module Cod
36
57
  # Creates a pipe connection that is visible to this process and its
37
58
  # children.
38
59
  #
39
- # @see Cod::Pipe
60
+ # @param serializer [#en,#de] optional serializer to use
61
+ # @return [Cod::Pipe]
40
62
  #
41
- def pipe(serializer=nil, pipe_pair=nil)
63
+ def pipe(serializer=nil)
42
64
  Cod::Pipe.new(serializer)
43
65
  end
44
66
  module_function :pipe
@@ -47,7 +69,9 @@ module Cod
47
69
  # things up so that you communication is bidirectional. Writes go to
48
70
  # #out and reads come from #in.
49
71
  #
50
- # @see Cod::BidirPipe
72
+ # @overload bidir_pipe(serializer=nil)
73
+ # @param serializer [#en,#de] optional serializer to use
74
+ # @return [Cod::BidirPipe]
51
75
  #
52
76
  def bidir_pipe(serializer=nil, pipe_pair=nil)
53
77
  Cod::BidirPipe.new(serializer, pipe_pair)
@@ -56,7 +80,9 @@ module Cod
56
80
 
57
81
  # Creates a tcp connection to the destination and returns a channel for it.
58
82
  #
59
- # @see Cod::TcpClient
83
+ # @param destination [String] an address to connect to, like 'localhost:1234'
84
+ # @param serializer [#en,#de] optional serializer to use
85
+ # @return [Cod::TcpClient]
60
86
  #
61
87
  def tcp(destination, serializer=nil)
62
88
  Cod::TcpClient.new(
@@ -67,7 +93,9 @@ module Cod
67
93
 
68
94
  # Creates a tcp listener on bind_to and returns a channel for it.
69
95
  #
70
- # @see Cod::TcpServer
96
+ # @param bind_to [String] an address and port to bind to, in the form "host:port"
97
+ # @param serializer [#en,#de] optional serializer to use
98
+ # @return [Cod::TcpServer]
71
99
  #
72
100
  def tcp_server(bind_to, serializer=nil)
73
101
  Cod::TcpServer.new(
@@ -78,7 +106,11 @@ module Cod
78
106
 
79
107
  # Creates a channel based on the beanstalkd messaging queue.
80
108
  #
81
- # @see Cod::Beanstalk::Channel
109
+ # @overload beanstalk(tube_name, server='localhost:11300')
110
+ # @param tube_name [String] name of the tube to send messages to /
111
+ # receive messages from
112
+ # @param server [String] address of the server to connect to
113
+ # @return [Cod::Beanstalk::Channel]
82
114
  #
83
115
  def beanstalk(tube_name, server=nil)
84
116
  Cod::Beanstalk::Channel.new(tube_name, server||'localhost:11300')
@@ -86,12 +118,12 @@ module Cod
86
118
  module_function :beanstalk
87
119
 
88
120
  # Runs a command via Process.spawn, then links a channel to the commands
89
- # stdout and stdin. Returns the commands pid and the channel.
121
+ # stdout and stdin.
90
122
  #
91
- # Example:
92
- # pid, channel = Cod.process('cat')
93
- #
94
- # @see Cod::Process
123
+ # @param command [String] command to execute in a subprocess
124
+ # (using +Process.spawn+)
125
+ # @param serializer [#en,#de] serializer to use for all messages in channel
126
+ # @return [Cod::Process]
95
127
  #
96
128
  def process(command, serializer=nil)
97
129
  Cod::Process.new(command, serializer)
@@ -102,7 +134,8 @@ module Cod
102
134
  # pipes #put method will print to stdout, and the #get method will read from
103
135
  # stdin.
104
136
  #
105
- # @see Cod::Pipe
137
+ # @param serializer [#en,#de] optional serializer to use
138
+ # @return [Cod::Pipe]
106
139
  #
107
140
  def stdio(serializer=nil)
108
141
  Cod::Pipe.new(serializer, [$stdin, $stdout])
@@ -130,8 +163,8 @@ module Cod
130
163
  # Indicates that a standing connection was lost and must be reconnected.
131
164
  #
132
165
  class ConnectionLost < StandardError
133
- def initialize
134
- super "Connection lost."
166
+ def initialize(msg=nil)
167
+ super msg || "Connection lost."
135
168
  end
136
169
  end
137
170
  end
@@ -144,6 +177,7 @@ require 'cod/iopair'
144
177
  require 'cod/channel'
145
178
 
146
179
  require 'cod/simple_serializer'
180
+ require 'cod/line_serializer'
147
181
 
148
182
  require 'cod/pipe'
149
183
  require 'cod/bidir_pipe'
@@ -1,3 +1,4 @@
1
+ # Implements communication via a beanstalkd server. See {Cod::Beanstalk::Channel}.
1
2
  module Cod::Beanstalk
2
3
  end
3
4
 
@@ -17,8 +17,10 @@ module Cod::Beanstalk
17
17
  JOB_PRIORITY = 0
18
18
 
19
19
  # Which tube this channel is connected to
20
+ # @return [String]
20
21
  attr_reader :tube_name
21
22
  # Beanstalkd server this channel is connected to
23
+ # @return [String]
22
24
  attr_reader :server_url
23
25
 
24
26
  def initialize(tube_name, server_url)
@@ -37,6 +39,11 @@ module Cod::Beanstalk
37
39
  initialize(other.tube_name, other.server_url)
38
40
  end
39
41
 
42
+ # Puts a job on the tube after serializing.
43
+ #
44
+ # @param msg [Object] message to send
45
+ # @return [void]
46
+ #
40
47
  def put(msg)
41
48
  pri = JOB_PRIORITY
42
49
  delay = 0
@@ -47,6 +54,10 @@ module Cod::Beanstalk
47
54
  fail "#put fails, #{answer.inspect}" unless answer == :inserted
48
55
  end
49
56
 
57
+ # Reads a job from the tube and decodes it as a message.
58
+ #
59
+ # @return [Object]
60
+ #
50
61
  def get
51
62
  id, msg = bs_reserve
52
63
 
@@ -67,6 +78,8 @@ module Cod::Beanstalk
67
78
  end
68
79
 
69
80
  # --------------------------------------------------------- service/client
81
+
82
+ #
70
83
  def service
71
84
  Service.new(self)
72
85
  end
@@ -0,0 +1,29 @@
1
+ module Cod
2
+
3
+ # A serializer that implements a line by line wire protocol. Only strings
4
+ # can be sent. An instance of this class can be used when constructing
5
+ # any channel, turning it into a line oriented channel speaking a clear text
6
+ # protocol.
7
+ #
8
+ class LineSerializer
9
+ # Turns a message into the wire format.
10
+ #
11
+ # @param msg [#to_s] message to send
12
+ # @return [String] buffer to be written to the wire
13
+ #
14
+ def en(msg)
15
+ msg.to_s + "\n"
16
+ end
17
+
18
+ # Deserializes a message from the wire.
19
+ #
20
+ # @param io [IO] the wire
21
+ # @return [String]
22
+ #
23
+ def de(io)
24
+ msg = io.gets
25
+ return msg.chomp if msg
26
+ raise EOFError
27
+ end
28
+ end
29
+ end
@@ -2,10 +2,10 @@
2
2
  module Cod
3
3
  # A cod channel based on IO.pipe.
4
4
  #
5
- # NOTE: If you embed Cod::Pipe channels into your messages, Cod will insert
6
- # the object id of that channel into the byte stream that is transmitted. On
7
- # receiving such an object id (a machine pointer), Cod will try to
8
- # reconstruct the channel that was at the origin of the id. This can
5
+ # Note that if you embed Cod::Pipe channels into your messages, Cod will
6
+ # insert the object id of that channel into the byte stream that is
7
+ # transmitted. On receiving such an object id (a machine pointer), Cod will
8
+ # try to reconstruct the channel that was at the origin of the id. This can
9
9
  # obviously only work if you have such an object in your address space.
10
10
  # There are multiple ways to construct such a situation. Say you want to
11
11
  # send a pipe channel to one of your (forked) childs: This will work if you
@@ -13,18 +13,26 @@ module Cod
13
13
  # share all objects that were available before the fork.
14
14
  #
15
15
  class Pipe < Channel
16
+ # The underlying IOPair.
17
+ # @private
16
18
  attr_reader :pipe
19
+
20
+ # The serializer for this pipe.
17
21
  attr_reader :serializer
18
22
 
19
23
  # A few methods that a pipe split must answer to. The split itself is
20
24
  # basically an array instance; these methods add some calling safety and
21
25
  # convenience.
22
26
  #
27
+ # @private
28
+ #
23
29
  module SplitMethods # :nodoc:
24
30
  def read; first; end
25
31
  def write; last; end
26
32
  end
27
-
33
+
34
+ # Creates a {Cod::Pipe}.
35
+ #
28
36
  def initialize(serializer=nil, pipe_pair=nil)
29
37
  super()
30
38
  @serializer = serializer || SimpleSerializer.new
@@ -35,6 +43,9 @@ module Cod
35
43
  # for the file descriptors stored in the pipe, so that a #close affects
36
44
  # only one copy.
37
45
  #
46
+ # @example
47
+ # pipe.dup # => anotherpipe
48
+ #
38
49
  def initialize_copy(other)
39
50
  super
40
51
  @serializer = other.serializer
@@ -48,6 +59,8 @@ module Cod
48
59
  # Returns self so that you can write for example:
49
60
  # read_end = pipe.dup.readonly
50
61
  #
62
+ # @private
63
+ #
51
64
  def readonly
52
65
  pipe.close_w
53
66
  self
@@ -58,6 +71,8 @@ module Cod
58
71
  # Returns self so that you can write for example:
59
72
  # write_end = pipe.dup.writeonly
60
73
  #
74
+ # @private
75
+ #
61
76
  def writeonly
62
77
  pipe.close_r
63
78
  self
@@ -65,8 +80,10 @@ module Cod
65
80
 
66
81
  # Actively splits this pipe into two ends, a read end and a write end. The
67
82
  # original pipe is closed, leaving only the two ends to work with. The
68
- # read end can only be read from (#get) and the write end can only be
69
- # written to (#put).
83
+ # read end can only be read from ({#get}) and the write end can only be
84
+ # written to ({#put}).
85
+ #
86
+ # @return [Array<Cod::Channel>]
70
87
  #
71
88
  def split
72
89
  [self.dup.readonly, self.dup.writeonly].tap { |split|
@@ -79,7 +96,10 @@ module Cod
79
96
  # Using #put on a pipe instance will close the other pipe end. Subsequent
80
97
  # #get will raise a Cod::InvalidOperation.
81
98
  #
82
- # Example:
99
+ # @param obj [Object] message to send to the channel
100
+ # @return [void]
101
+ #
102
+ # @example
83
103
  # pipe.put [:a, :message]
84
104
  #
85
105
  def put(obj)
@@ -92,25 +112,25 @@ module Cod
92
112
  # Using #get on a pipe instance will close the other pipe end. Subsequent
93
113
  # #put will receive a Cod::InvalidOperation.
94
114
  #
95
- # Example:
115
+ # @example
96
116
  # pipe.get # => obj
97
117
  #
98
118
  def get(opts={})
99
119
  raise Cod::WriteOnlyChannel unless can_read?
100
120
  pipe.close_w
101
121
 
102
- loop do
103
- ready = Cod.select(nil, self)
104
- return deserialize_one if ready
105
- end
122
+ return deserialize_one
106
123
  rescue EOFError
107
- fail "All pipe ends seem to be closed. Reading from this pipe will not "+
124
+ raise Cod::ConnectionLost,
125
+ "All pipe ends seem to be closed. Reading from this pipe will not "+
108
126
  "return any data."
109
127
  end
110
128
 
111
129
  # Closes the pipe completely. All active ends are closed. Note that you
112
130
  # can call this function on a closed pipe without getting an error raised.
113
131
  #
132
+ # @return [void]
133
+ #
114
134
  def close
115
135
  pipe.close
116
136
  end
@@ -121,6 +141,9 @@ module Cod
121
141
  result = Cod.select(timeout, self)
122
142
  not result.nil?
123
143
  end
144
+
145
+ # @private
146
+ #
124
147
  def to_read_fds
125
148
  r
126
149
  end
@@ -141,29 +164,49 @@ module Cod
141
164
 
142
165
  # Returns the read end of the pipe
143
166
  #
167
+ # @private
168
+ #
144
169
  def r
145
170
  pipe.r
146
171
  end
147
172
 
148
173
  # Returns the write end of the pipe
149
174
  #
175
+ # @private
176
+ #
150
177
  def w
151
178
  pipe.w
152
179
  end
153
180
 
154
181
  # --------------------------------------------------------- service/client
155
182
 
183
+ # Produces a service using this pipe as service channel.
184
+ # @see Cod::Service
185
+ #
186
+ # @return [Cod::Service]
187
+ #
156
188
  def service
157
189
  Service.new(self)
158
190
  end
191
+
192
+ # Produces a service client. Requests are sent to this channel, and answers
193
+ # are routed back to +answers_to+.
194
+ #
195
+ # @param answers_to [Cod::Channel] Where answers should be addressed to.
196
+ # @return [Cod::Service::Client]
197
+ #
159
198
  def client(answers_to)
160
199
  Service::Client.new(self, answers_to)
161
200
  end
162
201
 
163
202
  # ---------------------------------------------------------- serialization
203
+
204
+ # @private
164
205
  def _dump(depth) # :nodoc:
165
206
  object_id.to_s
166
207
  end
208
+
209
+ # @private
167
210
  def self._load(string) # :nodoc:
168
211
  ObjectSpace._id2ref(Integer(string))
169
212
  end
@@ -1,13 +1,33 @@
1
1
  module Cod
2
+
3
+ # A subprocess that is being run in server mode (think git-server). Use
4
+ # {Cod}.process to obtain an instance of this. You can then call {#channel} to
5
+ # obtain a Cod channel to communicate with the $stdio server you've spawned.
6
+ #
7
+ # @example List the files in a directory
8
+ # process = Cod.process('ls', Cod::LineSerializer.new)
9
+ # process.wait
10
+ # loop do
11
+ # # Will list all entries of the current dir in turn, already chomped.
12
+ # msg = process.get rescue nil
13
+ # break unless msg
14
+ # end
15
+ #
2
16
  class Process
17
+ # The pid of the process that was spawned.
18
+ # @return [Number]
3
19
  attr_reader :pid
4
20
 
21
+ # Constructs a process object and runs the command.
22
+ #
23
+ # @see Cod#process
5
24
  def initialize(command, serializer=nil)
6
25
  @serializer = serializer || SimpleSerializer.new
7
26
 
8
27
  run(command)
9
28
  end
10
29
 
30
+ # @private
11
31
  def run(command)
12
32
  @pipe = Cod.bidir_pipe(@serializer)
13
33
 
@@ -16,19 +36,49 @@ module Cod
16
36
  :out => @pipe.r.w)
17
37
  end
18
38
 
39
+ # Returns the cod channel associated with this process. The channel will
40
+ # have the process' standard output bound to its #get (input), and the
41
+ # process' standard input will be bound to #put (output).
42
+ #
43
+ # Note that when the process exits and all communication has been read from
44
+ # the channel, it will probably raise a Cod::ConnectionLost error.
45
+ #
46
+ # @example
47
+ # process = Cod.process('uname', LineSerializer.new)
48
+ # process.channel.get # => {Darwin,Linux,...}
49
+ #
50
+ # @return [Cod::Pipe]
51
+ #
19
52
  def channel
20
53
  @pipe
21
54
  end
22
55
 
56
+ # Stops the process unilaterally.
57
+ #
58
+ # @return [void]
59
+ #
23
60
  def kill
61
+ terminate
24
62
  ::Process.kill :TERM, @pid
25
63
  end
64
+
65
+ # Asks the process to terminate by closing its stanard input. This normally
66
+ # closes down the process, but no guarantees are made.
67
+ #
68
+ # @return [void]
69
+ #
26
70
  def terminate
27
71
  @pipe.w.close
28
72
  end
29
73
 
74
+ # Waits for the process to terminate and returns its exit value. May
75
+ # return nil, in which case someone else already reaped the process.
76
+ #
77
+ # @return [Number,nil]
78
+ #
30
79
  def wait
31
80
  ::Process.wait(@pid)
81
+ rescue Errno::ECHILD
32
82
  end
33
83
  end
34
84
  end
@@ -1,5 +1,13 @@
1
1
  module Cod
2
+ # A shortcurt for constructing a {Select}. See {Select#do} for more
3
+ # information.
4
+ #
5
+ # @param timeout [Number] seconds to block before giving up
6
+ # @param groups channels or io selectors to wait for
7
+ # @return [Hash,Array,Cod::Channel,IO]
8
+ #
2
9
  def select(timeout, groups)
10
+ # TODO create an overload without the timeout
3
11
  Select.new(timeout, groups).do
4
12
  end
5
13
  module_function :select
@@ -24,8 +32,10 @@ module Cod
24
32
  # Performs the IO.select and returns a thinned out version of that initial
25
33
  # groups, containing only FDs and channels that are ready for reading.
26
34
  #
35
+ # @return [Hash,Array,Cod::Channel,IO]
36
+ #
27
37
  def do
28
- fds = groups.values { |e| to_read_fd(e) }
38
+ fds = groups.values { |e| to_read_fd(e) }.compact
29
39
 
30
40
  # Perform select
31
41
  r,w,e = IO.select(fds, nil, nil, timeout)
@@ -131,6 +131,12 @@ module Cod
131
131
  # with a valid client later on. (hopefully)
132
132
  OtherEnd.new(params)
133
133
  end
134
+
135
+ # @private
136
+ #
137
+ def to_read_fds
138
+ @connection.socket if @connection
139
+ end
134
140
  private
135
141
  # Checks to see in which of the three connection phases we're in. If we're
136
142
  # past 1), shuts down the background worker thread.
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.4
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-07 00:00:00.000000000 Z
12
+ date: 2012-06-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &70125556634640 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70125556634640
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: rspec
27
- requirement: &70125556634180 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: '0'
33
38
  type: :development
34
39
  prerelease: false
35
- version_requirements: *70125556634180
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: flexmock
38
- requirement: &70125556633740 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: '0'
44
54
  type: :development
45
55
  prerelease: false
46
- version_requirements: *70125556633740
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: yard
49
- requirement: &70125556633260 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
@@ -54,10 +69,15 @@ dependencies:
54
69
  version: '0'
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *70125556633260
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
58
78
  - !ruby/object:Gem::Dependency
59
79
  name: guard
60
- requirement: &70125556632740 !ruby/object:Gem::Requirement
80
+ requirement: !ruby/object:Gem::Requirement
61
81
  none: false
62
82
  requirements:
63
83
  - - ! '>='
@@ -65,10 +85,15 @@ dependencies:
65
85
  version: '0'
66
86
  type: :development
67
87
  prerelease: false
68
- version_requirements: *70125556632740
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
69
94
  - !ruby/object:Gem::Dependency
70
95
  name: growl
71
- requirement: &70125556632280 !ruby/object:Gem::Requirement
96
+ requirement: !ruby/object:Gem::Requirement
72
97
  none: false
73
98
  requirements:
74
99
  - - ! '>='
@@ -76,7 +101,12 @@ dependencies:
76
101
  version: '0'
77
102
  type: :development
78
103
  prerelease: false
79
- version_requirements: *70125556632280
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
80
110
  description:
81
111
  email: kaspar.schiess@absurd.li
82
112
  executables: []
@@ -95,6 +125,7 @@ files:
95
125
  - lib/cod/bidir_pipe.rb
96
126
  - lib/cod/channel.rb
97
127
  - lib/cod/iopair.rb
128
+ - lib/cod/line_serializer.rb
98
129
  - lib/cod/pipe.rb
99
130
  - lib/cod/process.rb
100
131
  - lib/cod/protocol_buffers_serializer.rb
@@ -110,6 +141,8 @@ files:
110
141
  - examples/bs_ping_pong/ping.rb
111
142
  - examples/bs_ping_pong/pong.rb
112
143
  - examples/example_scaffold.rb
144
+ - examples/forked_server/forked.rb
145
+ - examples/forked_server/README
113
146
  - examples/master_child.rb
114
147
  - examples/netcat/README
115
148
  - examples/netcat/server.rb
@@ -134,7 +167,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
134
167
  version: '0'
135
168
  segments:
136
169
  - 0
137
- hash: -763744089584274604
170
+ hash: 4413858275826022328
138
171
  required_rubygems_version: !ruby/object:Gem::Requirement
139
172
  none: false
140
173
  requirements:
@@ -143,10 +176,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
143
176
  version: '0'
144
177
  segments:
145
178
  - 0
146
- hash: -763744089584274604
179
+ hash: 4413858275826022328
147
180
  requirements: []
148
181
  rubyforge_project:
149
- rubygems_version: 1.8.16
182
+ rubygems_version: 1.8.24
150
183
  signing_key:
151
184
  specification_version: 3
152
185
  summary: Really simple IPC. Pipes, TCP sockets, beanstalkd, ...