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 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