rainbows 0.90.0 → 0.90.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,10 +12,9 @@ rainbows [-c CONFIG_FILE] [-E RACK_ENV] [-D] [RACKUP_FILE]
12
12
 
13
13
  # DESCRIPTION
14
14
 
15
- A rackup(1)-like command to launch Rack applications using
16
- Rainbows!. It is expected to be started in your application root
17
- (APP_ROOT), but "Dir.chdir" may also be executed in the CONFIG_FILE or
18
- RACKUP_FILE.
15
+ A rackup(1)-like command to launch Rack applications using Rainbows!.
16
+ It is expected to be started in your application root (APP_ROOT),
17
+ but the "working_directory" directive may be used in the CONFIG_FILE.
19
18
 
20
19
  While Rainbows! takes a myriad of command-line options for
21
20
  compatibility with ruby(1) and rackup(1), it is recommended to stick
@@ -34,10 +33,9 @@ with rackup(1) but strongly discouraged.
34
33
  # UNICORN OPTIONS
35
34
  -c, \--config-file CONFIG_FILE
36
35
  : Path to the Unicorn-specific config file. The config file is
37
- implemented as a Ruby DSL, so Ruby code may executed (e.g.
38
- "Dir.chdir", "Process::UID.change_privilege"). See the RDoc/ri
39
- for the *Unicorn::Configurator* class for the full list of
40
- directives available from the DSL.
36
+ implemented as a Ruby DSL, so Ruby code may executed.
37
+ See the RDoc/ri for the *Unicorn::Configurator* class for the full
38
+ list of directives available from the DSL.
41
39
 
42
40
  -D, \--daemonize
43
41
  : Run daemonized in the background. The process is detached from
data/GIT-VERSION-GEN CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v0.90.0.GIT
4
+ DEF_VER=v0.90.1.GIT
5
5
 
6
6
  LF='
7
7
  '
data/TODO CHANGED
@@ -3,14 +3,15 @@
3
3
  We're lazy and pick the easy items to do first, then the ones people
4
4
  care about.
5
5
 
6
+ * Split out NeverBlock into NeverBlockEventMachine and NeverBlockReactor
7
+ NeverBlock will default to one of them (depending on NB upstream).
8
+
6
9
  * Improve test suite coverage. We won't waste cycles with puny
7
10
  unit tests, only integration tests that exercise externally
8
11
  visible parts.
9
12
 
10
13
  * EventMachine.spawn - should be like Revactor, maybe?
11
14
 
12
- * EventMachine deferrables
13
-
14
15
  * conditional app.deferred?(env) support
15
16
  Merb uses it, some other servers support it
16
17
 
data/lib/rainbows.rb CHANGED
@@ -60,6 +60,13 @@ module Rainbows
60
60
 
61
61
  # returns nil if accept fails
62
62
  if defined?(Fcntl::FD_CLOEXEC)
63
+ def sync_accept(sock)
64
+ rv = sock.accept
65
+ rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
66
+ rv
67
+ rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR
68
+ end
69
+
63
70
  def accept(sock)
64
71
  rv = sock.accept_nonblock
65
72
  rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
@@ -67,6 +74,11 @@ module Rainbows
67
74
  rescue Errno::EAGAIN, Errno::ECONNABORTED
68
75
  end
69
76
  else
77
+ def sync_accept(sock)
78
+ sock.accept
79
+ rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR
80
+ end
81
+
70
82
  def accept(sock)
71
83
  sock.accept_nonblock
72
84
  rescue Errno::EAGAIN, Errno::ECONNABORTED
@@ -3,7 +3,7 @@
3
3
  module Rainbows
4
4
 
5
5
  module Const
6
- RAINBOWS_VERSION = '0.90.0'
6
+ RAINBOWS_VERSION = '0.90.1'
7
7
 
8
8
  include Unicorn::Const
9
9
 
@@ -11,6 +11,8 @@ module Rainbows
11
11
  # Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ]
12
12
  ASYNC_CALLBACK = "async.callback".freeze
13
13
 
14
+ ASYNC_CLOSE = "async.close".freeze
15
+
14
16
  def post_init
