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