cool.io 1.2.1 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8df378d986336f5aa0c79e3c5e31f80a1571ab0d
4
+ data.tar.gz: 2d9c5aa507a285b4ee129c5a8ccfca8c5f5d8586
5
+ SHA512:
6
+ metadata.gz: f6a74e7fa7dd6bd728835225ecb8481c2e44a44dad2887aaba7d041d835bdcb76ace90ea2fe4aab6bd5db322f3d9ea0c1e81e4eb68da4a3d75273fdcb6781d86
7
+ data.tar.gz: deaffe41d393672ffb44c81766460f8e0736a21294e96acabe998fe01ea36dbdd55ef6b1efadf1e675460ffc0be43376052f9a2f4e3344d36e7c8b37730a3bc5
@@ -4,6 +4,8 @@ rvm:
4
4
  - 1.9.3
5
5
  - 2.0.0
6
6
  - 2.1.0
7
+ - 2.1.1
8
+ - ruby-head
7
9
  - rbx
8
10
 
9
11
  matrix:
data/CHANGES.md CHANGED
@@ -1,3 +1,9 @@
1
+ 1.2.2
2
+ -----
3
+
4
+ * Add timeout option to Loop#run and Loop#run_once. Default by nil
5
+ * Support Ruby 2.2.0
6
+
1
7
  1.2.1
2
8
  -----
3
9
 
data/README.md CHANGED
@@ -1,8 +1,7 @@
1
1
  Cool.io
2
2
  =======
3
3
 
4
- ### NOTE: cool.io is in maintenance mode only and is not being actively developed
5
- ### Please check out [Celluloid::IO](http://github.com/celluloid/celluloid-io) instead!
4
+ ### If you are interested in Celluloid based IO framework, please check out [Celluloid::IO](http://github.com/celluloid/celluloid-io)
6
5
 
7
6
  Cool.io is an event library for Ruby, built on the libev event library which
8
7
  provides a cross-platform interface to high performance system calls . This
@@ -15,10 +14,8 @@ applications.
15
14
 
16
15
  You can include Cool.io in your programs with:
17
16
 
18
- require 'rubygems'
19
17
  require 'cool.io'
20
18
 
21
- Questions? Sign up for the mailing list by emailing: [cool.io@librelist.com](mailto:cool.io@librelist.com)
22
19
 
23
20
  Anatomy
24
21
  -------
@@ -97,7 +94,6 @@ Example Program
97
94
 
98
95
  Cool.io provides a Sinatra-like DSL for authoring event-driven programs:
99
96
 
100
- require 'rubygems'
101
97
  require 'cool.io'
102
98
 
103
99
  ADDR = '127.0.0.1'
@@ -32,6 +32,7 @@ struct Coolio_Event
32
32
  struct Coolio_Loop
33
33
  {
34
34
  struct ev_loop *ev_loop;
35
+ struct ev_timer timer; /* for timeouts */
35
36
 
36
37
  int running;
37
38
  int events_received;
@@ -1,7 +1,9 @@
1
1
  #define EV_STANDALONE /* keeps ev from requiring config.h */
2
+
2
3
  #ifdef _WIN32
3
- # define EV_SELECT_IS_WINSOCKET 1 /* configure libev for windows select */
4
- # define FD_SETSIZE 2048 /* wishful thinking, as msvcrt6 [?] seems to only allow 512 fd's and 256 sockets max */
4
+ #define EV_SELECT_IS_WINSOCKET 1 /* configure libev for windows select */
5
+ #define EV_USE_MONOTONIC 0
6
+ #define EV_USE_REALTIME 0
5
7
  #endif
6
8
 
7
9
  #include "../libev/ev.h"
@@ -8,6 +8,10 @@ if have_func('rb_thread_blocking_region')
8
8
  $defs << '-DHAVE_RB_THREAD_BLOCKING_REGION'
9
9
  end
10
10
 
11
+ if have_func('rb_thread_call_without_gvl')
12
+ $defs << '-DHAVE_RB_THEREAD_CALL_WITHOUT_GVL'
13
+ end
14
+
11
15
  if have_func('rb_thread_alone')
12
16
  $defs << '-DHAVE_RB_THREAD_ALONE'
13
17
  end
@@ -11,14 +11,6 @@
11
11
 
12
12
  #include "cool.io.h"
13
13
 
14
- #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
15
- # define Coolio_Loop_may_block_safely() (1)
16
- #elif defined(HAVE_RB_THREAD_ALONE)
17
- # define Coolio_Loop_may_block_safely() (rb_thread_alone())
18
- #else /* just in case Ruby changes: */
19
- # define Coolio_Loop_may_block_safely() (0)
20
- #endif
21
-
22
14
  static VALUE mCoolio = Qnil;
23
15
  static VALUE cCoolio_Loop = Qnil;
24
16
 
@@ -28,10 +20,10 @@ static void Coolio_Loop_free(struct Coolio_Loop *loop);
28
20
 
29
21
  static VALUE Coolio_Loop_initialize(VALUE self);
30
22
  static VALUE Coolio_Loop_ev_loop_new(VALUE self, VALUE flags);
31
- static VALUE Coolio_Loop_run_once(VALUE self);
23
+ static VALUE Coolio_Loop_run_once(int argc, VALUE *argv, VALUE self);
32
24
  static VALUE Coolio_Loop_run_nonblock(VALUE self);
33
25
 
34
- static void Coolio_Loop_ev_loop_oneshot(struct Coolio_Loop *loop_data);
26
+ static void Coolio_Loop_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents);
35
27
  static void Coolio_Loop_dispatch_events(struct Coolio_Loop *loop_data);
