syro 3.0.0 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. checksums.yaml +5 -5
  2. data/.gems +3 -3
  3. data/README.md +85 -7
  4. data/lib/syro.rb +79 -11
  5. data/syro.gemspec +2 -2
  6. data/test/all.rb +89 -0
  7. metadata +7 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 26150785e6dd591e63f5e670b6dcc2a27dd01c08
4
- data.tar.gz: ea5d9a93323838ee03e3ca140946cee52a8cf339
2
+ SHA256:
3
+ metadata.gz: 91ee832b6435348393681438b2e8a167c1d5c56a3ebaaf494b4f3840b716adde
4
+ data.tar.gz: 3ba242a11056f3fd32da0b2198b9c10fa35e5c4f4db7217ec044fadf047f01b3
5
5
  SHA512:
6
- metadata.gz: e07343b0890bf4820471ebe54a7947713956e506465b0b5eff37c42fbeb50b0bf0957f57693d34118b0dcbe40ccd669689d11d4f3263330f712bdb2a0eaf2739
7
- data.tar.gz: 50a533a7933292c1b29fbcfd0bd8f8347128e7b6110ba97ce063939086cd86ffd52b2e4023e8800efee0aed43fa8f24a1a713a99317064140abbbdfc231acce6
6
+ metadata.gz: a4b4c73c4b4d731914a119a81b5bfc1154872f47343744532a0334525e0a2f978878015d1df11324376551c6617818193c0b6d462b9fbd54c3cd535b0a5ea78e
7
+ data.tar.gz: '0825e4cd946e7fba9d84907cce3442b53c158baaf2c0678864df1f0648b3834feb1976c1b62facdeb82e6fb172863958b432ba5ab7d067e26ce52bb3133ffea9'
data/.gems CHANGED
@@ -1,4 +1,4 @@
1
1
  cutest -v 1.2.3
2
- rack -v 2.0.1
3
- seg -v 1.1.0
4
- rack-test -v 0.6.3
2
+ seg -v 1.2.0
3
+ rack -v 2.2.3
4
+ rack-test -v 1.1.0
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
@@ -93,7 +99,7 @@ the inbox.
93
99
  `match`: Receives a String, a Symbol or a boolean, and returns true
94
100
  if it matches the request.
95
101
 
96
- `default`: Receives a block that will be executed inconditionally.
102
+ `default`: Receives a block that will be executed unconditionally.
97
103
 
98
104
  `on`: Receives a value to be matched, and a block that will be
99
105
  executed only if the request is matched.
@@ -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
 
data/lib/syro.rb CHANGED
@@ -209,11 +209,23 @@ class Syro
209
209
  end
210
210
 
211
211
  class Deck
212
- module API
213
- def initialize(code)
214
- @syro_code = code
212
+
213
+ # Attaches the supplied block to a subclass of Deck as #dispatch!
214
+ # Returns the subclassed Deck.
215
+ def self.implement(&code)
216
+ Class.new(self) do
217
+ define_method(:dispatch!, code)
218
+ private :dispatch!
219
+
220
+ # Instead of calling inspect on this anonymous class,
221
+ # defer to the superclass which is likely Syro::Deck.
222
+ define_method(:inspect) do
223
+ self.class.superclass.inspect
224
+ end
215
225
  end
226
+ end
216
227
 
228
+ module API
217
229
  def env
218
230
  @syro_env
219
231
  end
@@ -268,9 +280,8 @@ class Syro
268
280
  @syro_inbox = inbox
269
281
 
270
282
  catch(:halt) do
271
- instance_eval(&@syro_code)
272
-
273
- @syro_res.finish
283
+ dispatch!
284
+ finish!
274
285
  end
275
286
  end
276
287
 
@@ -278,7 +289,7 @@ class Syro
278
289
  path, script = env[Rack::PATH_INFO], env[Rack::SCRIPT_NAME]
279
290
 
280
291
  env[Rack::PATH_INFO] = @syro_path.curr
281
- env[Rack::SCRIPT_NAME] = @syro_path.prev
292
+ env[Rack::SCRIPT_NAME] = script.to_s + @syro_path.prev
282
293
  env[Syro::INBOX] = inbox
283
294
 
284
295
  halt(app.call(env))
@@ -297,6 +308,64 @@ class Syro
297
308
  throw(:halt, response)
298
309
  end
299
310
 
311
+ # Install a handler for a given status code. Once a handler is
312
+ # installed, it will be called by Syro before halting the
313
+ # request.
314
+ #
315
+ # handle 404 do
316
+ # res.text "Not found!"
317
+ # end
318
+ #
319
+ # If a new handler is installed for the same status code, the
320
+ # previous handler is overwritten. A handler is valid in the
321
+ # current scope and in all its nested branches. Blocks that end
322
+ # before the handler is installed are not affected.
323
+ #
324
+ # For example:
325
+ #
326
+ # on "foo" do
327
+ # # Not found
328
+ # end
329
+ #
330
+ # handle 404 do
331
+ # res.text "Not found!"
332
+ # end
333
+ #
334
+ # on "bar" do
335
+ # # Not found
336
+ # end
337
+ #
338
+ # on "baz" do
339
+ # # Not found
340
+ #
341
+ # handle 404 do
342
+ # res.text "Couldn't find baz"
343
+ # end
344
+ # end
345
+ #
346
+ # A request to "/foo" will return a 404, because the request
347
+ # method was not matched. But as the `on "foo"` block ends
348
+ # before the handler is installed, the result will be a blank
349
+ # screen. On the other hand, a request to "/bar" will return a
350
+ # 404 with the plain text "Not found!".
351
+ #
352
+ # Finally, a request to "/baz" will return a 404 with the plain text
353
+ # "Couldn't find baz", because by the time the `on "baz"` block ends
354
+ # a new handler is installed, and thus the previous one is overwritten.
355
+ #
356
+ # Any status code can be handled this way, even status `200`.
357
+ # In that case the handler will behave as a filter to be run
358
+ # after each successful request.
359
+ #
360
+ def handle(status, &block)
361
+ inbox[status] = block
362
+ end
363
+
364
+ def finish!
365
+ inbox[res.status]&.call
366
+ halt(res.finish)
367
+ end
368
+
300
369
  def consume(arg)
