cod 0.4.4 → 0.5.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.
@@ -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, ...