rack-jet_router 1.3.1 → 1.4.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.
- checksums.yaml +4 -4
- data/CHANGES.md +10 -0
- data/README.md +80 -72
- data/bench/Gemfile +5 -4
- data/bench/Rakefile.rb +3 -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
|