syro 3.0.1 → 3.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.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +84 -6
  3. data/lib/syro.rb +60 -3
  4. data/syro.gemspec +1 -1
  5. data/test/all.rb +46 -0
  6. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 88701e942289c077ba534c8779ee97c650d23973
4
- data.tar.gz: 65731a7bc5389784eeaadfb8fa55d938e30d41ca
3
+ metadata.gz: 585d2fe3559bfaa4cc09f2a6b25efac445ca739b
4
+ data.tar.gz: 99a97c194e80a465474f2439a7f80e938f6e69c8
5
5
  SHA512:
6
- metadata.gz: f83855e744ad2f3efe4508d6b0a9221262664a2e2ec5d62d75735157a5323e387529dd0e60b52605002f2d2690f6af46a9dce875c13730f3ad60373cf2ebedf3
7
- data.tar.gz: 31b066cd541d148bfe8ae8c2919f1ba27428de6cba381ab9b0157b768c688dbe9341b60d9f19569e759f1ab7199210ac6b3e45a52fd7d227af767886832bbaec
6
+ metadata.gz: 23adfa66e18f592c65688248b8f9c4f625c988c8a6392e84ea578cc2263396ef5170b1cde6a27132aa43b3139679f1e523ddfd38d07cf4ddb7fa09fd0f3a7804
7
+ data.tar.gz: e1ae691f6c4d9fee2aaaec0c814519bc0bb4cd6a765dea46b63e978564426bd9763b946e8e203e2ad8b0f64f55a766c2ad4be667a171180d85bacb08db9e6c3d
data/README.md CHANGED
@@ -48,10 +48,10 @@ end
48
48
 
49
49
  The block is evaluated in a sandbox where the following methods are
50
50
  available: `env`, `req`, `res`, `path`, `inbox`, `call`, `run`,
51
- `halt`, `consume`, `capture`, `root?` `match`, `default`, `on`,
52
- `root`,`get`, `put`, `head`, `post`, `patch`, `delete` and `options`.
53
- Three other methods are available for customizations: `default_headers`,
54
- `request_class` and `response_class`.
51
+ `halt`, `handle`, `finish!`, `consume`, `capture`, `root?` `match`,
52
+ `default`, `on`, `root`,`get`, `put`, `head`, `post`, `patch`,
53
+ `delete` and `options`. Three other methods are available for
54
+ customizations: `default_headers`, `request_class` and `response_class`.
55
55
 
56
56
  As a recommendation, user created variables should be instance
57
57
  variables. That way they won't mix with the API methods defined in
@@ -83,6 +83,12 @@ argument.
83
83
  `halt`: Terminates the request. It receives an array with the
84
84
  response as per Rack's specification.
85
85
 
86
+ `handle`: Installs a handler for a given status code. It receives
87
+ a status code and a block that will be executed from `finish!`.
88
+
89
+ `finish!`: Terminates the request by executing any installed handlers
90
+ and then halting with the current value of `res.finish`.
91
+
86
92
  `consume`: Match and consume a path segment.
87
93
 
88
94
  `capture`: Match and capture a path segment. The value is stored in
@@ -254,6 +260,78 @@ post do
254
260
  end
255
261
  ```
256
262
 
263
+ Handlers
264
+ --------
265
+
266
+ Status code handlers can be installed with the `handle` command,
267
+ which receives a status code and a block to be executed just before
268
+ finishing the request.
269
+
270
+ By default, if there are no matches in a Syro application the
271
+ response is a `404` with an empty body. If we decide to handle the
272
+ `404` requests and return a string, we can do as follows:
273
+
274
+ ```ruby
275
+ App = Syro.new do
276
+ handle 404 do
277
+ res.text "Not found!"
278
+ end
279
+
280
+ get do
281
+ res.text "Found!
282
+ end
283
+ end
284
+ ```
285
+
286
+ In this example, a `GET` request to `"/"` will return a status `200`
287
+ with the body `"Found!"`. Any other request will return a `404`
288
+ with the body `"Not found!"`.
289
+
290
+ If a new handler is installed for the same status code, the previous
291
+ handler is overwritten. A handler is valid in the current scope and
292
+ in all its nested branches. Blocks that end before the handler is
293
+ installed are not affected.
294
+
295
+ This is a contrived example that shows some edge cases when using handlers:
296
+
297
+ ```ruby
298
+ App = Syro.new do
299
+ on "foo" do
300
+ # 404, empty body
301
+ end
302
+
303
+ handle 404 do
304
+ res.text "Not found!"
305
+ end
306
+
307
+ on "bar" do
308
+ # 404, body is "Not found!"
309
+ end
310
+
311
+ on "baz" do
312
+ # 404, body is "Couldn't find baz"
313
+
314
+ handle 404 do
315
+ res.text "Couldn't find baz"
316
+ end
317
+ end
318
+ end
319
+ ```
320
+
321
+ A request to `"/foo"` will return a `404`, because the request
322
+ method was not matched. But as the `on "foo"` block ends before the
323
+ handler is installed, the result will be a blank screen. On the
324
+ other hand, a request to `"/bar"` will return a `404` with the plain
325
+ text `"Not found!"`.
326
+
327
+ Finally, a request to `"/baz"` will return a `404` with the plain text
328
+ `"Couldn't find baz"`, because by the time the `on "baz"` block ends
329
+ a new handler is installed, and thus the previous one is overwritten.
330
+
331
+ Any status code can be handled this way, even status `200`. In that
332
+ case the handler will behave as a filter to be run after each
333
+ successful request.
334
+
257
335
  Content type
258
336
  ------------
259
337
 
@@ -317,11 +395,11 @@ just use `Rack::Builder`:
317
395
  App = Rack::Builder.new do
318
396
  use Rack::Session::Cookie, secret: "..."
319
397
 
320
- run Syro.new do
398
+ run Syro.new {
321
399
  get do
322
400
  res.write("Hello, world")
323
401
  end
324
- end
402
+ }
325
403
  end
326
404
  ```
