rack-jet_router 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c763fbc224d9c9c21718bf685203b35694c20f3b
4
- data.tar.gz: 56bc71a1d83180f2970f1d95985fc8b2fa9b91bc
3
+ metadata.gz: dbed04ca4adfdce5a3e82234dd6182e18519dc62
4
+ data.tar.gz: bdec9db77792ffba5bca3e05ae4bf62f35cbf555
5
5
  SHA512:
6
- metadata.gz: b72f539eb7750a754daea1189aa0035e8010a93d7debf74ef7f6a4b927223daf19f4bfbf0165192aab08c0e395289fabc423ee4690e6ab0ff0417603185334f1
7
- data.tar.gz: 565dcf52b9c34e81803474de3c212f2b2b92d543a85057f3ab7959764f724b1ea2a5dc07c11caa4aca06a79aa318410000dabb98001ef4e0cbad4aaaa711fe11
6
+ metadata.gz: 0d90ffbb230351b7f0054d7d382b377dca41832cbfe2a47d6793c0ac88dbc075104535bf4529a6e1c50251365faadefb8d1fa02342d7104420cd8eb6316ea495
7
+ data.tar.gz: af141052683402fd8dedc4538b548bab59c816714e423236fe83cd70d2865f77337f5c6b9e0d93380e49fe4547995e36f256943be6426a20b1d04a82cfe69697
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Rack::JetRouter
2
2
 
3
- ($Release: 1.1.1 $)
3
+ ($Release: 1.2.0 $)
4
4
 
5
5
  Rack::JetRouter is crazy-fast router library for Rack application,
