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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c9698c593098db6a1123491fae8d2d4777697dfd
4
- data.tar.gz: 033cb54f5d56f48f32bc0e44cad8feb573ec6bdb
3
+ metadata.gz: 8cdc77d2745d750685951db77f69943a70cb6f22
4
+ data.tar.gz: b16b5fb680ff9cf0102b5cb32a79f0d3f6f6ccec
5
5
  SHA512:
6
- metadata.gz: 62d9b38427f883c2fd1c6a6c7725693e4a4e7bf346211d58de25b9a9fc1d1e6987bf62c27ba3101f443383e3d29bbb0b063a160b6c2818407003d5a6616bee9b
7
- data.tar.gz: 9918c2af70dcfa60c35ccfae7f4d506d58c582d33cb85e7d96cdd1aa89954ec592efb007eb2a6d0b5511471128208e12521292779d10054208b8dac065d02601
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
@@ -1,11 +1,9 @@
1
1
  before_install: 'git submodule update --init'
2
2
  script: 'ruby -r bundler/setup -S rake test'
3
3
 
4
- env:
5
- - 'RBXOPT=-X19'
6
-
7
4
  rvm:
8
5
  - 1.9.3
9
6
  - 2.0.0
10
- - rbx-head
11
- - jruby-head
7
+ - ruby
8
+ - rbx
9
+ - jruby
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
@@ -1,4 +1,13 @@
1
1
 
2
- source 'http://rubygems.org'
2
+ source 'https://rubygems.org'
3
+
3
4
  gemspec
5
+
4
6
  gem 'rake'
7
+ gem 'bacon'
8
+ gem 'muack'
9
+ gem 'rack'
10
+
11
+ platform :rbx do
12
+ gem 'rubysl-singleton' # used in rake
13
+ end
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, again
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) 1.9.3, 2.0.0, Rubinius and JRuby.
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 [Saya][] as an example Jellyfish application.
57
-
58
- [Saya]: https://github.com/godfat/saya
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:81:in `yell (method_missing)'\n"
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
- ### Default headers as a middleware
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
- get '/status' do
600
- status, headers, body = jellyfish.app.call(env)
601
- self.status status
602
- self.headers({'X-Temperature' => "30\u{2103}"}.merge(headers))
603
- self.body body
604
- end
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
- forward
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-2013, Lin Jen-Shin (godfat)
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', 'rake', *ARGV
6
+ exec Gem.ruby, '-S', $PROGRAM_NAME, *ARGV
8
7
  end
9
8
 
10
- Gemgem.dir = dir
11
- ($LOAD_PATH << File.expand_path("#{Gemgem.dir}/lib")).uniq!
12
-
13
- desc 'Generate gemspec'
14
- task 'gem:spec' do
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