rack-jet_router 1.2.0 → 1.3.1
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 +5 -5
- data/CHANGES.md +58 -0
- data/MIT-LICENSE +1 -1
- data/README.md +187 -77
- data/bench/Gemfile +13 -0
- data/bench/Rakefile.rb +11 -0
- data/bench/bench.rb +367 -0
- data/lib/rack/jet_router.rb +410 -210
- data/rack-jet_router.gemspec +21 -18
- data/test/builder_test.rb +638 -0
- data/test/misc_test.rb +23 -0
- data/test/router_test.rb +597 -0
- data/test/run_all.rb +6 -0
- data/test/{test_helper.rb → shared.rb} +8 -3
- metadata +36 -42
- data/Rakefile +0 -58
- data/test/rack/jet_router_test.rb +0 -726
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 55c3dc8f5747e61e864ee478f7336d755dc022cb4ebc2f75baeae2b2f1059bd8
|
4
|
+
data.tar.gz: '085324025413ff2d565f6aad7d8fc1827486d9454772f3c745fb1bd3f3d575f3'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 357cfa2babfeb364ff8ca485247eed0f1cb9f7a0c5d7c5ff7c907d593d488bb3e365e198cb6eaf89328b7f85c3e349e273793ade17ee1e94822eed139df90bd0
|
7
|
+
data.tar.gz: a6bb2a5d973fa092f356b733ce4ca5f1af335ec53f7d83f9087e0dec271ada489ae80576ba71a4199dd084a6ffc3a25e74e6c29d8e2a1ff0c0e535e2fcf83e8e
|
data/CHANGES.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
CHANGES
|
2
|
+
=======
|
3
|
+
|
4
|
+
|
5
|
+
Release 1.3.1 (2023-12-10)
|
6
|
+
--------------------------
|
7
|
+
|
8
|
+
* [bugfix] Fixed to correctly handle suffixed URL path pattern such as `/foo(.:format)`.
|
9
|
+
|
10
|
+
|
11
|
+
Release 1.3.0 (2023-12-09)
|
12
|
+
--------------------------
|
13
|
+
|
14
|
+
* [enhance] Performance has improved (about 10%). This improvement was achieved by optimizing regular expressions.
|
15
|
+
* [enhance] URL path mapping can be nested Hash as well as nested Array. See the document for detail.
|
16
|
+
* [enhance] Passing the `int_param: /(?:\A|_)id\z/` keyword argument to `JetRouter.new()` changes the router to treat urlpath parameter `id` or `xxx_id` as integer type.
|
17
|
+
* [enhance] Passing the `env_key:` keyword argument to `JetRouter.new()` changes the key of environemnt to store URL path parameter values.
|
18
|
+
* [change] Keyword parameter `urlpath_cache_size:` of `JetRouter.new()` is renamed to `cache_size:`. Old parameter name is also available for backward compatibility, but it is recommended to use new parameter name.
|
19
|
+
* [change] Rename `JetRouter#build_urlpath_parameter_vars()` to `JetRouter#build_param_values()`.
|
20
|
+
* [change] Update benchmark script to require 'benchmarker' gem.
|
21
|
+
|
22
|
+
|
23
|
+
Release 1.2.0 (2016-10-16)
|
24
|
+
--------------------------
|
25
|
+
|
26
|
+
* Change auto-redirection to be occurred only on GET or HEAD methods.
|
27
|
+
* Code is rewrited, especially around `Rack::JetRouter#compile_mapping()`.
|
28
|
+
* Update benchmark script to support `Hanabi::Router`.
|
29
|
+
|
30
|
+
|
31
|
+
Release 1.1.1 (2015-12-29)
|
32
|
+
--------------------------
|
33
|
+
|
34
|
+
* Fix benchmark script.
|
35
|
+
* Fix document.
|
36
|
+
|
37
|
+
|
38
|
+
Release 1.1.0 (2015-12-28)
|
39
|
+
--------------------------
|
40
|
+
|
41
|
+
* **NOTICE** `Rack::JetRouter#find()` is renamed to `#lookup()`.
|
42
|
+
`#find()` is also available for compatibility, but not recommended.
|
43
|
+
* Performance improved when number of URL path parameter is 1.
|
44
|
+
* Regular expression generated is improved.
|
45
|
+
* Benchmark script is improved to take some command-line options.
|
46
|
+
* Document fixed.
|
47
|
+
|
48
|
+
|
49
|
+
Release 1.0.1 (2015-12-06)
|
50
|
+
--------------------------
|
51
|
+
|
52
|
+
* Fix document
|
53
|
+
|
54
|
+
|
55
|
+
Release 1.0.0 (2015-12-06)
|
56
|
+
--------------------------
|
57
|
+
|
58
|
+
* First release
|
data/MIT-LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
$Copyright: copyright(c) 2015
|
3
|
+
$Copyright: copyright(c) 2015 kwatch@gmail.com $
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# Rack::JetRouter
|
2
2
|
|
3
|
-
($Release: 1.
|
3
|
+
($Release: 1.3.1 $)
|
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).
|
7
7
|
|
8
|
-
Rack::JetRouter requires Ruby >= 2.
|
8
|
+
Rack::JetRouter requires Ruby >= 2.4.
|
9
9
|
|
10
10
|
|
11
11
|
## Benchmark
|
@@ -14,45 +14,42 @@ Benchmark script is [here](https://github.com/kwatch/rack-jet_router/blob/releas
|
|
14
14
|
|
15
15
|
| Name | Version |
|
16
16
|
| ------------------ | ------- |
|
17
|
-
| Ruby | 2.
|
18
|
-
| Rack |
|
19
|
-
| Rack::JetRouter | 1.
|
17
|
+
| Ruby | 3.2.2 |
|
18
|
+
| Rack | 2.2.8 |
|
19
|
+
| Rack::JetRouter | 1.3.0 |
|
20
20
|
| Rack::Multiplexer | 0.0.8 |
|
21
|
-
| Sinatra | 1.
|
22
|
-
| Keight.rb | 0.
|
23
|
-
| Hanami
|
21
|
+
| Sinatra | 3.1.0 |
|
22
|
+
| Keight.rb | 1.0.0 |
|
23
|
+
| Hanami::Router | 2.0.2 |
|
24
24
|
|
25
|
-
(Macbook
|
25
|
+
(Macbook Pro, Apple M1 Pro, macOS Ventura 13.6.2)
|
26
26
|
|
27
27
|
|
28
28
|
### JetRouter vs. Rack vs. Sinatra vs. Keight.rb vs. Hanami:
|
29
29
|
|
30
30
|
```
|
31
|
-
## Ranking
|
32
|
-
(
|
33
|
-
(
|
34
|
-
(
|
35
|
-
(JetRouter)
|
36
|
-
(
|
37
|
-
(
|
38
|
-
(
|
39
|
-
(
|
40
|
-
(
|
41
|
-
(Hanami::Router) /api/
|
42
|
-
(
|
43
|
-
(
|
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%)
|
31
|
+
## Ranking usec/req Graph (longer=faster)
|
32
|
+
(JetRouter) /api/aaa01 0.2816 (100.0%) ********************
|
33
|
+
(Multiplexer) /api/aaa01 1.2586 ( 22.4%) ****
|
34
|
+
(Hanami::Router) /api/aaa01 1.3296 ( 21.2%) ****
|
35
|
+
(JetRouter) /api/aaa01/123 1.5861 ( 17.8%) ****
|
36
|
+
(Rack::Req+Res) /api/aaa01/123 1.7369 ( 16.2%) ***
|
37
|
+
(Rack::Req+Res) /api/aaa01 1.7438 ( 16.1%) ***
|
38
|
+
(Keight) /api/aaa01 1.8906 ( 14.9%) ***
|
39
|
+
(Keight) /api/aaa01/123 2.8998 ( 9.7%) **
|
40
|
+
(Multiplexer) /api/aaa01/123 2.9166 ( 9.7%) **
|
41
|
+
(Hanami::Router) /api/aaa01/123 4.0996 ( 6.9%) *
|
42
|
+
(Sinatra) /api/aaa01 49.6862 ( 0.6%)
|
43
|
+
(Sinatra) /api/aaa01/123 54.3448 ( 0.5%)
|
47
44
|
```
|
48
45
|
|
49
46
|
* If URL path has no path parameter (such as `/api/hello`),
|
50
|
-
|
47
|
+
JetRouter is significantly fast.
|
51
48
|
* If URL path contains path parameter (such as `/api/hello/:id`),
|
52
|
-
|
53
|
-
* Overhead of
|
49
|
+
JetRouter becomes slower, but it is enough small (about 1.3 usec/req).
|
50
|
+
* Overhead of JetRouter is smaller than that of Rack::Reqeuast +
|
54
51
|
Rack::Response.
|
55
|
-
* Hanami is
|
52
|
+
* Hanami is slower than JetRouter, but quite enough fast.
|
56
53
|
* Sinatra is too slow.
|
57
54
|
|
58
55
|
|
@@ -60,20 +57,37 @@ Benchmark script is [here](https://github.com/kwatch/rack-jet_router/blob/releas
|
|
60
57
|
|
61
58
|
```
|
62
59
|
## Ranking usec/req Graph (longer=faster)
|
63
|
-
(JetRouter)
|
64
|
-
(JetRouter)
|
65
|
-
(
|
66
|
-
(
|
67
|
-
(
|
68
|
-
(
|
69
|
-
(Multiplexer)
|
70
|
-
(Multiplexer)
|
60
|
+
(JetRouter) /api/aaa01 0.2816 (100.0%) ********************
|
61
|
+
(JetRouter) /api/zzz26 0.2823 ( 99.7%) ********************
|
62
|
+
(Multiplexer) /api/aaa01 1.2586 ( 22.4%) ****
|
63
|
+
(JetRouter) /api/aaa01/123 1.5861 ( 17.8%) ****
|
64
|
+
(Multiplexer) /api/aaa01/123 2.9166 ( 9.7%) **
|
65
|
+
(JetRouter) /api/zzz26/456 3.5767 ( 7.9%) **
|
66
|
+
(Multiplexer) /api/zzz26 14.8423 ( 1.9%)
|
67
|
+
(Multiplexer) /api/zzz26/456 16.8930 ( 1.7%)
|
71
68
|
```
|
72
69
|
|
73
70
|
* JetRouter is about 4~6 times faster than Rack::Multiplexer.
|
74
71
|
* Rack::Multiplexer is getting worse in promotion to the number of URL paths.
|
75
72
|
|
76
73
|
|
74
|
+
### JetRouter vs. Hanami::Router
|
75
|
+
|
76
|
+
```
|
77
|
+
## Ranking usec/req Graph (longer=faster)
|
78
|
+
(JetRouter) /api/aaa01 0.2816 (100.0%) ********************
|
79
|
+
(JetRouter) /api/zzz26 0.2823 ( 99.7%) ********************
|
80
|
+
(Hanami::Router) /api/zzz26 1.3280 ( 21.2%) ****
|
81
|
+
(Hanami::Router) /api/aaa01 1.3296 ( 21.2%) ****
|
82
|
+
(JetRouter) /api/aaa01/123 1.5861 ( 17.8%) ****
|
83
|
+
(JetRouter) /api/zzz26/456 3.5767 ( 7.9%) **
|
84
|
+
(Hanami::Router) /api/zzz26/456 4.0898 ( 6.9%) *
|
85
|
+
(Hanami::Router) /api/aaa01/123 4.0996 ( 6.9%) *
|
86
|
+
```
|
87
|
+
|
88
|
+
* Hanami is slower than JetRouter, but it has enough speed.
|
89
|
+
|
90
|
+
|
77
91
|
## Examples
|
78
92
|
|
79
93
|
### #1: Depends only on Request Path
|
@@ -189,6 +203,69 @@ handler.__send__(action, args)
|
|
189
203
|
## Topics
|
190
204
|
|
191
205
|
|
206
|
+
### Nested Array v.s. Nested Hash
|
207
|
+
|
208
|
+
URL path mapping can be not only nested Array but also nested Hash.
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
## nested Array
|
212
|
+
mapping = [
|
213
|
+
["/api", [
|
214
|
+
["/books", [
|
215
|
+
["" , book_list_api],
|
216
|
+
["/:id" , book_show_api],
|
217
|
+
]],
|
218
|
+
]],
|
219
|
+
]
|
220
|
+
|
221
|
+
## nested Hash
|
222
|
+
mapping = {
|
223
|
+
"/api" => {
|
224
|
+
"/books" => {
|
225
|
+
"" => book_list_api,
|
226
|
+
"/:id" => book_show_api,
|
227
|
+
},
|
228
|
+
},
|
229
|
+
}
|
230
|
+
```
|
231
|
+
|
232
|
+
But nested Hash mapping can't include request method mappings, because
|
233
|
+
it is hard to distinguish between URL path mapping and request method mapping.
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
## NOT OK
|
237
|
+
mapping = {
|
238
|
+
"/api" => {
|
239
|
+
"/books" => {
|
240
|
+
"" => {GET: book_list_api, POST: book_create_api},
|
241
|
+
"/:id" => {GET: book_show_api, PUT: book_update_api},
|
242
|
+
},
|
243
|
+
},
|
244
|
+
}
|
245
|
+
```
|
246
|
+
|
247
|
+
In this case, define subclass of Hash class and use it instead of Hash.
|
248
|
+
|
249
|
+
```ruby
|
250
|
+
class Map < Hash # define subclass of Hash class
|
251
|
+
end
|
252
|
+
|
253
|
+
def Map(**kwargs) # helper method to create subclass object
|
254
|
+
return Map.new.update(kwargs)
|
255
|
+
end
|
256
|
+
|
257
|
+
## OK
|
258
|
+
mapping = {
|
259
|
+
"/api" => {
|
260
|
+
"/books" => {
|
261
|
+
"" => Map(GET: book_list_api, POST: book_create_api),
|
262
|
+
"/:id" => Map(GET: book_show_api, PUT: book_update_api),
|
263
|
+
},
|
264
|
+
},
|
265
|
+
}
|
266
|
+
```
|
267
|
+
|
268
|
+
|
192
269
|
### URL Path Parameters
|
193
270
|
|
194
271
|
In Rack application, URL path parameters (such as `{"id"=>"123"}`) are
|
@@ -201,13 +278,19 @@ BookApp = proc {|env|
|
|
201
278
|
}
|
202
279
|
```
|
203
280
|
|
281
|
+
Key name can be changed by ``env_key:`` keyword argument of ``JetRouter.new()``.
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
router = Rack::JetRouter.new(mapping, env_key: "rack.urlpath_params")
|
285
|
+
```
|
286
|
+
|
204
287
|
If you want to tweak URL path parameters, define subclass of Rack::JetRouter
|
205
|
-
and override `#
|
288
|
+
and override `#build_param_values(names, values)`.
|
206
289
|
|
207
290
|
```ruby
|
208
291
|
class MyRouter < JetRouter
|
209
292
|
|
210
|
-
def
|
293
|
+
def build_param_values(names, values)
|
211
294
|
return names.zip(values).each_with_object({}) {|(k, v), d|
|
212
295
|
## converts urlpath pavam value into integer
|
213
296
|
v = v.to_i if k == 'id' || k.end_with?('_id')
|
@@ -219,7 +302,70 @@ end
|
|
219
302
|
```
|
220
303
|
|
221
304
|
|
222
|
-
###
|
305
|
+
### Integer Type Parameters
|
306
|
+
|
307
|
+
Keyword argument ``int_param:`` of ``JetRouter.new()`` specifies
|
308
|
+
parameter name pattern (regexp) to treat as integer type.
|
309
|
+
For example, ``int_param: /(?:\A|_)id\z/`` treats ``id`` or ``xxx_id``
|
310
|
+
parameter values as integer type.
|
311
|
+
|
312
|
+
```ruby
|
313
|
+
require 'rack'
|
314
|
+
require 'rack/jet_router'
|
315
|
+
|
316
|
+
rack_app = proc {|env|
|
317
|
+
params = env['rack.urlpath_params']
|
318
|
+
type = params["book_id"].class
|
319
|
+
text = "params=#{params.inspect}, type=#{type}"
|
320
|
+
[200, {}, [text]]
|
321
|
+
}
|
322
|
+
|
323
|
+
mapping = [
|
324
|
+
["/api/books/:book_id", rack_app]
|
325
|
+
]
|
326
|
+
router = Rack::JetRouter.new(mapping, int_param: /(?:\A|_)id\z/
|
327
|
+
|
328
|
+
env = Rack::MockRequest.env_for("/api/books/123")
|
329
|
+
tuple = router.call(env)
|
330
|
+
puts tuple[2] #=> params={"book_id"=>123}, type=Integer
|
331
|
+
```
|
332
|
+
|
333
|
+
Integer type parameters match to only integers.
|
334
|
+
|
335
|
+
```ruby
|
336
|
+
env = Rack::MockRequest.env_for("/api/books/FooBar")
|
337
|
+
tuple = router.call(env)
|
338
|
+
puts tuple[2] #=> 404 Not Found
|
339
|
+
```
|
340
|
+
|
341
|
+
|
342
|
+
<!--
|
343
|
+
|
344
|
+
### URL Path Multiple Extension
|
345
|
+
|
346
|
+
It is available to specify multiple extension of URL path.
|
347
|
+
|
348
|
+
```ruby
|
349
|
+
mapping = {
|
350
|
+
"/api/books" => {
|
351
|
+
"/:id(.html|.json)" => book_api,
|
352
|
+
},
|
353
|
+
}
|
354
|
+
```
|
355
|
+
|
356
|
+
In above example, the following URL path patterns are enabled.
|
357
|
+
|
358
|
+
* ``/api/books/:id``
|
359
|
+
* ``/api/books/:id.html``
|
360
|
+
* ``/api/books/:id.json``
|
361
|
+
|
362
|
+
Notice that ``env['rack.urlpath_params']['format']`` is not set
|
363
|
+
because ``:format`` is not specified in URL path pattern.
|
364
|
+
|
365
|
+
-->
|
366
|
+
|
367
|
+
|
368
|
+
### Auto-redirection
|
223
369
|
|
224
370
|
Rack::JetRouter implements auto-redirection.
|
225
371
|
|
@@ -281,42 +427,6 @@ Above methods are invoked from `Rack::JetRouter#call()`.
|
|
281
427
|
|
282
428
|
## Copyright and License
|
283
429
|
|
284
|
-
$Copyright: copyright(c) 2015
|
430
|
+
$Copyright: copyright(c) 2015 kwatch@gmail.com $
|
285
431
|
|
286
432
|
$License: MIT License $
|
287
|
-
|
288
|
-
|
289
|
-
## History
|
290
|
-
|
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
|
-
|
305
|
-
### 2015-12-28: Release 1.1.0
|
306
|
-
|
307
|
-
* **NOTICE** `Rack::JetRouter#find()` is renamed to `#lookup()`.<br>
|
308
|
-
`#find()` is also available for compatibility, but not recommended.
|
309
|
-
* Performance improved when number of URL path parameter is 1.
|
310
|
-
* Regular expression generated is improved.
|
311
|
-
* Benchmark script is improved to take some command-line options.
|
312
|
-
* Document fixed.
|
313
|
-
|
314
|
-
|
315
|
-
### 2015-12-06: Release 1.0.1
|
316
|
-
|
317
|
-
* Fix document
|
318
|
-
|
319
|
-
|
320
|
-
### 2015-12-06: Release 1.0.0
|
321
|
-
|
322
|
-
* First release
|
data/bench/Gemfile
ADDED
data/bench/Rakefile.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
|
5
|
+
desc "run 'bench.rb' script"
|
6
|
+
task :bench, :N do |t, args|
|
7
|
+
n = args[:N] || 1000000
|
8
|
+
ruby "bench.rb", "--N=#{n}"
|
9
|
+
#ruby "bench.rb", "--N=#{n}", "--sinatra=0", "--multiplexer=0"
|
10
|
+
#ruby "bench.rb", "--N=#{n}", "--rack=0", "--jetrouter=0", "--keight=0", "--multiplexer=0", "--sinatra=0", "--hanami=0"
|
11
|
+
end
|