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 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|