jellyfish 1.0.0 → 1.0.1
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/.travis.yml +7 -6
- data/CHANGES.md +10 -0
- data/Gemfile +3 -2
- data/README.md +80 -4
- data/jellyfish.gemspec +9 -6
- data/lib/jellyfish.rb +11 -3
- data/lib/jellyfish/test.rb +8 -13
- data/lib/jellyfish/version.rb +1 -1
- data/lib/jellyfish/websocket.rb +51 -0
- data/task/gemgem.rb +1 -5
- data/test/sinatra/test_base.rb +9 -9
- data/test/sinatra/test_chunked_body.rb +4 -4
- data/test/sinatra/test_error.rb +14 -15
- data/test/sinatra/test_multi_actions.rb +17 -17
- data/test/sinatra/test_routing.rb +27 -27
- data/test/test_from_readme.rb +13 -7
- data/test/test_inheritance.rb +5 -5
- data/test/test_log.rb +5 -7
- data/test/test_misc.rb +13 -3
- data/test/test_swagger.rb +8 -8
- data/test/test_threads.rb +1 -4
- data/test/test_websocket.rb +44 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0902d53ad06a343337f75988ca97c9ec058e0393
|
4
|
+
data.tar.gz: 9a5713043ab28c3e0f57b6498e8cc38f19a4e785
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fdcaab8c025ad02e84380e0a5015328267f3a24b90a5df5a7170530a91fcdaf240d3621c7a4da116db5a71dc564ea03c96d13d95e5d3f77d57dbe1a911b3d202
|
7
|
+
data.tar.gz: 164214d46c9396361c1dfe2e4032abb2ed1884ca150159f1f402feb77aaf36b77b8a00e79b49c0691f757e3bb97df1e69a7d98621eb4ecb17c0deb5a71da8d98
|
data/.travis.yml
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
before_install: 'git submodule update --init'
|
2
|
-
script: 'ruby -r bundler/setup -S rake test'
|
3
1
|
|
2
|
+
language: ruby
|
4
3
|
rvm:
|
5
|
-
-
|
6
|
-
- 2.
|
7
|
-
-
|
8
|
-
- rbx
|
4
|
+
- 2.0
|
5
|
+
- 2.1
|
6
|
+
- rbx-2
|
9
7
|
- jruby
|
8
|
+
|
9
|
+
script: 'ruby -r bundler/setup -S rake test'
|
10
|
+
sudo: false
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## Jellyfish 1.0.1 -- 2014-11-07
|
4
|
+
|
5
|
+
### Enhancements for Jellyfish core
|
6
|
+
|
7
|
+
* Now `Jellyfish.handle` could take multiple exceptions as the arguments.
|
8
|
+
|
9
|
+
### Other enhancements
|
10
|
+
|
11
|
+
* Introduced `Jellyfish::WebSocket` for basic websocket support.
|
12
|
+
|
3
13
|
## Jellyfish 1.0.0 -- 2014-03-17
|
4
14
|
|
5
15
|
### Incompatible changes
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -24,6 +24,12 @@ For Rack applications or Rack middlewares. Around 250 lines of code.
|
|
24
24
|
* Don't make things complicated only for _some_ convenience, but for
|
25
25
|
_great_ convenience, or simply stay simple for simplicity.
|
26
26
|
* More features are added as extensions.
|
27
|
+
* Consider use [rack-protection][] if you're not only building an API server.
|
28
|
+
* Consider use [websocket_parser][] if you're trying to use WebSocket.
|
29
|
+
Please check example below.
|
30
|
+
|
31
|
+
[rack-protection]: https://github.com/rkh/rack-protection
|
32
|
+
[websocket_parser]: https://github.com/afcapel/websocket_parser
|
27
33
|
|
28
34
|
## FEATURES:
|
29
35
|
|
@@ -53,9 +59,9 @@ Because Sinatra is too complex and inconsistent for me.
|
|
53
59
|
|
54
60
|
## SYNOPSIS:
|
55
61
|
|
56
|
-
You could also take a look at [config.ru]
|
57
|
-
[Swagger](https://helloreverb.com/developers/swagger) to generate
|
58
|
-
documentation.
|
62
|
+
You could also take a look at [config.ru](config.ru) as an example, which
|
63
|
+
also uses [Swagger](https://helloreverb.com/developers/swagger) to generate
|
64
|
+
API documentation.
|
59
65
|
|
60
66
|
### Hello Jellyfish, your lovely config.ru
|
61
67
|
|
@@ -262,6 +268,32 @@ GET /
|
|
262
268
|
["You found nothing."]]
|
263
269
|
-->
|
264
270
|
|
271
|
+
### Custom error handler for multiple errors
|
272
|
+
|
273
|
+
``` ruby
|
274
|
+
require 'jellyfish'
|
275
|
+
class Tank
|
276
|
+
include Jellyfish
|
277
|
+
handle Jellyfish::NotFound, NameError do |e|
|
278
|
+
status 404
|
279
|
+
"You found nothing."
|
280
|
+
end
|
281
|
+
get '/yell' do
|
282
|
+
yell
|
283
|
+
end
|
284
|
+
end
|
285
|
+
use Rack::ContentLength
|
286
|
+
use Rack::ContentType, 'text/plain'
|
287
|
+
run Tank.new
|
288
|
+
```
|
289
|
+
|
290
|
+
<!---
|
291
|
+
GET /
|
292
|
+
[404,
|
293
|
+
{'Content-Length' => '18', 'Content-Type' => 'text/plain'},
|
294
|
+
["You found nothing."]]
|
295
|
+
-->
|
296
|
+
|
265
297
|
### Access Rack::Request and params
|
266
298
|
|
267
299
|
``` ruby
|
@@ -880,9 +912,53 @@ GET /chunked
|
|
880
912
|
"2\r\n3\n\r\n", "2\r\n4\n\r\n", "0\r\n\r\n"]]
|
881
913
|
-->
|
882
914
|
|
915
|
+
### Using WebSocket
|
916
|
+
|
917
|
+
Note that this only works for Rack servers which support [hijack][].
|
918
|
+
You're better off with a threaded server such as [Rainbows!][] with
|
919
|
+
thread based concurrency model, or [Puma][].
|
920
|
+
|
921
|
+
Event-driven based server is a whole different story though. Since
|
922
|
+
EventMachine is basically dead, we could see if there would be a
|
923
|
+
[Celluloid-IO][] based web server production ready in the future,
|
924
|
+
so that we could take the advantage of event based approach.
|
925
|
+
|
926
|
+
[hijack]: http://www.rubydoc.info/github/rack/rack/file/SPEC#Hijacking
|
927
|
+
[Rainbows!]: http://rainbows.bogomips.org/
|
928
|
+
[Puma]: http://puma.io/
|
929
|
+
[Celluloid-IO]: https://github.com/celluloid/celluloid-io
|
930
|
+
|
931
|
+
``` ruby
|
932
|
+
class Tank
|
933
|
+
include Jellyfish
|
934
|
+
controller_include Jellyfish::WebSocket
|
935
|
+
get '/echo' do
|
936
|
+
switch_protocol do |msg|
|
937
|
+
ws_write(msg)
|
938
|
+
end
|
939
|
+
ws_write('Hi!')
|
940
|
+
ws_start
|
941
|
+
end
|
942
|
+
end
|
943
|
+
run Tank.new
|
944
|
+
```
|
945
|
+
|
946
|
+
<!---
|
947
|
+
GET /echo
|
948
|
+
sock.string.should.eq <<-HTTP.chomp
|
949
|
+
HTTP/1.1 101 Switching Protocols\r
|
950
|
+
Upgrade: websocket\r
|
951
|
+
Connection: Upgrade\r
|
952
|
+
Sec-WebSocket-Accept: Kfh9QIsMVZcl6xEPYxPHzW8SZ8w=\r
|
953
|
+
\r
|
954
|
+
\x81\u0003Hi!
|
955
|
+
HTTP
|
956
|
+
[200, {}, ['']]
|
957
|
+
-->
|
958
|
+
|
883
959
|
### Use Swagger to generate API documentation
|
884
960
|
|
885
|
-
For a complete example, checkout [config.ru]
|
961
|
+
For a complete example, checkout [config.ru](config.ru).
|
886
962
|
|
887
963
|
``` ruby
|
888
964
|
require 'jellyfish'
|
data/jellyfish.gemspec
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# stub: jellyfish 1.0.
|
2
|
+
# stub: jellyfish 1.0.1 ruby lib
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "jellyfish"
|
6
|
-
s.version = "1.0.
|
6
|
+
s.version = "1.0.1"
|
7
7
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
9
9
|
s.require_paths = ["lib"]
|
10
10
|
s.authors = ["Lin Jen-Shin (godfat)"]
|
11
|
-
s.date = "2014-
|
11
|
+
s.date = "2014-11-07"
|
12
12
|
s.description = "Pico web framework for building API-centric web applications.\nFor Rack applications or Rack middlewares. Around 250 lines of code."
|
13
13
|
s.email = ["godfat (XD) godfat.org"]
|
14
14
|
s.files = [
|
@@ -38,6 +38,7 @@ Gem::Specification.new do |s|
|
|
38
38
|
"lib/jellyfish/swagger.rb",
|
39
39
|
"lib/jellyfish/test.rb",
|
40
40
|
"lib/jellyfish/version.rb",
|
41
|
+
"lib/jellyfish/websocket.rb",
|
41
42
|
"public/css/screen.css",
|
42
43
|
"public/index.html",
|
43
44
|
"public/js/shred.bundle.js",
|
@@ -56,10 +57,11 @@ Gem::Specification.new do |s|
|
|
56
57
|
"test/test_log.rb",
|
57
58
|
"test/test_misc.rb",
|
58
59
|
"test/test_swagger.rb",
|
59
|
-
"test/test_threads.rb"
|
60
|
+
"test/test_threads.rb",
|
61
|
+
"test/test_websocket.rb"]
|
60
62
|
s.homepage = "https://github.com/godfat/jellyfish"
|
61
63
|
s.licenses = ["Apache License 2.0"]
|
62
|
-
s.rubygems_version = "2.
|
64
|
+
s.rubygems_version = "2.4.2"
|
63
65
|
s.summary = "Pico web framework for building API-centric web applications."
|
64
66
|
s.test_files = [
|
65
67
|
"test/sinatra/test_base.rb",
|
@@ -72,5 +74,6 @@ Gem::Specification.new do |s|
|
|
72
74
|
"test/test_log.rb",
|
73
75
|
"test/test_misc.rb",
|
74
76
|
"test/test_swagger.rb",
|
75
|
-
"test/test_threads.rb"
|
77
|
+
"test/test_threads.rb",
|
78
|
+
"test/test_websocket.rb"]
|
76
79
|
end
|
data/lib/jellyfish.rb
CHANGED
@@ -10,6 +10,7 @@ module Jellyfish
|
|
10
10
|
autoload :NormalizedPath , 'jellyfish/normalized_path'
|
11
11
|
|
12
12
|
autoload :ChunkedBody, 'jellyfish/chunked_body'
|
13
|
+
autoload :WebSocket , 'jellyfish/websocket'
|
13
14
|
|
14
15
|
Cascade = Object.new
|
15
16
|
GetValue = Object.new
|
@@ -129,7 +130,9 @@ module Jellyfish
|
|
129
130
|
module DSL
|
130
131
|
def routes ; @routes ||= {}; end
|
131
132
|
def handlers; @handlers ||= {}; end
|
132
|
-
def handle
|
133
|
+
def handle *exceptions, &block
|
134
|
+
exceptions.each{ |exp| handlers[exp] = block }
|
135
|
+
end
|
133
136
|
def handle_exceptions value=GetValue
|
134
137
|
if value == GetValue
|
135
138
|
@handle_exceptions
|
@@ -187,8 +190,13 @@ module Jellyfish
|
|
187
190
|
cascade(ctrl, env)
|
188
191
|
when Response
|
189
192
|
handle(ctrl, res, env['rack.errors'])
|
190
|
-
|
191
|
-
res
|
193
|
+
when Array
|
194
|
+
res
|
195
|
+
when NilClass # make sure we return rack triple
|
196
|
+
ctrl.block_call(nil, lambda{|_|_})
|
197
|
+
else
|
198
|
+
raise TypeError.new("Expect the response to be a Jellyfish::Response" \
|
199
|
+
" or Rack triple (Array), but got: #{res.inspect}")
|
192
200
|
end
|
193
201
|
rescue => e
|
194
202
|
handle(ctrl, e, env['rack.errors'])
|
data/lib/jellyfish/test.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
|
2
|
-
require '
|
3
|
-
require '
|
2
|
+
require 'pork/auto'
|
3
|
+
require 'muack'
|
4
4
|
require 'jellyfish'
|
5
|
+
require 'rack'
|
5
6
|
|
6
|
-
|
7
|
+
Pork::Executor.__send__(:include, Muack::API)
|
7
8
|
|
8
|
-
|
9
|
-
%w[options get head post put delete patch].
|
10
|
-
|
9
|
+
copy :jellyfish do
|
10
|
+
module_eval(%w[options get head post put delete patch].map{ |method|
|
11
|
+
<<-RUBY
|
11
12
|
def #{method} path='/', app=app, env={}
|
12
13
|
File.open(File::NULL) do |input|
|
13
14
|
app.call({'PATH_INFO' => path ,
|
@@ -20,11 +21,5 @@ shared :jellyfish do
|
|
20
21
|
end
|
21
22
|
end
|
22
23
|
RUBY
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
module Kernel
|
27
|
-
def eq? rhs
|
28
|
-
self == rhs
|
29
|
-
end
|
24
|
+
}.join("\n"))
|
30
25
|
end
|
data/lib/jellyfish/version.rb
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
require 'jellyfish'
|
3
|
+
require 'websocket_parser'
|
4
|
+
require 'digest/sha1'
|
5
|
+
|
6
|
+
module Jellyfish
|
7
|
+
module WebSocket
|
8
|
+
::WebSocket.constants.each do |const|
|
9
|
+
const_set(const, ::WebSocket.const_get(const))
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :sock, :parser
|
13
|
+
|
14
|
+
def switch_protocol &block
|
15
|
+
key = env['HTTP_SEC_WEBSOCKET_KEY']
|
16
|
+
accept = [Digest::SHA1.digest("#{key}#{GUID}")].pack('m0')
|
17
|
+
@sock = env['rack.hijack'].call
|
18
|
+
sock.binmode
|
19
|
+
sock.write(<<-HTTP)
|
20
|
+
HTTP/1.1 101 Switching Protocols\r
|
21
|
+
Upgrade: websocket\r
|
22
|
+
Connection: Upgrade\r
|
23
|
+
Sec-WebSocket-Accept: #{accept}\r
|
24
|
+
\r
|
25
|
+
HTTP
|
26
|
+
@parser = Parser.new
|
27
|
+
parser.on_message(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def ws_start
|
31
|
+
while !sock.closed? && IO.select([sock]) do
|
32
|
+
ws_read
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def ws_read bytes=8192
|
37
|
+
parser << sock.readpartial(bytes)
|
38
|
+
rescue EOFError
|
39
|
+
sock.close
|
40
|
+
end
|
41
|
+
|
42
|
+
def ws_write msg
|
43
|
+
sock << Message.new(msg).to_data
|
44
|
+
end
|
45
|
+
|
46
|
+
def ws_close
|
47
|
+
sock << Message.close.to_data
|
48
|
+
sock.close
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/task/gemgem.rb
CHANGED
@@ -34,7 +34,7 @@ module Gemgem
|
|
34
34
|
s.executables = bin_files
|
35
35
|
end
|
36
36
|
spec_create.call(spec)
|
37
|
-
spec.homepage
|
37
|
+
spec.homepage ||= "https://github.com/godfat/#{spec.name}"
|
38
38
|
self.spec = spec
|
39
39
|
end
|
40
40
|
|
@@ -227,10 +227,6 @@ end # of gem namespace
|
|
227
227
|
desc 'Run tests'
|
228
228
|
task :test do
|
229
229
|
next if Gemgem.test_files.empty?
|
230
|
-
|
231
|
-
require 'bacon'
|
232
|
-
Bacon.extend(Bacon::TestUnitOutput, Bacon::NewlineErrorDescription)
|
233
|
-
Bacon.summary_on_exit
|
234
230
|
Gemgem.test_files.each{ |file| require "#{Gemgem.dir}/#{file[0..-4]}" }
|
235
231
|
end
|
236
232
|
|
data/test/sinatra/test_base.rb
CHANGED
@@ -3,9 +3,9 @@ require 'jellyfish/test'
|
|
3
3
|
|
4
4
|
# stolen from sinatra
|
5
5
|
describe 'Sinatra base_test.rb' do
|
6
|
-
|
6
|
+
paste :jellyfish
|
7
7
|
|
8
|
-
|
8
|
+
would 'process requests with #call' do
|
9
9
|
app = Class.new{
|
10
10
|
include Jellyfish
|
11
11
|
get '/' do
|
@@ -18,7 +18,7 @@ describe 'Sinatra base_test.rb' do
|
|
18
18
|
body .should.eq ['Hello World']
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
would 'not maintain state between requests' do
|
22
22
|
app = Class.new{
|
23
23
|
include Jellyfish
|
24
24
|
get '/state' do
|
@@ -63,35 +63,35 @@ describe 'Sinatra base_test.rb' do
|
|
63
63
|
end
|
64
64
|
}.new(inner_app)
|
65
65
|
|
66
|
-
|
66
|
+
would 'create a middleware that responds to #call with .new' do
|
67
67
|
app.respond_to?(:call).should.eq true
|
68
68
|
end
|
69
69
|
|
70
|
-
|
70
|
+
would 'expose the downstream app' do
|
71
71
|
app.app.object_id.should.eq inner_app.object_id
|
72
72
|
end
|
73
73
|
|
74
|
-
|
74
|
+
would 'intercept requests' do
|
75
75
|
status, _, body = get('/', app)
|
76
76
|
status.should.eq 200
|
77
77
|
body .should.eq ['Hello from middleware']
|
78
78
|
end
|
79
79
|
|
80
|
-
|
80
|
+
would 'forward requests downstream when no matching route found' do
|
81
81
|
status, headers, body = get('/missing', app)
|
82
82
|
status .should.eq 210
|
83
83
|
headers['X-Downstream'].should.eq 'true'
|
84
84
|
body .should.eq ['Hello from downstream']
|
85
85
|
end
|
86
86
|
|
87
|
-
|
87
|
+
would 'call the downstream app directly and return result' do
|
88
88
|
status, headers, body = get('/low-level-forward', app)
|
89
89
|
status .should.eq 210
|
90
90
|
headers['X-Downstream'].should.eq 'true'
|
91
91
|
body .should.eq ['Hello from downstream']
|
92
92
|
end
|
93
93
|
|
94
|
-
|
94
|
+
would 'forward the request and integrate the response' do
|
95
95
|
status, headers, body =
|
96
96
|
get('/explicit-forward', Rack::ContentLength.new(app))
|
97
97
|
|
@@ -3,9 +3,9 @@ require 'jellyfish/test'
|
|
3
3
|
|
4
4
|
# stolen from sinatra
|
5
5
|
describe 'Sinatra streaming_test.rb' do
|
6
|
-
|
6
|
+
paste :jellyfish
|
7
7
|
|
8
|
-
|
8
|
+
would 'return the concatinated body' do
|
9
9
|
app = Class.new{
|
10
10
|
include Jellyfish
|
11
11
|
get '/' do
|
@@ -20,7 +20,7 @@ describe 'Sinatra streaming_test.rb' do
|
|
20
20
|
body.to_a.join.should.eq 'Hello World!'
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
would 'postpone body generation' do
|
24
24
|
stream = Jellyfish::ChunkedBody.new{ |out|
|
25
25
|
10.times{ |i| out[i] }
|
26
26
|
}
|
@@ -30,7 +30,7 @@ describe 'Sinatra streaming_test.rb' do
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
would 'give access to route specific params' do
|
34
34
|
app = Class.new{
|
35
35
|
include Jellyfish
|
36
36
|
get(%r{/(?<name>\w+)}){ |m|
|