36
28
 
37
29
  #define DEFAULT_EVENTBUF_SIZE 32
@@ -54,7 +46,7 @@ void Init_coolio_loop()
54
46
 
55
47
  rb_define_method(cCoolio_Loop, "initialize", Coolio_Loop_initialize, 0);
56
48
  rb_define_private_method(cCoolio_Loop, "ev_loop_new", Coolio_Loop_ev_loop_new, 1);
57
- rb_define_method(cCoolio_Loop, "run_once", Coolio_Loop_run_once, 0);
49
+ rb_define_method(cCoolio_Loop, "run_once", Coolio_Loop_run_once, -1);
58
50
  rb_define_method(cCoolio_Loop, "run_nonblock", Coolio_Loop_run_nonblock, 0);
59
51
  }
60
52
 
@@ -63,6 +55,7 @@ static VALUE Coolio_Loop_allocate(VALUE klass)
63
55
  struct Coolio_Loop *loop = (struct Coolio_Loop *)xmalloc(sizeof(struct Coolio_Loop));
64
56
 
65
57
  loop->ev_loop = 0;
58
+ ev_init(&loop->timer, Coolio_Loop_timeout_callback);
66
59
  loop->running = 0;
67
60
  loop->events_received = 0;
68
61
  loop->eventbuf_size = DEFAULT_EVENTBUF_SIZE;
@@ -174,79 +167,57 @@ void Coolio_Loop_process_event(VALUE watcher, int revents)
174
167
  loop_data->events_received++;
175
168
  }
176
169
 
170
+ /* Called whenever a timeout fires on the event loop */
171
+ static void Coolio_Loop_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
172
+ {
173
+ /* We don't actually need to do anything here, the mere firing of the
174
+ timer is sufficient to interrupt the selector. However, libev still wants a callback */
175
+ }
176
+
177
177
  /**
178
178
  * call-seq:
179
179
  * Coolio::Loop.run_once -> nil
180
180
  *
181
181
  * Run the Coolio::Loop once, blocking until events are received.
182
182
  */