15
17
  @remote_addr = ::TCPSocket === @_io ? @_io.peeraddr.last : LOCALHOST
16
18
  @env = {}
@@ -37,6 +37,7 @@ module Rainbows
37
37
 
38
38
  def initialize(io)
39
39
  @_io = io
40
+ @body = nil
40
41
  end
41
42
 
42
43
  alias write send_data
@@ -54,6 +55,9 @@ module Rainbows
54
55
  @env[REMOTE_ADDR] = @remote_addr
55
56
  @env[ASYNC_CALLBACK] = method(:response_write)
56
57
 
58
+ # we're not sure if anybody uses this, but Thin sets it, too
59
+ @env[ASYNC_CLOSE] = EM::DefaultDeferrable.new
60
+
57
61
  response = catch(:async) { APP.call(@env.update(RACK_DEFAULTS)) }
58
62
 
59
63
  # too tricky to support pipelining with :async since the
@@ -77,9 +81,14 @@ module Rainbows
77
81
  end while true
78
82
  end
79
83
 
80
- def response_write(response, out = [], alive = false)
81
- body = response.last
82
- unless body.respond_to?(:to_path)
84
+ def response_write(response, out = [ CONN_CLOSE ], alive = false)
85
+ @body = body = response.last
86
+ if body.respond_to?(:errback) && body.respond_to?(:callback)
87
+ body.callback { quit }
88
+ body.errback { quit }
89
+ HttpResponse.write(self, response, out)
90
+ return
91
+ elsif ! body.respond_to?(:to_path)
83
92
  HttpResponse.write(self, response, out)
84
93
  quit unless alive
85
94
  return
@@ -120,6 +129,8 @@ module Rainbows
120
129
  end
121
130
 
122
131
  def unbind
132
+ async_close = @env[ASYNC_CLOSE] and async_close.succeed
133
+ @body.respond_to?(:fail) and @body.fail
123
134
  @_io.close
124
135
  end
125
136
  end
@@ -44,8 +44,7 @@ module Rainbows
44
44
  def sync_worker
45
45
  s = LISTENERS.first
46
46
  begin
47
- process_client(s.accept)
48
- rescue Errno::EINTR, Errno::ECONNABORTED
47
+ c = Rainbows.sync_accept(s) and process_client(c)
49
48
  rescue => e
50
49
  Error.listen_loop(e)
51
50
  end while G.alive
@@ -34,8 +34,8 @@ module Rainbows
34
34
  # unlikely one. Since this case is (or should be) uncommon,
35
35
  # just busy wait when we have to.
36
36
  sleep(0.01)
37
- else
38
- klass.new(l.accept) do |c|
37
+ elsif c = Rainbows.sync_accept(l)
38
+ klass.new(c) do |c|
39
39
  begin
40
40
  lock.synchronize { G.cur += 1 }
41
41
  process_client(c)
@@ -44,7 +44,6 @@ module Rainbows
44
44
  end
45
45
  end
46
46
  end
47
- rescue Errno::EINTR, Errno::ECONNABORTED
48
47
  rescue => e
49
48
  Error.listen_loop(e)
50
49
  end while G.alive
data/rainbows.gemspec CHANGED
@@ -41,8 +41,7 @@ Gem::Specification.new do |s|
41
41
  s.test_files = test_files
42
42
 
43
43
  # we need Unicorn for the HTTP parser and process management
44
- # Unicorn 0.95.0 should be released on or around Nov 13/14/15, 2009
45
- s.add_dependency(%q<unicorn>, ["~> 0.95.0"])
44
+ s.add_dependency(%q<unicorn>, ["~> 0.95.0", "< 0.97.0"])
46
45
 
47
46
  # Unicorn already depends on Rack
48
47
  # s.add_dependency(%q<rack>)
@@ -0,0 +1,3 @@
1
+ These examples in this directory are stolen from Thin 1.2.5 with only
2
+ trivial changes. All examples in this directory retain their original
3
+ license (MIT) and copyrights.
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env rackup -s thin
2
+ #
3
+ # async_app.ru
4
+ # raggi/thin
5
+ #
6
+ # A second demo app for async rack + thin app processing!
7
+ # Now using http status code 100 instead.
8
+ #
9
+ # Created by James Tucker on 2008-06-17.
10
+ # Copyright 2008 James Tucker <raggi@rubyforge.org>.
11
+ #
12
+ #--
13
+ # Benchmark Results:
14
+ #
15
+ # raggi@mbk:~$ ab -c 100 -n 500 http://127.0.0.1:3000/
16
+ # This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0
17
+ # Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
18
+ # Copyright 2006 The Apache Software Foundation, http://www.apache.org/
19
+ #
20
+ # Benchmarking 127.0.0.1 (be patient)
21
+ # Completed 100 requests
22
+ # Completed 200 requests
23
+ # Completed 300 requests
24
+ # Completed 400 requests
25
+ # Finished 500 requests
26
+ #
27
+ #
28
+ # Server Software: thin
29
+ # Server Hostname: 127.0.0.1
30
+ # Server Port: 3000
31
+ #
32
+ # Document Path: /
33
+ # Document Length: 12 bytes
34
+ #
35
+ # Concurrency Level: 100
36
+ # Time taken for tests: 5.263089 seconds
37
+ # Complete requests: 500
38
+ # Failed requests: 0
39
+ # Write errors: 0
40
+ # Total transferred: 47000 bytes
41
+ # HTML transferred: 6000 bytes
42
+ # Requests per second: 95.00 [#/sec] (mean)
43
+ # Time per request: 1052.618 [ms] (mean)
44
+ # Time per request: 10.526 [ms] (mean, across all concurrent requests)
45
+ # Transfer rate: 8.55 [Kbytes/sec] received
46
+ #
47
+ # Connection Times (ms)
48
+ # min mean[+/-sd] median max
49
+ # Connect: 0 3 2.2 3 8
50
+ # Processing: 1042 1046 3.1 1046 1053
51
+ # Waiting: 1037 1042 3.6 1041 1050
52
+ # Total: 1045 1049 3.1 1049 1057
53
+ #
54
+ # Percentage of the requests served within a certain time (ms)
55
+ # 50% 1049
56
+ # 66% 1051
57
+ # 75% 1053
58
+ # 80% 1053
59
+ # 90% 1054
60
+ # 95% 1054
61
+ # 98% 1056
62
+ # 99% 1057
63
+ # 100% 1057 (longest request)
64
+
65
+ class DeferrableBody
66
+ include EventMachine::Deferrable
67
+
68
+ def call(body)
69
+ body.each do |chunk|
70
+ @body_callback.call(chunk)
71
+ end
72
+ end
73
+
74
+ def each &blk
75
+ @body_callback = blk
76
+ end
77
+
78
+ end
79
+
80
+ class AsyncApp
81
+
82
+ # This is a template async response. N.B. Can't use string for body on 1.9
83
+ AsyncResponse = [-1, {}, []].freeze
84
+
85
+ def call(env)
86
+
87
+ body = DeferrableBody.new
88
+
89
+ # Get the headers out there asap, let the client know we're alive...
90
+ EventMachine::next_tick { env['async.callback'].call [200, {'Content-Type' => 'text/plain'}, body] }
91
+
92
+ # Semi-emulate a long db request, instead of a timer, in reality we'd be
93
+ # waiting for the response data. Whilst this happens, other connections
94
+ # can be serviced.
95
+ # This could be any callback based thing though, a deferrable waiting on
96
+ # IO data, a db request, an http request, an smtp send, whatever.
97
+ EventMachine::add_timer(1) {
98
+ body.call ["Woah, async!\n"]
99
+
100
+ EventMachine::next_tick {
101
+ # This could actually happen any time, you could spawn off to new
102
+ # threads, pause as a good looking lady walks by, whatever.
103
+ # Just shows off how we can defer chunks of data in the body, you can
104
+ # even call this many times.
105
+ body.call ["Cheers then!"]
106
+ body.succeed
107
+ }
108
+ }
109
+
110
+ # throw :async # Still works for supporting non-async frameworks...
111
+
112
+ AsyncResponse # May end up in Rack :-)
113
+ end
114
+
115
+ end
116
+
117
+ # The additions to env for async.connection and async.callback absolutely
118
+ # destroy the speed of the request if Lint is doing it's checks on env.
119
+ # It is also important to note that an async response will not pass through
120
+ # any further middleware, as the async response notification has been passed
121
+ # right up to the webserver, and the callback goes directly there too.
122
+ # Middleware could possibly catch :async, and also provide a different
123
+ # async.connection and async.callback.
124
+
125
+ # use Rack::Lint
126
+ run AsyncApp.new
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env rackup -s thin
2
+ #
3
+ # async_tailer.ru
4
+ # raggi/thin
5
+ #
6
+ # Tested with 150 spawned tails on OS X
7
+ #
8
+ # Created by James Tucker on 2008-06-18.
9
+ # Copyright 2008 James Tucker <raggi@rubyforge.org>.
10
+
11
+ # Uncomment if appropriate for you..
12
+ # EM.epoll
13
+ # EM.kqueue
14
+
15
+ tail_log_file = ENV["TAIL_LOG_FILE"] or abort "TAIL_LOG_FILE= env must be set"
16
+ unless ::File.file?(tail_log_file) && ::File.readable?(tail_log_file)
17
+ abort "#{tail_log_file} must be a readable regular file"
18
+ end
19
+
20
+ class DeferrableBody
21
+ include EventMachine::Deferrable
22
+
23
+ def initialize
24
+ @queue = []
25
+ # make sure to flush out the queue before closing the connection
26
+ callback{
27
+ until @queue.empty?
28
+ @queue.shift.each{|chunk| @body_callback.call(chunk) }
29
+ end
30
+ }
31
+ end
32
+
33
+ def schedule_dequeue
34
+ return unless @body_callback
35
+ EventMachine::next_tick do
36
+ next unless body = @queue.shift
37
+ body.each do |chunk|
38
+ @body_callback.call(chunk)
39
+ end
40
+ schedule_dequeue unless @queue.empty?
41
+ end
42
+ end
43
+
44
+ def call(body)
45
+ @queue << body
46
+ schedule_dequeue
47
+ end
48
+
49
+ def each &blk
50
+ @body_callback = blk
51
+ schedule_dequeue
52
+ end
53
+
54
+ end
55
+
56
+ module TailRenderer
57
+ attr_accessor :callback
58
+
59
+ def receive_data(data)
60
+ @callback.call([data])
61
+ end
62
+
63
+ def unbind
64
+ @callback.succeed
65
+ end
66
+ end
67
+
68
+ class AsyncTailer
69
+
70
+ AsyncResponse = [-1, {}, []].freeze
71
+
72
+ def call(env)
73
+
74
+ body = DeferrableBody.new
75
+
76
+ EventMachine::next_tick do
77
+
78
+ env['async.callback'].call [200, {'Content-Type' => 'text/html'}, body]
79
+
80
+ body.call ["<h1>Async Tailer</h1><pre>"]
81
+
82
+ end
83
+
84
+ EventMachine::popen("tail -f #{ENV["TAIL_LOG_FILE"]}", TailRenderer) do |t|
85
+
86
+ t.callback = body
87
+
88
+ # If for some reason we 'complete' body, close the tail.
89
+ body.callback do
90
+ t.close_connection
91
+ end
92
+
93
+ # If for some reason the client disconnects, close the tail.
94
+ body.errback do
95
+ t.close_connection
96
+ end
97
+
98
+ end
99
+
100
+ AsyncResponse
101
+ end
102
+
103
+ end
104
+
105
+ run AsyncTailer.new
@@ -0,0 +1,41 @@
1
+ #!/bin/sh
2
+ nr=${nr-4}
3
+ . ./test-lib.sh
4
+
5
+ # ApacheBench (ab) is commonly installed in the sbin paths in Debian-based
6
+ # systems...
7
+ AB="$(which ab 2>/dev/null || :)"
8
+ if test -z "$AB"
9
+ then
10
+ AB=$(PATH=/usr/local/sbin:/usr/sbin:$PATH which ab 2>/dev/null || :)
11
+ fi
12
+
13
+ if test -z "$AB"
14
+ then
15
+ t_info "skipping $T since 'ab' could not be found"
16
+ exit 0
17
+ fi
18
+
19
+ t_plan 4 "quiet spurious wakeups for $model"
20
+
21
+ t_begin "setup and start" && {
22
+ rainbows_setup $model
23
+ echo "preload_app true" >> $unicorn_config
24
+ echo "worker_processes $nr" >> $unicorn_config
25
+ rainbows -D env.ru -c $unicorn_config -E none
26
+ rainbows_wait_start
27
+ }
28
+
29
+ t_begin "spam the server with requests" && {
30
+ $AB -c1 -n100 http://$listen/
31
+ }
32
+
33
+ t_begin "killing succeeds" && {
34
+ kill -QUIT $rainbows_pid
35
+ }
36
+
37
+ t_begin "check stderr" && {
38
+ check_stderr
39
+ }
40
+
41
+ t_done
@@ -0,0 +1,57 @@
1
+ #!/bin/sh
2
+ nr=${nr-5}
3
+ . ./test-lib.sh
4
+ case $model in
5
+ NeverBlock|EventMachine) ;;
6
+ *)
7
+ t_info "skipping $T since it's not compatible with $model"
8
+ exit 0
9
+ ;;
10
+ esac
11
+
12
+ t_plan 7 "async_app test for test for EM"
13
+
14
+ CONFIG_RU=async_examples/async_app.ru
15
+
16
+ t_begin "setup and start" && {
17
+ rainbows_setup
18
+ rtmpfiles a b c curl_err expect
19
+
20
+ # this does not does not support Rack::Lint
21
+ rainbows -E deployment -D $CONFIG_RU -c $unicorn_config
22
+ rainbows_wait_start
23
+ }
24
+
25
+ t_begin "send async requests off in parallel" && {
26
+ t0=$(date +%s)
27
+ curl --no-buffer -sSf http://$listen/ > $a 2>> $curl_err &
28
+ curl --no-buffer -sSf http://$listen/ > $b 2>> $curl_err &
29
+ curl --no-buffer -sSf http://$listen/ > $c 2>> $curl_err &
30
+ }
31
+
32
+ t_begin "wait for curl terminations" && {
33
+ wait
34
+ t1=$(date +%s)
35
+ elapsed=$(( $t1 - $t0 ))
36
+ t_info "elapsed=$elapsed"
37
+ }
38
+
39
+ t_begin "termination signal sent" && {
40
+ kill $rainbows_pid
41
+ }
42
+
43
+ t_begin "no errors from curl" && {
44
+ test ! -s $curl_err
45
+ }
46
+
47
+ t_begin "no errors in stderr" && check_stderr
48
+
49
+ t_begin "responses match expected" && {
50
+ echo 'Woah, async!' > $expect
51
+ printf 'Cheers then!' >> $expect
52
+ cmp $expect $a
53
+ cmp $expect $b
54
+ cmp $expect $c
55
+ }
56
+
57
+ t_done
@@ -0,0 +1,74 @@
1
+ #!/bin/sh
2
+ nr=${nr-5}
3
+ . ./test-lib.sh
4
+ case $model in
5
+ NeverBlock|EventMachine) ;;
6
+ *)
7
+ t_info "skipping $T since it's not compatible with $model"
8
+ exit 0
9
+ ;;
10
+ esac
11
+
12
+ t_plan 8 "async_tailer test for test for EM"
13
+
14
+ CONFIG_RU=async_examples/async_tailer.ru
15
+
16
+ t_begin "setup and start" && {
17
+ rainbows_setup
18
+ rtmpfiles a b c curl_err TAIL_LOG_FILE expect
19
+
20
+ printf '<h1>Async Tailer</h1><pre>' >> $expect
21
+
22
+ export TAIL_LOG_FILE
23
+
24
+ # this does not does not support Rack::Lint
25
+ rainbows -E deployment -D $CONFIG_RU -c $unicorn_config
26
+ rainbows_wait_start
27
+ }
28
+
29
+ t_begin "send async requests off in parallel" && {
30
+ t0=$(date +%s)
31
+ curl --no-buffer -sSf http://$listen/ > $a 2>> $curl_err &
32
+ curl_a=$!
33
+ curl --no-buffer -sSf http://$listen/ > $b 2>> $curl_err &
34
+ curl_b=$!
35
+ curl --no-buffer -sSf http://$listen/ > $c 2>> $curl_err &
36
+ curl_c=$!
37
+ }
38
+
39
+ t_begin "generate log output" && {
40
+ for i in $(awk "BEGIN {for(i=0;i<$nr;i++) print i}" < /dev/null)
41
+ do
42
+ date >> $TAIL_LOG_FILE
43
+ sleep 1
44
+ done
45
+ # sometimes tail(1) can be slow
46
+ sleep 2
47
+ }
48
+
49
+ t_begin "kill curls and wait for termination" && {
50
+ kill $curl_a $curl_b $curl_c
51
+ wait
52
+ t1=$(date +%s)
53
+ elapsed=$(( $t1 - $t0 ))
54
+ t_info "elapsed=$elapsed"
55
+ }
56
+
57
+ t_begin "termination signal sent" && {
58
+ kill $rainbows_pid
59
+ }
60
+
61
+ t_begin "no errors from curl" && {
62
+ test ! -s $curl_err
63
+ }
64
+
65
+ t_begin "no errors in stderr" && check_stderr
66
+
67
+ t_begin "responses match expected" && {
68
+ cat $TAIL_LOG_FILE >> $expect
69
+ cmp $expect $a
70
+ cmp $expect $b
71
+ cmp $expect $c
72
+ }
73
+
74
+ t_done
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rainbows
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.90.0
4
+ version: 0.90.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rainbows! hackers
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-22 00:00:00 +00:00
12
+ date: 2009-12-30 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -21,6 +21,9 @@ dependencies:
21
21
  - - ~>
22
22
  - !ruby/object:Gem::Version
23
23
  version: 0.95.0
24
+ - - <
25
+ - !ruby/object:Gem::Version
26
+ version: 0.97.0
24
27
  version:
25
28
  description: |-
26
29
  Rainbows! is an HTTP server for sleepy Rack applications. It is based on
@@ -74,7 +77,6 @@ extra_rdoc_files:
74
77
  - lib/rainbows/thread_spawn.rb
75
78
  - LICENSE
76
79
  - NEWS
77
- - rainbows.1
78
80
  - README
79
81
  - SIGNALS
80
82
  - TODO
@@ -150,6 +152,9 @@ files:
150
152
  - t/README
151
153
  - t/async-response-no-autochunk.ru
152
154
  - t/async-response.ru
155
+ - t/async_examples/README
156
+ - t/async_examples/async_app.ru
157
+ - t/async_examples/async_tailer.ru
153
158
  - t/async_sinatra.ru
154
159
  - t/bin/content-md5-put
155
160
  - t/bin/sha1sum.rb
@@ -193,18 +198,20 @@ files:
193
198
  - t/t0009.ru
194
199
  - t/t0010-keepalive-timeout-effective.sh
195
200
  - t/t0011-close-on-exec-set.sh
201
+ - t/t0012-spurious-wakeups-quiet.sh
196
202
  - t/t0100-rack-input-hammer.sh
197
203
  - t/t0101-rack-input-trailer.sh
198
204
  - t/t0102-rack-input-short.sh
199
205
  - t/t0200-async-response.sh
200
206
  - t/t0201-async-response-no-autochunk.sh
201
207
  - t/t0300-async_sinatra.sh
208
+ - t/t0400-em-async-app.sh
209
+ - t/t0401-em-async-tailer.sh
202
210
  - t/t9000-rack-app-pool.sh
203
211
  - t/t9000.ru
204
212
  - t/test-lib.sh
205
213
  - t/worker-follows-master-to-death.ru
206
214
  - vs_Unicorn
207
- - rainbows.1
208
215
  has_rdoc: true
209
216
  homepage: http://rainbows.rubyforge.org/
210
217
  licenses: []
data/rainbows.1 DELETED
File without changes