6
6
  derived from [Keight.rb](https://github.com/kwatch/keight/tree/ruby).
@@ -10,22 +10,40 @@ Rack::JetRouter requires Ruby >= 2.0.
10
10
 
11
11
  ## Benchmark
12
12
 
13
- Benchmark script is [here](https://github.com/kwatch/rack-jet_router/blob/dev/bench/bench.rb).
13
+ Benchmark script is [here](https://github.com/kwatch/rack-jet_router/blob/release/bench/bench.rb).
14
14
 
15
- ### JetRouter vs. Rack vs. Sinatra vs. Keight.rb:
15
+ | Name | Version |
16
+ | ------------------ | ------- |
17
+ | Ruby | 2.3.1 |
18
+ | Rack | 1.6.4 |
19
+ | Rack::JetRouter | 1.2.0 |
20
+ | Rack::Multiplexer | 0.0.8 |
21
+ | Sinatra | 1.4.6 |
22
+ | Keight.rb | 0.3.0 |
23
+ | Hanami | 0.8.0 |
24
+
25
+ (Macbook Air, Intel Core i7 1.7GHz, OS X EL Capitan)
26
+
27
+
28
+ ### JetRouter vs. Rack vs. Sinatra vs. Keight.rb vs. Hanami:
16
29
 
17
30
  ```
18
- ## Ranking 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
31
+ ## Ranking real
32
+ (Rack plain) /api/aaa01 0.9316 (100.0%) ********************
33
+ (Rack plain) /api/aaa01/123 1.0222 ( 91.1%) ******************
34
+ (JetRouter) /api/aaa01 1.4191 ( 65.6%) *************
35
+ (JetRouter) /api/aaa01/123 6.0146 ( 15.5%) ***
36
+ (Multiplexer) /api/aaa01 6.1026 ( 15.3%) ***
37
+ (Keight.rb) /api/aaa01 7.2330 ( 12.9%) ***
38
+ (R::Req+Res) /api/aaa01 10.7835 ( 8.6%) **
39
+ (R::Req+Res) /api/aaa01/123 10.8412 ( 8.6%) **
40
+ (Keight.rb) /api/aaa01/123 10.8708 ( 8.6%) **
41
+ (Hanami::Router) /api/zzz26 11.5185 ( 8.1%) **
42
+ (Hanami::Router) /api/aaa01 11.7033 ( 8.0%) **
43
+ (Hanami::Router) /api/aaa01/123 17.9229 ( 5.2%) *
44
+ (Multiplexer) /api/aaa01/123 18.6987 ( 5.0%) *
45
+ (Sinatra) /api/aaa01 109.7597 ( 0.8%)
46
+ (Sinatra) /api/aaa01/123 121.3258 ( 0.8%)
29
47
  ```
30
48
 
31
49
  * If URL path has no path parameter (such as `/api/hello`),
@@ -34,6 +52,7 @@ Benchmark script is [here](https://github.com/kwatch/rack-jet_router/blob/dev/be
34
52
  Rack::JetRouter becomes slower, but it is enough small (about 6usec/req).
35
53
  * Overhead of Rack::JetRouter is smaller than that of Rack::Reqeuast +
36
54
  Rack::Response.
55
+ * Hanami is a litte slow.
37
56
  * Sinatra is too slow.
38
57
 
39
58
 
@@ -41,17 +60,18 @@ Benchmark script is [here](https://github.com/kwatch/rack-jet_router/blob/dev/be
41
60
 
42
61
  ```
43
62
  ## Ranking usec/req Graph (longer=faster)
44
- (JetRouter) /api/aaa01 1.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
63
+ (JetRouter) /api/aaa01 1.4191 ( 65.6%) *************
64
+ (JetRouter) /api/zzz26 1.4300 ( 65.1%) *************
65
+ (JetRouter) /api/aaa01/123 6.0146 ( 15.5%) ***
66
+ (Multiplexer) /api/aaa01 6.1026 ( 15.3%) ***
67
+ (JetRouter) /api/zzz26/789 6.9102 ( 13.5%) ***
68
+ (Multiplexer) /api/aaa01/123 18.6987 ( 5.0%) *
69
+ (Multiplexer) /api/zzz26 30.7618 ( 3.0%) *
70
+ (Multiplexer) /api/zzz26/789 42.6660 ( 2.2%)
52
71
  ```
53
72
 
54
- * JetRouter is about 3~5 times faster than Rack::Multiplexer.
73
+ * JetRouter is about 4~6 times faster than Rack::Multiplexer.
74
+ * Rack::Multiplexer is getting worse in promotion to the number of URL paths.
55
75
 
56
76
 
57
77
  ## Examples
@@ -199,6 +219,18 @@ end
199
219
  ```
200
220
 
201
221
 
222
+ ### Auto-redirection.
223
+
224
+ Rack::JetRouter implements auto-redirection.
225
+
226
+ * When `/foo` is provided and `/foo/` is requested, then Rack::JetRouter redirects to `/foo` automatically.
227
+ * When `/foo/` is provided and `/foo` is requested, then Rack::JetRouter redirects to `/foo/` automatically.
228
+
229
+ Notice that auto-redirection is occurred only on `GET` or `HEAD` methods, because
230
+ browser cannot handle redirection on `POST`, `PUT`, and `DELETE` methods correctly.
231
+ Don't depend on auto-redirection feature so much.
232
+
233
+
202
234
  ### Variable URL Path Cache
203
235
 
204
236
  It is useful to classify URL path patterns into two types: fixed and variable.
@@ -249,7 +281,7 @@ Above methods are invoked from `Rack::JetRouter#call()`.
249
281
 
250
282
  ## Copyright and License
251
283
 
252
- $Copyright: copyright(c) 2015 kuwata-lab.com all rights reserved $
284
+ $Copyright: copyright(c) 2015-2016 kuwata-lab.com all rights reserved $
253
285
 
254
286
  $License: MIT License $
255
287
 
@@ -257,6 +289,19 @@ $License: MIT License $
257
289
  ## History
258
290
 
259
291
 
292
+ ### 2016-10-16: Release 1.2.0
293
+
294
+ * Change auto-redirection to be occurred only on GET or HEAD methods.
295
+ * Code is rewrited, especially around `Rack::JetRouter#compile_mapping()`.
296
+ * Update benchmark script to support `Hanabi::Router`.
297
+
298
+
299
+ ### 2015-12-29: Release 1.1.1
300
+
301
+ * Fix benchmark script.
302
+ * Fix document.
303
+
304
+
260
305
  ### 2015-12-28: Release 1.1.0
261
306
 
262
307
  * **NOTICE** `Rack::JetRouter#find()` is renamed to `#lookup()`.<br>
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  ###
4
- ### $Release: 1.1.1 $
4
+ ### $Release: 1.2.0 $
5
5
  ### $Copyright: copyright(c) 2015 kuwata-lab.com all rights reserved $
6
6
  ### $License: MIT License $
7
7
  ###
@@ -55,7 +55,7 @@ module Rack
55
55
  ##
56
56
  class JetRouter
57
57
 
58
- RELEASE = '$Release: 1.1.1 $'.split()[1]
58
+ RELEASE = '$Release: 1.2.0 $'.split()[1]
59
59
 
60
60
  def initialize(mapping, urlpath_cache_size: 0,
61
61
  enable_urlpath_param_range: true)
@@ -64,12 +64,15 @@ module Rack
64
64
  (@urlpath_rexp, # ex: {'/api/books'=>BooksApp}
65
65
  @fixed_urlpath_dict, # ex: [[%r'\A/api/books/([^./]+)\z', ['id'], BookApp]]
66
66
  @variable_urlpath_list, # ex: %r'\A(?:/api(?:/books(?:/[^./]+(\z))))\z'
67
+ @all_entrypoints, # ex: [['/api/books', BooksAPI'], ['/api/orders', OrdersAPI]]
67
68
  ) = compile_mapping(mapping)
68
69
  ## cache for variable urlpath (= containg urlpath parameters)
69
70
  @urlpath_cache_size = urlpath_cache_size
70
71
  @variable_urlpath_cache = urlpath_cache_size > 0 ? {} : nil
71
72
  end
72
73
 
74
+ attr_reader :urlpath_rexp
75
+
73
76
  ## Finds rack app according to PATH_INFO and REQUEST_METHOD and invokes it.
74
77
  def call(env)
75
78
  #; [!fpw8x] finds mapped app according to env['PATH_INFO'].
@@ -77,10 +80,15 @@ module Rack
77
80
  app, urlpath_params = lookup(req_path)
78
81
  #; [!wxt2g] guesses correct urlpath and redirects to it automaticaly when request path not found.
79
82
  #; [!3vsua] doesn't redict automatically when request path is '/'.
80
- unless app || req_path == '/'
83
+ if ! app && should_redirect?(env)
81
84
  location = req_path =~ /\/\z/ ? req_path[0..-2] : req_path + '/'
82
85
  app, urlpath_params = lookup(location)
83
- return redirect_to(location) if app
86
+ if app
87
+ #; [!hyk62] adds QUERY_STRING to redirect location.
88
+ qs = env['QUERY_STRING']
89
+ location = "#{location}?#{qs}" if qs && ! qs.empty?
90
+ return redirect_to(location)
91
+ end
84
92
  end
85
93
  #; [!30x0k] returns 404 when request urlpath not found.
86
94
  return error_not_found(env) unless app
@@ -143,6 +151,12 @@ module Rack
143
151
 
144
152
  alias find lookup # :nodoc: # for backward compatilibity
145
153
 
154
+ ## Yields pair of urlpath pattern and app.
155
+ def each(&block)
156
+ #; [!ep0pw] yields pair of urlpath pattern and app.
157
+ @all_entrypoints.each(&block)
158
+ end
159
+
146
160
  protected
147
161
 
148
162
  ## Returns [404, {...}, [...]]. Override in subclass if necessary.
@@ -157,6 +171,18 @@ module Rack
157
171
  return [405, {"Content-Type"=>"text/plain"}, ["405 Method Not Allowed"]]
158
172
  end
159
173
 
174
+ ## Returns false when request path is '/' or request method is not GET nor HEAD.
175
+ ## (It is not recommended to redirect when request method is POST, PUT or DELETE,
176
+ ## because browser doesn't handle redirect correctly on those methods.)
177
+ def should_redirect?(env)
178
+ #; [!dsu34] returns false when request path is '/'.
179
+ #; [!ycpqj] returns true when request method is GET or HEAD.
180
+ #; [!7q8xu] returns false when request method is POST, PUT or DELETE.
181
+ return false if env['PATH_INFO'] == '/'
182
+ req_method = env['REQUEST_METHOD']
183
+ return req_method == 'GET' || req_method == 'HEAD'
184
+ end
185
+
160
186
  ## Returns [301, {"Location"=>location, ...}, [...]]. Override in subclass if necessary.
161
187
  def redirect_to(location)
162
188
  content = "Redirect to #{location}"
@@ -188,73 +214,73 @@ module Rack
188
214
 
189
215
  ## Compiles urlpath mapping. Called from '#initialize()'.
190
216
  def compile_mapping(mapping)
191
- rexp_buf = ['\A']
192
- fixed_urlpaths = {} # ex: {'/api/books'=>BooksApp}
193
- variable_urlpaths = [] # ex: [[%r'\A/api/books/([^./]+)\z', ['id'], BookApp]]
194
- _compile_array(mapping, rexp_buf, '', '',
195
- fixed_urlpaths, variable_urlpaths)
196
- ## ex: %r'\A(?:/api(?:/books(?:/[^./]+(\z)|/[^./]+/edit(\z))))\z'
197
- rexp_buf << '\z'
198
- urlpath_rexp = Regexp.new(rexp_buf.join())
217
+ ## entry points which has no urlpath parameters
218
+ ## ex:
219
+ ## { '/' => HomeApp,
220
+ ## '/api/books' => BooksApp,
221
+ ## '/api/authors => AuthorsApp,
222
+ ## }
223
+ dict = {}
224
+ ## entry points which has one or more urlpath parameters
225
+ ## ex:
226
+ ## [
227
+ ## [%r!\A/api/books/([^./]+)\z!, ["id"], BookApp, (11..-1)],
228
+ ## [%r!\A/api/authors/([^./]+)\z!, ["id"], AuthorApp, (12..-1)],
229
+ ## ]
230
+ list = []
231
+ #
232
+ all = []
233
+ rexp_str = _compile_mapping(mapping, "", "") do |entry_point|
234
+ obj, urlpath_pat, urlpath_rexp, param_names = entry_point
235
+ all << [urlpath_pat, obj]
236
+ if urlpath_rexp
237
+ range = @enable_urlpath_param_range ? range_of_urlpath_param(urlpath_pat) : nil
238
+ list << [urlpath_rexp, param_names, obj, range]
239
+ else
240
+ dict[urlpath_pat] = obj
241
+ end
242
+ end
243
+ ## ex: %r!^A(?:api(?:/books/[^./]+(\z)|/authors/[^./]+(\z)))\z!
244
+ urlpath_rexp = Regexp.new("\\A#{rexp_str}\\z")
199
245
  #; [!xzo7k] returns regexp, hash, and array.
200
- return urlpath_rexp, fixed_urlpaths, variable_urlpaths
246
+ return urlpath_rexp, dict, list, all
201
247
  end
202
248
 
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
207
- rexp_buf << '(?:'
208
- len = rexp_buf.length
209
- mapping.each do |child_urlpath_pat, obj|
210
- rexp_buf << '|' if rexp_buf.length != len
211
- curr_urlpath_pat = "#{base_urlpath_pat}#{urlpath_pat}"
249
+ def _compile_mapping(mapping, base_urlpath, parent_urlpath, &block)
250
+ arr = []
251
+ mapping.each do |urlpath, obj|
252
+ full_urlpath = "#{base_urlpath}#{urlpath}"
212
253
  #; [!ospaf] accepts nested mapping.
213
254
  if obj.is_a?(Array)
214
- _compile_array(obj, rexp_buf, curr_urlpath_pat, child_urlpath_pat,
215
- fixed_dict, variable_list)
255
+ rexp_str = _compile_mapping(obj, full_urlpath, urlpath, &block)
216
256
  #; [!2ktpf] handles end-point.
217
257
  else
218
- _compile_object(obj, rexp_buf, curr_urlpath_pat, child_urlpath_pat,
219
- fixed_dict, variable_list)
258
+ #; [!guhdc] if mapping dict is specified...
259
+ if obj.is_a?(Hash)
260
+ obj = normalize_mapping_keys(obj)
261
+ end
262
+ #; [!vfytw] handles urlpath pattern as variable when urlpath param exists.
263
+ full_urlpath_rexp_str, param_names = compile_urlpath_pattern(full_urlpath, true)
264
+ if param_names # has urlpath params
265
+ full_urlpath_rexp = Regexp.new("\\A#{full_urlpath_rexp_str}\\z")
266
+ rexp_str, _ = compile_urlpath_pattern(urlpath, false)
267
+ rexp_str << '(\z)'
268
+ entry_point = [obj, full_urlpath, full_urlpath_rexp, param_names]
269
+ #; [!l63vu] handles urlpath pattern as fixed when no urlpath params.
270
+ else # has no urlpath params
271
+ entry_point = [obj, full_urlpath, nil, nil]
272
+ end
273
+ yield entry_point
220
274
  end
275
+ arr << rexp_str if rexp_str
221
276
  end
222
- #; [!gfxgr] deletes unnecessary grouping.
223
- if rexp_buf.length == len
224
- x = rexp_buf.pop() # delete '(?:'
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] = ''
233
- else
234
- rexp_buf << ')'
235
- end
236
- end
237
-
238
- def _compile_object(obj, rexp_buf, base_urlpath_pat, urlpath_pat,
239
- fixed_dict, variable_list)
240
- #; [!guhdc] if mapping dict is specified...
241
- if obj.is_a?(Hash)
242
- obj = normalize_mapping_keys(obj)
243
- end
244
- #; [!l63vu] handles urlpath pattern as fixed when no urlpath params.
245
- full_urlpath_pat = "#{base_urlpath_pat}#{urlpath_pat}"
246
- full_urlpath_rexp_str, param_names = compile_urlpath_pattern(full_urlpath_pat, true)
247
- fixed_pattern = param_names.nil?
248
- if fixed_pattern
249
- fixed_dict[full_urlpath_pat] = obj
250
- #; [!vfytw] handles urlpath pattern as variable when urlpath param exists.
251
- else
252
- rexp_str, _ = compile_urlpath_pattern(urlpath_pat, false)
253
- rexp_buf << (rexp_str << '(\z)')
254
- full_urlpath_rexp = Regexp.new("\\A#{full_urlpath_rexp_str}\\z")
255
- range = @enable_urlpath_param_range ? range_of_urlpath_param(full_urlpath_pat) : nil
256
- variable_list << [full_urlpath_rexp, param_names, obj, range]
257
- end
277
+ #; [!pv2au] deletes unnecessary urlpath regexp.
278
+ return nil if arr.empty?
279
+ #; [!bh9lo] deletes unnecessary grouping.
280
+ parent_urlpath_rexp_str, _ = compile_urlpath_pattern(parent_urlpath, false)
281
+ return "#{parent_urlpath_rexp_str}#{arr[0]}" if arr.length == 1
282
+ #; [!iza1g] adds grouping if necessary.
283
+ return "#{parent_urlpath_rexp_str}(?:#{arr.join('|')})"
258
284
  end
259
285
 
260
286
  ## Compiles '/books/:id' into ['/books/([^./]+)', ["id"]].
@@ -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.1.1 $'.split()[1]
5
+ spec.version = '$Release: 1.2.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.1.1 $
4
+ ### $Release: 1.2.0 $
5
5
  ### $Copyright: copyright(c) 2015 kuwata-lab.com all rights reserved $
6
6
  ### $License: MIT License $
7
7
  ###
@@ -154,23 +154,39 @@ describe Rack::JetRouter do
154
154
  end
155
155
  end
156
156
 
157
- it "[!gfxgr] deletes unnecessary grouping." do
157
+ it "[!iza1g] adds grouping if necessary." do
158
158
  mapping = [
159
- ['/' , welcome_app],
160
- ['/api/books' , book_list_api],
159
+ ['/api', [
160
+ ['/books', [
161
+ ['/:id' , book_show_api],
162
+ ]],
163
+ ]],
164
+ ['/admin', [
165
+ ['/books', [
166
+ ['/:id' , admin_book_show_app],
167
+ ]],
168
+ ]],
161
169
  ]
162
170
  expected = '
163
171
  \A
172
+ (?:
173
+ /api
174
+ /books
175
+ /[^./]+(\z)
176
+ |
177
+ /admin
178
+ /books
179
+ /[^./]+(\z)
180
+ )
164
181
  \z
165
182
  '.gsub(/\s+/, '')
166
183
  jet_router.instance_exec(self) do |_|
167
184
  rexp, dict, list = compile_mapping(mapping)
168
185
  _.ok {rexp} == Regexp.new(expected)
169
- _.ok {dict} == {
170
- '/' => welcome_app,
171
- '/api/books' => book_list_api,
172
- }
186
+ _.ok {dict} == {}
173
187
  _.ok {list} == [
188
+ [%r'\A/api/books/([^./]+)\z', ['id'], book_show_api, (11..-1)],
189
+ [%r'\A/admin/books/([^./]+)\z', ['id'], admin_book_show_app, (13..-1)],
174
190
  ]
175
191
  end
176
192
  end
@@ -188,17 +204,7 @@ describe Rack::JetRouter do
188
204
  ]],
189
205
  ]],
