rack-jet_router 1.1.1 → 1.2.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/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
|