183
- static VALUE Coolio_Loop_run_once(VALUE self)
183
+ static VALUE Coolio_Loop_run_once(int argc, VALUE *argv, VALUE self)
184
184
  {
185
+ VALUE timeout;
185
186
  VALUE nevents;
187
+ struct Coolio_Loop *loop_data;
186
188
 
187
- if (Coolio_Loop_may_block_safely()) {
188
- struct Coolio_Loop *loop_data;
189
-
190
- Data_Get_Struct(self, struct Coolio_Loop, loop_data);
189
+ rb_scan_args(argc, argv, "01", &timeout);
191
190
 
192
- assert(loop_data->ev_loop && !loop_data->events_received);
191
+ if (timeout != Qnil && NUM2DBL(timeout) < 0) {
192
+ rb_raise(rb_eArgError, "time interval must be positive");
193
+ }
193
194
 
194
- Coolio_Loop_ev_loop_oneshot(loop_data);
195
- Coolio_Loop_dispatch_events(loop_data);
195
+ Data_Get_Struct(self, struct Coolio_Loop, loop_data);
196
196
 
197
- nevents = INT2NUM(loop_data->events_received);
198
- loop_data->events_received = 0;
197
+ assert(loop_data->ev_loop && !loop_data->events_received);
199
198
 
199
+ /* Implement the optional timeout (if any) as a ev_timer */
200
+ /* Using the technique written at
201
+ http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev_timer_code_relative_and_opti,
202
+ the timer is not stopped/started everytime when timeout is specified, instead,
203
+ the timer is stopped when timeout is not specified. */
204
+ if (timeout != Qnil) {
205
+ /* It seems libev is not a fan of timers being zero, so fudge a little */
206
+ loop_data->timer.repeat = NUM2DBL(timeout) + 0.0001;
207
+ ev_timer_again(loop_data->ev_loop, &loop_data->timer);
200
208
  } else {
201
- nevents = Coolio_Loop_run_nonblock(self);
202
- rb_thread_schedule();
209
+ ev_timer_stop(loop_data->ev_loop, &loop_data->timer);
203
210
  }
204
211
 
205
- return nevents;
206
- }
207
-
208
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
209
- #define HAVE_EV_LOOP_ONESHOT
210
-
211
- static void Coolio_Loop_ev_loop_oneshot(struct Coolio_Loop *loop_data)
212
- {
212
+ /* libev is patched to release the GIL when it makes its system call */
213
213
  RUN_LOOP(loop_data, EVLOOP_ONESHOT);
214
- }
215
- #endif
216
-
217
- /* Ruby 1.8 requires us to periodically run the event loop then defer back to
218
- * the green threads scheduler */
219
- #ifndef HAVE_EV_LOOP_ONESHOT
220
- #define BLOCKING_INTERVAL 0.01 /* Block for 10ms at a time */
221
-
222
- /* Stub for scheduler's ev_timer callback */
223
- static void timer_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
224
- {
225
- ev_timer_again (ev_loop, timer);
226
- }
227
-
228
- /* Run the event loop, calling rb_thread_schedule every 10ms */
229
- static void Coolio_Loop_ev_loop_oneshot(struct Coolio_Loop *loop_data)
230
- {
231
- struct ev_timer timer;
232
- struct timeval tv;
233
214
 
234
- /* Set up an ev_timer to unblock the loop every 10ms */
235
- ev_timer_init(&timer, timer_callback, BLOCKING_INTERVAL, BLOCKING_INTERVAL);
236
- ev_timer_start(loop_data->ev_loop, &timer);
237
-
238
- /* Loop until we receive events */
239
- while(!loop_data->events_received) {
240
- TRAP_BEG;
241
- RUN_LOOP(loop_data, EVLOOP_ONESHOT);
242
- TRAP_END;
243
-
244
- rb_thread_schedule();
245
- }
215
+ Coolio_Loop_dispatch_events(loop_data);
216
+ nevents = INT2NUM(loop_data->events_received);
217
+ loop_data->events_received = 0;
246
218
 
247
- ev_timer_stop(loop_data->ev_loop, &timer);
219
+ return nevents;
248
220
  }
249
- #endif
250
221
 
251
222
  /**
252
223
  * call-seq:
@@ -3242,7 +3242,7 @@ time_update (EV_P_ ev_tstamp max_block)
3242
3242
  }
3243
3243
 
3244
3244
  /* ########## COOLIO PATCHERY HO! ########## */
3245
- #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
3245
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
3246
3246
  static
3247
3247
  VALUE ev_backend_poll(void **args)
3248
3248
  {
@@ -3257,7 +3257,7 @@ int
3257
3257
  ev_run (EV_P_ int flags)
3258
3258
  {
3259
3259
  /* ########## COOLIO PATCHERY HO! ########## */
3260
- #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
3260
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
3261
3261
  void *poll_args[2];
3262
3262
  #endif
3263
3263
  /* ######################################## */
@@ -3415,10 +3415,24 @@ Let this be a lesson to the all: CALLBACKS FUCKING BLOW
3415
3415
  #######################################################################
3416
3416
  */
3417
3417
 
3418
- #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
3418
+ /*
3419
+ simulate to rb_thread_call_without_gvl using rb_theread_blocking_region.
3420
+ https://github.com/brianmario/mysql2/blob/master/ext/mysql2/client.h#L8
3421
+ */
3422
+
3423
+ #ifndef HAVE_RB_THREAD_CALL_WITHOUT_GVL
3424
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
3425
+
3426
+ #define rb_thread_call_without_gvl(func, data1, ubf, data2) \
3427
+ rb_thread_blocking_region((rb_blocking_function_t *)func, data1, ubf, data2)
3428
+
3429
+ #endif
3430
+ #endif
3431
+
3432
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
3419
3433
  poll_args[0] = (void *)loop;
3420
3434
  poll_args[1] = (void *)&waittime;
3421
- rb_thread_blocking_region(ev_backend_poll, (void *)&poll_args, RUBY_UBF_IO, 0);
3435
+ rb_thread_call_without_gvl(ev_backend_poll, (void *)&poll_args, RUBY_UBF_IO, 0);
3422
3436
  #else
3423
3437
  backend_poll (EV_A_ waittime);
3424
3438
  #endif
@@ -88,12 +88,12 @@ module Coolio
88
88
  # event callbacks to watchers until all watchers associated with
89
89
  # the loop have been disabled or detached. The loop may be
90
90
  # explicitly stopped by calling the stop method on the loop object.
91
- def run
91
+ def run(timeout = nil)
92
92
  raise RuntimeError, "no watchers for this loop" if @watchers.empty?
93
93
 
94
94
  @running = true
95
95
  while @running and not @active_watchers.zero?
96
- run_once
96
+ run_once(timeout)
97
97
  end
98
98
  @running = false
99
99
  end
@@ -1,5 +1,5 @@
1
1
  module Coolio
2
- VERSION = "1.2.1"
2
+ VERSION = "1.2.2"
3
3
 
4
4
  def self.version; VERSION; end
5
5
  end
@@ -4,6 +4,13 @@ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
4
4
  require 'rspec'
5
5
  require 'cool.io'
6
6
 
7
+ def unused_port
8
+ s = TCPServer.open(0)
9
+ port = s.addr[1]
10
+ s.close
11
+ port
12
+ end
13
+
7
14
  RSpec.configure do |c|
8
15
  if RUBY_PLATFORM =~ /mingw|win32/
9
16
  $stderr.puts "Skip some specs on Windows"
@@ -0,0 +1,130 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ TIMEOUT = 0.010
4
+ HOST = '127.0.0.1'
5
+ PORT = unused_port
6
+
7
+ def send_data(data)
8
+ io = TCPSocket.new('127.0.0.1', PORT)
9
+ begin
10
+ io.write data
11
+ ensure
12
+ io.close
13
+ end
14
+ end
15
+
16
+ class MyConnection < Coolio::Socket
17
+ attr_accessor :data, :connected, :closed
18
+
19
+ def initialize(io, on_message)
20
+ super(io)
21
+ @on_message = on_message
22
+ end
23
+
24
+ def on_connect
25
+ @connected = true
26
+ end
27
+
28
+ def on_close
29
+ @closed = true
30
+ end
31
+
32
+ def on_read(data)
33
+ @on_message.call(data)
34
+ end
35
+ end
36
+
37
+ @data = ""
38
+ def on_message(data)
39
+ @data = data
40
+ end
41
+
42
+ def test_run(data = nil)
43
+ reactor = Coolio::Loop.new
44
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
45
+ reactor.attach(server)
46
+ thread = Thread.new { reactor.run }
47
+ send_data(data) if data
48
+ sleep TIMEOUT
49
+ reactor.stop
50
+ server.detach
51
+ send_data('') # to leave from blocking loop
52
+ thread.join
53
+ @data
54
+ ensure
55
+ server.close
56
+ end
57
+
58
+ def test_run_once(data = nil)
59
+ reactor = Coolio::Loop.new
60
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
61
+ reactor.attach(server)
62
+ thread = Thread.new do
63
+ reactor.run_once # on_connect
64
+ reactor.run_once # on_read
65
+ end
66
+ send_data(data) if data
67
+ thread.join
68
+ server.detach
69
+ @data
70
+ ensure
71
+ server.close
72
+ end
73
+
74
+ def test_run_once_timeout(timeout = TIMEOUT)
75
+ reactor = Coolio::Loop.new
76
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
77
+ reactor.attach(server)
78
+ running = true
79
+ thread = Thread.new { reactor.run_once(timeout) }
80
+ sleep timeout
81
+ server.detach
82
+ thread.join
83
+ @data
84
+ ensure
85
+ server.close
86
+ end
87
+
88
+ def test_run_timeout(data = nil, timeout = TIMEOUT)
89
+ reactor = Coolio::Loop.new
90
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
91
+ reactor.attach(server)
92
+ running = true
93
+ thread = Thread.new do
94
+ while running and reactor.has_active_watchers?
95
+ reactor.run_once(timeout)
96
+ end
97
+ end
98
+ send_data(data) if data
99
+ sleep timeout
100
+ server.detach
101
+ running = false # another send is not required
102
+ thread.join
103
+ @data
104
+ ensure
105
+ server.close
106
+ end
107
+
108
+ describe Coolio::TCPServer do
109
+
110
+ it '#run' do
111
+ test_run("hello").should == "hello"
112
+ end
113
+
114
+ it '#run_once' do
115
+ test_run_once("hello").should == "hello"
116
+ end
117
+
118
+ it '#run_once(timeout)' do
119
+ test_run_once_timeout # should not block
120
+ end
121
+
122
+ it '#run_once(-timeout)' do
123
+ expect { test_run_once_timeout(-0.1) }.to raise_error(ArgumentError)
124
+ end
125
+
126
+ it '#run(timeout)' do
127
+ test_run_timeout("hello").should == "hello"
128
+ end
129
+
130
+ end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cool.io
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
5
- prerelease:
4
+ version: 1.2.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Tony Arcieri
@@ -10,54 +9,48 @@ authors:
10
9
  autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2014-02-21 00:00:00.000000000 Z
12
+ date: 2014-04-16 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: rake-compiler
17
16
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
17
  requirements:
20
- - - ~>
18
+ - - "~>"
21
19
  - !ruby/object:Gem::Version
22
20
  version: 0.8.3
23
21
  type: :development
24
22
  prerelease: false
25
23
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
24
  requirements:
28
- - - ~>
25
+ - - "~>"
29
26
  - !ruby/object:Gem::Version
30
27
  version: 0.8.3
31
28
  - !ruby/object:Gem::Dependency
32
29
  name: rspec
33
30
  requirement: !ruby/object:Gem::Requirement
34
- none: false
35
31
  requirements:
36
- - - ! '>='
32
+ - - ">="
37
33
  - !ruby/object:Gem::Version
38
34
  version: 2.13.0
39
35
  type: :development
40
36
  prerelease: false
41
37
  version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
38
  requirements:
44
- - - ! '>='
39
+ - - ">="
45
40
  - !ruby/object:Gem::Version
46
41
  version: 2.13.0
47
42
  - !ruby/object:Gem::Dependency
48
43
  name: rdoc
49
44
  requirement: !ruby/object:Gem::Requirement
50
- none: false
51
45
  requirements:
52
- - - ! '>='
46
+ - - ">="
53
47
  - !ruby/object:Gem::Version
54
48
  version: 3.6.0
55
49
  type: :development
56
50
  prerelease: false
57
51
  version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
52
  requirements:
60
- - - ! '>='
53
+ - - ">="
61
54
  - !ruby/object:Gem::Version
62
55
  version: 3.6.0
63
56
  description: Cool.io provides a high performance event framework for Ruby which uses
@@ -72,9 +65,9 @@ extensions:
72
65
  - ext/iobuffer/extconf.rb
73
66
  extra_rdoc_files: []
74
67
  files:
75
- - .gitignore
76
- - .rspec
77
- - .travis.yml
68
+ - ".gitignore"
69
+ - ".rspec"
70
+ - ".travis.yml"
78
71
  - CHANGES.md
79
72
  - Gemfile
80
73
  - LICENSE
@@ -148,44 +141,39 @@ files:
148
141
  - spec/dns_spec.rb
149
142
  - spec/spec_helper.rb
150
143
  - spec/stat_watcher_spec.rb
144
+ - spec/tcp_server_spec.rb
151
145
  - spec/timer_watcher_spec.rb
152
146
  - spec/unix_listener_spec.rb
153
147
  - spec/unix_server_spec.rb
154
148
  homepage: http://coolio.github.com
155
149
  licenses: []
150
+ metadata: {}
156
151
  post_install_message:
157
152
  rdoc_options: []
158
153
  require_paths:
159
154
  - lib
160
155
  required_ruby_version: !ruby/object:Gem::Requirement
161
- none: false
162
156
  requirements:
163
- - - ! '>='
157
+ - - ">="
164
158
  - !ruby/object:Gem::Version
165
159
  version: '0'
166
- segments:
167
- - 0
168
- hash: 2606014626052723115
169
160
  required_rubygems_version: !ruby/object:Gem::Requirement
170
- none: false
171
161
  requirements:
172
- - - ! '>='
162
+ - - ">="
173
163
  - !ruby/object:Gem::Version
174
164
  version: '0'
175
- segments:
176
- - 0
177
- hash: 2606014626052723115
178
165
  requirements: []
179
166
  rubyforge_project:
180
- rubygems_version: 1.8.23
167
+ rubygems_version: 2.2.2
181
168
  signing_key:
182
- specification_version: 3
169
+ specification_version: 4
183
170
  summary: A cool framework for doing high performance I/O in Ruby
184
171
  test_files:
185
172
  - spec/async_watcher_spec.rb
186
173
  - spec/dns_spec.rb
187
174
  - spec/spec_helper.rb
188
175
  - spec/stat_watcher_spec.rb
176
+ - spec/tcp_server_spec.rb
189
177
  - spec/timer_watcher_spec.rb
190
178
  - spec/unix_listener_spec.rb
191
179
  - spec/unix_server_spec.rb