rack-jet_router 1.0.1 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a998880ec075549282c7341aa711e2a9955ac330
4
- data.tar.gz: a8792a73ca113ae0710654a2ccde67556719ba37
3
+ metadata.gz: da49b22574962dc8935525feba719fd735003f43
4
+ data.tar.gz: 9fa472683f55e9b366cc46b5f4f984295dffdf66
5
5
  SHA512:
6
- metadata.gz: db17dd3ebcf17be118fec1b96b38f95ac7ac5f13266811f43df417ac36ec98f9e6ca2bbab9272c0f302dc164846796464f7171c1fc5f83826acafc6e54cf50cb
7
- data.tar.gz: 07a8f384854139e73fb1bfca49b6ff1ad81dcf03cff5276baa86ad39a6e39558706837f98098cd8c0ba8636458f17081c49ca1ae41cb2da809d1fe7082bb3819
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 usec/req
13
- (Rack plain) /api/hello 1.555 (100.0%) ********************
14
- (JetRouter) /api/hello 1.597 ( 97.4%) *******************
15
- (JetRouter) /api/hello/123 6.424 ( 24.2%) *****
16
- (R::Req+Res) /api/hello 9.837 ( 15.8%) ***
17
- (Sinatra) /api/hello 106.965 ( 1.5%)
18
- (Sinatra) /api/hello/123 116.672 ( 1.3%)
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 just a litte slower than plain Rack application.
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 6.4ns/req).
25
- * Overhead of Rack::JetRouter is smaller than that of Rack::Reqeust +
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
- urlpath_mapping = [
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(urlpath_mapping)
56
- p router.find('/api/books/123.html')
57
- #=> [book_api, {"id"=>"123", "format"=>"html"}]
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
- urlpath_mapping = [
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(urlpath_mapping)
87
- p router.find('/api/books/123')
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
- urlpath_mapping = [
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(urlpath_mapping)
133
- p router.find('/api/books/123')
134
- #=> [{"GET"=>[BooksAPI, :show], "PUT"=>..., "DELETE"=>...}, {"id"=>"123"}]
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"}`) is available via
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 possible to classify URL path patterns into two types: fixed and variable.
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.find('/api/books/123') # caches even varaible urlpath
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
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  ###
4
- ### $Release: 1.0.1 $
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.find('/api/books/123.html')
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.find('/api/books/123')
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
- def initialize(mapping, urlpath_cache_size: 0)
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 = find(req_path)
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 = find(location)
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
- ## find('/api/books/123') #=> [BookApp, {"id"=>"123"}]
104
- def find(req_path)
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
- m = full_urlpath_rexp.match(req_path)
124
- param_values = m.captures
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
- _compile_mapping(mapping, rexp_buf, prefix_pat, fixed_urlpaths, variable_urlpaths)
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 _compile_mapping(mapping, rexp_buf, prefix_pat, fixed_dict, variable_list)
192
- param_pat1 = '[^./]+'
193
- param_pat2 = '([^./]+)'
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 |urlpath_pat, obj|
209
+ mapping.each do |child_urlpath_pat, obj|
197
210
  rexp_buf << '|' if rexp_buf.length != len
198
- full_urlpath_pat = "#{prefix_pat}#{urlpath_pat}"
211
+ curr_urlpath_pat = "#{base_urlpath_pat}#{urlpath_pat}"
199
212
  #; [!ospaf] accepts nested mapping.
200
213
  if obj.is_a?(Array)
201
- rexp_str, _ = compile_urlpath_pattern(urlpath_pat, param_pat1)
202
- rexp_buf << rexp_str
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
- #; [!guhdc] if mapping dict is specified...
213
- if obj.is_a?(Hash)
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, param_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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "rack-jet_router"
5
- spec.version = '$Release: 1.0.1 $'.split()[1]
5
+ spec.version = '$Release: 1.1.0 $'.split()[1]
6
6
  spec.authors = ["makoto kuwata"]
7
7
  spec.email = ["kwa(at)kuwata-lab.com"]
8
8
 
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  ###
4
- ### $Release: 1.0.1 $
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
- (?:/[^./]+(\z))
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
- (?:/[^./]+(\z))
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
- (?:/[^./]+(\z))
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 '#find()' do
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.find('/api/books/123')
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.find('/api')} == nil
414
- ok {jet_router.find('/api/book')} == nil
415
- ok {jet_router.find('/api/books/')} == nil
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.find('/')} == [welcome_app, nil]
420
- ok {jet_router.find('/api/books')} == [book_list_api, nil]
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.find('/admin/books')} == [dict, nil]
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.find('/')
478
+ obj, vars = jet_router.lookup('/')
427
479
  ok {vars} == nil
428
- obj, vars = jet_router.find('/api/books')
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.find('/api/books/123')
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.find('/api/books/999')
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.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"}]
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.find('/books/4'); ok {pair4} == [book_show_api, {"id"=>"4"}]
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.find('/books/1')
482
- pair2 = r.find('/books/2')
483
- pair3 = r.find('/books/3')
484
- pair4 = r.find('/books/4')
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.find('/books/3')} == pair3
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.find('/books/1')} == pair1
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.1
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-06 00:00:00.000000000 Z
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
- Super-fast router class for Rack application, derived from Keight.rb.
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
- - test/test_helper.rb
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.0.14
91
+ rubygems_version: 2.5.1
91
92
  signing_key:
92
93
  specification_version: 4
93
94
  summary: Super-fast router class for Rack