190
206
  ]
191
- expected = '
192
- \A
193
- (?:
194
- /api
195
- (?:
196
- /books2
197
- /[^./]+(\z)
198
- )
199
- )
200
- \z
201
- '.gsub(/\s+/, '')
207
+ expected = '\A/api/books2/[^./]+(\z)\z'
202
208
  jet_router.instance_exec(self) do |_|
203
209
  rexp, dict, list = compile_mapping(mapping)
204
210
  _.ok {rexp} == Regexp.new(expected)
@@ -213,7 +219,7 @@ describe Rack::JetRouter do
213
219
  end
214
220
  end
215
221
 
216
- it "[!bh9lo] deletes unnecessary grouping which contains only an element." do
222
+ it "[!bh9lo] deletes unnecessary grouping." do
217
223
  mapping = [
218
224
  ['/api', [
219
225
  ['/books', [
@@ -223,13 +229,9 @@ describe Rack::JetRouter do
223
229
  ]
224
230
  expected = '
225
231
  \A
226
- (?:
227
- /api
228
- (?:
229
- /books
230
- /[^./]+(\z)
231
- )
232
- )
232
+ /api
233
+ /books
234
+ /[^./]+(\z)
233
235
  \z
234
236
  '.gsub(/\s+/, '')
235
237
  jet_router.instance_exec(self) do |_|
@@ -318,16 +320,10 @@ describe Rack::JetRouter do
318
320
  ]
319
321
  expected = '
320
322
  \A
321
- (?:
322
323
  /admin
323
- (?:
324
- /api
325
- (?:
326
- /books
327
- /[^./]+(\z)
328
- )
329
- )
330
- )
324
+ /api
325
+ /books
326
+ /[^./]+(\z)
331
327
  \z
332
328
  '.gsub(/\s+/, '')
333
329
  jet_router.instance_exec(self) do |_|
@@ -379,6 +375,38 @@ describe Rack::JetRouter do
379
375
  end
380
376
 
381
377
 
378
+ describe '#should_redirect?' do
379
+
380
+ it "[!dsu34] returns false when request path is '/'." do
381
+ jet_router.instance_exec(self) do |_|
382
+ _.ok {should_redirect?(_.new_env('GET' , '/'))} == false
383
+ _.ok {should_redirect?(_.new_env('POST' , '/'))} == false
384
+ _.ok {should_redirect?(_.new_env('PUT' , '/'))} == false
385
+ _.ok {should_redirect?(_.new_env('DELETE', '/'))} == false
386
+ _.ok {should_redirect?(_.new_env('HEAD' , '/'))} == false
387
+ _.ok {should_redirect?(_.new_env('PATCH' , '/'))} == false
388
+ end
389
+ end
390
+
391
+ it "[!ycpqj] returns true when request method is GET or HEAD." do
392
+ jet_router.instance_exec(self) do |_|
393
+ _.ok {should_redirect?(_.new_env('GET' , '/index'))} == true
394
+ _.ok {should_redirect?(_.new_env('HEAD' , '/index'))} == true
395
+ end
396
+ end
397
+
398
+ it "[!7q8xu] returns false when request method is POST, PUT or DELETE." do
399
+ jet_router.instance_exec(self) do |_|
400
+ _.ok {should_redirect?(_.new_env('POST' , '/index'))} == false
401
+ _.ok {should_redirect?(_.new_env('PUT' , '/index'))} == false
402
+ _.ok {should_redirect?(_.new_env('DELETE', '/index'))} == false
403
+ _.ok {should_redirect?(_.new_env('PATCH' , '/index'))} == false
404
+ end
405
+ end
406
+
407
+ end
408
+
409
+
382
410
  describe '#error_not_found()' do
383
411
 
384
412
  it "[!mlruv] returns 404 response." do
@@ -422,9 +450,8 @@ describe Rack::JetRouter do
422
450
  )
423
451
  |
424
452
  /admin
425
- (?:/books
453
+ /books
426
454
  /[^./]+(\z)
427
- )
428
455
  )
429
456
  \z
430
457
  '.gsub(/\s+/, '')
@@ -591,6 +618,13 @@ describe Rack::JetRouter do
591
618
  ok {r.call(new_env(:GET, '/'))} == [404, {"Content-Type"=>"text/plain"}, ["404 Not Found"]]
592
619
  end
593
620
 
621
+ it "[!hyk62] adds QUERY_STRING to redirect location." do
622
+ headers = {"Content-Type"=>"text/plain", "Location"=>"/api/books?x=1&y=2"}
623
+ content = "Redirect to /api/books?x=1&y=2"
624
+ env = new_env(:GET, '/api/books/', {"QUERY_STRING"=>"x=1&y=2"})
625
+ ok {jet_router.call(env)} == [301, headers, [content]]
626
+ end
627
+
594
628
  it "[!30x0k] returns 404 when request urlpath not found." do
595
629
  expected = [404, {"Content-Type"=>"text/plain"}, ["404 Not Found"]]
596
630
  ok {jet_router.call(new_env(:GET, '/xxx'))} == expected
@@ -654,6 +688,28 @@ describe Rack::JetRouter do
654
688
  end
655
689
 
656
690
 
691
+ describe '#each()' do
692
+
693
+ it "[!ep0pw] yields pair of urlpath pattern and app." do
694
+ arr = []
695
+ jet_router.each do |upath, app|
696
+ arr << [upath, app]
697
+ end
698
+ ok {arr[0]} == ["/", welcome_app]
699
+ ok {arr[1]} == ["/index.html", welcome_app]
700
+ ok {arr[2]} == ["/api/books", book_list_api]
701
+ ok {arr[3]} == ["/api/books/new", book_new_api]
702
+ ok {arr[4]} == ["/api/books/:id", book_show_api]
703
+ ok {arr[5]} == ["/api/books/:id/edit", book_edit_api]
704
+ ok {arr[6]} == ["/api/books/:book_id/comments", comment_create_api]
705
+ ok {arr[7]} == ["/api/books/:book_id/comments/:comment_id", comment_update_api]
706
+ ok {arr[8]} == ["/admin/books", {"GET"=>admin_book_list_app, "POST"=>admin_book_create_app}]
707
+ ok {arr[9]} == ["/admin/books/:id", {"GET"=>admin_book_show_app, "PUT"=>admin_book_update_app, "DELETE"=>admin_book_delete_app}]
708
+ end
709
+
710
+ end
711
+
712
+
657
713
  describe 'REQUEST_METHODS' do
658
714
 
659
715
  it "[!haggu] contains available request methods." do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-jet_router
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - makoto kuwata
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-29 00:00:00.000000000 Z
11
+ date: 2016-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler