jellyfish 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8cdc77d2745d750685951db77f69943a70cb6f22
4
- data.tar.gz: b16b5fb680ff9cf0102b5cb32a79f0d3f6f6ccec
3
+ metadata.gz: 0902d53ad06a343337f75988ca97c9ec058e0393
4
+ data.tar.gz: 9a5713043ab28c3e0f57b6498e8cc38f19a4e785
5
5
  SHA512:
6
- metadata.gz: 67e8a8e8ca2ccfa92e74a19f75a15ab2fd7722bd28be1370a61dec0a304b96700f93977d5db330c3fedd4f3f61a960d1c48b8ee93863aca4b7589bbdefa59003
7
- data.tar.gz: efbd49c456bb91e07f1a201c7d7399234d17c44fee34073e010724307b504eaf37662a1c19666e6f8efeb4687ba37048402072c9e3a28698dcc72b07548d3b47
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
- - 1.9.3
6
- - 2.0.0
7
- - ruby
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
@@ -4,9 +4,10 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'rake'
7
- gem 'bacon'
8
- gem 'muack'
9
7
  gem 'rack'
8
+ gem 'pork'
9
+ gem 'muack'
10
+ gem 'websocket_parser'
10
11
 
11
12
  platform :rbx do
12
13
  gem 'rubysl-singleton' # used in rake
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][] as an example, which also uses
57
- [Swagger](https://helloreverb.com/developers/swagger) to generate API
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.0 ruby lib
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.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-03-17"
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.2.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 exception, &block; handlers[exception] = block; end
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
- else # make sure we return rack triple
191
- res || ctrl.block_call(nil, lambda{|_|_})
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'])
@@ -1,13 +1,14 @@
1
1
 
2
- require 'bacon'
3
- require 'rack'
2
+ require 'pork/auto'
3
+ require 'muack'
4
4
  require 'jellyfish'
5
+ require 'rack'
5
6
 
6
- Bacon.summary_on_exit
7
+ Pork::Executor.__send__(:include, Muack::API)
7
8
 
8
- shared :jellyfish do
9
- %w[options get head post put delete patch].each do |method|
10
- instance_eval <<-RUBY
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
- end
24
- end
25
-
26
- module Kernel
27
- def eq? rhs
28
- self == rhs
29
- end
24
+ }.join("\n"))
30
25
  end
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Jellyfish
3
- VERSION = '1.0.0'
3
+ VERSION = '1.0.1'
4
4
  end
@@ -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 = "https://github.com/godfat/#{spec.name}"
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
 
@@ -3,9 +3,9 @@ require 'jellyfish/test'
3
3
 
4
4
  # stolen from sinatra
5
5
  describe 'Sinatra base_test.rb' do
6
- behaves_like :jellyfish
6
+ paste :jellyfish
7
7
 
8
- should 'process requests with #call' do
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
- should 'not maintain state between requests' do
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
- should 'create a middleware that responds to #call with .new' do
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
- should 'expose the downstream app' do
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
- should 'intercept requests' do
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
- should 'forward requests downstream when no matching route found' do
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
- should 'call the downstream app directly and return result' do
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
- should 'forward the request and integrate the response' do
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
- behaves_like :jellyfish
6
+ paste :jellyfish
7
7
 
8
- should 'return the concatinated body' do
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
- should 'postpone body generation' do
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
- should 'give access to route specific params' do
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|