jellyfish 0.9.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -2
- data/.travis.yml +3 -5
- data/CHANGES.md +18 -0
- data/Gemfile +10 -1
- data/README.md +121 -22
- data/Rakefile +6 -15
- data/config.ru +105 -0
- data/jellyfish.gemspec +21 -24
- data/lib/jellyfish.rb +24 -17
- data/lib/jellyfish/json.rb +64 -0
- data/lib/jellyfish/multi_actions.rb +1 -1
- data/lib/jellyfish/swagger.rb +166 -0
- data/lib/jellyfish/test.rb +4 -1
- data/lib/jellyfish/version.rb +1 -1
- data/public/css/screen.css +1070 -0
- data/public/index.html +45 -0
- data/public/js/shred.bundle.js +2765 -0
- data/public/js/shred/content.js +193 -0
- data/public/js/swagger-ui.js +2116 -0
- data/public/js/swagger.js +1400 -0
- data/task/README.md +54 -0
- data/task/gemgem.rb +151 -157
- data/test/sinatra/test_error.rb +31 -4
- data/test/test_from_readme.rb +2 -1
- data/test/test_log.rb +42 -0
- data/test/test_misc.rb +24 -0
- data/test/test_swagger.rb +131 -0
- data/test/test_threads.rb +1 -1
- metadata +25 -52
- data/task/.gitignore +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cdc77d2745d750685951db77f69943a70cb6f22
|
4
|
+
data.tar.gz: b16b5fb680ff9cf0102b5cb32a79f0d3f6f6ccec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 67e8a8e8ca2ccfa92e74a19f75a15ab2fd7722bd28be1370a61dec0a304b96700f93977d5db330c3fedd4f3f61a960d1c48b8ee93863aca4b7589bbdefa59003
|
7
|
+
data.tar.gz: efbd49c456bb91e07f1a201c7d7399234d17c44fee34073e010724307b504eaf37662a1c19666e6f8efeb4687ba37048402072c9e3a28698dcc72b07548d3b47
|
data/.gitignore
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
pkg
|
2
|
-
*.rbc
|
1
|
+
/pkg/
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## Jellyfish 1.0.0 -- 2014-03-17
|
4
|
+
|
5
|
+
### Incompatible changes
|
6
|
+
|
7
|
+
* Renamed `forward` to `cascade` to better aligned with Rack.
|
8
|
+
|
9
|
+
### Enhancements for Jellyfish core
|
10
|
+
|
11
|
+
* Introduced `log` and `log_error` for for controllers.
|
12
|
+
* Introduced `not_found` to trigger 404 response.
|
13
|
+
* Now we separate the idea of 404 and cascade. Use `not_found` for 404
|
14
|
+
responses, and `cascade` for forwarding requests.
|
15
|
+
|
16
|
+
### Other enhancements
|
17
|
+
|
18
|
+
* Now we have Jellyfish::Swagger to generate Swagger documentation.
|
19
|
+
Read README.md for more detail or checkout config.ru for a full example.
|
20
|
+
|
3
21
|
## Jellyfish 0.9.2 -- 2013-09-26
|
4
22
|
|
5
23
|
* Do not rescue Exception since we don't really want to rescue something
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -17,13 +17,13 @@ For Rack applications or Rack middlewares. Around 250 lines of code.
|
|
17
17
|
|
18
18
|
## DESIGN:
|
19
19
|
|
20
|
-
* Learn the HTTP way instead of using some pointless helpers
|
21
|
-
* Learn the Rack way instead of wrapping Rack functionalities
|
22
|
-
* Learn regular expression for routes instead of custom syntax
|
23
|
-
* Embrace simplicity over convenience
|
24
|
-
* Don't make things complicated only for _some_ convenience, but
|
20
|
+
* Learn the HTTP way instead of using some pointless helpers.
|
21
|
+
* Learn the Rack way instead of wrapping around Rack functionalities.
|
22
|
+
* Learn regular expression for routes instead of custom syntax.
|
23
|
+
* Embrace simplicity over convenience.
|
24
|
+
* Don't make things complicated only for _some_ convenience, but for
|
25
25
|
_great_ convenience, or simply stay simple for simplicity.
|
26
|
-
* More features are added as extensions
|
26
|
+
* More features are added as extensions.
|
27
27
|
|
28
28
|
## FEATURES:
|
29
29
|
|
@@ -31,7 +31,7 @@ For Rack applications or Rack middlewares. Around 250 lines of code.
|
|
31
31
|
* Simple
|
32
32
|
* Modular
|
33
33
|
* No templates (You could use [tilt](https://github.com/rtomayko/tilt))
|
34
|
-
* No ORM
|
34
|
+
* No ORM (You could use [sequel](http://sequel.jeremyevans.net/))
|
35
35
|
* No `dup` in `call`
|
36
36
|
* Regular expression routes, e.g. `get %r{^/(?<id>\d+)$}`
|
37
37
|
* String routes, e.g. `get '/'`
|
@@ -45,7 +45,7 @@ Because Sinatra is too complex and inconsistent for me.
|
|
45
45
|
|
46
46
|
## REQUIREMENTS:
|
47
47
|
|
48
|
-
* Tested with MRI (official CRuby)
|
48
|
+
* Tested with MRI (official CRuby), Rubinius and JRuby.
|
49
49
|
|
50
50
|
## INSTALLATION:
|
51
51
|
|
@@ -53,9 +53,9 @@ Because Sinatra is too complex and inconsistent for me.
|
|
53
53
|
|
54
54
|
## SYNOPSIS:
|
55
55
|
|
56
|
-
You could also take a look at [
|
57
|
-
|
58
|
-
|
56
|
+
You could also take a look at [config.ru][] as an example, which also uses
|
57
|
+
[Swagger](https://helloreverb.com/developers/swagger) to generate API
|
58
|
+
documentation.
|
59
59
|
|
60
60
|
### Hello Jellyfish, your lovely config.ru
|
61
61
|
|
@@ -230,7 +230,7 @@ body = case RUBY_ENGINE
|
|
230
230
|
when 'jruby'
|
231
231
|
"No one hears you: (eval):9:in `Tank'\n"
|
232
232
|
when 'rbx'
|
233
|
-
"No one hears you: kernel/delta/kernel.rb:
|
233
|
+
"No one hears you: kernel/delta/kernel.rb:78:in `yell (method_missing)'\n"
|
234
234
|
else
|
235
235
|
"No one hears you: (eval):9:in `block in <class:Tank>'\n"
|
236
236
|
end
|
@@ -239,6 +239,29 @@ body = case RUBY_ENGINE
|
|
239
239
|
[body]]
|
240
240
|
-->
|
241
241
|
|
242
|
+
### Custom error 404 handler
|
243
|
+
|
244
|
+
``` ruby
|
245
|
+
require 'jellyfish'
|
246
|
+
class Tank
|
247
|
+
include Jellyfish
|
248
|
+
handle Jellyfish::NotFound do |e|
|
249
|
+
status 404
|
250
|
+
"You found nothing."
|
251
|
+
end
|
252
|
+
end
|
253
|
+
use Rack::ContentLength
|
254
|
+
use Rack::ContentType, 'text/plain'
|
255
|
+
run Tank.new
|
256
|
+
```
|
257
|
+
|
258
|
+
<!---
|
259
|
+
GET /
|
260
|
+
[404,
|
261
|
+
{'Content-Length' => '18', 'Content-Type' => 'text/plain'},
|
262
|
+
["You found nothing."]]
|
263
|
+
-->
|
264
|
+
|
242
265
|
### Access Rack::Request and params
|
243
266
|
|
244
267
|
``` ruby
|
@@ -352,6 +375,38 @@ GET /status
|
|
352
375
|
["30\u{2103}\n"]]
|
353
376
|
-->
|
354
377
|
|
378
|
+
### Override dispatch for processing before action
|
379
|
+
|
380
|
+
We don't have before action built-in, but we could override `dispatch` in
|
381
|
+
the controller to do the same thing. CAVEAT: Remember to call `super`.
|
382
|
+
|
383
|
+
``` ruby
|
384
|
+
require 'jellyfish'
|
385
|
+
class Tank
|
386
|
+
include Jellyfish
|
387
|
+
controller_include Module.new{
|
388
|
+
def dispatch
|
389
|
+
@state = 'jumps'
|
390
|
+
super
|
391
|
+
end
|
392
|
+
}
|
393
|
+
|
394
|
+
get do
|
395
|
+
"Jelly #{@state}.\n"
|
396
|
+
end
|
397
|
+
end
|
398
|
+
use Rack::ContentLength
|
399
|
+
use Rack::ContentType, 'text/plain'
|
400
|
+
run Tank.new
|
401
|
+
```
|
402
|
+
|
403
|
+
<!---
|
404
|
+
GET /123
|
405
|
+
[200,
|
406
|
+
{'Content-Length' => '13', 'Content-Type' => 'text/plain'},
|
407
|
+
["Jelly jumps.\n"]]
|
408
|
+
-->
|
409
|
+
|
355
410
|
### Extension: MultiActions (Filters)
|
356
411
|
|
357
412
|
``` ruby
|
@@ -525,6 +580,9 @@ GET /123
|
|
525
580
|
|
526
581
|
### Jellyfish as a middleware
|
527
582
|
|
583
|
+
If the Jellyfish middleware cannot find a corresponding action, it would
|
584
|
+
then forward the request to the lower application. We call this `cascade`.
|
585
|
+
|
528
586
|
``` ruby
|
529
587
|
require 'jellyfish'
|
530
588
|
class Heater
|
@@ -556,6 +614,9 @@ GET /
|
|
556
614
|
|
557
615
|
### Modify response as a middleware
|
558
616
|
|
617
|
+
We could also explicitly call the lower app. This would give us more
|
618
|
+
flexibility than simply forwarding it.
|
619
|
+
|
559
620
|
``` ruby
|
560
621
|
require 'jellyfish'
|
561
622
|
class Heater
|
@@ -590,24 +651,34 @@ GET /status
|
|
590
651
|
["See header X-Temperature\n"]]
|
591
652
|
-->
|
592
653
|
|
593
|
-
###
|
654
|
+
### Override cascade for customized forwarding
|
655
|
+
|
656
|
+
We could also override `cascade` in order to craft custom response when
|
657
|
+
forwarding is happening. Note that whenever this forwarding is happening,
|
658
|
+
Jellyfish won't try to merge the headers from `dispatch` method, because
|
659
|
+
in this case Jellyfish is served as a pure proxy. As result we need to
|
660
|
+
explicitly merge the headers if we really want.
|
594
661
|
|
595
662
|
``` ruby
|
596
663
|
require 'jellyfish'
|
597
664
|
class Heater
|
598
665
|
include Jellyfish
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
666
|
+
controller_include Module.new{
|
667
|
+
def dispatch
|
668
|
+
headers_merge('X-Temperature' => "35\u{2103}")
|
669
|
+
super
|
670
|
+
end
|
671
|
+
|
672
|
+
def cascade
|
673
|
+
status, headers, body = jellyfish.app.call(env)
|
674
|
+
halt [status, headers_merge(headers), body]
|
675
|
+
end
|
676
|
+
}
|
605
677
|
end
|
606
678
|
|
607
679
|
class Tank
|
608
680
|
include Jellyfish
|
609
681
|
get '/status' do
|
610
|
-
headers_merge('X-Temperature' => "35\u{2103}")
|
611
682
|
"\n"
|
612
683
|
end
|
613
684
|
end
|
@@ -634,7 +705,7 @@ class Heater
|
|
634
705
|
include Jellyfish
|
635
706
|
get '/status' do
|
636
707
|
env['temperature'] = 30
|
637
|
-
|
708
|
+
cascade
|
638
709
|
end
|
639
710
|
end
|
640
711
|
|
@@ -809,6 +880,34 @@ GET /chunked
|
|
809
880
|
"2\r\n3\n\r\n", "2\r\n4\n\r\n", "0\r\n\r\n"]]
|
810
881
|
-->
|
811
882
|
|
883
|
+
### Use Swagger to generate API documentation
|
884
|
+
|
885
|
+
For a complete example, checkout [config.ru][].
|
886
|
+
|
887
|
+
``` ruby
|
888
|
+
require 'jellyfish'
|
889
|
+
class Tank
|
890
|
+
include Jellyfish
|
891
|
+
get %r{^/(?<id>\d+)$}, :notes => 'This is an API note' do |match|
|
892
|
+
"Jelly ##{match[:id]}\n"
|
893
|
+
end
|
894
|
+
end
|
895
|
+
use Rack::ContentLength
|
896
|
+
use Rack::ContentType, 'text/plain'
|
897
|
+
map '/swagger' do
|
898
|
+
run Jellyfish::Swagger.new('', Tank)
|
899
|
+
end
|
900
|
+
run Tank.new
|
901
|
+
```
|
902
|
+
|
903
|
+
<!---
|
904
|
+
GET /swagger
|
905
|
+
[200,
|
906
|
+
{'Content-Type' => 'application/json; charset=utf-8',
|
907
|
+
'Content-Length' => '81'},
|
908
|
+
['{"swaggerVersion":"1.2","info":{},"apiVersion":"0.1.0","apis":[{"path":"/{id}"}]}']]
|
909
|
+
-->
|
910
|
+
|
812
911
|
## CONTRIBUTORS:
|
813
912
|
|
814
913
|
* Lin Jen-Shin (@godfat)
|
@@ -817,7 +916,7 @@ GET /chunked
|
|
817
916
|
|
818
917
|
Apache License 2.0
|
819
918
|
|
820
|
-
Copyright (c) 2012-
|
919
|
+
Copyright (c) 2012-2014, Lin Jen-Shin (godfat)
|
821
920
|
|
822
921
|
Licensed under the Apache License, Version 2.0 (the "License");
|
823
922
|
you may not use this file except in compliance with the License.
|
data/Rakefile
CHANGED
@@ -1,23 +1,14 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
|
3
2
|
begin
|
4
3
|
require "#{dir = File.dirname(__FILE__)}/task/gemgem"
|
5
4
|
rescue LoadError
|
6
5
|
sh 'git submodule update --init'
|
7
|
-
exec Gem.ruby, '-S',
|
6
|
+
exec Gem.ruby, '-S', $PROGRAM_NAME, *ARGV
|
8
7
|
end
|
9
8
|
|
10
|
-
Gemgem.dir
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
Gemgem.spec = Gemgem.create do |s|
|
16
|
-
require 'jellyfish/version'
|
17
|
-
s.name = 'jellyfish'
|
18
|
-
s.version = Jellyfish::VERSION
|
19
|
-
%w[rack bacon muack].each{ |g| s.add_development_dependency(g) }
|
20
|
-
end
|
21
|
-
|
22
|
-
Gemgem.write
|
9
|
+
Gemgem.init(dir) do |s|
|
10
|
+
require 'jellyfish/version'
|
11
|
+
s.name = 'jellyfish'
|
12
|
+
s.version = Jellyfish::VERSION
|
13
|
+
%w[].each{ |g| s.add_runtime_dependency(g) }
|
23
14
|
end
|
data/config.ru
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/env unicorn -N -Ilib
|
2
|
+
|
3
|
+
require 'jellyfish'
|
4
|
+
|
5
|
+
class Jelly
|
6
|
+
include Jellyfish
|
7
|
+
|
8
|
+
controller_include Module.new{
|
9
|
+
def dispatch
|
10
|
+
headers_merge 'Content-Type' => 'application/json; charset=utf-8',
|
11
|
+
'Access-Control-Allow-Origin' => '*'
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def render obj
|
16
|
+
["#{Jellyfish::Json.encode(obj)}\n"]
|
17
|
+
end
|
18
|
+
}
|
19
|
+
|
20
|
+
def self.info
|
21
|
+
{:title => 'Jellyfish Swagger UI',
|
22
|
+
:description =>
|
23
|
+
'This is a simple example for using Jellyfish and' \
|
24
|
+
' Swagger UI altogether. You could also try the' \
|
25
|
+
' <a href="http://swagger.wordnik.com/">official Swagger UI app</a>,' \
|
26
|
+
' and fill it with the swagger URL.'
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.swagger_apiVersion
|
31
|
+
'1.0.0'
|
32
|
+
end
|
33
|
+
|
34
|
+
handle Jellyfish::NotFound do |e|
|
35
|
+
status 404
|
36
|
+
body %Q|{"error":{"name":"NotFound"}}\n|
|
37
|
+
end
|
38
|
+
|
39
|
+
handle StandardError do |error|
|
40
|
+
jellyfish.log_error(error, env['rack.errors'])
|
41
|
+
|
42
|
+
name = error.class.name
|
43
|
+
message = error.message
|
44
|
+
|
45
|
+
status 500
|
46
|
+
body render('error' => {'name' => name, 'message' => message})
|
47
|
+
end
|
48
|
+
|
49
|
+
get '/users',
|
50
|
+
:summary => 'List users',
|
51
|
+
:notes => 'Note that we do not really have users.' do
|
52
|
+
render [:name => 'jellyfish']
|
53
|
+
end
|
54
|
+
|
55
|
+
post '/users',
|
56
|
+
:summary => 'Create a user',
|
57
|
+
:notes => 'Here we demonstrate how to write the swagger doc.',
|
58
|
+
:parameters => {:name => {:type => :string, :required => true,
|
59
|
+
:description => 'The name of the user'},
|
60
|
+
:sane => {:type => :boolean,
|
61
|
+
:description => 'If the user is sane'},
|
62
|
+
:type => {:type => :string,
|
63
|
+
:description => 'What kind of user',
|
64
|
+
:enum => %w[good neutral evil]}},
|
65
|
+
:responseMessages => [{:code => 400, :message => 'Invalid name'}] do
|
66
|
+
render :message => "jellyfish #{request.params['name']} created."
|
67
|
+
end
|
68
|
+
|
69
|
+
put %r{\A/users/(?<id>\d+)},
|
70
|
+
:summary => 'Update a user',
|
71
|
+
:parameters => {:id => {:type => :integer,
|
72
|
+
:description => 'The id of the user'}} do |match|
|
73
|
+
render :message => "jellyfish ##{match[:id]} updated."
|
74
|
+
end
|
75
|
+
|
76
|
+
delete %r{\A/users/(?<id>\d+)} do |match|
|
77
|
+
render :message => "jellyfish ##{match[:id]} deleted."
|
78
|
+
end
|
79
|
+
|
80
|
+
get %r{\A/posts/(?<year>\d+)-(?<month>\d+)/(?<name>\w+)},
|
81
|
+
:summary => 'Get a post' do |match|
|
82
|
+
render Hash[match.names.zip(match.captures)]
|
83
|
+
end
|
84
|
+
|
85
|
+
get '/posts/tags/ruby' do
|
86
|
+
render []
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
App = Rack::Builder.app do
|
91
|
+
use Rack::CommonLogger
|
92
|
+
use Rack::Chunked
|
93
|
+
use Rack::ContentLength
|
94
|
+
use Rack::Deflater
|
95
|
+
|
96
|
+
map '/swagger' do
|
97
|
+
run Jellyfish::Swagger.new('', Jelly)
|
98
|
+
end
|
99
|
+
|
100
|
+
run Rack::Cascade.new([Rack::File.new('public/index.html'),
|
101
|
+
Rack::File.new('public'),
|
102
|
+
Jelly.new])
|
103
|
+
end
|
104
|
+
|
105
|
+
run App
|