327
405
 
@@ -269,8 +269,7 @@ class Syro
269
269
 
270
270
  catch(:halt) do
271
271
  instance_eval(&@syro_code)
272
-
273
- @syro_res.finish
272
+ finish!
274
273
  end
275
274
  end
276
275
 
@@ -297,6 +296,64 @@ class Syro
297
296
  throw(:halt, response)
298
297
  end
299
298
 
299
+ # Install a handler for a given status code. Once a handler is
300
+ # installed, it will be called by Syro before halting the
301
+ # request.
302
+ #
303
+ # handle 404 do
304
+ # res.text "Not found!"
305
+ # end
306
+ #
307
+ # If a new handler is installed for the same status code, the
308
+ # previous handler is overwritten. A handler is valid in the
309
+ # current scope and in all its nested branches. Blocks that end
310
+ # before the handler is installed are not affected.
311
+ #
312
+ # For example:
313
+ #
314
+ # on "foo" do
315
+ # # Not found
316
+ # end
317
+ #
318
+ # handle 404 do
319
+ # res.text "Not found!"
320
+ # end
321
+ #
322
+ # on "bar" do
323
+ # # Not found
324
+ # end
325
+ #
326
+ # on "baz" do
327
+ # # Not found
328
+ #
329
+ # handle 404 do
330
+ # res.text "Couldn't find baz"
331
+ # end
332
+ # end
333
+ #
334
+ # A request to "/foo" will return a 404, because the request
335
+ # method was not matched. But as the `on "foo"` block ends
336
+ # before the handler is installed, the result will be a blank
337
+ # screen. On the other hand, a request to "/bar" will return a
338
+ # 404 with the plain text "Not found!".
339
+ #
340
+ # Finally, a request to "/baz" will return a 404 with the plain text
341
+ # "Couldn't find baz", because by the time the `on "baz"` block ends
342
+ # a new handler is installed, and thus the previous one is overwritten.
343
+ #
344
+ # Any status code can be handled this way, even status `200`.
345
+ # In that case the handler will behave as a filter to be run
346
+ # after each successful request.
347
+ #
348
+ def handle(status, &block)
349
+ inbox[status] = block
350
+ end
351
+
352
+ def finish!
353
+ inbox[res.status]&.call
354
+ halt(res.finish)
355
+ end
356
+
300
357
  def consume(arg)
301
358
  @syro_path.consume(arg)
302
359
  end
@@ -319,7 +376,7 @@ class Syro
319
376
  end
320
377
 
321
378
  def default
322
- yield; halt(res.finish)
379
+ yield; finish!
323
380
  end
324
381
 
325
382
  def on(arg)
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "syro"
3
- s.version = "3.0.1"
3
+ s.version = "3.1.0"
4
4
  s.summary = "Simple router"
5
5
  s.description = "Simple router for web applications"
6
6
  s.authors = ["Michel Martens"]
@@ -79,6 +79,26 @@ comments = Syro.new do
79
79
  end
80
80
  end
81
81
 
82
+ handlers = Syro.new do
83
+ on "without_handler" do
84
+ # Not found
85
+ end
86
+
87
+ handle(404) do
88
+ res.text "Not found!"
89
+ end
90
+
91
+ on "with_handler" do
92
+ # Not found
93
+ end
94
+
95
+ on "with_local_handler" do
96
+ handle(404) do
97
+ res.text "Also not found!"
98
+ end
99
+ end
100
+ end
101
+
82
102
  app = Syro.new do
83
103
  get do
84
104
  res.write "GET /"
@@ -194,6 +214,10 @@ app = Syro.new do
194
214
  run(json)
195
215
  end
196
216
 
217
+ on "handlers" do
218
+ run(handlers)
219
+ end
220
+
197
221
  on "private" do
198
222
  res.status = 401
199
223
  res.write("Unauthorized")
@@ -369,3 +393,25 @@ test "content type" do |f|
369
393
  f.get("/json")
370
394
  assert_equal "application/json", f.last_response.headers["Content-Type"]
371
395
  end
396
+
397
+ test "status code handling" do |f|
398
+ f.get("/handlers")
399
+ assert_equal 404, f.last_response.status
400
+ assert_equal "text/plain", f.last_response.headers["Content-Type"]
401
+ assert_equal "Not found!", f.last_response.body
402
+
403
+ f.get("/handlers/without_handler")
404
+ assert_equal 404, f.last_response.status
405
+ assert_equal nil, f.last_response.headers["Content-Type"]
406
+ assert_equal "", f.last_response.body
407
+
408
+ f.get("/handlers/with_handler")
409
+ assert_equal 404, f.last_response.status
410
+ assert_equal "text/plain", f.last_response.headers["Content-Type"]
411
+ assert_equal "Not found!", f.last_response.body
412
+
413
+ f.get("/handlers/with_local_handler")
414
+ assert_equal 404, f.last_response.status
415
+ assert_equal "text/plain", f.last_response.headers["Content-Type"]
416
+ assert_equal "Also not found!", f.last_response.body
417
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syro
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michel Martens
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-27 00:00:00.000000000 Z
11
+ date: 2018-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: seg
@@ -103,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
103
  version: '0'
104
104
  requirements: []
105
105
  rubyforge_project:
106
- rubygems_version: 2.4.5.1
106
+ rubygems_version: 2.6.11
107
107
  signing_key:
108
108
  specification_version: 4
109
109
  summary: Simple router