rack-jet_router 1.3.1 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +16 -0
- data/README.md +80 -72
- data/bench/Gemfile +5 -4
- data/bench/Rakefile.rb +6 -3
- data/bench/bench.rb +330 -244
- data/lib/rack/jet_router.rb +119 -62
- data/rack-jet_router.gemspec +1 -1
- data/test/builder_test.rb +129 -41
- data/test/router_test.rb +49 -7
- data/test/shared.rb +2 -2
- metadata +2 -2
data/bench/bench.rb
CHANGED
@@ -5,48 +5,85 @@
|
|
5
5
|
## Usage:
|
6
6
|
## $ gem install bundler
|
7
7
|
## $ bundler install
|
8
|
-
## $ ruby bench.rb
|
9
|
-
## $ ruby bench.rb
|
8
|
+
## $ ruby bench.rb -n 1000000
|
9
|
+
## $ ruby bench.rb -n 1000000 --rack=off --sinatra=off --multiplexer=off # --jet=off --keight=off --hanami=off --httprouter=off
|
10
10
|
##
|
11
11
|
|
12
12
|
|
13
13
|
$LOAD_PATH << File.absolute_path("../../lib", __FILE__)
|
14
14
|
|
15
|
+
|
15
16
|
require 'benchmarker'
|
16
17
|
Benchmarker.parse_cmdopts()
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
19
|
+
|
20
|
+
class GetFlag
|
21
|
+
|
22
|
+
def initialize(defaults={})
|
23
|
+
@flag_all = to_bool($opt_all)
|
24
|
+
@defaults = defaults
|
25
|
+
@output = String.new
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :output
|
29
|
+
|
30
|
+
def call(key, lib, &b)
|
31
|
+
default = @defaults[key]
|
32
|
+
default != nil or
|
33
|
+
raise KeyError.new(key.inspect)
|
34
|
+
opt_val = eval "$opt_#{key}"
|
35
|
+
arr = [to_bool(opt_val), @flag_all, default]
|
36
|
+
flag = arr.find {|x| x != nil }
|
37
|
+
#
|
38
|
+
begin
|
39
|
+
require lib
|
40
|
+
rescue LoadError
|
41
|
+
flag = nil
|
42
|
+
end
|
43
|
+
#
|
44
|
+
version = flag == false ? "(skipped)" \
|
45
|
+
: flag == nil ? "(not installed)" \
|
46
|
+
: yield
|
47
|
+
gem = [lib].flatten()[0].gsub('/', '-')
|
48
|
+
@output << "** %-20s : %s\n" % [gem, version]
|
49
|
+
#
|
50
|
+
return flag
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_bool(val, default=nil)
|
54
|
+
case val
|
55
|
+
when 'on' , 'true' , 'yes', '1' ; return true
|
56
|
+
when 'off', 'false', 'no' , '0' ; return false
|
57
|
+
else ; return default
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
37
61
|
end
|
38
62
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
63
|
+
get_flag = GetFlag.new(
|
64
|
+
:rack => true,
|
65
|
+
:rocket => false,
|
66
|
+
:jet => true,
|
67
|
+
:keight => true,
|
68
|
+
:hanami => true,
|
69
|
+
:httprouter => true,
|
70
|
+
:multiplexer => true,
|
71
|
+
:sinatra => true,
|
72
|
+
)
|
73
|
+
|
74
|
+
|
75
|
+
flag_rack = get_flag.(:rack , "rack" ) { Rack.release }
|
76
|
+
flag_rocket = get_flag.(:rocket , "rocketrouter" ) { RocketRouter::VERSION }
|
77
|
+
flag_jet = get_flag.(:jet , "rack/jet_router" ) { Rack::JetRouter::RELEASE }
|
78
|
+
flag_keight = get_flag.(:keight , "keight" ) { K8::RELEASE }
|
79
|
+
flag_hanami = get_flag.(:hanami , "hanami/router" ) { Hanami::Router::VERSION }
|
80
|
+
flag_httprouter = get_flag.(:httprouter , "http_router" ) { require "http_router/version"; HttpRouter::VERSION }
|
81
|
+
flag_multiplexer = get_flag.(:multiplexer, "rack/multiplexer") { Rack::Multiplexer::VERSION }
|
82
|
+
flag_sinatra = get_flag.(:sinatra , "sinatra/base" ) { Sinatra::VERSION }
|
45
83
|
|
46
84
|
|
47
85
|
ENTRIES = ('a'..'z').map.with_index {|x, i| "%s%02d" % [x*3, i+1] }
|
48
86
|
|
49
|
-
#flag_sinatra = false # because too slow
|
50
87
|
target_urlpaths = [
|
51
88
|
"/api/aaa01",
|
52
89
|
"/api/aaa01/123",
|
@@ -57,13 +94,36 @@ target_urlpaths = [
|
|
57
94
|
]
|
58
95
|
|
59
96
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
97
|
+
def generate_apps(env_key, key_class)
|
98
|
+
if key_class == String ; id, c_id = 'id', 'comment_id'
|
99
|
+
elsif key_class == Symbol ; id, c_id = :id, :comment_id
|
100
|
+
else ; raise "** internal error: key_class=#{key_class.inspect}"
|
64
101
|
end
|
102
|
+
index_app = proc {|env|
|
103
|
+
[200, {"Content-Type"=>"text/html"}, ["<h1>hello</h1>"]]
|
104
|
+
}
|
105
|
+
show_app = proc {|env|
|
106
|
+
d = env[env_key]
|
107
|
+
[200, {"Content-Type"=>"text/html"}, ["<h1>id=#{d[id]}</h1>"]]
|
108
|
+
}
|
109
|
+
comment_app = proc {|env|
|
110
|
+
d = env[env_key]
|
111
|
+
[200, {"Content-Type"=>"text/html"}, ["<h1>id=#{d[id]}, comment_id=#{d[c_id]}</h1>"]]
|
112
|
+
}
|
113
|
+
return index_app, show_app, comment_app
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
def let(*args)
|
118
|
+
return (yield *args)
|
119
|
+
end
|
120
|
+
|
65
121
|
|
66
|
-
|
122
|
+
rack_app = flag_rack && let() {
|
123
|
+
#proc do |env|
|
124
|
+
# [200, {"Content-Type"=>"text/html"}, ["<h1>hello</h1>"]]
|
125
|
+
#end
|
126
|
+
proc do |env; req, resp|
|
67
127
|
req = Rack::Request.new(env)
|
68
128
|
resp = Rack::Response.new
|
69
129
|
#[resp.status, resp.headers, ["<h1>hello</h1>"]]
|
@@ -73,73 +133,134 @@ if flag_rack
|
|
73
133
|
resp.write("<h1>hello</h1>")
|
74
134
|
resp.finish()
|
75
135
|
end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
136
|
+
}
|
137
|
+
|
138
|
+
|
139
|
+
rocket_app = flag_rocket && let() {
|
140
|
+
index_app, show_app, comment_app = generate_apps('rack.urlpath_params', String)
|
141
|
+
mapping = {
|
142
|
+
"/api" => ENTRIES.each_with_object({}) {|x, map|
|
143
|
+
map.update({
|
144
|
+
"/#{x}" => {
|
145
|
+
"" => index_app,
|
146
|
+
"/{id}" => show_app,
|
147
|
+
"/{id}/comments" => {
|
148
|
+
"/{comment_id}" => comment_app,
|
149
|
+
},
|
150
|
+
},
|
151
|
+
})
|
152
|
+
},
|
153
|
+
}
|
154
|
+
opts = {
|
155
|
+
}
|
156
|
+
RocketRouter.new(mapping, **opts)
|
157
|
+
}
|
158
|
+
|
159
|
+
|
160
|
+
jet_app = flag_jet && let() {
|
161
|
+
index_app, show_app, comment_app = generate_apps('rack.urlpath_params', String)
|
162
|
+
mapping = {
|
163
|
+
"/api" => ENTRIES.each_with_object({}) {|x, map|
|
164
|
+
map.update({
|
165
|
+
"/#{x}" => {
|
166
|
+
"" => index_app,
|
167
|
+
"/:id" => show_app,
|
168
|
+
"/:id/comments" => {
|
169
|
+
"/:comment_id" => comment_app,
|
170
|
+
},
|
171
|
+
},
|
172
|
+
})
|
173
|
+
},
|
174
|
+
}
|
175
|
+
opts = {
|
176
|
+
cache_size: 0,
|
177
|
+
_enable_range: true,
|
178
|
+
#prefix_minlength_target: /\A\/api\/\w/,
|
179
|
+
}
|
180
|
+
Rack::JetRouter.new(mapping, **opts)
|
181
|
+
}
|
182
|
+
|
183
|
+
|
184
|
+
keight_app = flag_keight && let() {
|
185
|
+
class K8HelloAction < K8::Action
|
186
|
+
mapping '', :GET=>:do_index
|
187
|
+
mapping '/{id}', :GET=>:do_show
|
188
|
+
def do_index()
|
189
|
+
"<h1>hello</h1>"
|
190
|
+
end
|
191
|
+
def do_show(id)
|
192
|
+
"<h1>id=#{id.inspect}</h1>"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
class K8CommentAction < K8::Action
|
196
|
+
mapping '/{comment_id}', :GET=>:do_show
|
197
|
+
def do_show(id, comment_id)
|
198
|
+
"<h1>id=#{id}, comment_id=#{comment_id}</h1>"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
mapping = [
|
202
|
+
["/api", ENTRIES.each_with_object([]) {|x, arr|
|
203
|
+
arr << ["/#{x}" , K8HelloAction]
|
204
|
+
arr << ["/#{x}/{id}/comments", K8CommentAction]
|
205
|
+
}
|
206
|
+
],
|
207
|
+
]
|
208
|
+
opts = {
|
209
|
+
#urlpath_cache_size: 0,
|
210
|
+
}
|
211
|
+
K8::RackApplication.new(mapping, **opts)
|
212
|
+
}
|
213
|
+
|
214
|
+
|
215
|
+
hanami_app = flag_hanami && let() {
|
216
|
+
## ref: https://github.com/hanami/router
|
217
|
+
index_app, show_app, comment_app = generate_apps('router.params', Symbol)
|
218
|
+
Hanami::Router.new do
|
219
|
+
scope "api" do
|
220
|
+
ENTRIES.each do |x|
|
221
|
+
get "/#{x}" , to: index_app
|
222
|
+
get "/#{x}/:id", to: show_app
|
223
|
+
get "/#{x}/:id/comments/:comment_id", to: comment_app
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
}
|
115
228
|
|
116
229
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
[200, {"Content-Type"=>"text/html"}, ["<h1>id=#{d['id']}, comment_id=#{d['comment_id']}</h1>"]]
|
130
|
-
}
|
230
|
+
httprouter_app = flag_httprouter && let() {
|
231
|
+
require 'uri'
|
232
|
+
require 'cgi'
|
233
|
+
class <<URI
|
234
|
+
#alias unescape decode_www_form
|
235
|
+
def unescape(x)
|
236
|
+
CGI.unescape(x)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
#
|
240
|
+
index_app, show_app, comment_app = generate_apps('router.params', Symbol)
|
241
|
+
HttpRouter.new.tap do |r|
|
131
242
|
ENTRIES.each do |x|
|
132
|
-
|
133
|
-
|
134
|
-
|
243
|
+
r.add("/api/#{x}" ).to(index_app)
|
244
|
+
r.add("/api/#{x}/:id").to(show_app)
|
245
|
+
r.add("/api/#{x}/:id/comments/:comment_id").to(comment_app)
|
135
246
|
end
|
136
247
|
end
|
248
|
+
}
|
137
249
|
|
138
|
-
end
|
139
250
|
|
251
|
+
multiplexer_app = flag_multiplexer && let() {
|
252
|
+
index_app, show_app, comment_app = generate_apps('rack.request.query_hash', String)
|
253
|
+
Rack::Multiplexer.new().tap do |app|
|
254
|
+
ENTRIES.each do |x|
|
255
|
+
app.get "/api/#{x}" , index_app
|
256
|
+
app.get "/api/#{x}/:id" , show_app
|
257
|
+
app.get "/api/#{x}/:id/comments/:comment_id" , comment_app
|
258
|
+
end
|
259
|
+
end
|
260
|
+
}
|
140
261
|
|
141
|
-
if flag_sinatra
|
142
262
|
|
263
|
+
sinatra_app = flag_sinatra && let() {
|
143
264
|
class SinaApp < Sinatra::Base
|
144
265
|
## run benchmarks without middlewares
|
145
266
|
set :sessions , false
|
@@ -148,76 +269,19 @@ if flag_sinatra
|
|
148
269
|
set :x_cascade , false
|
149
270
|
#
|
150
271
|
ENTRIES.each do |x|
|
151
|
-
get "/api/#{x}"
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
end
|
160
|
-
|
161
|
-
|
162
|
-
if flag_keight
|
163
|
-
|
164
|
-
class K8HelloAction < K8::Action
|
165
|
-
mapping '', :GET=>:do_index
|
166
|
-
mapping '/{id}', :GET=>:do_show
|
167
|
-
def do_index ; "<h1>hello</h1>"; end
|
168
|
-
def do_show(id) ; "<h1>id=#{id.inspect}</h1>"; end
|
169
|
-
end
|
170
|
-
|
171
|
-
class K8CommentAction < K8::Action
|
172
|
-
mapping '/{comment_id}', :GET=>:do_show
|
173
|
-
def do_show(id, comment_id); "<h1>id=#{id}, comment_id=#{comment_id}</h1>"; end
|
174
|
-
end
|
175
|
-
|
176
|
-
k8_app = (proc {
|
177
|
-
mapping = [
|
178
|
-
["/api", ENTRIES.each_with_object([]) {|x, arr|
|
179
|
-
arr << ["/#{x}" , K8HelloAction]
|
180
|
-
arr << ["/#{x}/{id}/comments", K8CommentAction]
|
181
|
-
}
|
182
|
-
],
|
183
|
-
]
|
184
|
-
opts = {
|
185
|
-
#urlpath_cache_size: 0,
|
186
|
-
}
|
187
|
-
K8::RackApplication.new(mapping, **opts)
|
188
|
-
}).call()
|
189
|
-
|
190
|
-
#k8_app.find('/api/books') # warm up
|
191
|
-
|
192
|
-
end
|
193
|
-
|
194
|
-
|
195
|
-
if flag_hanami
|
196
|
-
|
197
|
-
## ref: https://github.com/hanami/router
|
198
|
-
hanami_app = Hanami::Router.new do
|
199
|
-
index_handler = proc do |env|
|
200
|
-
[200, {"Content-Type": "text/html"}, ["<h1>hello</h1>"]]
|
201
|
-
end
|
202
|
-
show_handler = proc do |env|
|
203
|
-
d = env['router.params']
|
204
|
-
[200, {"Content-Type": "text/html"}, ["<h1>id=#{d[:id]}</h1>"]]
|
205
|
-
end
|
206
|
-
comment_handler = proc do |env|
|
207
|
-
d = env['router.params']
|
208
|
-
[200, {"Content-Type": "text/html"}, ["<h1>id=#{d[:id]}, comment_id=#{d[:comment_id]}</h1>"]]
|
209
|
-
end
|
210
|
-
#
|
211
|
-
scope "api" do
|
212
|
-
ENTRIES.each do |x|
|
213
|
-
get "/#{x}" , to: index_handler
|
214
|
-
get "/#{x}/:id", to: show_handler
|
215
|
-
get "/#{x}/:id/comments/:comment_id", to: comment_handler
|
272
|
+
get "/api/#{x}" do
|
273
|
+
"<h1>hello</h1>"
|
274
|
+
end
|
275
|
+
get "/api/#{x}/:id" do
|
276
|
+
"<h1>id=#{params['id']}</h1>"
|
277
|
+
end
|
278
|
+
get "/api/#{x}/:id/comments/:comment_id" do
|
279
|
+
"<h1>id=#{params['id']}, comment_id=#{params['comment_id']}</h1>"
|
216
280
|
end
|
217
281
|
end
|
218
282
|
end
|
219
|
-
|
220
|
-
|
283
|
+
SinaApp.new
|
284
|
+
}
|
221
285
|
|
222
286
|
|
223
287
|
begin
|
@@ -235,10 +299,12 @@ def newenv(path)
|
|
235
299
|
end
|
236
300
|
|
237
301
|
|
238
|
-
N = (
|
302
|
+
N = Benchmarker::OPTIONS.delete(:loop) || 1000_000
|
239
303
|
title = "Router library benchmark"
|
240
|
-
|
304
|
+
width = target_urlpaths.collect(&:length).max()
|
305
|
+
Benchmarker.scope(title, width: width + 17, loop: 1, iter: 1, extra: 0, sleep: 0) do
|
241
306
|
|
307
|
+
puts get_flag.output()
|
242
308
|
puts "** N=#{N}"
|
243
309
|
puts ""
|
244
310
|
|
@@ -251,117 +317,137 @@ Benchmarker.scope(title, width: 33, loop: 1, iter: 1, extra: 0, sleep: 0) do
|
|
251
317
|
end
|
252
318
|
|
253
319
|
### Rack
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
320
|
+
#flag_rack and target_urlpaths.each do |path|
|
321
|
+
# rack_app.call(newenv(path)) # warm up
|
322
|
+
# task "(Rack plain app) #{path}" do # no routing
|
323
|
+
# env = newenv(path)
|
324
|
+
# i = 0; n = N
|
325
|
+
# while (i += 1) <= n
|
326
|
+
# result = rack_app.call(env)
|
327
|
+
# end
|
328
|
+
# result
|
329
|
+
# end
|
330
|
+
#end
|
331
|
+
flag_rack and target_urlpaths.each do |path|
|
332
|
+
rack_app.call(newenv(path)) # warm up
|
333
|
+
task "(Rack::Req+Res) #{path}" do # no routing
|
334
|
+
env = newenv(path)
|
335
|
+
i = 0; n = N
|
336
|
+
while (i += 1) <= n
|
337
|
+
result = rack_app.call(env)
|
338
|
+
end
|
339
|
+
result
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
### RocketRouter
|
344
|
+
flag_rocket and target_urlpaths.each do |path|
|
345
|
+
rocket_app.call(newenv(path)) # warm up
|
346
|
+
task "(RocketRouter) #{path}" do
|
347
|
+
env = newenv(path)
|
348
|
+
i = 0; n = N
|
349
|
+
while (i += 1) <= n
|
350
|
+
result = rocket_app.call(env)
|
351
|
+
#result = rocket_app.find(path)
|
275
352
|
end
|
353
|
+
result
|
276
354
|
end
|
277
355
|
end
|
278
356
|
|
279
357
|
### Rack::JetRouter
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
end
|
289
|
-
tuple
|
358
|
+
flag_jet and target_urlpaths.each do |path|
|
359
|
+
jet_app.call(newenv(path)) # warm up
|
360
|
+
task "(JetRouter) #{path}" do
|
361
|
+
env = newenv(path)
|
362
|
+
i = 0; n = N
|
363
|
+
while (i += 1) <= n
|
364
|
+
result = jet_app.call(env)
|
365
|
+
#result = jet_app.find(path)
|
290
366
|
end
|
367
|
+
result
|
291
368
|
end
|
292
369
|
end
|
293
370
|
|
294
|
-
###
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
tuple = mpx_app.call(env)
|
303
|
-
end
|
304
|
-
tuple
|
371
|
+
### Keight
|
372
|
+
flag_keight and target_urlpaths.each do |path|
|
373
|
+
keight_app.call(newenv(path)) # warm up
|
374
|
+
task "(Keight) #{path}" do
|
375
|
+
env = newenv(path)
|
376
|
+
i = 0; n = N
|
377
|
+
while (i += 1) <= n
|
378
|
+
result = keight_app.call(env)
|
305
379
|
end
|
380
|
+
result
|
306
381
|
end
|
307
382
|
end
|
308
383
|
|
309
|
-
###
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
end
|
319
|
-
tuple
|
384
|
+
### Hanami::Router
|
385
|
+
flag_hanami and target_urlpaths.each do |path|
|
386
|
+
hanami_app.call(newenv(path)) # warm up
|
387
|
+
task "(Hanami::Router) #{path}" do
|
388
|
+
env = newenv(path)
|
389
|
+
i = 0; n = N
|
390
|
+
while (i += 1) <= n
|
391
|
+
result = hanami_app.call(env)
|
392
|
+
#result = hanami_app.route(path)
|
320
393
|
end
|
394
|
+
result
|
321
395
|
end
|
322
396
|
end
|
323
397
|
|
324
|
-
###
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
end
|
334
|
-
tuple
|
398
|
+
### HttpRouter
|
399
|
+
flag_httprouter and target_urlpaths.each do |path|
|
400
|
+
httprouter_app.call(newenv(path)) # warm up
|
401
|
+
task "(HttpRouter) #{path}" do
|
402
|
+
env = newenv(path)
|
403
|
+
i = 0; n = N
|
404
|
+
while (i += 1) <= n
|
405
|
+
result = httprouter_app.call(env)
|
406
|
+
#result = httprouter_app.route(path)
|
335
407
|
end
|
408
|
+
result
|
336
409
|
end
|
337
410
|
end
|
338
411
|
|
339
|
-
###
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
tuple = hanami_app.call(env)
|
348
|
-
end
|
349
|
-
tuple
|
412
|
+
### Rack::Multiplexer
|
413
|
+
flag_multiplexer and target_urlpaths.each do |path|
|
414
|
+
multiplexer_app.call(newenv(path)) # warm up
|
415
|
+
task "(Multiplexer) #{path}" do
|
416
|
+
env = newenv(path)
|
417
|
+
i = 0; n = N
|
418
|
+
while (i += 1) <= n
|
419
|
+
result = multiplexer_app.call(env)
|
350
420
|
end
|
421
|
+
result
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
### Sinatra
|
426
|
+
flag_sinatra and target_urlpaths.each do |path|
|
427
|
+
sinatra_app.call(newenv(path)) # warm up
|
428
|
+
task "(Sinatra) #{path}" do
|
429
|
+
env = newenv(path)
|
430
|
+
i = 0; n = N
|
431
|
+
while (i += 1) <= n
|
432
|
+
result = sinatra_app.call(env)
|
433
|
+
end
|
434
|
+
result
|
351
435
|
end
|
352
436
|
end
|
353
437
|
|
354
438
|
## validation
|
355
|
-
validate do |
|
356
|
-
tuple =
|
357
|
-
assert tuple[0] == 200, "200
|
439
|
+
validate do |result| # or: validate do |result, task_name, tag|
|
440
|
+
tuple = result
|
441
|
+
assert tuple[0] == 200, "Expected 200 but got #{tuple[0]}"
|
358
442
|
body = tuple[2].each {|x| break x }
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
443
|
+
expected_bodies = [
|
444
|
+
"<h1>hello</h1>",
|
445
|
+
"<h1>id=123</h1>",
|
446
|
+
"<h1>id=456</h1>",
|
447
|
+
"<h1>id=123, comment_id=7</h1>",
|
448
|
+
"<h1>id=456, comment_id=7</h1>",
|
449
|
+
]
|
450
|
+
assert expected_bodies.include?(body), "#{body.inspect}: Unpexpected body."
|
365
451
|
end
|
366
452
|
|
367
453
|
end
|