rack-jet_router 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "rack-jet_router"
5
+ spec.version = '$Release: 1.0.0 $'.split()[1]
6
+ spec.authors = ["makoto kuwata"]
7
+ spec.email = ["kwa(at)kuwata-lab.com"]
8
+
9
+ spec.summary = "Super-fast router class for Rack"
10
+ spec.description = <<'END'
11
+ Super-fast router class for Rack application, derived from Keight.rb.
12
+ END
13
+ spec.homepage = "https://github.com/kwatch/rack-jet_router"
14
+ spec.license = "MIT-License"
15
+
16
+ spec.files = Dir[*%w[
17
+ README.md MIT-LICENSE Rakefile rack-jet_router.gemspec
18
+ lib/**/*.rb
19
+ test/test_helper.rb test/**/*_test.rb
20
+ ]]
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.required_ruby_version = '>= 2.0'
24
+ spec.add_development_dependency "bundler"
25
+ spec.add_development_dependency "minitest"
26
+ spec.add_development_dependency "minitest-ok"
27
+ end
@@ -0,0 +1,618 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ###
4
+ ### $Release: 1.0.0 $
5
+ ### $Copyright: copyright(c) 2015 kuwata-lab.com all rights reserved $
6
+ ### $License: MIT License $
7
+ ###
8
+
9
+ require_relative '../test_helper'
10
+
11
+
12
+ describe Rack::JetRouter do
13
+
14
+ welcome_app = proc {|env| [200, {}, ["welcome_app"]]}
15
+ #
16
+ book_list_api = proc {|env| [200, {}, ["book_list_api"]]}
17
+ book_create_api = proc {|env| [200, {}, ["book_create_api"]]}
18
+ book_new_api = proc {|env| [200, {}, ["book_new_api"]]}
19
+ book_show_api = proc {|env| [200, {}, ["book_show_api"]]}
20
+ book_update_api = proc {|env| [200, {}, ["book_update_api"]]}
21
+ book_delete_api = proc {|env| [200, {}, ["book_delete_api"]]}
22
+ book_edit_api = proc {|env| [200, {}, ["book_edit_api"]]}
23
+ #
24
+ comment_create_api = proc {|env| [200, {}, ["comment_create_api"]]}
25
+ comment_update_api = proc {|env| [200, {}, ["comment_update_api"]]}
26
+ #
27
+ admin_book_list_app = proc {|env| [200, {}, ["admin_book_list_app"]]}
28
+ admin_book_create_app = proc {|env| [200, {}, ["admin_book_create_app"]]}
29
+ admin_book_new_app = proc {|env| [200, {}, ["admin_book_new_app"]]}
30
+ admin_book_show_app = proc {|env| [200, {}, ["admin_book_show_app"]]}
31
+ admin_book_update_app = proc {|env| [200, {}, ["admin_book_update_app"]]}
32
+ admin_book_delete_app = proc {|env| [200, {}, ["admin_book_delete_app"]]}
33
+ admin_book_edit_app = proc {|env| [200, {}, ["admin_book_edit_app"]]}
34
+ #
35
+ whole_urlpath_mapping = [
36
+ ['/' , welcome_app],
37
+ ['/index.html' , welcome_app],
38
+ ['/api' , [
39
+ ['/books' , [
40
+ ['' , book_list_api],
41
+ ['/new' , book_new_api],
42
+ ['/:id' , book_show_api],
43
+ ['/:id/edit' , book_edit_api],
44
+ ]],
45
+ ['/books/:book_id/comments', [
46
+ ['' , comment_create_api],
47
+ ['/:comment_id' , comment_update_api],
48
+ ]],
49
+ ]],
50
+ ['/admin' , [
51
+ ['/books' , [
52
+ ['' , {:GET=>admin_book_list_app, :POST=>admin_book_create_app}],
53
+ ['/:id' , {:GET=>admin_book_show_app, :PUT=>admin_book_update_app, :DELETE=>admin_book_delete_app}],
54
+ ]],
55
+ ]],
56
+ ]
57
+ #
58
+ jet_router = Rack::JetRouter.new(whole_urlpath_mapping)
59
+ #
60
+ def new_env(req_method, req_path, opts={})
61
+ opts[:method] = req_method.to_s
62
+ env = ::Rack::MockRequest.env_for(req_path, opts)
63
+ return env
64
+ end
65
+
66
+
67
+ describe '#compile_urlpath_pattern()' do
68
+
69
+ it "[!joozm] escapes metachars with backslash in text part." do
70
+ jet_router.instance_exec(self) do |_|
71
+ _.ok {compile_urlpath_pattern('/foo.html')} == ['/foo\.html', nil]
72
+ end
73
+ end
74
+
75
+ it "[!rpezs] converts '/books/:id' into '/books/([^./]+)'." do
76
+ jet_router.instance_exec(self) do |_|
77
+ _.ok {compile_urlpath_pattern('/books/:id')} == ['/books/([^./]+)', ['id']]
78
+ end
79
+ end
80
+
81
+ it "[!4dcsa] converts '/index(.:format)' into '/index(?:\.([^./]+))?'." do
82
+ jet_router.instance_exec(self) do |_|
83
+ _.ok {compile_urlpath_pattern('/index(.:format)')} == ['/index(?:\.([^./]+))?', ['format']]
84
+ end
85
+ end
86
+
87
+ it "[!1d5ya] rethrns compiled string and nil when no urlpath parameters nor parens." do
88
+ jet_router.instance_exec(self) do |_|
89
+ _.ok {compile_urlpath_pattern('/index')} == ['/index', nil]
90
+ end
91
+ end
92
+
93
+ it "[!of1zq] returns compiled string and urlpath param names when urlpath param or parens exist." do
94
+ jet_router.instance_exec(self) do |_|
95
+ _.ok {compile_urlpath_pattern('/books/:id')} == ['/books/([^./]+)', ['id']]
96
+ _.ok {compile_urlpath_pattern('/books/:id(.:format)')} == ['/books/([^./]+)(?:\.([^./]+))?', ['id', 'format']]
97
+ _.ok {compile_urlpath_pattern('/index(.html)')} == ['/index(?:\.html)?', []]
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+
104
+ describe '#compile_mapping()' do
105
+
106
+ it "[!xzo7k] returns regexp, hash, and array." do
107
+ mapping = [
108
+ ['/', welcome_app],
109
+ ['/books/:id' , book_show_api],
110
+ ]
111
+ expected = '
112
+ \A
113
+ (?:
114
+ /books/[^./]+(\z)
115
+ )
116
+ \z
117
+ '.gsub(/\s+/, '')
118
+ jet_router.instance_exec(self) do |_|
119
+ rexp, dict, list = compile_mapping(mapping)
120
+ _.ok {rexp} == Regexp.new(expected)
121
+ _.ok {dict} == {
122
+ '/' => welcome_app,
123
+ }
124
+ _.ok {list} == [
125
+ [%r'\A/books/([^./]+)\z', ['id'], book_show_api],
126
+ ]
127
+ end
128
+ end
129
+
130
+ it "[!gfxgr] deletes unnecessary grouping." do
131
+ mapping = [
132
+ ['/' , welcome_app],
133
+ ['/api/books' , book_list_api],
134
+ ]
135
+ expected = '
136
+ \A
137
+ \z
138
+ '.gsub(/\s+/, '')
139
+ jet_router.instance_exec(self) do |_|
140
+ rexp, dict, list = compile_mapping(mapping)
141
+ _.ok {rexp} == Regexp.new(expected)
142
+ _.ok {dict} == {
143
+ '/' => welcome_app,
144
+ '/api/books' => book_list_api,
145
+ }
146
+ _.ok {list} == [
147
+ ]
148
+ end
149
+ end
150
+
151
+ it "[!pv2au] deletes unnecessary urlpath regexp." do
152
+ mapping = [
153
+ ['/' , welcome_app],
154
+ ['/api', [
155
+ ['/books', [
156
+ ['' , book_list_api],
157
+ ['/new' , book_new_api],
158
+ ]],
159
+ ['/books2', [
160
+ ['/:id' , book_show_api],
161
+ ]],
162
+ ]],
163
+ ]
164
+ expected = '
165
+ \A
166
+ (?:
167
+ /api
168
+ (?:
169
+ /books2
170
+ (?:/[^./]+(\z))
171
+ )
172
+ )
173
+ \z
174
+ '.gsub(/\s+/, '')
175
+ jet_router.instance_exec(self) do |_|
176
+ rexp, dict, list = compile_mapping(mapping)
177
+ _.ok {rexp} == Regexp.new(expected)
178
+ _.ok {dict} == {
179
+ '/' => welcome_app,
180
+ '/api/books' => book_list_api,
181
+ '/api/books/new' => book_new_api,
182
+ }
183
+ _.ok {list} == [
184
+ [%r'\A/api/books2/([^./]+)\z', ['id'], book_show_api],
185
+ ]
186
+ end
187
+ end
188
+
189
+ it "[!l63vu] handles urlpath pattern as fixed when no urlpath params." do
190
+ mapping = [
191
+ ['/api/books' , book_list_api],
192
+ ]
193
+ expected = '
194
+ \A
195
+ \z
196
+ '.gsub(/\s+/, '')
197
+ jet_router.instance_exec(self) do |_|
198
+ rexp, dict, list = compile_mapping(mapping)
199
+ _.ok {rexp} == Regexp.new(expected)
200
+ _.ok {dict} == {
201
+ '/api/books' => book_list_api,
202
+ }
203
+ _.ok {list} == [
204
+ ]
205
+ end
206
+ end
207
+
208
+ it "[!vfytw] handles urlpath pattern as variable when urlpath param exists." do
209
+ mapping = [
210
+ ['/api/books/:id' , book_show_api],
211
+ ]
212
+ expected = '
213
+ \A
214
+ (?:
215
+ /api/books/[^./]+(\z)
216
+ )
217
+ \z
218
+ '.gsub(/\s+/, '')
219
+ jet_router.instance_exec(self) do |_|
220
+ rexp, dict, list = compile_mapping(mapping)
221
+ _.ok {rexp} == Regexp.new(expected)
222
+ _.ok {dict} == {
223
+ }
224
+ _.ok {list} == [
225
+ [%r'\A/api/books/([^./]+)\z', ['id'], book_show_api],
226
+ ]
227
+ end
228
+ end
229
+
230
+ it "[!2ktpf] handles end-point." do
231
+ mapping = [
232
+ ['/' , welcome_app],
233
+ ['/api/books' , book_list_api],
234
+ ['/api/books/:id' , book_show_api],
235
+ ]
236
+ expected = '
237
+ \A
238
+ (?:
239
+ /api/books/[^./]+(\z)
240
+ )
241
+ \z
242
+ '.gsub(/\s+/, '')
243
+ jet_router.instance_exec(self) do |_|
244
+ rexp, dict, list = compile_mapping(mapping)
245
+ _.ok {rexp} == Regexp.new(expected)
246
+ _.ok {dict} == {
247
+ '/' => welcome_app,
248
+ '/api/books' => book_list_api,
249
+ }
250
+ _.ok {list} == [
251
+ [%r'\A/api/books/([^./]+)\z', ['id'], book_show_api],
252
+ ]
253
+ end
254
+ end
255
+
256
+ it "[!ospaf] accepts nested mapping." do
257
+ mapping = [
258
+ ['/admin', [
259
+ ['/api', [
260
+ ['/books', [
261
+ ['', book_list_api],
262
+ ['/:id', book_show_api],
263
+ ]],
264
+ ]],
265
+ ]],
266
+ ]
267
+ expected = '
268
+ \A
269
+ (?:
270
+ /admin
271
+ (?:
272
+ /api
273
+ (?:
274
+ /books
275
+ (?:/[^./]+(\z))
276
+ )
277
+ )
278
+ )
279
+ \z
280
+ '.gsub(/\s+/, '')
281
+ jet_router.instance_exec(self) do |_|
282
+ rexp, dict, list = compile_mapping(mapping)
283
+ _.ok {rexp} == Regexp.new(expected)
284
+ _.ok {dict} == {
285
+ '/admin/api/books' => book_list_api,
286
+ }
287
+ _.ok {list} == [
288
+ [%r'\A/admin/api/books/([^./]+)\z', ['id'], book_show_api],
289
+ ]
290
+ end
291
+ end
292
+
293
+ describe "[!guhdc] if mapping dict is specified..." do
294
+
295
+ it "[!r7cmk] converts keys into string." do
296
+ mapping = [
297
+ ['/books', {:GET=>book_list_api, :POST=>book_create_api}]
298
+ ]
299
+ Rack::JetRouter.new([]).instance_exec(self) do |_|
300
+ rexp, dict, list = compile_mapping(mapping)
301
+ _.ok {dict['/books']} == {'GET'=>book_list_api, 'POST'=>book_create_api}
302
+ end
303
+ end
304
+
305
+ it "[!z9kww] allows 'ANY' as request method." do
306
+ mapping = [
307
+ ['/books', {'ANY'=>book_list_api, 'POST'=>book_create_api}]
308
+ ]
309
+ Rack::JetRouter.new([]).instance_exec(self) do |_|
310
+ rexp, dict, list = compile_mapping(mapping)
311
+ _.ok {dict['/books']} == {'ANY'=>book_list_api, 'POST'=>book_create_api}
312
+ end
313
+ end
314
+
315
+ it "[!k7sme] raises error when unknown request method specified." do
316
+ mapping = [
317
+ ['/books', {"UNLOCK"=>book_list_api}]
318
+ ]
319
+ Rack::JetRouter.new([]).instance_exec(self) do |_|
320
+ pr = proc { compile_mapping(mapping) }
321
+ _.ok {pr}.raise?(ArgumentError, '"UNLOCK": unknown request method.')
322
+ end
323
+ end
324
+
325
+ end
326
+
327
+ end
328
+
329
+
330
+ describe '#error_not_found()' do
331
+
332
+ it "[!mlruv] returns 404 response." do
333
+ expected = [404, {"Content-Type"=>"text/plain"}, ["404 Not Found"]]
334
+ env = new_env('GET', '/xxx')
335
+ jet_router.instance_exec(self) do |_|
336
+ _.ok {error_not_found(env)} == expected
337
+ end
338
+ end
339
+
340
+ end
341
+
342
+
343
+ describe '#error_not_allowed()' do
344
+
345
+ it "[!mjigf] returns 405 response." do
346
+ expected = [405, {"Content-Type"=>"text/plain"}, ["405 Method Not Allowed"]]
347
+ env = new_env('POST', '/')
348
+ jet_router.instance_exec(self) do |_|
349
+ _.ok {error_not_allowed(env)} == expected
350
+ end
351
+ end
352
+
353
+ end
354
+
355
+
356
+ describe '#initialize()' do
357
+
358
+ it "[!u2ff4] compiles urlpath mapping." do
359
+ jet_router.instance_exec(self) do |_|
360
+ expected = '
361
+ \A
362
+ (?:
363
+ /api
364
+ (?:
365
+ /books
366
+ (?:/[^./]+(\z)|/[^./]+/edit(\z))
367
+ |
368
+ /books/[^./]+/comments
369
+ (?:(\z)|/[^./]+(\z))
370
+ )
371
+ |
372
+ /admin
373
+ (?:/books
374
+ (?:/[^./]+(\z))
375
+ )
376
+ )
377
+ \z
378
+ '.gsub(/\s+/, '')
379
+ _.ok {@urlpath_rexp} == Regexp.new(expected)
380
+ _.ok {@fixed_urlpath_dict} == {
381
+ '/' => welcome_app,
382
+ '/index.html' => welcome_app,
383
+ '/api/books' => book_list_api,
384
+ '/api/books/new' => book_new_api,
385
+ '/admin/books' => {
386
+ 'GET'=>admin_book_list_app,
387
+ 'POST'=>admin_book_create_app,
388
+ },
389
+ }
390
+ _.ok {@variable_urlpath_list} == [
391
+ [%r'\A/api/books/([^./]+)\z', ['id'], book_show_api],
392
+ [%r'\A/api/books/([^./]+)/edit\z', ['id'], book_edit_api],
393
+ [%r'\A/api/books/([^./]+)/comments\z', ['book_id'], comment_create_api],
394
+ [%r'\A/api/books/([^./]+)/comments/([^./]+)\z', ['book_id', 'comment_id'], comment_update_api],
395
+ [%r'\A/admin/books/([^./]+)\z', ['id'], {'GET' => admin_book_show_app,
396
+ 'PUT' => admin_book_update_app,
397
+ 'DELETE' => admin_book_delete_app}],
398
+ ]
399
+ end
400
+ end
401
+
402
+ end
403
+
404
+
405
+ describe '#find()' do
406
+
407
+ it "[!ijqws] returns mapped object and urlpath parameter values when urlpath found." do
408
+ ret = jet_router.find('/api/books/123')
409
+ ok {ret} == [book_show_api, {"id"=>"123"}]
410
+ end
411
+
412
+ it "[!vpdzn] returns nil when urlpath not found." do
413
+ ok {jet_router.find('/api')} == nil
414
+ ok {jet_router.find('/api/book')} == nil
415
+ ok {jet_router.find('/api/books/')} == nil
416
+ end
417
+
418
+ it "[!24khb] finds in fixed urlpaths at first." do
419
+ ok {jet_router.find('/')} == [welcome_app, nil]
420
+ ok {jet_router.find('/api/books')} == [book_list_api, nil]
421
+ dict = {'GET'=>admin_book_list_app, 'POST'=>admin_book_create_app}
422
+ ok {jet_router.find('/admin/books')} == [dict, nil]
423
+ end
424
+
425
+ it "[!iwyzd] urlpath param value is nil when found in fixed urlpaths." do
426
+ obj, vars = jet_router.find('/')
427
+ ok {vars} == nil
428
+ obj, vars = jet_router.find('/api/books')
429
+ ok {vars} == nil
430
+ end
431
+
432
+ it "[!upacd] finds in variable urlpath cache if it is enabled." do
433
+ mapping = [
434
+ ['/api/books/:id', book_show_api],
435
+ ]
436
+ r = Rack::JetRouter.new(mapping, urlpath_cache_size: 3)
437
+ pair = r.find('/api/books/123')
438
+ ok {pair} == [book_show_api, {"id"=>"123"}]
439
+ r.instance_exec(self) do |_|
440
+ _.ok {@variable_urlpath_cache} == {'/api/books/123'=>pair}
441
+ #
442
+ @variable_urlpath_cache['/api/books/999'] = [book_list_api, {"ID"=>"111"}]
443
+ end
444
+ pair = r.find('/api/books/999')
445
+ ok {pair} == [book_list_api, {"ID"=>"111"}]
446
+ end
447
+
448
+ it "[!84inr] caches result when variable urlpath cache enabled." do
449
+ mapping = [
450
+ ['/books/:id', book_show_api],
451
+ ]
452
+ r = Rack::JetRouter.new(mapping, urlpath_cache_size: 3)
453
+ #
454
+ pair1 = r.find('/books/1'); ok {pair1} == [book_show_api, {"id"=>"1"}]
455
+ pair2 = r.find('/books/2'); ok {pair2} == [book_show_api, {"id"=>"2"}]
456
+ pair3 = r.find('/books/3'); ok {pair3} == [book_show_api, {"id"=>"3"}]
457
+ r.instance_exec(self) do |_|
458
+ _.ok {@variable_urlpath_cache} == {
459
+ '/books/1'=>pair1,
460
+ '/books/2'=>pair2,
461
+ '/books/3'=>pair3,
462
+ }
463
+ end
464
+ #
465
+ pair4 = r.find('/books/4'); ok {pair4} == [book_show_api, {"id"=>"4"}]
466
+ r.instance_exec(self) do |_|
467
+ _.ok {@variable_urlpath_cache} == {
468
+ '/books/2'=>pair2,
469
+ '/books/3'=>pair3,
470
+ '/books/4'=>pair4,
471
+ }
472
+ end
473
+ end
474
+
475
+ it "[!1zx7t] variable urlpath cache is based on LRU." do
476
+ mapping = [
477
+ ['/books/:id', book_show_api],
478
+ ]
479
+ r = Rack::JetRouter.new(mapping, urlpath_cache_size: 3)
480
+ #
481
+ pair1 = r.find('/books/1')
482
+ pair2 = r.find('/books/2')
483
+ pair3 = r.find('/books/3')
484
+ pair4 = r.find('/books/4')
485
+ r.instance_exec(self) do |_|
486
+ _.ok {@variable_urlpath_cache} == {
487
+ '/books/2'=>pair2,
488
+ '/books/3'=>pair3,
489
+ '/books/4'=>pair4,
490
+ }
491
+ end
492
+ #
493
+ ok {r.find('/books/3')} == pair3
494
+ r.instance_exec(self) do |_|
495
+ _.ok {@variable_urlpath_cache} == {
496
+ '/books/2'=>pair2,
497
+ '/books/4'=>pair4,
498
+ '/books/3'=>pair3,
499
+ }
500
+ end
501
+ #
502
+ ok {r.find('/books/1')} == pair1
503
+ r.instance_exec(self) do |_|
504
+ _.ok {@variable_urlpath_cache} == {
505
+ '/books/4'=>pair4,
506
+ '/books/3'=>pair3,
507
+ '/books/1'=>pair1,
508
+ }
509
+ end
510
+ end
511
+
512
+ end
513
+
514
+
515
+ describe '#call()' do
516
+
517
+ it "[!hse47] invokes app mapped to request urlpath." do
518
+ ok {jet_router.call(new_env(:GET, '/api/books/123'))} == [200, {}, ["book_show_api"]]
519
+ ok {jet_router.call(new_env(:PUT, '/admin/books/123'))} == [200, {}, ["admin_book_update_app"]]
520
+ end
521
+
522
+ it "[!fpw8x] finds mapped app according to env['PATH_INFO']." do
523
+ ok {jet_router.call(new_env(:GET, '/api/books'))} == [200, {}, ["book_list_api"]]
524
+ ok {jet_router.call(new_env(:GET, '/api/books/123'))} == [200, {}, ["book_show_api"]]
525
+ end
526
+
527
+ it "[!wxt2g] guesses correct urlpath and redirects to it automaticaly when request path not found." do
528
+ headers = {"Content-Type"=>"text/plain", "Location"=>"/api/books"}
529
+ content = "Redirect to /api/books"
530
+ ok {jet_router.call(new_env(:GET, '/api/books/'))} == [301, headers, [content]]
531
+ #
532
+ headers = {"Content-Type"=>"text/plain", "Location"=>"/api/books/78"}
533
+ content = "Redirect to /api/books/78"
534
+ ok {jet_router.call(new_env(:GET, '/api/books/78/'))} == [301, headers, [content]]
535
+ end
536
+
537
+ it "[!3vsua] doesn't redict automatically when request path is '/'." do
538
+ r = Rack::JetRouter.new([['/api/books', book_list_api]])
539
+ ok {r.call(new_env(:GET, '/'))} == [404, {"Content-Type"=>"text/plain"}, ["404 Not Found"]]
540
+ end
541
+
542
+ it "[!30x0k] returns 404 when request urlpath not found." do
543
+ expected = [404, {"Content-Type"=>"text/plain"}, ["404 Not Found"]]
544
+ ok {jet_router.call(new_env(:GET, '/xxx'))} == expected
545
+ ok {jet_router.call(new_env(:GET, '/api/book'))} == expected
546
+ end
547
+
548
+ describe "[!gclbs] if mapped object is a Hash..." do
549
+
550
+ it "[!p1fzn] invokes app mapped to request method." do
551
+ ok {jet_router.call(new_env(:GET, '/admin/books'))} == [200, {}, ["admin_book_list_app"]]
552
+ ok {jet_router.call(new_env(:POST, '/admin/books'))} == [200, {}, ["admin_book_create_app"]]
553
+ ok {jet_router.call(new_env(:GET, '/admin/books/123'))} == [200, {}, ["admin_book_show_app"]]
554
+ ok {jet_router.call(new_env(:PUT, '/admin/books/123'))} == [200, {}, ["admin_book_update_app"]]
555
+ ok {jet_router.call(new_env(:DELETE, '/admin/books/123'))} == [200, {}, ["admin_book_delete_app"]]
556
+ end
557
+
558
+ it "[!5m64a] returns 405 when request method is not allowed." do
559
+ expected = [405, {"Content-Type"=>"text/plain"}, ["405 Method Not Allowed"]]
560
+ ok {jet_router.call(new_env(:PUT, '/admin/books'))} == expected
561
+ ok {jet_router.call(new_env(:FOOBAR, '/admin/books'))} == expected
562
+ end
563
+
564
+ it "[!ys1e2] uses GET method when HEAD is not mapped." do
565
+ ok {jet_router.call(new_env(:HEAD, '/admin/books'))} == [200, {}, ["admin_book_list_app"]]
566
+ ok {jet_router.call(new_env(:HEAD, '/admin/books/123'))} == [200, {}, ["admin_book_show_app"]]
567
+ end
568
+
569
+ it "[!2hx6j] try ANY method when request method is not mapped." do
570
+ mapping = [
571
+ ['/admin/books', {:ANY=>admin_book_list_app}]
572
+ ]
573
+ r = Rack::JetRouter.new(mapping)
574
+ expected = [200, {}, ["admin_book_list_app"]]
575
+ ok {r.call(new_env(:GET, '/admin/books'))} == expected
576
+ ok {r.call(new_env(:POST, '/admin/books'))} == expected
577
+ ok {r.call(new_env(:PUT, '/admin/books'))} == expected
578
+ ok {r.call(new_env(:DELETE, '/admin/books'))} == expected
579
+ end
580
+
581
+ end
582
+
583
+ it "[!2c32f] stores urlpath parameters as env['rack.urlpath_params']." do
584
+ env = new_env(:GET, '/api/books')
585
+ jet_router.call(env)
586
+ ok {env['rack.urlpath_params']} == nil
587
+ env = new_env(:GET, '/api/books/123')
588
+ jet_router.call(env)
589
+ ok {env['rack.urlpath_params']} == {"id"=>"123"}
590
+ env = new_env(:GET, '/api/books/123/comments/999')
591
+ jet_router.call(env)
592
+ ok {env['rack.urlpath_params']} == {"book_id"=>"123", "comment_id"=>"999"}
593
+ #
594
+ env = new_env(:GET, '/admin/books')
595
+ jet_router.call(env)
596
+ ok {env['rack.urlpath_params']} == nil
597
+ env = new_env(:GET, '/admin/books/123')
598
+ jet_router.call(env)
599
+ ok {env['rack.urlpath_params']} == {"id"=>"123"}
600
+ end
601
+
602
+ end
603
+
604
+
605
+ describe 'REQUEST_METHODS' do
606
+
607
+ it "[!haggu] contains available request methods." do
608
+ Rack::JetRouter::REQUEST_METHODS.each do |k, v|
609
+ ok {k}.is_a?(String)
610
+ ok {v}.is_a?(Symbol)
611
+ ok {v.to_s} == k
612
+ end
613
+ end
614
+
615
+ end
616
+
617
+
618
+ end