jellyfish 0.9.2 → 1.0.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 +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
|