jellyfish 0.6.0 → 0.8.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 +7 -0
- data/.gitignore +0 -1
- data/.travis.yml +1 -0
- data/CHANGES.md +71 -0
- data/README.md +488 -20
- data/jellyfish.gemspec +25 -10
- data/lib/jellyfish.rb +84 -40
- data/lib/jellyfish/chunked_body.rb +20 -0
- data/lib/jellyfish/multi_actions.rb +35 -0
- data/lib/jellyfish/newrelic.rb +0 -1
- data/lib/jellyfish/normalized_params.rb +55 -0
- data/lib/jellyfish/normalized_path.rb +13 -0
- data/lib/jellyfish/sinatra.rb +6 -47
- data/lib/jellyfish/test.rb +7 -2
- data/lib/jellyfish/version.rb +1 -1
- data/task/gemgem.rb +7 -6
- data/test/sinatra/test_base.rb +110 -0
- data/test/sinatra/test_chunked_body.rb +43 -0
- data/test/sinatra/test_error.rb +145 -0
- data/test/sinatra/test_multi_actions.rb +217 -0
- data/test/sinatra/test_routing.rb +425 -0
- data/test/test_from_readme.rb +39 -0
- data/test/test_inheritance.rb +88 -0
- metadata +32 -25
- data/example/config.ru +0 -118
- data/example/rainbows.rb +0 -4
- data/example/server.sh +0 -3
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 30753a754648b5b6d9a72d9165c763b3e4db9383
|
4
|
+
data.tar.gz: dc78ee33ce6341c19ff571bb78ab2485ef5f2fbd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: aa1d7ff64073a23ffb0b8d4d218e5e2f3ec898c102ad2ee7786c2af062792be705b2941ca98f7fc3e59b095ec88945b3788b065eb22b48e00eae041be930843e
|
7
|
+
data.tar.gz: 1f33f9aa6f449b034e81c1fb33e30e91c91a580e1310230e487649606add2afeaddf13fa10ea9a9daac1dc878797a390d6211e7486315ff9ee19b34012acb225
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,5 +1,76 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## Jellyfish 0.8.0
|
4
|
+
|
5
|
+
### Incompatible changes
|
6
|
+
|
7
|
+
* Now there's no longer Jellyfish#controller but Jellyfish.controller,
|
8
|
+
as there's no much point for making the controller per-instance.
|
9
|
+
You do this to override the controller method instead:
|
10
|
+
|
11
|
+
``` ruby
|
12
|
+
class MyApp
|
13
|
+
include Jellyfish
|
14
|
+
def self.controller
|
15
|
+
MyController
|
16
|
+
end
|
17
|
+
class MyController < Jellyfish::Controller
|
18
|
+
def hi
|
19
|
+
'hi'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
get{ hi }
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
* You can also change the controller by assigning it. The same as above:
|
27
|
+
|
28
|
+
``` ruby
|
29
|
+
class MyApp
|
30
|
+
include Jellyfish
|
31
|
+
class MyController < Jellyfish::Controller
|
32
|
+
def hi
|
33
|
+
'hi'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
controller MyController
|
37
|
+
get{ hi }
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
### Enhancements for Jellyfish core
|
42
|
+
|
43
|
+
* Introduced Jellyfish.controller_include which makes it easy to pick
|
44
|
+
modules to be included in built-in controller.
|
45
|
+
* Introduced Controller#halt as a short hand for `throw :halt`
|
46
|
+
* Now default route is `//`. Using `get{ 'Hello, World!' }` is effectively
|
47
|
+
the same as `get(//){ 'Hello, World!' }`
|
48
|
+
* Now inheritance works.
|
49
|
+
* Now it raises TypeError if passing a route doesn't respond to :match.
|
50
|
+
* Now Jellyfish would find the most suitable error handler to handle
|
51
|
+
errors, i.e. It would find the error handler which would handle the
|
52
|
+
nearest exception class in the ancestors chain. Previously it would
|
53
|
+
only find the first one which matches, ignoring the rest. It would
|
54
|
+
also cache the result upon a lookup.
|
55
|
+
|
56
|
+
### Enhancements for Jellyfish extension
|
57
|
+
|
58
|
+
* Added `Jellyfish::ChunkedBody` which is similar to `Sinatra::Stream`.
|
59
|
+
|
60
|
+
* Added `Jellyfish::MultiAction` which gives you some kind of ability to do
|
61
|
+
before or after filters. See README.md for usage.
|
62
|
+
|
63
|
+
* Added `Jellyfish::NormalizedParams` which gives you some kind of Sinatra
|
64
|
+
flavoured params.
|
65
|
+
|
66
|
+
* Added `Jellyfish::NormalizedPath` which would unescape incoming PATH_INFO
|
67
|
+
so you could match '/f%C3%B6%C3%B6' with '/föö'.
|
68
|
+
|
69
|
+
### Enhancements for Jellyfish::Sinatra
|
70
|
+
|
71
|
+
* Now `Jellyfish::Sinatra` includes `Jellyfish::MultiAction`,
|
72
|
+
`Jellyfish::NormalizedParams`, and `Jellyfish::NormalizedPath`.
|
73
|
+
|
3
74
|
## Jellyfish 0.6.0 -- 2012-11-02
|
4
75
|
|
5
76
|
### Enhancements for Jellyfish core
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@ by Lin Jen-Shin ([godfat](http://godfat.org))
|
|
13
13
|
## DESCRIPTION:
|
14
14
|
|
15
15
|
Pico web framework for building API-centric web applications.
|
16
|
-
For Rack applications or Rack middlewares.
|
16
|
+
For Rack applications or Rack middlewares. Around 200 lines of code.
|
17
17
|
|
18
18
|
## DESIGN:
|
19
19
|
|
@@ -23,18 +23,21 @@ For Rack applications or Rack middlewares. Under 200 lines of code.
|
|
23
23
|
* Embrace simplicity over convenience
|
24
24
|
* Don't make things complicated only for _some_ convenience, but
|
25
25
|
_great_ convenience, or simply stay simple for simplicity.
|
26
|
+
* More features are added as extensions
|
26
27
|
|
27
28
|
## FEATURES:
|
28
29
|
|
29
30
|
* Minimal
|
30
31
|
* Simple
|
31
|
-
*
|
32
|
+
* Modular
|
33
|
+
* No templates (You could use [tilt](https://github.com/rtomayko/tilt))
|
32
34
|
* No ORM
|
33
35
|
* No `dup` in `call`
|
34
36
|
* Regular expression routes, e.g. `get %r{^/(?<id>\d+)$}`
|
35
37
|
* String routes, e.g. `get '/'`
|
36
38
|
* Custom routes, e.g. `get Matcher.new`
|
37
|
-
* Build for either Rack applications or Rack
|
39
|
+
* Build for either Rack applications or Rack middleware
|
40
|
+
* Include extensions for more features (There's a Sinatra extension)
|
38
41
|
|
39
42
|
## WHY?
|
40
43
|
|
@@ -65,6 +68,13 @@ use Rack::ContentType, 'text/plain'
|
|
65
68
|
run Tank.new
|
66
69
|
```
|
67
70
|
|
71
|
+
<!---
|
72
|
+
GET /
|
73
|
+
[200,
|
74
|
+
{'Content-Length' => '12', 'Content-Type' => 'text/plain'},
|
75
|
+
["Jelly Kelly\n"]]
|
76
|
+
-->
|
77
|
+
|
68
78
|
### Regular expression routes
|
69
79
|
|
70
80
|
``` ruby
|
@@ -80,6 +90,13 @@ use Rack::ContentType, 'text/plain'
|
|
80
90
|
run Tank.new
|
81
91
|
```
|
82
92
|
|
93
|
+
<!---
|
94
|
+
GET /123
|
95
|
+
[200,
|
96
|
+
{'Content-Length' => '11', 'Content-Type' => 'text/plain'},
|
97
|
+
["Jelly #123\n"]]
|
98
|
+
-->
|
99
|
+
|
83
100
|
### Custom matcher routes
|
84
101
|
|
85
102
|
``` ruby
|
@@ -100,6 +117,13 @@ use Rack::ContentType, 'text/plain'
|
|
100
117
|
run Tank.new
|
101
118
|
```
|
102
119
|
|
120
|
+
<!---
|
121
|
+
GET /hctam
|
122
|
+
[200,
|
123
|
+
{'Content-Length' => '5', 'Content-Type' => 'text/plain'},
|
124
|
+
["true\n"]]
|
125
|
+
-->
|
126
|
+
|
103
127
|
### Different HTTP status and custom headers
|
104
128
|
|
105
129
|
``` ruby
|
@@ -119,6 +143,14 @@ use Rack::ContentType, 'text/plain'
|
|
119
143
|
run Tank.new
|
120
144
|
```
|
121
145
|
|
146
|
+
<!---
|
147
|
+
POST /
|
148
|
+
[201,
|
149
|
+
{'Content-Length' => '18', 'Content-Type' => 'text/plain',
|
150
|
+
'X-Jellyfish-Life' => '100', 'X-Jellyfish-Mana' => '200'},
|
151
|
+
["Jellyfish 100/200\n"]]
|
152
|
+
-->
|
153
|
+
|
122
154
|
### Redirect helper
|
123
155
|
|
124
156
|
``` ruby
|
@@ -134,6 +166,17 @@ use Rack::ContentType, 'text/plain'
|
|
134
166
|
run Tank.new
|
135
167
|
```
|
136
168
|
|
169
|
+
<!---
|
170
|
+
GET /lookup
|
171
|
+
body = File.read("#{File.dirname(
|
172
|
+
File.expand_path(__FILE__))}/../lib/jellyfish/public/302.html").
|
173
|
+
gsub('VAR_URL', ':///')
|
174
|
+
[302,
|
175
|
+
{'Content-Length' => body.bytesize.to_s, 'Content-Type' => 'text/html',
|
176
|
+
'Location' => ':///'},
|
177
|
+
[body]]
|
178
|
+
-->
|
179
|
+
|
137
180
|
### Crash-proof
|
138
181
|
|
139
182
|
``` ruby
|
@@ -149,6 +192,15 @@ use Rack::ContentType, 'text/plain'
|
|
149
192
|
run Tank.new
|
150
193
|
```
|
151
194
|
|
195
|
+
<!---
|
196
|
+
GET /crash
|
197
|
+
body = File.read("#{File.dirname(
|
198
|
+
File.expand_path(__FILE__))}/../lib/jellyfish/public/500.html")
|
199
|
+
[500,
|
200
|
+
{'Content-Length' => body.bytesize.to_s, 'Content-Type' => 'text/html'},
|
201
|
+
[body]]
|
202
|
+
-->
|
203
|
+
|
152
204
|
### Custom error handler
|
153
205
|
|
154
206
|
``` ruby
|
@@ -168,7 +220,74 @@ use Rack::ContentType, 'text/plain'
|
|
168
220
|
run Tank.new
|
169
221
|
```
|
170
222
|
|
171
|
-
|
223
|
+
<!---
|
224
|
+
GET /yell
|
225
|
+
body = case RUBY_ENGINE
|
226
|
+
when 'jruby'
|
227
|
+
"No one hears you: (eval):9:in `Tank'\n"
|
228
|
+
when 'rbx'
|
229
|
+
"No one hears you: kernel/delta/kernel.rb:81:in `yell (method_missing)'\n"
|
230
|
+
else
|
231
|
+
"No one hears you: (eval):9:in `block in <class:Tank>'\n"
|
232
|
+
end
|
233
|
+
[403,
|
234
|
+
{'Content-Length' => body.bytesize.to_s, 'Content-Type' => 'text/plain'},
|
235
|
+
[body]]
|
236
|
+
-->
|
237
|
+
|
238
|
+
### Access Rack::Request and params
|
239
|
+
|
240
|
+
``` ruby
|
241
|
+
require 'jellyfish'
|
242
|
+
class Tank
|
243
|
+
include Jellyfish
|
244
|
+
get '/report' do
|
245
|
+
"Your name is #{request.params['name']}\n"
|
246
|
+
end
|
247
|
+
end
|
248
|
+
use Rack::ContentLength
|
249
|
+
use Rack::ContentType, 'text/plain'
|
250
|
+
run Tank.new
|
251
|
+
```
|
252
|
+
|
253
|
+
<!---
|
254
|
+
GET /report?name=godfat
|
255
|
+
[200,
|
256
|
+
{'Content-Length' => '20', 'Content-Type' => 'text/plain'},
|
257
|
+
["Your name is godfat\n"]]
|
258
|
+
-->
|
259
|
+
|
260
|
+
### Re-dispatch the request with modified env
|
261
|
+
|
262
|
+
``` ruby
|
263
|
+
require 'jellyfish'
|
264
|
+
class Tank
|
265
|
+
include Jellyfish
|
266
|
+
get '/report' do
|
267
|
+
status, headers, body = jellyfish.call(env.merge('PATH_INFO' => '/info'))
|
268
|
+
self.status status
|
269
|
+
self.headers headers
|
270
|
+
self.body body
|
271
|
+
end
|
272
|
+
get('/info'){ "OK\n" }
|
273
|
+
end
|
274
|
+
use Rack::ContentLength
|
275
|
+
use Rack::ContentType, 'text/plain'
|
276
|
+
run Tank.new
|
277
|
+
```
|
278
|
+
|
279
|
+
<!---
|
280
|
+
GET /report
|
281
|
+
[200,
|
282
|
+
{'Content-Length' => '3', 'Content-Type' => 'text/plain'},
|
283
|
+
["OK\n"]]
|
284
|
+
-->
|
285
|
+
|
286
|
+
### Include custom helper in built-in controller
|
287
|
+
|
288
|
+
Basically it's the same as defining a custom controller and then
|
289
|
+
include the helper. This is merely a short hand. See next section
|
290
|
+
for defining a custom controller.
|
172
291
|
|
173
292
|
``` ruby
|
174
293
|
require 'jellyfish'
|
@@ -178,33 +297,92 @@ class Heater
|
|
178
297
|
temperature
|
179
298
|
end
|
180
299
|
|
181
|
-
|
182
|
-
class Controller < Jellyfish::Controller
|
300
|
+
module Helper
|
183
301
|
def temperature
|
184
302
|
"30\u{2103}\n"
|
185
303
|
end
|
186
304
|
end
|
305
|
+
controller_include Helper
|
187
306
|
end
|
188
307
|
use Rack::ContentLength
|
189
308
|
use Rack::ContentType, 'text/plain'
|
190
309
|
run Heater.new
|
191
310
|
```
|
192
311
|
|
193
|
-
|
312
|
+
<!---
|
313
|
+
GET /status
|
314
|
+
[200,
|
315
|
+
{'Content-Length' => '6', 'Content-Type' => 'text/plain'},
|
316
|
+
["30\u{2103}\n"]]
|
317
|
+
-->
|
318
|
+
|
319
|
+
### Define custom controller manually
|
320
|
+
|
321
|
+
This is effectively the same as defining a helper module as above and
|
322
|
+
include it, but more flexible and extensible.
|
323
|
+
|
324
|
+
``` ruby
|
325
|
+
require 'jellyfish'
|
326
|
+
class Heater
|
327
|
+
include Jellyfish
|
328
|
+
get '/status' do
|
329
|
+
temperature
|
330
|
+
end
|
331
|
+
|
332
|
+
class Controller < Jellyfish::Controller
|
333
|
+
def temperature
|
334
|
+
"30\u{2103}\n"
|
335
|
+
end
|
336
|
+
end
|
337
|
+
controller Controller
|
338
|
+
end
|
339
|
+
use Rack::ContentLength
|
340
|
+
use Rack::ContentType, 'text/plain'
|
341
|
+
run Heater.new
|
342
|
+
```
|
194
343
|
|
195
|
-
|
344
|
+
<!---
|
345
|
+
GET /status
|
346
|
+
[200,
|
347
|
+
{'Content-Length' => '6', 'Content-Type' => 'text/plain'},
|
348
|
+
["30\u{2103}\n"]]
|
349
|
+
-->
|
196
350
|
|
197
|
-
|
198
|
-
* Force params encoding to Encoding.default_external
|
351
|
+
### Extension: MultiActions (Filters)
|
199
352
|
|
200
353
|
``` ruby
|
201
354
|
require 'jellyfish'
|
202
355
|
class Tank
|
203
356
|
include Jellyfish
|
204
|
-
|
205
|
-
|
357
|
+
controller_include Jellyfish::MultiActions
|
358
|
+
|
359
|
+
get do # wildcard before filter
|
360
|
+
@state = 'jumps'
|
206
361
|
end
|
207
|
-
|
362
|
+
get do
|
363
|
+
"Jelly #{@state}.\n"
|
364
|
+
end
|
365
|
+
end
|
366
|
+
use Rack::ContentLength
|
367
|
+
use Rack::ContentType, 'text/plain'
|
368
|
+
run Tank.new
|
369
|
+
```
|
370
|
+
|
371
|
+
<!---
|
372
|
+
GET /123
|
373
|
+
[200,
|
374
|
+
{'Content-Length' => '13', 'Content-Type' => 'text/plain'},
|
375
|
+
["Jelly jumps.\n"]]
|
376
|
+
-->
|
377
|
+
|
378
|
+
### Extension: NormalizedParams (with force_encoding)
|
379
|
+
|
380
|
+
``` ruby
|
381
|
+
require 'jellyfish'
|
382
|
+
class Tank
|
383
|
+
include Jellyfish
|
384
|
+
controller_include Jellyfish::NormalizedParams
|
385
|
+
|
208
386
|
get %r{^/(?<id>\d+)$} do
|
209
387
|
"Jelly ##{params[:id]}\n"
|
210
388
|
end
|
@@ -214,16 +392,78 @@ use Rack::ContentType, 'text/plain'
|
|
214
392
|
run Tank.new
|
215
393
|
```
|
216
394
|
|
217
|
-
|
395
|
+
<!---
|
396
|
+
GET /123
|
397
|
+
[200,
|
398
|
+
{'Content-Length' => '11', 'Content-Type' => 'text/plain'},
|
399
|
+
["Jelly #123\n"]]
|
400
|
+
-->
|
401
|
+
|
402
|
+
### Extension: NormalizedPath (with unescaping)
|
218
403
|
|
219
404
|
``` ruby
|
220
405
|
require 'jellyfish'
|
221
406
|
class Tank
|
222
407
|
include Jellyfish
|
223
|
-
|
224
|
-
|
408
|
+
controller_include Jellyfish::NormalizedPath
|
409
|
+
|
410
|
+
get "/\u{56e7}" do
|
411
|
+
"#{env['PATH_INFO']}=#{path_info}\n"
|
412
|
+
end
|
413
|
+
end
|
414
|
+
use Rack::ContentLength
|
415
|
+
use Rack::ContentType, 'text/plain'
|
416
|
+
run Tank.new
|
417
|
+
```
|
418
|
+
|
419
|
+
<!---
|
420
|
+
GET /%E5%9B%A7
|
421
|
+
[200,
|
422
|
+
{'Content-Length' => '16', 'Content-Type' => 'text/plain'},
|
423
|
+
["/%E5%9B%A7=/\u{56e7}\n"]]
|
424
|
+
-->
|
425
|
+
|
426
|
+
### Extension: Sinatra flavoured controller
|
427
|
+
|
428
|
+
It's an extension collection contains:
|
429
|
+
|
430
|
+
* MultiActions
|
431
|
+
* NormalizedParams
|
432
|
+
* NormalizedPath
|
433
|
+
|
434
|
+
``` ruby
|
435
|
+
require 'jellyfish'
|
436
|
+
class Tank
|
437
|
+
include Jellyfish
|
438
|
+
controller_include Jellyfish::Sinatra
|
439
|
+
|
440
|
+
get do # wildcard before filter
|
441
|
+
@state = 'jumps'
|
442
|
+
end
|
443
|
+
get %r{^/(?<id>\d+)$} do
|
444
|
+
"Jelly ##{params[:id]} #{@state}.\n"
|
225
445
|
end
|
226
|
-
|
446
|
+
end
|
447
|
+
use Rack::ContentLength
|
448
|
+
use Rack::ContentType, 'text/plain'
|
449
|
+
run Tank.new
|
450
|
+
```
|
451
|
+
|
452
|
+
<!---
|
453
|
+
GET /123
|
454
|
+
[200,
|
455
|
+
{'Content-Length' => '18', 'Content-Type' => 'text/plain'},
|
456
|
+
["Jelly #123 jumps.\n"]]
|
457
|
+
-->
|
458
|
+
|
459
|
+
### Extension: NewRelic
|
460
|
+
|
461
|
+
``` ruby
|
462
|
+
require 'jellyfish'
|
463
|
+
class Tank
|
464
|
+
include Jellyfish
|
465
|
+
controller_include Jellyfish::NewRelic
|
466
|
+
|
227
467
|
get '/' do
|
228
468
|
"OK\n"
|
229
469
|
end
|
@@ -237,6 +477,48 @@ run Tank.new
|
|
237
477
|
NewRelic::Agent.manual_start(:developer_mode => true)
|
238
478
|
```
|
239
479
|
|
480
|
+
<!---
|
481
|
+
GET /
|
482
|
+
[200,
|
483
|
+
{'Content-Length' => '3', 'Content-Type' => 'text/plain'},
|
484
|
+
["OK\n"]]
|
485
|
+
-->
|
486
|
+
|
487
|
+
### Extension: Using multiple extensions with custom controller
|
488
|
+
|
489
|
+
This is effectively the same as using Jellyfish::Sinatra extension.
|
490
|
+
Note that the controller should be assigned lastly in order to include
|
491
|
+
modules remembered in controller_include.
|
492
|
+
|
493
|
+
``` ruby
|
494
|
+
require 'jellyfish'
|
495
|
+
class Tank
|
496
|
+
include Jellyfish
|
497
|
+
class MyController < Jellyfish::Controller
|
498
|
+
include Jellyfish::MultiActions
|
499
|
+
end
|
500
|
+
controller_include NormalizedParams, NormalizedPath
|
501
|
+
controller MyController
|
502
|
+
|
503
|
+
get do # wildcard before filter
|
504
|
+
@state = 'jumps'
|
505
|
+
end
|
506
|
+
get %r{^/(?<id>\d+)$} do
|
507
|
+
"Jelly ##{params[:id]} #{@state}.\n"
|
508
|
+
end
|
509
|
+
end
|
510
|
+
use Rack::ContentLength
|
511
|
+
use Rack::ContentType, 'text/plain'
|
512
|
+
run Tank.new
|
513
|
+
```
|
514
|
+
|
515
|
+
<!---
|
516
|
+
GET /123
|
517
|
+
[200,
|
518
|
+
{'Content-Length' => '18', 'Content-Type' => 'text/plain'},
|
519
|
+
["Jelly #123 jumps.\n"]]
|
520
|
+
-->
|
521
|
+
|
240
522
|
### Jellyfish as a middleware
|
241
523
|
|
242
524
|
``` ruby
|
@@ -261,6 +543,146 @@ use Heater
|
|
261
543
|
run Tank.new
|
262
544
|
```
|
263
545
|
|
546
|
+
<!---
|
547
|
+
GET /
|
548
|
+
[200,
|
549
|
+
{'Content-Length' => '12', 'Content-Type' => 'text/plain'},
|
550
|
+
["Jelly Kelly\n"]]
|
551
|
+
-->
|
552
|
+
|
553
|
+
### Modify response as a middleware
|
554
|
+
|
555
|
+
``` ruby
|
556
|
+
require 'jellyfish'
|
557
|
+
class Heater
|
558
|
+
include Jellyfish
|
559
|
+
get '/status' do
|
560
|
+
status, headers, body = jellyfish.app.call(env)
|
561
|
+
self.status status
|
562
|
+
self.headers headers
|
563
|
+
self.body body
|
564
|
+
headers_merge('X-Temperature' => "30\u{2103}")
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
class Tank
|
569
|
+
include Jellyfish
|
570
|
+
get '/status' do
|
571
|
+
"See header X-Temperature\n"
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
use Rack::ContentLength
|
576
|
+
use Rack::ContentType, 'text/plain'
|
577
|
+
use Heater
|
578
|
+
run Tank.new
|
579
|
+
```
|
580
|
+
|
581
|
+
<!---
|
582
|
+
GET /status
|
583
|
+
[200,
|
584
|
+
{'Content-Length' => '25', 'Content-Type' => 'text/plain',
|
585
|
+
'X-Temperature' => "30\u{2103}"},
|
586
|
+
["See header X-Temperature\n"]]
|
587
|
+
-->
|
588
|
+
|
589
|
+
### Default headers as a middleware
|
590
|
+
|
591
|
+
``` ruby
|
592
|
+
require 'jellyfish'
|
593
|
+
class Heater
|
594
|
+
include Jellyfish
|
595
|
+
get '/status' do
|
596
|
+
status, headers, body = jellyfish.app.call(env)
|
597
|
+
self.status status
|
598
|
+
self.headers({'X-Temperature' => "30\u{2103}"}.merge(headers))
|
599
|
+
self.body body
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
class Tank
|
604
|
+
include Jellyfish
|
605
|
+
get '/status' do
|
606
|
+
headers_merge('X-Temperature' => "35\u{2103}")
|
607
|
+
"\n"
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
use Rack::ContentLength
|
612
|
+
use Rack::ContentType, 'text/plain'
|
613
|
+
use Heater
|
614
|
+
run Tank.new
|
615
|
+
```
|
616
|
+
|
617
|
+
<!---
|
618
|
+
GET /status
|
619
|
+
[200,
|
620
|
+
{'Content-Length' => '1', 'Content-Type' => 'text/plain',
|
621
|
+
'X-Temperature' => "35\u{2103}"},
|
622
|
+
["\n"]]
|
623
|
+
-->
|
624
|
+
|
625
|
+
### Simple before action as a middleware
|
626
|
+
|
627
|
+
``` ruby
|
628
|
+
require 'jellyfish'
|
629
|
+
class Heater
|
630
|
+
include Jellyfish
|
631
|
+
get '/status' do
|
632
|
+
env['temperature'] = 30
|
633
|
+
forward
|
634
|
+
end
|
635
|
+
end
|
636
|
+
|
637
|
+
class Tank
|
638
|
+
include Jellyfish
|
639
|
+
get '/status' do
|
640
|
+
"#{env['temperature']}\u{2103}\n"
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
use Rack::ContentLength
|
645
|
+
use Rack::ContentType, 'text/plain'
|
646
|
+
use Heater
|
647
|
+
run Tank.new
|
648
|
+
```
|
649
|
+
|
650
|
+
<!---
|
651
|
+
GET /status
|
652
|
+
[200,
|
653
|
+
{'Content-Length' => '6', 'Content-Type' => 'text/plain'},
|
654
|
+
["30\u{2103}\n"]]
|
655
|
+
-->
|
656
|
+
|
657
|
+
### Halt in before action
|
658
|
+
|
659
|
+
``` ruby
|
660
|
+
require 'jellyfish'
|
661
|
+
class Tank
|
662
|
+
include Jellyfish
|
663
|
+
controller_include Jellyfish::MultiActions
|
664
|
+
|
665
|
+
get do # wildcard before filter
|
666
|
+
body "Done!\n"
|
667
|
+
halt
|
668
|
+
end
|
669
|
+
get '/' do
|
670
|
+
"Never reach.\n"
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
use Rack::ContentLength
|
675
|
+
use Rack::ContentType, 'text/plain'
|
676
|
+
run Tank.new
|
677
|
+
```
|
678
|
+
|
679
|
+
<!---
|
680
|
+
GET /status
|
681
|
+
[200,
|
682
|
+
{'Content-Length' => '6', 'Content-Type' => 'text/plain'},
|
683
|
+
["Done!\n"]]
|
684
|
+
-->
|
685
|
+
|
264
686
|
### One huge tank
|
265
687
|
|
266
688
|
``` ruby
|
@@ -289,6 +711,13 @@ end
|
|
289
711
|
run HugeTank
|
290
712
|
```
|
291
713
|
|
714
|
+
<!---
|
715
|
+
GET /status
|
716
|
+
[200,
|
717
|
+
{'Content-Length' => '6', 'Content-Type' => 'text/plain'},
|
718
|
+
["30\u{2103}\n"]]
|
719
|
+
-->
|
720
|
+
|
292
721
|
### Raise exceptions
|
293
722
|
|
294
723
|
``` ruby
|
@@ -315,17 +744,48 @@ use Protector
|
|
315
744
|
run Tank.new
|
316
745
|
```
|
317
746
|
|
318
|
-
|
747
|
+
<!---
|
748
|
+
GET /
|
749
|
+
[200,
|
750
|
+
{'Content-Length' => '29', 'Content-Type' => 'text/plain'},
|
751
|
+
["Protected: Oops, tank broken\n"]]
|
752
|
+
-->
|
753
|
+
|
754
|
+
### Chunked transfer encoding (streaming) with Jellyfish::ChunkedBody
|
319
755
|
|
320
756
|
You would need a proper server setup.
|
321
757
|
Here's an example with Rainbows and fibers:
|
322
758
|
|
759
|
+
``` ruby
|
760
|
+
class Tank
|
761
|
+
include Jellyfish
|
762
|
+
get '/chunked' do
|
763
|
+
ChunkedBody.new{ |out|
|
764
|
+
(0..4).each{ |i| out.call("#{i}\n") }
|
765
|
+
}
|
766
|
+
end
|
767
|
+
end
|
768
|
+
use Rack::Chunked
|
769
|
+
use Rack::ContentType, 'text/plain'
|
770
|
+
run Tank.new
|
771
|
+
```
|
772
|
+
|
773
|
+
<!---
|
774
|
+
GET /chunked
|
775
|
+
[200,
|
776
|
+
{'Content-Type' => 'text/plain', 'Transfer-Encoding' => 'chunked'},
|
777
|
+
["2\r\n0\n\r\n", "2\r\n1\n\r\n", "2\r\n2\n\r\n",
|
778
|
+
"2\r\n3\n\r\n", "2\r\n4\n\r\n", "0\r\n\r\n"]]
|
779
|
+
-->
|
780
|
+
|
781
|
+
### Chunked transfer encoding (streaming) with custom body
|
782
|
+
|
323
783
|
``` ruby
|
324
784
|
class Tank
|
325
785
|
include Jellyfish
|
326
786
|
class Body
|
327
787
|
def each
|
328
|
-
(0..4).each{ |i| yield "#{i}\n"
|
788
|
+
(0..4).each{ |i| yield "#{i}\n" }
|
329
789
|
end
|
330
790
|
end
|
331
791
|
get '/chunked' do
|
@@ -337,6 +797,14 @@ use Rack::ContentType, 'text/plain'
|
|
337
797
|
run Tank.new
|
338
798
|
```
|
339
799
|
|
800
|
+
<!---
|
801
|
+
GET /chunked
|
802
|
+
[200,
|
803
|
+
{'Content-Type' => 'text/plain', 'Transfer-Encoding' => 'chunked'},
|
804
|
+
["2\r\n0\n\r\n", "2\r\n1\n\r\n", "2\r\n2\n\r\n",
|
805
|
+
"2\r\n3\n\r\n", "2\r\n4\n\r\n", "0\r\n\r\n"]]
|
806
|
+
-->
|
807
|
+
|
340
808
|
## CONTRIBUTORS:
|
341
809
|
|
342
810
|
* Lin Jen-Shin (@godfat)
|
@@ -345,7 +813,7 @@ run Tank.new
|
|
345
813
|
|
346
814
|
Apache License 2.0
|
347
815
|
|
348
|
-
Copyright (c) 2012, Lin Jen-Shin (godfat)
|
816
|
+
Copyright (c) 2012-2013, Lin Jen-Shin (godfat)
|
349
817
|
|
350
818
|
Licensed under the Apache License, Version 2.0 (the "License");
|
351
819
|
you may not use this file except in compliance with the License.
|