jellyfish 0.6.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|