rack-jet_router 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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 --N=1000_000
9
- ## $ ruby bench.rb --N=1000_000 --rack=0 --sinatra=0 --multiplexer=0 # --hanami=0 --jetrouter=0 --keight=0
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
- require 'rack' rescue nil unless '0' == $opt_rack
19
- require 'rack/jet_router' rescue nil unless '0' == $opt_jetrouter
20
- require 'rack/multiplexer' rescue nil unless '0' == $opt_multiplexer
21
- require 'sinatra/base' rescue nil unless '0' == $opt_sinatra
22
- require 'keight' rescue nil unless '0' == $opt_keight
23
- require 'hanami/router' rescue nil unless '0' == $opt_hanami
24
-
25
- flag_rack = defined?(Rack) && $opt_rack != '0'
26
- flag_jetrouter = defined?(Rack::JetRouter)
27
- flag_multiplexer = defined?(Rack::Multiplexer)
28
- flag_sinatra = defined?(Sinatra)
29
- flag_keight = defined?(K8)
30
- flag_hanami = defined?(Hanami::Router)
31
-
32
- def version_info(name, opt, flag)
33
- version = opt == '0' ? "(skipped)" \
34
- : ! flag ? "(not installed)" \
35
- : yield
36
- return "** %-20s : %s" % [name, version]
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
- puts version_info("rack" , $opt_rack , flag_rack ) { Rack.release }
40
- puts version_info("rack-jet_router" , $opt_jetrouter , flag_jetrouter ) { Rack::JetRouter::RELEASE }
41
- puts version_info("rack-multiplexer", $opt_multiplexer, flag_multiplexer) { Rack::Multiplexer::VERSION }
42
- puts version_info("sinatra" , $opt_sinatra , flag_sinatra ) { Sinatra::VERSION }
43
- puts version_info("keight" , $opt_keight , flag_keight ) { K8::RELEASE }
44
- puts version_info("hanami-router" , $opt_hanami , flag_hanami ) { Hanami::Router::VERSION }
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
- if flag_rack
61
-
62
- rack_app1 = proc do |env|
63
- [200, {"Content-Type"=>"text/html"}, ["<h1>hello</h1>"]]
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
- rack_app4 = proc do |env; req, resp|
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
- end
78
-
79
-
80
- if flag_jetrouter
81
-
82
- jet_router = proc {
83
- handler1 = proc {|env|
84
- [200, {"Content-Type"=>"text/html"}, ["<h1>hello</h1>"]]
85
- }
86
- handler2 = proc {|env|
87
- d = env['rack.urlpath_params']
88
- [200, {"Content-Type"=>"text/html"}, ["<h1>id=#{d['id']}</h1>"]]
89
- }
90
- handler3 = proc {|env|
91
- d = env['rack.urlpath_params']
92
- [200, {"Content-Type"=>"text/html"}, ["<h1>id=#{d['id']}, comment_id=#{d['comment_id']}</h1>"]]
93
- }
94
- mapping = [
95
- ['/api', ENTRIES.each_with_object([]) {|x, arr|
96
- arr << ["/#{x}", [
97
- ['', handler1],
98
- ['/:id', handler2],
99
- ]]
100
- arr << ["/#{x}/:id/comments", [
101
- ['/:comment_id', handler3],
102
- ]]
103
- },
104
- ],
105
- ]
106
- opts = {
107
- cache_size: ($opt_k8cache || 0).to_i,
108
- _enable_range: $opt_k8range != '0',
109
- #prefix_minlength_target: /\A\/api\/\w/,
110
- }
111
- Rack::JetRouter.new(mapping, **opts)
112
- }.call()
113
-
114
- end
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
- if flag_multiplexer
118
-
119
- mpx_app = Rack::Multiplexer.new().tap do |app|
120
- handler1 = proc {|env|
121
- [200, {"Content-Type"=>"text/html"}, ["<h1>hello</h1>"]]
122
- }
123
- handler2 = proc {|env|
124
- d = env['rack.request.query_hash']
125
- [200, {"Content-Type"=>"text/html"}, ["<h1>id=#{d['id']}</h1>"]]
126
- }
127
- handler3 = proc {|env|
128
- d = env['rack.request.query_hash']
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
- app.get "/api/#{x}" , handler1
133
- app.get "/api/#{x}/:id" , handler2
134
- app.get "/api/#{x}/:id/comments/:comment_id" , handler3
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}" do "<h1>hello</h1>" end
152
- get "/api/#{x}/:id" do "<h1>id=#{params['id']}</h1>" end
153
- get "/api/#{x}/:id/comments/:comment_id" do "<h1>id=#{params['id']}, comment_id=#{params['comment_id']}</h1>" end
154
- end
155
- end
156
-
157
- sina_app = SinaApp.new
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
- end
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 = ($opt_N || 100000).to_i
302
+ N = Benchmarker::OPTIONS.delete(:loop) || 1000_000
239
303
  title = "Router library benchmark"
