rack-jet_router 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +69 -24
- data/lib/rack/jet_router.rb +88 -62
- data/rack-jet_router.gemspec +1 -1
- data/test/rack/jet_router_test.rb +94 -38
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dbed04ca4adfdce5a3e82234dd6182e18519dc62
|
4
|
+
data.tar.gz: bdec9db77792ffba5bca3e05ae4bf62f35cbf555
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d90ffbb230351b7f0054d7d382b377dca41832cbfe2a47d6793c0ac88dbc075104535bf4529a6e1c50251365faadefb8d1fa02342d7104420cd8eb6316ea495
|
7
|
+
data.tar.gz: af141052683402fd8dedc4538b548bab59c816714e423236fe83cd70d2865f77337f5c6b9e0d93380e49fe4547995e36f256943be6426a20b1d04a82cfe69697
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Rack::JetRouter
|
2
2
|
|
3
|
-
($Release: 1.
|
3
|
+
($Release: 1.2.0 $)
|
4
4
|
|
5
5
|
Rack::JetRouter is crazy-fast router library for Rack application,
|
6
6
|
derived from [Keight.rb](https://github.com/kwatch/keight/tree/ruby).
|
@@ -10,22 +10,40 @@ Rack::JetRouter requires Ruby >= 2.0.
|
|
10
10
|
|
11
11
|
## Benchmark
|
12
12
|
|
13
|
-
Benchmark script is [here](https://github.com/kwatch/rack-jet_router/blob/
|
13
|
+
Benchmark script is [here](https://github.com/kwatch/rack-jet_router/blob/release/bench/bench.rb).
|
14
14
|
|
15
|
-
|
15
|
+
| Name | Version |
|
16
|
+
| ------------------ | ------- |
|
17
|
+
| Ruby | 2.3.1 |
|
18
|
+
| Rack | 1.6.4 |
|
19
|
+
| Rack::JetRouter | 1.2.0 |
|
20
|
+
| Rack::Multiplexer | 0.0.8 |
|
21
|
+
| Sinatra | 1.4.6 |
|
22
|
+
| Keight.rb | 0.3.0 |
|
23
|
+
| Hanami | 0.8.0 |
|
24
|
+
|
25
|
+
(Macbook Air, Intel Core i7 1.7GHz, OS X EL Capitan)
|
26
|
+
|
27
|
+
|
28
|
+
### JetRouter vs. Rack vs. Sinatra vs. Keight.rb vs. Hanami:
|
16
29
|
|
17
30
|
```
|
18
|
-
## Ranking
|
19
|
-
(Rack plain) /api/aaa01
|
20
|
-
(Rack plain) /api/aaa01/123
|
21
|
-
(
|
22
|
-
(
|
23
|
-
(
|
24
|
-
(
|
25
|
-
(
|
26
|
-
(
|
27
|
-
(
|
28
|
-
(
|
31
|
+
## Ranking real
|
32
|
+
(Rack plain) /api/aaa01 0.9316 (100.0%) ********************
|
33
|
+
(Rack plain) /api/aaa01/123 1.0222 ( 91.1%) ******************
|
34
|
+
(JetRouter) /api/aaa01 1.4191 ( 65.6%) *************
|
35
|
+
(JetRouter) /api/aaa01/123 6.0146 ( 15.5%) ***
|
36
|
+
(Multiplexer) /api/aaa01 6.1026 ( 15.3%) ***
|
37
|
+
(Keight.rb) /api/aaa01 7.2330 ( 12.9%) ***
|
38
|
+
(R::Req+Res) /api/aaa01 10.7835 ( 8.6%) **
|
39
|
+
(R::Req+Res) /api/aaa01/123 10.8412 ( 8.6%) **
|
40
|
+
(Keight.rb) /api/aaa01/123 10.8708 ( 8.6%) **
|
41
|
+
(Hanami::Router) /api/zzz26 11.5185 ( 8.1%) **
|
42
|
+
(Hanami::Router) /api/aaa01 11.7033 ( 8.0%) **
|
43
|
+
(Hanami::Router) /api/aaa01/123 17.9229 ( 5.2%) *
|
44
|
+
(Multiplexer) /api/aaa01/123 18.6987 ( 5.0%) *
|
45
|
+
(Sinatra) /api/aaa01 109.7597 ( 0.8%)
|
46
|
+
(Sinatra) /api/aaa01/123 121.3258 ( 0.8%)
|
29
47
|
```
|
30
48
|
|
31
49
|
* If URL path has no path parameter (such as `/api/hello`),
|
@@ -34,6 +52,7 @@ Benchmark script is [here](https://github.com/kwatch/rack-jet_router/blob/dev/be
|
|
34
52
|
Rack::JetRouter becomes slower, but it is enough small (about 6usec/req).
|
35
53
|
* Overhead of Rack::JetRouter is smaller than that of Rack::Reqeuast +
|
36
54
|
Rack::Response.
|
55
|
+
* Hanami is a litte slow.
|
37
56
|
* Sinatra is too slow.
|
38
57
|
|
39
58
|
|
@@ -41,17 +60,18 @@ Benchmark script is [here](https://github.com/kwatch/rack-jet_router/blob/dev/be
|
|
41
60
|
|
42
61
|
```
|
43
62
|
## Ranking usec/req Graph (longer=faster)
|
44
|
-
(JetRouter) /api/aaa01 1.
|
45
|
-
(JetRouter) /api/
|
46
|
-
(JetRouter) /api/
|
47
|
-
(
|
48
|
-
(
|
49
|
-
(Multiplexer) /api/aaa01/123 18.
|
50
|
-
(Multiplexer) /api/zzz26
|
51
|
-
(Multiplexer) /api/zzz26/789
|
63
|
+
(JetRouter) /api/aaa01 1.4191 ( 65.6%) *************
|
64
|
+
(JetRouter) /api/zzz26 1.4300 ( 65.1%) *************
|
65
|
+
(JetRouter) /api/aaa01/123 6.0146 ( 15.5%) ***
|
66
|
+
(Multiplexer) /api/aaa01 6.1026 ( 15.3%) ***
|
67
|
+
(JetRouter) /api/zzz26/789 6.9102 ( 13.5%) ***
|
68
|
+
(Multiplexer) /api/aaa01/123 18.6987 ( 5.0%) *
|
69
|
+
(Multiplexer) /api/zzz26 30.7618 ( 3.0%) *
|
70
|
+
(Multiplexer) /api/zzz26/789 42.6660 ( 2.2%)
|
52
71
|
```
|
53
72
|
|
54
|
-
* JetRouter is about
|
73
|
+
* JetRouter is about 4~6 times faster than Rack::Multiplexer.
|
74
|
+
* Rack::Multiplexer is getting worse in promotion to the number of URL paths.
|
55
75
|
|
56
76
|
|
57
77
|
## Examples
|
@@ -199,6 +219,18 @@ end
|
|
199
219
|
```
|
200
220
|
|
201
221
|
|
222
|
+
### Auto-redirection.
|
223
|
+
|
224
|
+
Rack::JetRouter implements auto-redirection.
|
225
|
+
|
226
|
+
* When `/foo` is provided and `/foo/` is requested, then Rack::JetRouter redirects to `/foo` automatically.
|
227
|
+
* When `/foo/` is provided and `/foo` is requested, then Rack::JetRouter redirects to `/foo/` automatically.
|
228
|
+
|
229
|
+
Notice that auto-redirection is occurred only on `GET` or `HEAD` methods, because
|
230
|
+
browser cannot handle redirection on `POST`, `PUT`, and `DELETE` methods correctly.
|
231
|
+
Don't depend on auto-redirection feature so much.
|
232
|
+
|
233
|
+
|
202
234
|
### Variable URL Path Cache
|
203
235
|
|
204
236
|
It is useful to classify URL path patterns into two types: fixed and variable.
|
@@ -249,7 +281,7 @@ Above methods are invoked from `Rack::JetRouter#call()`.
|
|
249
281
|
|
250
282
|
## Copyright and License
|
251
283
|
|
252
|
-
$Copyright: copyright(c) 2015 kuwata-lab.com all rights reserved $
|
284
|
+
$Copyright: copyright(c) 2015-2016 kuwata-lab.com all rights reserved $
|
253
285
|
|
254
286
|
$License: MIT License $
|
255
287
|
|
@@ -257,6 +289,19 @@ $License: MIT License $
|
|
257
289
|
## History
|
258
290
|
|
259
291
|
|
292
|
+
### 2016-10-16: Release 1.2.0
|
293
|
+
|
294
|
+
* Change auto-redirection to be occurred only on GET or HEAD methods.
|
295
|
+
* Code is rewrited, especially around `Rack::JetRouter#compile_mapping()`.
|
296
|
+
* Update benchmark script to support `Hanabi::Router`.
|
297
|
+
|
298
|
+
|
299
|
+
### 2015-12-29: Release 1.1.1
|
300
|
+
|
301
|
+
* Fix benchmark script.
|
302
|
+
* Fix document.
|
303
|
+
|
304
|
+
|
260
305
|
### 2015-12-28: Release 1.1.0
|
261
306
|
|
262
307
|
* **NOTICE** `Rack::JetRouter#find()` is renamed to `#lookup()`.<br>
|
data/lib/rack/jet_router.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
3
|
###
|
4
|
-
### $Release: 1.
|
4
|
+
### $Release: 1.2.0 $
|
5
5
|
### $Copyright: copyright(c) 2015 kuwata-lab.com all rights reserved $
|
6
6
|
### $License: MIT License $
|
7
7
|
###
|
@@ -55,7 +55,7 @@ module Rack
|
|
55
55
|
##
|
56
56
|
class JetRouter
|
57
57
|
|
58
|
-
RELEASE = '$Release: 1.
|
58
|
+
RELEASE = '$Release: 1.2.0 $'.split()[1]
|
59
59
|
|
60
60
|
def initialize(mapping, urlpath_cache_size: 0,
|
61
61
|
enable_urlpath_param_range: true)
|
@@ -64,12 +64,15 @@ module Rack
|
|
64
64
|
(@urlpath_rexp, # ex: {'/api/books'=>BooksApp}
|
65
65
|
@fixed_urlpath_dict, # ex: [[%r'\A/api/books/([^./]+)\z', ['id'], BookApp]]
|
66
66
|
@variable_urlpath_list, # ex: %r'\A(?:/api(?:/books(?:/[^./]+(\z))))\z'
|
67
|
+
@all_entrypoints, # ex: [['/api/books', BooksAPI'], ['/api/orders', OrdersAPI]]
|
67
68
|
) = compile_mapping(mapping)
|
68
69
|
## cache for variable urlpath (= containg urlpath parameters)
|
69
70
|
@urlpath_cache_size = urlpath_cache_size
|
70
71
|
@variable_urlpath_cache = urlpath_cache_size > 0 ? {} : nil
|
71
72
|
end
|
72
73
|
|
74
|
+
attr_reader :urlpath_rexp
|
75
|
+
|
73
76
|
## Finds rack app according to PATH_INFO and REQUEST_METHOD and invokes it.
|
74
77
|
def call(env)
|
75
78
|
#; [!fpw8x] finds mapped app according to env['PATH_INFO'].
|
@@ -77,10 +80,15 @@ module Rack
|
|
77
80
|
app, urlpath_params = lookup(req_path)
|
78
81
|
#; [!wxt2g] guesses correct urlpath and redirects to it automaticaly when request path not found.
|
79
82
|
#; [!3vsua] doesn't redict automatically when request path is '/'.
|
80
|
-
|
83
|
+
if ! app && should_redirect?(env)
|
81
84
|
location = req_path =~ /\/\z/ ? req_path[0..-2] : req_path + '/'
|
82
85
|
app, urlpath_params = lookup(location)
|
83
|
-
|
86
|
+
if app
|
87
|
+
#; [!hyk62] adds QUERY_STRING to redirect location.
|
88
|
+
qs = env['QUERY_STRING']
|
89
|
+
location = "#{location}?#{qs}" if qs && ! qs.empty?
|
90
|
+
return redirect_to(location)
|
91
|
+
end
|
84
92
|
end
|
85
93
|
#; [!30x0k] returns 404 when request urlpath not found.
|
86
94
|
return error_not_found(env) unless app
|
@@ -143,6 +151,12 @@ module Rack
|
|
143
151
|
|
144
152
|
alias find lookup # :nodoc: # for backward compatilibity
|
145
153
|
|
154
|
+
## Yields pair of urlpath pattern and app.
|
155
|
+
def each(&block)
|
156
|
+
#; [!ep0pw] yields pair of urlpath pattern and app.
|
157
|
+
@all_entrypoints.each(&block)
|
158
|
+
end
|
159
|
+
|
146
160
|
protected
|
147
161
|
|
148
162
|
## Returns [404, {...}, [...]]. Override in subclass if necessary.
|
@@ -157,6 +171,18 @@ module Rack
|
|
157
171
|
return [405, {"Content-Type"=>"text/plain"}, ["405 Method Not Allowed"]]
|
158
172
|
end
|
159
173
|
|
174
|
+
## Returns false when request path is '/' or request method is not GET nor HEAD.
|
175
|
+
## (It is not recommended to redirect when request method is POST, PUT or DELETE,
|
176
|
+
## because browser doesn't handle redirect correctly on those methods.)
|
177
|
+
def should_redirect?(env)
|
178
|
+
#; [!dsu34] returns false when request path is '/'.
|
179
|
+
#; [!ycpqj] returns true when request method is GET or HEAD.
|
180
|
+
#; [!7q8xu] returns false when request method is POST, PUT or DELETE.
|
181
|
+
return false if env['PATH_INFO'] == '/'
|
182
|
+
req_method = env['REQUEST_METHOD']
|
183
|
+
return req_method == 'GET' || req_method == 'HEAD'
|
184
|
+
end
|
185
|
+
|
160
186
|
## Returns [301, {"Location"=>location, ...}, [...]]. Override in subclass if necessary.
|
161
187
|
def redirect_to(location)
|
162
188
|
content = "Redirect to #{location}"
|
@@ -188,73 +214,73 @@ module Rack
|
|
188
214
|
|
189
215
|
## Compiles urlpath mapping. Called from '#initialize()'.
|
190
216
|
def compile_mapping(mapping)
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
##
|
197
|
-
|
198
|
-
|
217
|
+
## entry points which has no urlpath parameters
|
218
|
+
## ex:
|
219
|
+
## { '/' => HomeApp,
|
220
|
+
## '/api/books' => BooksApp,
|
221
|
+
## '/api/authors => AuthorsApp,
|
222
|
+
## }
|
223
|
+
dict = {}
|
224
|
+
## entry points which has one or more urlpath parameters
|
225
|
+
## ex:
|
226
|
+
## [
|
227
|
+
## [%r!\A/api/books/([^./]+)\z!, ["id"], BookApp, (11..-1)],
|
228
|
+
## [%r!\A/api/authors/([^./]+)\z!, ["id"], AuthorApp, (12..-1)],
|
229
|
+
## ]
|
230
|
+
list = []
|
231
|
+
#
|
232
|
+
all = []
|
233
|
+
rexp_str = _compile_mapping(mapping, "", "") do |entry_point|
|
234
|
+
obj, urlpath_pat, urlpath_rexp, param_names = entry_point
|
235
|
+
all << [urlpath_pat, obj]
|
236
|
+
if urlpath_rexp
|
237
|
+
range = @enable_urlpath_param_range ? range_of_urlpath_param(urlpath_pat) : nil
|
238
|
+
list << [urlpath_rexp, param_names, obj, range]
|
239
|
+
else
|
240
|
+
dict[urlpath_pat] = obj
|
241
|
+
end
|
242
|
+
end
|
243
|
+
## ex: %r!^A(?:api(?:/books/[^./]+(\z)|/authors/[^./]+(\z)))\z!
|
244
|
+
urlpath_rexp = Regexp.new("\\A#{rexp_str}\\z")
|
199
245
|
#; [!xzo7k] returns regexp, hash, and array.
|
200
|
-
return urlpath_rexp,
|
246
|
+
return urlpath_rexp, dict, list, all
|
201
247
|
end
|
202
248
|
|
203
|
-
def
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
rexp_buf << '(?:'
|
208
|
-
len = rexp_buf.length
|
209
|
-
mapping.each do |child_urlpath_pat, obj|
|
210
|
-
rexp_buf << '|' if rexp_buf.length != len
|
211
|
-
curr_urlpath_pat = "#{base_urlpath_pat}#{urlpath_pat}"
|
249
|
+
def _compile_mapping(mapping, base_urlpath, parent_urlpath, &block)
|
250
|
+
arr = []
|
251
|
+
mapping.each do |urlpath, obj|
|
252
|
+
full_urlpath = "#{base_urlpath}#{urlpath}"
|
212
253
|
#; [!ospaf] accepts nested mapping.
|
213
254
|
if obj.is_a?(Array)
|
214
|
-
|
215
|
-
fixed_dict, variable_list)
|
255
|
+
rexp_str = _compile_mapping(obj, full_urlpath, urlpath, &block)
|
216
256
|
#; [!2ktpf] handles end-point.
|
217
257
|
else
|
218
|
-
|
219
|
-
|
258
|
+
#; [!guhdc] if mapping dict is specified...
|
259
|
+
if obj.is_a?(Hash)
|
260
|
+
obj = normalize_mapping_keys(obj)
|
261
|
+
end
|
262
|
+
#; [!vfytw] handles urlpath pattern as variable when urlpath param exists.
|
263
|
+
full_urlpath_rexp_str, param_names = compile_urlpath_pattern(full_urlpath, true)
|
264
|
+
if param_names # has urlpath params
|
265
|
+
full_urlpath_rexp = Regexp.new("\\A#{full_urlpath_rexp_str}\\z")
|
266
|
+
rexp_str, _ = compile_urlpath_pattern(urlpath, false)
|
267
|
+
rexp_str << '(\z)'
|
268
|
+
entry_point = [obj, full_urlpath, full_urlpath_rexp, param_names]
|
269
|
+
#; [!l63vu] handles urlpath pattern as fixed when no urlpath params.
|
270
|
+
else # has no urlpath params
|
271
|
+
entry_point = [obj, full_urlpath, nil, nil]
|
272
|
+
end
|
273
|
+
yield entry_point
|
220
274
|
end
|
275
|
+
arr << rexp_str if rexp_str
|
221
276
|
end
|
222
|
-
#; [!
|
223
|
-
if
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
#; [!bh9lo] deletes unnecessary grouping which contains only an element.
|
230
|
-
elsif rexp_buf.length == len + 1
|
231
|
-
rexp_buf[-2] == '(?:' or raise "assertion failed: rexp_buf[-2]=#{rexp_buf[-2].inspect}"
|
232
|
-
rexp_buf[-2] = ''
|
233
|
-
else
|
234
|
-
rexp_buf << ')'
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
def _compile_object(obj, rexp_buf, base_urlpath_pat, urlpath_pat,
|
239
|
-
fixed_dict, variable_list)
|
240
|
-
#; [!guhdc] if mapping dict is specified...
|
241
|
-
if obj.is_a?(Hash)
|
242
|
-
obj = normalize_mapping_keys(obj)
|
243
|
-
end
|
244
|
-
#; [!l63vu] handles urlpath pattern as fixed when no urlpath params.
|
245
|
-
full_urlpath_pat = "#{base_urlpath_pat}#{urlpath_pat}"
|
246
|
-
full_urlpath_rexp_str, param_names = compile_urlpath_pattern(full_urlpath_pat, true)
|
247
|
-
fixed_pattern = param_names.nil?
|
248
|
-
if fixed_pattern
|
249
|
-
fixed_dict[full_urlpath_pat] = obj
|
250
|
-
#; [!vfytw] handles urlpath pattern as variable when urlpath param exists.
|
251
|
-
else
|
252
|
-
rexp_str, _ = compile_urlpath_pattern(urlpath_pat, false)
|
253
|
-
rexp_buf << (rexp_str << '(\z)')
|
254
|
-
full_urlpath_rexp = Regexp.new("\\A#{full_urlpath_rexp_str}\\z")
|
255
|
-
range = @enable_urlpath_param_range ? range_of_urlpath_param(full_urlpath_pat) : nil
|
256
|
-
variable_list << [full_urlpath_rexp, param_names, obj, range]
|
257
|
-
end
|
277
|
+
#; [!pv2au] deletes unnecessary urlpath regexp.
|
278
|
+
return nil if arr.empty?
|
279
|
+
#; [!bh9lo] deletes unnecessary grouping.
|
280
|
+
parent_urlpath_rexp_str, _ = compile_urlpath_pattern(parent_urlpath, false)
|
281
|
+
return "#{parent_urlpath_rexp_str}#{arr[0]}" if arr.length == 1
|
282
|
+
#; [!iza1g] adds grouping if necessary.
|
283
|
+
return "#{parent_urlpath_rexp_str}(?:#{arr.join('|')})"
|
258
284
|
end
|
259
285
|
|
260
286
|
## Compiles '/books/:id' into ['/books/([^./]+)', ["id"]].
|
data/rack-jet_router.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
3
|
###
|
4
|
-
### $Release: 1.
|
4
|
+
### $Release: 1.2.0 $
|
5
5
|
### $Copyright: copyright(c) 2015 kuwata-lab.com all rights reserved $
|
6
6
|
### $License: MIT License $
|
7
7
|
###
|
@@ -154,23 +154,39 @@ describe Rack::JetRouter do
|
|
154
154
|
end
|
155
155
|
end
|
156
156
|
|
157
|
-
it "[!
|
157
|
+
it "[!iza1g] adds grouping if necessary." do
|
158
158
|
mapping = [
|
159
|
-
['/'
|
160
|
-
|
159
|
+
['/api', [
|
160
|
+
['/books', [
|
161
|
+
['/:id' , book_show_api],
|
162
|
+
]],
|
163
|
+
]],
|
164
|
+
['/admin', [
|
165
|
+
['/books', [
|
166
|
+
['/:id' , admin_book_show_app],
|
167
|
+
]],
|
168
|
+
]],
|
161
169
|
]
|
162
170
|
expected = '
|
163
171
|
\A
|
172
|
+
(?:
|
173
|
+
/api
|
174
|
+
/books
|
175
|
+
/[^./]+(\z)
|
176
|
+
|
|
177
|
+
/admin
|
178
|
+
/books
|
179
|
+
/[^./]+(\z)
|
180
|
+
)
|
164
181
|
\z
|
165
182
|
'.gsub(/\s+/, '')
|
166
183
|
jet_router.instance_exec(self) do |_|
|
167
184
|
rexp, dict, list = compile_mapping(mapping)
|
168
185
|
_.ok {rexp} == Regexp.new(expected)
|
169
|
-
_.ok {dict} == {
|
170
|
-
'/' => welcome_app,
|
171
|
-
'/api/books' => book_list_api,
|
172
|
-
}
|
186
|
+
_.ok {dict} == {}
|
173
187
|
_.ok {list} == [
|
188
|
+
[%r'\A/api/books/([^./]+)\z', ['id'], book_show_api, (11..-1)],
|
189
|
+
[%r'\A/admin/books/([^./]+)\z', ['id'], admin_book_show_app, (13..-1)],
|
174
190
|
]
|
175
191
|
end
|
176
192
|
end
|
@@ -188,17 +204,7 @@ describe Rack::JetRouter do
|
|
188
204
|
]],
|
189
205
|
]],
|
190
206
|
]
|
191
|
-
expected = '
|
192
|
-
\A
|
193
|
-
(?:
|
194
|
-
/api
|
195
|
-
(?:
|
196
|
-
/books2
|
197
|
-
/[^./]+(\z)
|
198
|
-
)
|
199
|
-
)
|
200
|
-
\z
|
201
|
-
'.gsub(/\s+/, '')
|
207
|
+
expected = '\A/api/books2/[^./]+(\z)\z'
|
202
208
|
jet_router.instance_exec(self) do |_|
|
203
209
|
rexp, dict, list = compile_mapping(mapping)
|
204
210
|
_.ok {rexp} == Regexp.new(expected)
|
@@ -213,7 +219,7 @@ describe Rack::JetRouter do
|
|
213
219
|
end
|
214
220
|
end
|
215
221
|
|
216
|
-
it "[!bh9lo] deletes unnecessary grouping
|
222
|
+
it "[!bh9lo] deletes unnecessary grouping." do
|
217
223
|
mapping = [
|
218
224
|
['/api', [
|
219
225
|
['/books', [
|
@@ -223,13 +229,9 @@ describe Rack::JetRouter do
|
|
223
229
|
]
|
224
230
|
expected = '
|
225
231
|
\A
|
226
|
-
|
227
|
-
/
|
228
|
-
|
229
|
-
/books
|
230
|
-
/[^./]+(\z)
|
231
|
-
)
|
232
|
-
)
|
232
|
+
/api
|
233
|
+
/books
|
234
|
+
/[^./]+(\z)
|
233
235
|
\z
|
234
236
|
'.gsub(/\s+/, '')
|
235
237
|
jet_router.instance_exec(self) do |_|
|
@@ -318,16 +320,10 @@ describe Rack::JetRouter do
|
|
318
320
|
]
|
319
321
|
expected = '
|
320
322
|
\A
|
321
|
-
(?:
|
322
323
|
/admin
|
323
|
-
|
324
|
-
/
|
325
|
-
(
|
326
|
-
/books
|
327
|
-
/[^./]+(\z)
|
328
|
-
)
|
329
|
-
)
|
330
|
-
)
|
324
|
+
/api
|
325
|
+
/books
|
326
|
+
/[^./]+(\z)
|
331
327
|
\z
|
332
328
|
'.gsub(/\s+/, '')
|
333
329
|
jet_router.instance_exec(self) do |_|
|
@@ -379,6 +375,38 @@ describe Rack::JetRouter do
|
|
379
375
|
end
|
380
376
|
|
381
377
|
|
378
|
+
describe '#should_redirect?' do
|
379
|
+
|
380
|
+
it "[!dsu34] returns false when request path is '/'." do
|
381
|
+
jet_router.instance_exec(self) do |_|
|
382
|
+
_.ok {should_redirect?(_.new_env('GET' , '/'))} == false
|
383
|
+
_.ok {should_redirect?(_.new_env('POST' , '/'))} == false
|
384
|
+
_.ok {should_redirect?(_.new_env('PUT' , '/'))} == false
|
385
|
+
_.ok {should_redirect?(_.new_env('DELETE', '/'))} == false
|
386
|
+
_.ok {should_redirect?(_.new_env('HEAD' , '/'))} == false
|
387
|
+
_.ok {should_redirect?(_.new_env('PATCH' , '/'))} == false
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
it "[!ycpqj] returns true when request method is GET or HEAD." do
|
392
|
+
jet_router.instance_exec(self) do |_|
|
393
|
+
_.ok {should_redirect?(_.new_env('GET' , '/index'))} == true
|
394
|
+
_.ok {should_redirect?(_.new_env('HEAD' , '/index'))} == true
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
it "[!7q8xu] returns false when request method is POST, PUT or DELETE." do
|
399
|
+
jet_router.instance_exec(self) do |_|
|
400
|
+
_.ok {should_redirect?(_.new_env('POST' , '/index'))} == false
|
401
|
+
_.ok {should_redirect?(_.new_env('PUT' , '/index'))} == false
|
402
|
+
_.ok {should_redirect?(_.new_env('DELETE', '/index'))} == false
|
403
|
+
_.ok {should_redirect?(_.new_env('PATCH' , '/index'))} == false
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
end
|
408
|
+
|
409
|
+
|
382
410
|
describe '#error_not_found()' do
|
383
411
|
|
384
412
|
it "[!mlruv] returns 404 response." do
|
@@ -422,9 +450,8 @@ describe Rack::JetRouter do
|
|
422
450
|
)
|
423
451
|
|
|
424
452
|
/admin
|
425
|
-
|
453
|
+
/books
|
426
454
|
/[^./]+(\z)
|
427
|
-
)
|
428
455
|
)
|
429
456
|
\z
|
430
457
|
'.gsub(/\s+/, '')
|
@@ -591,6 +618,13 @@ describe Rack::JetRouter do
|
|
591
618
|
ok {r.call(new_env(:GET, '/'))} == [404, {"Content-Type"=>"text/plain"}, ["404 Not Found"]]
|
592
619
|
end
|
593
620
|
|
621
|
+
it "[!hyk62] adds QUERY_STRING to redirect location." do
|
622
|
+
headers = {"Content-Type"=>"text/plain", "Location"=>"/api/books?x=1&y=2"}
|
623
|
+
content = "Redirect to /api/books?x=1&y=2"
|
624
|
+
env = new_env(:GET, '/api/books/', {"QUERY_STRING"=>"x=1&y=2"})
|
625
|
+
ok {jet_router.call(env)} == [301, headers, [content]]
|
626
|
+
end
|
627
|
+
|
594
628
|
it "[!30x0k] returns 404 when request urlpath not found." do
|
595
629
|
expected = [404, {"Content-Type"=>"text/plain"}, ["404 Not Found"]]
|
596
630
|
ok {jet_router.call(new_env(:GET, '/xxx'))} == expected
|
@@ -654,6 +688,28 @@ describe Rack::JetRouter do
|
|
654
688
|
end
|
655
689
|
|
656
690
|
|
691
|
+
describe '#each()' do
|
692
|
+
|
693
|
+
it "[!ep0pw] yields pair of urlpath pattern and app." do
|
694
|
+
arr = []
|
695
|
+
jet_router.each do |upath, app|
|
696
|
+
arr << [upath, app]
|
697
|
+
end
|
698
|
+
ok {arr[0]} == ["/", welcome_app]
|
699
|
+
ok {arr[1]} == ["/index.html", welcome_app]
|
700
|
+
ok {arr[2]} == ["/api/books", book_list_api]
|
701
|
+
ok {arr[3]} == ["/api/books/new", book_new_api]
|
702
|
+
ok {arr[4]} == ["/api/books/:id", book_show_api]
|
703
|
+
ok {arr[5]} == ["/api/books/:id/edit", book_edit_api]
|
704
|
+
ok {arr[6]} == ["/api/books/:book_id/comments", comment_create_api]
|
705
|
+
ok {arr[7]} == ["/api/books/:book_id/comments/:comment_id", comment_update_api]
|
706
|
+
ok {arr[8]} == ["/admin/books", {"GET"=>admin_book_list_app, "POST"=>admin_book_create_app}]
|
707
|
+
ok {arr[9]} == ["/admin/books/:id", {"GET"=>admin_book_show_app, "PUT"=>admin_book_update_app, "DELETE"=>admin_book_delete_app}]
|
708
|
+
end
|
709
|
+
|
710
|
+
end
|
711
|
+
|
712
|
+
|
657
713
|
describe 'REQUEST_METHODS' do
|
658
714
|
|
659
715
|
it "[!haggu] contains available request methods." do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-jet_router
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- makoto kuwata
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-10-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|