rack-jet_router 1.0.1 → 1.1.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 +63 -27
- data/lib/rack/jet_router.rb +76 -44
- data/rack-jet_router.gemspec +1 -1
- data/test/rack/jet_router_test.rb +94 -42
- metadata +17 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da49b22574962dc8935525feba719fd735003f43
|
4
|
+
data.tar.gz: 9fa472683f55e9b366cc46b5f4f984295dffdf66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0e12966a998ba92c5c9edbeefa549ba2985650a2d4a164c5e0c0a23487b6622695cb4beab2c55af60e8269f579a97dcb84150386d1e972a1cd9b7aefaa1c75f
|
7
|
+
data.tar.gz: 6dfec9e6f75093ce20bdaefdd3ddac9dbd27fd5f5711f64d9bd44982bb7e58337d09604f552828d2ac76888cc5d545e0859701e64f3ce12d6d1eb1df820e8ef1
|
data/README.md
CHANGED
@@ -1,32 +1,59 @@
|
|
1
1
|
# Rack::JetRouter
|
2
2
|
|
3
|
+
($Release: 0.0.0 $)
|
4
|
+
|
3
5
|
Rack::JetRouter is crazy-fast router library for Rack application,
|
4
6
|
derived from [Keight.rb](https://github.com/kwatch/keight/tree/ruby).
|
5
7
|
|
8
|
+
Rack::JetRouter requires Ruby >= 2.0.
|
9
|
+
|
6
10
|
|
7
11
|
## Benchmark
|
8
12
|
|
9
13
|
Benchmark script is [here](https://github.com/kwatch/rack-jet_router/blob/dev/bench/bench.rb).
|
10
14
|
|
15
|
+
### JetRouter vs. Rack vs. Sinatra vs. Keight.rb:
|
16
|
+
|
11
17
|
```
|
12
|
-
## Ranking
|
13
|
-
(Rack plain) /api/
|
14
|
-
(
|
15
|
-
(
|
16
|
-
(R::Req+Res) /api/
|
17
|
-
(
|
18
|
-
(
|
18
|
+
## Ranking usec/req Graph (longer=faster)
|
19
|
+
(Rack plain) /api/aaa01 1.0619 ***************
|
20
|
+
(Rack plain) /api/aaa01/123 0.8729 ******************
|
21
|
+
(R::Req+Res) /api/aaa01 9.5361 **
|
22
|
+
(R::Req+Res) /api/aaa01/123 9.5321 **
|
23
|
+
(JetRouter) /api/aaa01 1.3231 ************
|
24
|
+
(JetRouter) /api/aaa01/123 5.9796 ***
|
25
|
+
(Keight.rb) /api/aaa01 6.4314 **
|
26
|
+
(Keight.rb) /api/aaa01/123 10.2339 **
|
27
|
+
(Sinatra) /api/aaa01 104.7575
|
28
|
+
(Sinatra) /api/aaa01/123 115.8220
|
19
29
|
```
|
20
30
|
|
21
31
|
* If URL path has no path parameter (such as `/api/hello`),
|
22
|
-
Rack::JetRouter is
|
32
|
+
Rack::JetRouter is a litte shower than plain Rack application.
|
23
33
|
* If URL path contains path parameter (such as `/api/hello/:id`),
|
24
|
-
Rack::JetRouter becomes slower, but it is enough small (about
|
25
|
-
* Overhead of Rack::JetRouter is smaller than that of Rack::
|
34
|
+
Rack::JetRouter becomes slower, but it is enough small (about 6usec/req).
|
35
|
+
* Overhead of Rack::JetRouter is smaller than that of Rack::Reqeuast +
|
26
36
|
Rack::Response.
|
27
37
|
* Sinatra is too slow.
|
28
38
|
|
29
39
|
|
40
|
+
### JetRouter vs. Rack::Multiplexer:
|
41
|
+
|
42
|
+
```
|
43
|
+
## Ranking usec/req Graph (longer=faster)
|
44
|
+
(JetRouter) /api/aaa01 1.3231 ************
|
45
|
+
(JetRouter) /api/aaa01/123 5.9796 ***
|
46
|
+
(JetRouter) /api/zzz26 1.4089 ***********
|
47
|
+
(JetRouter) /api/zzz26/789 6.5142 **
|
48
|
+
(Multiplexer) /api/aaa01 5.9073 ***
|
49
|
+
(Multiplexer) /api/aaa01/123 18.2102 *
|
50
|
+
(Multiplexer) /api/zzz26 24.4013 *
|
51
|
+
(Multiplexer) /api/zzz26/789 36.1558
|
52
|
+
```
|
53
|
+
|
54
|
+
* JetRouter is about 3~5 times faster than Rack::Multiplexer.
|
55
|
+
|
56
|
+
|
30
57
|
## Examples
|
31
58
|
|
32
59
|
### #1: Depends only on Request Path
|
@@ -38,7 +65,7 @@ require 'rack'
|
|
38
65
|
require 'rack/jet_router'
|
39
66
|
|
40
67
|
## Assume that welcome_app, books_api, ... are Rack application.
|
41
|
-
|
68
|
+
mapping = [
|
42
69
|
['/' , welcome_app],
|
43
70
|
['/api', [
|
44
71
|
['/books', [
|
@@ -52,9 +79,9 @@ urlpath_mapping = [
|
|
52
79
|
]],
|
53
80
|
]
|
54
81
|
|
55
|
-
router = Rack::JetRouter.new(
|
56
|
-
p router.
|
57
|
-
#=> [book_api, {"id"=>"123", "format"=>"
|
82
|
+
router = Rack::JetRouter.new(mapping)
|
83
|
+
p router.lookup('/api/books/123.json')
|
84
|
+
#=> [book_api, {"id"=>"123", "format"=>"json"}]
|
58
85
|
|
59
86
|
status, headers, body = router.call(env)
|
60
87
|
```
|
@@ -69,7 +96,7 @@ require 'rack'
|
|
69
96
|
require 'rack/jet_router'
|
70
97
|
|
71
98
|
## Assume that welcome_app, book_list_api, ... are Rack application.
|
72
|
-
|
99
|
+
mapping = [
|
73
100
|
['/' , {GET: welcome_app}],
|
74
101
|
['/api', [
|
75
102
|
['/books', [
|
@@ -83,8 +110,8 @@ urlpath_mapping = [
|
|
83
110
|
]],
|
84
111
|
]
|
85
112
|
|
86
|
-
router = Rack::JetRouter.new(
|
87
|
-
p router.
|
113
|
+
router = Rack::JetRouter.new(mapping)
|
114
|
+
p router.lookup('/api/books/123')
|
88
115
|
#=> [{"GET"=>book_show_api, "PUT"=>book_update_api}, {"id"=>"123", "format"=>nil}]
|
89
116
|
|
90
117
|
status, headers, body = router.call(env)
|
@@ -118,7 +145,7 @@ class BooksAPI < API
|
|
118
145
|
def delete(id: nil); ....; end
|
119
146
|
end
|
120
147
|
|
121
|
-
|
148
|
+
mapping = [
|
122
149
|
['/api', [
|
123
150
|
['/books', [
|
124
151
|
['' , {GET: [BooksAPI, :index],
|
@@ -129,11 +156,10 @@ urlpath_mapping = [
|
|
129
156
|
]],
|
130
157
|
]],
|
131
158
|
]
|
132
|
-
router = Rack::JetRouter.new(
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
dict, args = router.find('/api/books/123')
|
159
|
+
router = Rack::JetRouter.new(mapping)
|
160
|
+
dict, args = router.lookup('/api/books/123')
|
161
|
+
p dict #=> {"GET"=>[BooksAPI, :show], "PUT"=>[...], "DELETE"=>[...]}
|
162
|
+
p args #=> {"id"=>"123"}
|
137
163
|
klass, action = dict["GET"]
|
138
164
|
handler = klass.new(Rack::Request.new(env), Rack::Response.new)
|
139
165
|
handler.__send__(action, args)
|
@@ -145,8 +171,8 @@ handler.__send__(action, args)
|
|
145
171
|
|
146
172
|
### URL Path Parameters
|
147
173
|
|
148
|
-
URL path parameters (such as `{"id"=>"123"}`)
|
149
|
-
`env['rack.urlpath_params']`.
|
174
|
+
In Rack application, URL path parameters (such as `{"id"=>"123"}`) are
|
175
|
+
available via `env['rack.urlpath_params']`.
|
150
176
|
|
151
177
|
```ruby
|
152
178
|
BookApp = proc {|env|
|
@@ -175,7 +201,7 @@ end
|
|
175
201
|
|
176
202
|
### Variable URL Path Cache
|
177
203
|
|
178
|
-
It is
|
204
|
+
It is useful to classify URL path patterns into two types: fixed and variable.
|
179
205
|
|
180
206
|
* **Fixed URL path pattern** doesn't contain any urlpath paramters.<br>
|
181
207
|
Example: `/`, `/login`, `/api/books`
|
@@ -189,7 +215,7 @@ as well as fixed ones. It will make routing much faster.
|
|
189
215
|
```ruby
|
190
216
|
## Enable variable urlpath cache.
|
191
217
|
router = Rack::JetRouter.new(urlpath_mapping, urlpath_cache_size: 200)
|
192
|
-
p router.
|
218
|
+
p router.lookup('/api/books/123') # caches even varaible urlpath
|
193
219
|
```
|
194
220
|
|
195
221
|
|
@@ -226,6 +252,16 @@ $License: MIT License $
|
|
226
252
|
## History
|
227
253
|
|
228
254
|
|
255
|
+
### 2015-12-28: Release 1.1.0
|
256
|
+
|
257
|
+
* **NOTICE** `Rack::JetRouter#find()` is renamed to `#lookup()`.<br>
|
258
|
+
`#find()` is also available for compatibility, but not recommended.
|
259
|
+
* Performance improved when number of URL path parameter is 1.
|
260
|
+
* Regular expression generated is improved.
|
261
|
+
* Benchmark script is improved to take some command-line options.
|
262
|
+
* Document fixed.
|
263
|
+
|
264
|
+
|
229
265
|
### 2015-12-06: Release 1.0.1
|
230
266
|
|
231
267
|
* Fix document
|
data/lib/rack/jet_router.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
3
|
###
|
4
|
-
### $Release: 1.0
|
4
|
+
### $Release: 1.1.0 $
|
5
5
|
### $Copyright: copyright(c) 2015 kuwata-lab.com all rights reserved $
|
6
6
|
### $License: MIT License $
|
7
7
|
###
|
@@ -30,7 +30,7 @@ module Rack
|
|
30
30
|
## ]],
|
31
31
|
## ]
|
32
32
|
## router = Rack::JetRouter.new(urlpath_mapping)
|
33
|
-
## router.
|
33
|
+
## router.lookup('/api/books/123.html')
|
34
34
|
## #=> [book_api, {"id"=>"123", "format"=>"html"}]
|
35
35
|
## status, headers, body = router.call(env)
|
36
36
|
##
|
@@ -49,13 +49,17 @@ module Rack
|
|
49
49
|
## ]],
|
50
50
|
## ]
|
51
51
|
## router = Rack::JetRouter.new(urlpath_mapping)
|
52
|
-
## router.
|
52
|
+
## router.lookup('/api/books/123')
|
53
53
|
## #=> [{"GET"=>book_show_api, "PUT"=>book_update_api}, {"id"=>"123", "format"=>nil}]
|
54
54
|
## status, headers, body = router.call(env)
|
55
55
|
##
|
56
56
|
class JetRouter
|
57
57
|
|
58
|
-
|
58
|
+
RELEASE = '$Release: 1.1.0 $'.split()[1]
|
59
|
+
|
60
|
+
def initialize(mapping, urlpath_cache_size: 0,
|
61
|
+
enable_urlpath_param_range: true)
|
62
|
+
@enable_urlpath_param_range = enable_urlpath_param_range
|
59
63
|
#; [!u2ff4] compiles urlpath mapping.
|
60
64
|
(@urlpath_rexp, # ex: {'/api/books'=>BooksApp}
|
61
65
|
@fixed_urlpath_dict, # ex: [[%r'\A/api/books/([^./]+)\z', ['id'], BookApp]]
|
@@ -70,12 +74,12 @@ module Rack
|
|
70
74
|
def call(env)
|
71
75
|
#; [!fpw8x] finds mapped app according to env['PATH_INFO'].
|
72
76
|
req_path = env['PATH_INFO']
|
73
|
-
app, urlpath_params =
|
77
|
+
app, urlpath_params = lookup(req_path)
|
74
78
|
#; [!wxt2g] guesses correct urlpath and redirects to it automaticaly when request path not found.
|
75
79
|
#; [!3vsua] doesn't redict automatically when request path is '/'.
|
76
80
|
unless app || req_path == '/'
|
77
81
|
location = req_path =~ /\/\z/ ? req_path[0..-2] : req_path + '/'
|
78
|
-
app, urlpath_params =
|
82
|
+
app, urlpath_params = lookup(location)
|
79
83
|
return redirect_to(location) if app
|
80
84
|
end
|
81
85
|
#; [!30x0k] returns 404 when request urlpath not found.
|
@@ -100,8 +104,8 @@ module Rack
|
|
100
104
|
## Finds app or Hash mapped to request path.
|
101
105
|
##
|
102
106
|
## ex:
|
103
|
-
##
|
104
|
-
def
|
107
|
+
## lookup('/api/books/123') #=> [BookApp, {"id"=>"123"}]
|
108
|
+
def lookup(req_path)
|
105
109
|
#; [!24khb] finds in fixed urlpaths at first.
|
106
110
|
#; [!iwyzd] urlpath param value is nil when found in fixed urlpaths.
|
107
111
|
obj = @fixed_urlpath_dict[req_path]
|
@@ -119,9 +123,15 @@ module Rack
|
|
119
123
|
index = m.captures.find_index('')
|
120
124
|
return nil unless index
|
121
125
|
#; [!ijqws] returns mapped object and urlpath parameter values when urlpath found.
|
122
|
-
full_urlpath_rexp, param_names, obj = @variable_urlpath_list[index]
|
123
|
-
|
124
|
-
|
126
|
+
full_urlpath_rexp, param_names, obj, range = @variable_urlpath_list[index]
|
127
|
+
if range
|
128
|
+
## "/books/123"[7..-1] is faster than /\A\/books\/(\d+)\z/.match("/books/123")
|
129
|
+
str = req_path[range]
|
130
|
+
param_values = [str]
|
131
|
+
else
|
132
|
+
m = full_urlpath_rexp.match(req_path)
|
133
|
+
param_values = m.captures
|
134
|
+
end
|
125
135
|
vars = build_urlpath_parameter_vars(param_names, param_values)
|
126
136
|
#; [!84inr] caches result when variable urlpath cache enabled.
|
127
137
|
if cache
|
@@ -131,6 +141,8 @@ module Rack
|
|
131
141
|
return obj, vars
|
132
142
|
end
|
133
143
|
|
144
|
+
alias find lookup # :nodoc: # for backward compatilibity
|
145
|
+
|
134
146
|
protected
|
135
147
|
|
136
148
|
## Returns [404, {...}, [...]]. Override in subclass if necessary.
|
@@ -177,10 +189,10 @@ module Rack
|
|
177
189
|
## Compiles urlpath mapping. Called from '#initialize()'.
|
178
190
|
def compile_mapping(mapping)
|
179
191
|
rexp_buf = ['\A']
|
180
|
-
prefix_pat = ''
|
181
192
|
fixed_urlpaths = {} # ex: {'/api/books'=>BooksApp}
|
182
193
|
variable_urlpaths = [] # ex: [[%r'\A/api/books/([^./]+)\z', ['id'], BookApp]]
|
183
|
-
|
194
|
+
_compile_array(mapping, rexp_buf, '', '',
|
195
|
+
fixed_urlpaths, variable_urlpaths)
|
184
196
|
## ex: %r'\A(?:/api(?:/books(?:/[^./]+(\z)|/[^./]+/edit(\z))))\z'
|
185
197
|
rexp_buf << '\z'
|
186
198
|
urlpath_rexp = Regexp.new(rexp_buf.join())
|
@@ -188,57 +200,67 @@ module Rack
|
|
188
200
|
return urlpath_rexp, fixed_urlpaths, variable_urlpaths
|
189
201
|
end
|
190
202
|
|
191
|
-
def
|
192
|
-
|
193
|
-
|
203
|
+
def _compile_array(mapping, rexp_buf, base_urlpath_pat, urlpath_pat,
|
204
|
+
fixed_dict, variable_list)
|
205
|
+
rexp_str, _ = compile_urlpath_pattern(urlpath_pat, false)
|
206
|
+
rexp_buf << rexp_str
|
194
207
|
rexp_buf << '(?:'
|
195
208
|
len = rexp_buf.length
|
196
|
-
mapping.each do |
|
209
|
+
mapping.each do |child_urlpath_pat, obj|
|
197
210
|
rexp_buf << '|' if rexp_buf.length != len
|
198
|
-
|
211
|
+
curr_urlpath_pat = "#{base_urlpath_pat}#{urlpath_pat}"
|
199
212
|
#; [!ospaf] accepts nested mapping.
|
200
213
|
if obj.is_a?(Array)
|
201
|
-
|
202
|
-
|
203
|
-
len2 = rexp_buf.length
|
204
|
-
_compile_mapping(obj, rexp_buf, full_urlpath_pat, fixed_dict, variable_list)
|
205
|
-
#; [!pv2au] deletes unnecessary urlpath regexp.
|
206
|
-
if rexp_buf.length == len2
|
207
|
-
x = rexp_buf.pop()
|
208
|
-
x == rexp_str or raise "assertion failed"
|
209
|
-
end
|
214
|
+
_compile_array(obj, rexp_buf, curr_urlpath_pat, child_urlpath_pat,
|
215
|
+
fixed_dict, variable_list)
|
210
216
|
#; [!2ktpf] handles end-point.
|
211
217
|
else
|
212
|
-
|
213
|
-
|
214
|
-
obj = normalize_mapping_keys(obj)
|
215
|
-
end
|
216
|
-
#; [!l63vu] handles urlpath pattern as fixed when no urlpath params.
|
217
|
-
full_urlpath_rexp_str, param_names = compile_urlpath_pattern(full_urlpath_pat, param_pat2)
|
218
|
-
fixed_pattern = param_names.nil?
|
219
|
-
if fixed_pattern
|
220
|
-
fixed_dict[full_urlpath_pat] = obj
|
221
|
-
#; [!vfytw] handles urlpath pattern as variable when urlpath param exists.
|
222
|
-
else
|
223
|
-
rexp_str, _ = compile_urlpath_pattern(urlpath_pat, param_pat1)
|
224
|
-
rexp_buf << rexp_str << '(\z)'
|
225
|
-
full_urlpath_rexp = Regexp.new("\\A#{full_urlpath_rexp_str}\\z")
|
226
|
-
variable_list << [full_urlpath_rexp, param_names, obj]
|
227
|
-
end
|
218
|
+
_compile_object(obj, rexp_buf, curr_urlpath_pat, child_urlpath_pat,
|
219
|
+
fixed_dict, variable_list)
|
228
220
|
end
|
229
221
|
end
|
230
222
|
#; [!gfxgr] deletes unnecessary grouping.
|
231
223
|
if rexp_buf.length == len
|
232
224
|
x = rexp_buf.pop() # delete '(?:'
|
233
225
|
x == '(?:' or raise "assertion failed"
|
226
|
+
#; [!pv2au] deletes unnecessary urlpath regexp.
|
227
|
+
x = rexp_buf.pop() # delete rexp_str
|
228
|
+
x == rexp_str or raise "assertion failed"
|
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] = ''
|
234
233
|
else
|
235
234
|
rexp_buf << ')'
|
236
235
|
end
|
237
236
|
end
|
238
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
|
258
|
+
end
|
259
|
+
|
239
260
|
## Compiles '/books/:id' into ['/books/([^./]+)', ["id"]].
|
240
|
-
def compile_urlpath_pattern(urlpath_pat,
|
261
|
+
def compile_urlpath_pattern(urlpath_pat, enable_capture=true)
|
241
262
|
s = "".dup()
|
263
|
+
param_pat = enable_capture ? '([^./]+)' : '[^./]+'
|
242
264
|
param_names = []
|
243
265
|
pos = 0
|
244
266
|
urlpath_pat.scan(/:(\w+)|\((.*?)\)/) do |name, optional|
|
@@ -275,6 +297,16 @@ module Rack
|
|
275
297
|
end
|
276
298
|
end
|
277
299
|
|
300
|
+
def range_of_urlpath_param(urlpath_pattern) # ex: '/books/:id/edit'
|
301
|
+
#; [!syrdh] returns Range object when urlpath_pattern contains just one param.
|
302
|
+
#; [!skh4z] returns nil when urlpath_pattern contains more than two params.
|
303
|
+
#; [!acj5b] returns nil when urlpath_pattern contains no params.
|
304
|
+
rexp = /:\w+|\(.*?\)/
|
305
|
+
arr = urlpath_pattern.split(rexp, -1) # ex: ['/books/', '/edit']
|
306
|
+
return nil unless arr.length == 2
|
307
|
+
return (arr[0].length .. -(arr[1].length+1)) # ex: 7..-6 (Range object)
|
308
|
+
end
|
309
|
+
|
278
310
|
def normalize_mapping_keys(dict)
|
279
311
|
#; [!r7cmk] converts keys into string.
|
280
312
|
#; [!z9kww] allows 'ANY' as request method.
|
data/rack-jet_router.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
3
|
###
|
4
|
-
### $Release: 1.0
|
4
|
+
### $Release: 1.1.0 $
|
5
5
|
### $Copyright: copyright(c) 2015 kuwata-lab.com all rights reserved $
|
6
6
|
### $License: MIT License $
|
7
7
|
###
|
@@ -64,6 +64,35 @@ describe Rack::JetRouter do
|
|
64
64
|
end
|
65
65
|
|
66
66
|
|
67
|
+
describe '#range_of_urlpath_param()' do
|
68
|
+
|
69
|
+
it "[!syrdh] returns Range object when urlpath_pattern contains just one param." do
|
70
|
+
jet_router.instance_exec(self) do |_|
|
71
|
+
r1 = range_of_urlpath_param('/books/:id')
|
72
|
+
_.ok {r1} == (7..-1)
|
73
|
+
_.ok {'/books/123'[r1]} == '123'
|
74
|
+
r2 = range_of_urlpath_param('/books/:id.html')
|
75
|
+
_.ok {r2} == (7..-6)
|
76
|
+
_.ok {'/books/4567.html'[r2]} == '4567'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it "[!skh4z] returns nil when urlpath_pattern contains more than two params." do
|
81
|
+
jet_router.instance_exec(self) do |_|
|
82
|
+
_.ok {range_of_urlpath_param('/books/:book_id/comments/:comment_id')} == nil
|
83
|
+
_.ok {range_of_urlpath_param('/books/:id(:format)')} == nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it "[!acj5b] returns nil when urlpath_pattern contains no params." do
|
88
|
+
jet_router.instance_exec(self) do |_|
|
89
|
+
_.ok {range_of_urlpath_param('/books')} == nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
|
67
96
|
describe '#compile_urlpath_pattern()' do
|
68
97
|
|
69
98
|
it "[!joozm] escapes metachars with backslash in text part." do
|
@@ -110,9 +139,7 @@ describe Rack::JetRouter do
|
|
110
139
|
]
|
111
140
|
expected = '
|
112
141
|
\A
|
113
|
-
(?:
|
114
142
|
/books/[^./]+(\z)
|
115
|
-
)
|
116
143
|
\z
|
117
144
|
'.gsub(/\s+/, '')
|
118
145
|
jet_router.instance_exec(self) do |_|
|
@@ -122,7 +149,7 @@ describe Rack::JetRouter do
|
|
122
149
|
'/' => welcome_app,
|
123
150
|
}
|
124
151
|
_.ok {list} == [
|
125
|
-
[%r'\A/books/([^./]+)\z', ['id'], book_show_api],
|
152
|
+
[%r'\A/books/([^./]+)\z', ['id'], book_show_api, (7..-1)],
|
126
153
|
]
|
127
154
|
end
|
128
155
|
end
|
@@ -167,7 +194,7 @@ describe Rack::JetRouter do
|
|
167
194
|
/api
|
168
195
|
(?:
|
169
196
|
/books2
|
170
|
-
|
197
|
+
/[^./]+(\z)
|
171
198
|
)
|
172
199
|
)
|
173
200
|
\z
|
@@ -181,7 +208,36 @@ describe Rack::JetRouter do
|
|
181
208
|
'/api/books/new' => book_new_api,
|
182
209
|
}
|
183
210
|
_.ok {list} == [
|
184
|
-
[%r'\A/api/books2/([^./]+)\z', ['id'], book_show_api],
|
211
|
+
[%r'\A/api/books2/([^./]+)\z', ['id'], book_show_api, (12..-1)],
|
212
|
+
]
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
it "[!bh9lo] deletes unnecessary grouping which contains only an element." do
|
217
|
+
mapping = [
|
218
|
+
['/api', [
|
219
|
+
['/books', [
|
220
|
+
['/:id' , book_show_api],
|
221
|
+
]],
|
222
|
+
]],
|
223
|
+
]
|
224
|
+
expected = '
|
225
|
+
\A
|
226
|
+
(?:
|
227
|
+
/api
|
228
|
+
(?:
|
229
|
+
/books
|
230
|
+
/[^./]+(\z)
|
231
|
+
)
|
232
|
+
)
|
233
|
+
\z
|
234
|
+
'.gsub(/\s+/, '')
|
235
|
+
jet_router.instance_exec(self) do |_|
|
236
|
+
rexp, dict, list = compile_mapping(mapping)
|
237
|
+
_.ok {rexp} == Regexp.new(expected)
|
238
|
+
_.ok {dict} == {}
|
239
|
+
_.ok {list} == [
|
240
|
+
[%r'\A/api/books/([^./]+)\z', ['id'], book_show_api, (11..-1)],
|
185
241
|
]
|
186
242
|
end
|
187
243
|
end
|
@@ -211,9 +267,7 @@ describe Rack::JetRouter do
|
|
211
267
|
]
|
212
268
|
expected = '
|
213
269
|
\A
|
214
|
-
(?:
|
215
270
|
/api/books/[^./]+(\z)
|
216
|
-
)
|
217
271
|
\z
|
218
272
|
'.gsub(/\s+/, '')
|
219
273
|
jet_router.instance_exec(self) do |_|
|
@@ -222,7 +276,7 @@ describe Rack::JetRouter do
|
|
222
276
|
_.ok {dict} == {
|
223
277
|
}
|
224
278
|
_.ok {list} == [
|
225
|
-
[%r'\A/api/books/([^./]+)\z', ['id'], book_show_api],
|
279
|
+
[%r'\A/api/books/([^./]+)\z', ['id'], book_show_api, (11..-1)],
|
226
280
|
]
|
227
281
|
end
|
228
282
|
end
|
@@ -235,9 +289,7 @@ describe Rack::JetRouter do
|
|
235
289
|
]
|
236
290
|
expected = '
|
237
291
|
\A
|
238
|
-
(?:
|
239
292
|
/api/books/[^./]+(\z)
|
240
|
-
)
|
241
293
|
\z
|
242
294
|
'.gsub(/\s+/, '')
|
243
295
|
jet_router.instance_exec(self) do |_|
|
@@ -248,7 +300,7 @@ describe Rack::JetRouter do
|
|
248
300
|
'/api/books' => book_list_api,
|
249
301
|
}
|
250
302
|
_.ok {list} == [
|
251
|
-
[%r'\A/api/books/([^./]+)\z', ['id'], book_show_api],
|
303
|
+
[%r'\A/api/books/([^./]+)\z', ['id'], book_show_api, (11..-1)],
|
252
304
|
]
|
253
305
|
end
|
254
306
|
end
|
@@ -272,7 +324,7 @@ describe Rack::JetRouter do
|
|
272
324
|
/api
|
273
325
|
(?:
|
274
326
|
/books
|
275
|
-
|
327
|
+
/[^./]+(\z)
|
276
328
|
)
|
277
329
|
)
|
278
330
|
)
|
@@ -285,7 +337,7 @@ describe Rack::JetRouter do
|
|
285
337
|
'/admin/api/books' => book_list_api,
|
286
338
|
}
|
287
339
|
_.ok {list} == [
|
288
|
-
[%r'\A/admin/api/books/([^./]+)\z', ['id'], book_show_api],
|
340
|
+
[%r'\A/admin/api/books/([^./]+)\z', ['id'], book_show_api, (17..-1)],
|
289
341
|
]
|
290
342
|
end
|
291
343
|
end
|
@@ -371,7 +423,7 @@ describe Rack::JetRouter do
|
|
371
423
|
|
|
372
424
|
/admin
|
373
425
|
(?:/books
|
374
|
-
|
426
|
+
/[^./]+(\z)
|
375
427
|
)
|
376
428
|
)
|
377
429
|
\z
|
@@ -388,13 +440,13 @@ describe Rack::JetRouter do
|
|
388
440
|
},
|
389
441
|
}
|
390
442
|
_.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],
|
443
|
+
[%r'\A/api/books/([^./]+)\z', ['id'], book_show_api, (11..-1)],
|
444
|
+
[%r'\A/api/books/([^./]+)/edit\z', ['id'], book_edit_api, (11..-6)],
|
445
|
+
[%r'\A/api/books/([^./]+)/comments\z', ['book_id'], comment_create_api, (11..-10)],
|
446
|
+
[%r'\A/api/books/([^./]+)/comments/([^./]+)\z', ['book_id', 'comment_id'], comment_update_api, nil],
|
395
447
|
[%r'\A/admin/books/([^./]+)\z', ['id'], {'GET' => admin_book_show_app,
|
396
448
|
'PUT' => admin_book_update_app,
|
397
|
-
'DELETE' => admin_book_delete_app}],
|
449
|
+
'DELETE' => admin_book_delete_app}, (13..-1)],
|
398
450
|
]
|
399
451
|
end
|
400
452
|
end
|
@@ -402,30 +454,30 @@ describe Rack::JetRouter do
|
|
402
454
|
end
|
403
455
|
|
404
456
|
|
405
|
-
describe '#
|
457
|
+
describe '#lookup()' do
|
406
458
|
|
407
459
|
it "[!ijqws] returns mapped object and urlpath parameter values when urlpath found." do
|
408
|
-
ret = jet_router.
|
460
|
+
ret = jet_router.lookup('/api/books/123')
|
409
461
|
ok {ret} == [book_show_api, {"id"=>"123"}]
|
410
462
|
end
|
411
463
|
|
412
464
|
it "[!vpdzn] returns nil when urlpath not found." do
|
413
|
-
ok {jet_router.
|
414
|
-
ok {jet_router.
|
415
|
-
ok {jet_router.
|
465
|
+
ok {jet_router.lookup('/api')} == nil
|
466
|
+
ok {jet_router.lookup('/api/book')} == nil
|
467
|
+
ok {jet_router.lookup('/api/books/')} == nil
|
416
468
|
end
|
417
469
|
|
418
470
|
it "[!24khb] finds in fixed urlpaths at first." do
|
419
|
-
ok {jet_router.
|
420
|
-
ok {jet_router.
|
471
|
+
ok {jet_router.lookup('/')} == [welcome_app, nil]
|
472
|
+
ok {jet_router.lookup('/api/books')} == [book_list_api, nil]
|
421
473
|
dict = {'GET'=>admin_book_list_app, 'POST'=>admin_book_create_app}
|
422
|
-
ok {jet_router.
|
474
|
+
ok {jet_router.lookup('/admin/books')} == [dict, nil]
|
423
475
|
end
|
424
476
|
|
425
477
|
it "[!iwyzd] urlpath param value is nil when found in fixed urlpaths." do
|
426
|
-
obj, vars = jet_router.
|
478
|
+
obj, vars = jet_router.lookup('/')
|
427
479
|
ok {vars} == nil
|
428
|
-
obj, vars = jet_router.
|
480
|
+
obj, vars = jet_router.lookup('/api/books')
|
429
481
|
ok {vars} == nil
|
430
482
|
end
|
431
483
|
|
@@ -434,14 +486,14 @@ describe Rack::JetRouter do
|
|
434
486
|
['/api/books/:id', book_show_api],
|
435
487
|
]
|
436
488
|
r = Rack::JetRouter.new(mapping, urlpath_cache_size: 3)
|
437
|
-
pair = r.
|
489
|
+
pair = r.lookup('/api/books/123')
|
438
490
|
ok {pair} == [book_show_api, {"id"=>"123"}]
|
439
491
|
r.instance_exec(self) do |_|
|
440
492
|
_.ok {@variable_urlpath_cache} == {'/api/books/123'=>pair}
|
441
493
|
#
|
442
494
|
@variable_urlpath_cache['/api/books/999'] = [book_list_api, {"ID"=>"111"}]
|
443
495
|
end
|
444
|
-
pair = r.
|
496
|
+
pair = r.lookup('/api/books/999')
|
445
497
|
ok {pair} == [book_list_api, {"ID"=>"111"}]
|
446
498
|
end
|
447
499
|
|
@@ -451,9 +503,9 @@ describe Rack::JetRouter do
|
|
451
503
|
]
|
452
504
|
r = Rack::JetRouter.new(mapping, urlpath_cache_size: 3)
|
453
505
|
#
|
454
|
-
pair1 = r.
|
455
|
-
pair2 = r.
|
456
|
-
pair3 = r.
|
506
|
+
pair1 = r.lookup('/books/1'); ok {pair1} == [book_show_api, {"id"=>"1"}]
|
507
|
+
pair2 = r.lookup('/books/2'); ok {pair2} == [book_show_api, {"id"=>"2"}]
|
508
|
+
pair3 = r.lookup('/books/3'); ok {pair3} == [book_show_api, {"id"=>"3"}]
|
457
509
|
r.instance_exec(self) do |_|
|
458
510
|
_.ok {@variable_urlpath_cache} == {
|
459
511
|
'/books/1'=>pair1,
|
@@ -462,7 +514,7 @@ describe Rack::JetRouter do
|
|
462
514
|
}
|
463
515
|
end
|
464
516
|
#
|
465
|
-
pair4 = r.
|
517
|
+
pair4 = r.lookup('/books/4'); ok {pair4} == [book_show_api, {"id"=>"4"}]
|
466
518
|
r.instance_exec(self) do |_|
|
467
519
|
_.ok {@variable_urlpath_cache} == {
|
468
520
|
'/books/2'=>pair2,
|
@@ -478,10 +530,10 @@ describe Rack::JetRouter do
|
|
478
530
|
]
|
479
531
|
r = Rack::JetRouter.new(mapping, urlpath_cache_size: 3)
|
480
532
|
#
|
481
|
-
pair1 = r.
|
482
|
-
pair2 = r.
|
483
|
-
pair3 = r.
|
484
|
-
pair4 = r.
|
533
|
+
pair1 = r.lookup('/books/1')
|
534
|
+
pair2 = r.lookup('/books/2')
|
535
|
+
pair3 = r.lookup('/books/3')
|
536
|
+
pair4 = r.lookup('/books/4')
|
485
537
|
r.instance_exec(self) do |_|
|
486
538
|
_.ok {@variable_urlpath_cache} == {
|
487
539
|
'/books/2'=>pair2,
|
@@ -490,7 +542,7 @@ describe Rack::JetRouter do
|
|
490
542
|
}
|
491
543
|
end
|
492
544
|
#
|
493
|
-
ok {r.
|
545
|
+
ok {r.lookup('/books/3')} == pair3
|
494
546
|
r.instance_exec(self) do |_|
|
495
547
|
_.ok {@variable_urlpath_cache} == {
|
496
548
|
'/books/2'=>pair2,
|
@@ -499,7 +551,7 @@ describe Rack::JetRouter do
|
|
499
551
|
}
|
500
552
|
end
|
501
553
|
#
|
502
|
-
ok {r.
|
554
|
+
ok {r.lookup('/books/1')} == pair1
|
503
555
|
r.instance_exec(self) do |_|
|
504
556
|
_.ok {@variable_urlpath_cache} == {
|
505
557
|
'/books/4'=>pair4,
|
metadata
CHANGED
@@ -1,72 +1,73 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-jet_router
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.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: 2015-12-
|
11
|
+
date: 2015-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: minitest-ok
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
-
description:
|
56
|
-
|
55
|
+
description: 'Super-fast router class for Rack application, derived from Keight.rb.
|
56
|
+
|
57
|
+
'
|
57
58
|
email:
|
58
59
|
- kwa(at)kuwata-lab.com
|
59
60
|
executables: []
|
60
61
|
extensions: []
|
61
62
|
extra_rdoc_files: []
|
62
63
|
files:
|
63
|
-
- README.md
|
64
64
|
- MIT-LICENSE
|
65
|
+
- README.md
|
65
66
|
- Rakefile
|
66
|
-
- rack-jet_router.gemspec
|
67
67
|
- lib/rack/jet_router.rb
|
68
|
-
-
|
68
|
+
- rack-jet_router.gemspec
|
69
69
|
- test/rack/jet_router_test.rb
|
70
|
+
- test/test_helper.rb
|
70
71
|
homepage: https://github.com/kwatch/rack-jet_router
|
71
72
|
licenses:
|
72
73
|
- MIT-License
|
@@ -77,17 +78,17 @@ require_paths:
|
|
77
78
|
- lib
|
78
79
|
required_ruby_version: !ruby/object:Gem::Requirement
|
79
80
|
requirements:
|
80
|
-
- -
|
81
|
+
- - ">="
|
81
82
|
- !ruby/object:Gem::Version
|
82
83
|
version: '2.0'
|
83
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
85
|
requirements:
|
85
|
-
- -
|
86
|
+
- - ">="
|
86
87
|
- !ruby/object:Gem::Version
|
87
88
|
version: '0'
|
88
89
|
requirements: []
|
89
90
|
rubyforge_project:
|
90
|
-
rubygems_version: 2.
|
91
|
+
rubygems_version: 2.5.1
|
91
92
|
signing_key:
|
92
93
|
specification_version: 4
|
93
94
|
summary: Super-fast router class for Rack
|