301
370
  @syro_path.consume(arg)
302
371
  end
@@ -319,7 +388,7 @@ class Syro
319
388
  end
320
389
 
321
390
  def default
322
- yield; halt(res.finish)
391
+ yield; finish!
323
392
  end
324
393
 
325
394
  def on(arg)
@@ -363,11 +432,10 @@ class Syro
363
432
  end
364
433
 
365
434
  def initialize(deck = Deck, &code)
366
- @deck = deck
367
- @code = code
435
+ @deck = deck.implement(&code)
368
436
  end
369
437
 
370
438
  def call(env, inbox = env.fetch(Syro::INBOX, {}))
371
- @deck.new(@code).call(env, inbox)
439
+ @deck.new.call(env, inbox)
372
440
  end
373
441
  end
data/syro.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "syro"
3
- s.version = "3.0.0"
3
+ s.version = "3.2.1"
4
4
  s.summary = "Simple router"
5
5
  s.description = "Simple router for web applications"
6
6
  s.authors = ["Michel Martens"]
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.files = `git ls-files`.split("\n")
12
12
 
13
13
  s.add_dependency "seg"
14
- s.add_dependency "rack", "~> 2"
14
+ s.add_dependency "rack", ">= 1.6.0"
15
15
  s.add_development_dependency "cutest"
16
16
  s.add_development_dependency "rack-test"
17
17
  end
data/test/all.rb CHANGED
@@ -79,6 +79,48 @@ 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
+
102
+ path_info = Syro.new do
103
+ on "foo" do
104
+ get do
105
+ res.text req.path
106
+ end
107
+ end
108
+
109
+ get do
110
+ res.text req.path
111
+ end
112
+ end
113
+
114
+ script_name = Syro.new do
115
+ on "path" do
116
+ run(path_info)
117
+ end
118
+ end
119
+
120
+ exception = Syro.new do
121
+ get { res.text(this_method_does_not_exist) }
122
+ end
123
+
82
124
  app = Syro.new do
83
125
  get do
84
126
  res.write "GET /"
@@ -194,6 +236,10 @@ app = Syro.new do
194
236
  run(json)
195
237
  end
196
238
 
239
+ on "handlers" do
240
+ run(handlers)
241
+ end
242
+
197
243
  on "private" do
198
244
  res.status = 401
199
245
  res.write("Unauthorized")
@@ -214,6 +260,14 @@ app = Syro.new do
214
260
  on "json" do
215
261
  res.json "json!"
216
262
  end
263
+
264
+ on "script" do
265
+ run(script_name)
266
+ end
267
+
268
+ on "exception" do
269
+ run(exception)
270
+ end
217
271
  end
218
272
 
219
273
  setup do
@@ -369,3 +423,38 @@ test "content type" do |f|
369
423
  f.get("/json")
370
424
  assert_equal "application/json", f.last_response.headers["Content-Type"]
371
425
  end
426
+
427
+ test "status code handling" do |f|
428
+ f.get("/handlers")
429
+ assert_equal 404, f.last_response.status
430
+ assert_equal "text/plain", f.last_response.headers["Content-Type"]
431
+ assert_equal "Not found!", f.last_response.body
432
+
433
+ f.get("/handlers/without_handler")
434
+ assert_equal 404, f.last_response.status
435
+ assert_equal nil, f.last_response.headers["Content-Type"]
436
+ assert_equal "", f.last_response.body
437
+
438
+ f.get("/handlers/with_handler")
439
+ assert_equal 404, f.last_response.status
440
+ assert_equal "text/plain", f.last_response.headers["Content-Type"]
441
+ assert_equal "Not found!", f.last_response.body
442
+
443
+ f.get("/handlers/with_local_handler")
444
+ assert_equal 404, f.last_response.status
445
+ assert_equal "text/plain", f.last_response.headers["Content-Type"]
446
+ assert_equal "Also not found!", f.last_response.body
447
+ end
448
+
449
+ test "script name and path info" do |f|
450
+ f.get("/script/path")
451
+ assert_equal 200, f.last_response.status
452
+ assert_equal "/script/path", f.last_response.body
453
+ end
454
+
455
+ test "deck exceptions reference a named class" do |f|
456
+ f.get("/exception")
457
+ rescue NameError => exception
458
+ ensure
459
+ assert exception.to_s.include?("Syro::Deck")
460
+ 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.0
4
+ version: 3.2.1
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-24 00:00:00.000000000 Z
11
+ date: 2021-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: seg
@@ -28,16 +28,16 @@ dependencies:
28
28
  name: rack
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '2'
33
+ version: 1.6.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '2'
40
+ version: 1.6.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: cutest
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -102,8 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  requirements: []
105
- rubyforge_project:
106
- rubygems_version: 2.4.5.1
105
+ rubygems_version: 3.0.3
107
106
  signing_key:
108
107
  specification_version: 4
109
108
  summary: Simple router