240
- Benchmarker.scope(title, width: 33, loop: 1, iter: 1, extra: 0, sleep: 0) do
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
- if flag_rack
255
- #target_urlpaths.each do |x|
256
- # rack_app1.call(newenv(x)) # warm up
257
- # task "(Rack plain app) #{x}" do # no routing
258
- # env = newenv(x)
259
- # i = 0; n = N
260
- # while (i += 1) <= n
261
- # tuple = rack_app1.call(env)
262
- # end
263
- # tuple
264
- # end
265
- #end
266
- target_urlpaths.each do |x|
267
- rack_app4.call(newenv(x)) # warm up
268
- task "(Rack::Req+Res) #{x}" do # no routing
269
- env = newenv(x)
270
- i = 0; n = N
271
- while (i += 1) <= n
272
- tuple = rack_app4.call(env)
273
- end
274
- tuple
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
- if flag_jetrouter
281
- target_urlpaths.each do |x|
282
- jet_router.call(newenv(x)) # warm up
283
- task "(JetRouter) #{x}" do
284
- env = newenv(x)
285
- i = 0; n = N
286
- while (i += 1) <= n
287
- tuple = jet_router.call(env)
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
- ### Rack::Multiplexer
295
- if flag_multiplexer
296
- target_urlpaths.each do |x|
297
- mpx_app.call(newenv(x)) # warm up
298
- task "(Multiplexer) #{x}" do
299
- env = newenv(x)
300
- i = 0; n = N
301
- while (i += 1) <= n
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
- ### Sinatra
310
- if flag_sinatra
311
- target_urlpaths.each do |x|
312
- sina_app.call(newenv(x)) # warm up
313
- task "(Sinatra) #{x}" do
314
- env = newenv(x)
315
- i = 0; n = N
316
- while (i += 1) <= n
317
- tuple = sina_app.call(env)
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
- ### Keight
325
- if flag_keight
326
- target_urlpaths.each do |x|
327
- k8_app.call(newenv(x)) # warm up
328
- task "(Keight) #{x}" do
329
- env = newenv(x)
330
- i = 0; n = N
331
- while (i += 1) <= n
332
- tuple = k8_app.call(env)
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
- ### Hanami
340
- if flag_hanami
341
- target_urlpaths.each do |x|
342
- hanami_app.call(newenv(x)) # warm up
343
- task "(Hanami::Router) #{x}" do
344
- env = newenv(x)
345
- i = 0; n = N
346
- while (i += 1) <= n
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 |val| # or: validate do |val, task_name, tag|
356
- tuple = val
357
- assert tuple[0] == 200, "200 expected but got #{tuple[0]}"
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
- assert body == "<h1>hello</h1>" || \
360
- body == "<h1>id=123</h1>" || \
361
- body == "<h1>id=456</h1>" || \
362
- body == "<h1>id=123, comment_id=7</h1>" || \
363
- body == "<h1>id=456, comment_id=7</h1>", \
364
- "#{body.inspect}: unpexpected body"
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