jellyfish 1.0.2 → 1.1.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 +4 -4
- data/.travis.yml +1 -0
- data/CHANGES.md +14 -0
- data/README.md +194 -119
- data/bench/bench_builder.rb +44 -0
- data/config.ru +5 -39
- data/jellyfish.gemspec +12 -18
- data/lib/jellyfish.rb +1 -3
- data/lib/jellyfish/builder.rb +52 -0
- data/lib/jellyfish/test.rb +8 -8
- data/lib/jellyfish/urlmap.rb +26 -0
- data/lib/jellyfish/version.rb +1 -1
- data/task/gemgem.rb +11 -2
- data/test/rack/test_builder.rb +154 -0
- data/test/rack/test_urlmap.rb +180 -0
- data/test/sinatra/test_base.rb +1 -1
- data/test/sinatra/test_routing.rb +0 -60
- data/test/test_from_readme.rb +31 -20
- metadata +15 -17
- data/lib/jellyfish/multi_actions.rb +0 -31
- data/lib/jellyfish/sinatra.rb +0 -13
- data/lib/jellyfish/swagger.rb +0 -166
- data/public/css/screen.css +0 -1070
- data/public/index.html +0 -45
- data/public/js/shred.bundle.js +0 -2765
- data/public/js/shred/content.js +0 -193
- data/public/js/swagger-ui.js +0 -2116
- data/public/js/swagger.js +0 -1400
- data/test/sinatra/test_multi_actions.rb +0 -217
- data/test/test_swagger.rb +0 -131
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f177b766380820557e01f05b9da4efacc4895984
|
4
|
+
data.tar.gz: 800a25a7fa325e5c0c40c7604b94cbddb32c43a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 288710b695398769a27f730e3a2d2d6fe921b140455a968f57515503628af9f7759506c89c9291b860e862b1eb6ca56d076b9ca01c2429d53626bc021d189904
|
7
|
+
data.tar.gz: 78edee35fbbac6dd332fc9eb9290b1dc26c27b74abd72c57166bb0d429ac6f3d441c3c88f56f014befdeb32825a47bf6d395dae04e21c624b6c52caa986aa589
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## Jellyfish 1.1.0 -- 2015-09-25
|
4
|
+
|
5
|
+
### Incompatible changes
|
6
|
+
|
7
|
+
* `Jellyfish::Sinatra`, `Jellyfish::MultiActions`, and `Jellyfish::Swagger`
|
8
|
+
were extracted to
|
9
|
+
[jellyfish-contrib](https://github.com/godfat/jellyfish-contrib)
|
10
|
+
|
11
|
+
### Other enhancements
|
12
|
+
|
13
|
+
* Added `Jellyfish::Builder` and `Jellyfish::URLMap` which is 36 times faster
|
14
|
+
than `Rack::Builder` and `Rack::URLMap` given an application with
|
15
|
+
1000 routes.
|
16
|
+
|
3
17
|
## Jellyfish 1.0.2 -- 2014-12-09
|
4
18
|
|
5
19
|
* `Jellyfish::NewRelic` is fixed. Thanks Jason R. Clark (@jasonrclark)
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Jellyfish [](http://travis-ci.org/godfat/jellyfish) [](https://coveralls.io/r/godfat/jellyfish)
|
1
|
+
# Jellyfish [](http://travis-ci.org/godfat/jellyfish) [](https://coveralls.io/r/godfat/jellyfish) [](https://gitter.im/godfat/jellyfish)
|
2
2
|
|
3
3
|
by Lin Jen-Shin ([godfat](http://godfat.org))
|
4
4
|
|
@@ -13,7 +13,11 @@ by Lin Jen-Shin ([godfat](http://godfat.org))
|
|
13
13
|
## DESCRIPTION:
|
14
14
|
|
15
15
|
Pico web framework for building API-centric web applications.
|
16
|
-
For Rack applications or Rack
|
16
|
+
For Rack applications or Rack middleware. Around 250 lines of code.
|
17
|
+
|
18
|
+
Check [jellyfish-contrib][] for extra extensions.
|
19
|
+
|
20
|
+
[jellyfish-contrib]: https://github.com/godfat/jellyfish-contrib
|
17
21
|
|
18
22
|
## DESIGN:
|
19
23
|
|
@@ -43,7 +47,7 @@ For Rack applications or Rack middlewares. Around 250 lines of code.
|
|
43
47
|
* String routes, e.g. `get '/'`
|
44
48
|
* Custom routes, e.g. `get Matcher.new`
|
45
49
|
* Build for either Rack applications or Rack middleware
|
46
|
-
* Include extensions for more features (
|
50
|
+
* Include extensions for more features (checkout [jellyfish-contrib][])
|
47
51
|
|
48
52
|
## WHY?
|
49
53
|
|
@@ -59,9 +63,7 @@ Because Sinatra is too complex and inconsistent for me.
|
|
59
63
|
|
60
64
|
## SYNOPSIS:
|
61
65
|
|
62
|
-
You could also take a look at [config.ru](config.ru) as an example
|
63
|
-
also uses [Swagger](https://helloreverb.com/developers/swagger) to generate
|
64
|
-
API documentation.
|
66
|
+
You could also take a look at [config.ru](config.ru) as an example.
|
65
67
|
|
66
68
|
### Hello Jellyfish, your lovely config.ru
|
67
69
|
|
@@ -439,33 +441,148 @@ GET /123
|
|
439
441
|
["Jelly jumps.\n"]]
|
440
442
|
-->
|
441
443
|
|
442
|
-
### Extension:
|
444
|
+
### Extension: Jellyfish::Builder, a faster Rack::Builder and Rack::URLMap
|
445
|
+
|
446
|
+
Default `Rack::Builder` and `Rack::URLMap` is routing via linear search,
|
447
|
+
which could be very slow with a large number of routes. We could use
|
448
|
+
`Jellyfish::Builder` in this case because it would compile the routes
|
449
|
+
into a regular expression, it would be matching much faster than
|
450
|
+
linear search.
|
451
|
+
|
452
|
+
Note that `Jellyfish::Builder` is not a complete compatible implementation.
|
453
|
+
The followings are intentional:
|
454
|
+
|
455
|
+
* There's no `Jellyfish::Builder.call` because it doesn't make sense in my
|
456
|
+
opinion. Always use `Jellyfish::Builder.app` instead.
|
457
|
+
|
458
|
+
* There's no `Jellyfish::Builder.parse_file` and
|
459
|
+
`Jellyfish::Builder.new_from_string` because Rack servers are not
|
460
|
+
going to use `Jellyfish::Builder` to parse `config.ru` at this point.
|
461
|
+
We could provide this if there's a need.
|
462
|
+
|
463
|
+
* `Jellyfish::URLMap` does not modify `env`, and it would call the app with
|
464
|
+
another instance of Hash. Mutating data is a bad idea.
|
465
|
+
|
466
|
+
* `Jellyfish::URLMap` does not try to match on host because I am not sure
|
467
|
+
if there's anyone would need this feature?
|
468
|
+
|
469
|
+
* All other tests passed the same test suites for `Rack::Builder`.
|
443
470
|
|
444
471
|
``` ruby
|
445
472
|
require 'jellyfish'
|
446
|
-
class Tank
|
447
|
-
include Jellyfish
|
448
|
-
controller_include Jellyfish::MultiActions
|
449
473
|
|
450
|
-
|
451
|
-
|
474
|
+
run Jellyfish::Builder.app{
|
475
|
+
map '/a' do; run lambda{ |_| [200, {}, ["a\n"] ] }; end
|
476
|
+
map '/b' do; run lambda{ |_| [200, {}, ["b\n"] ] }; end
|
477
|
+
map '/c' do; run lambda{ |_| [200, {}, ["c\n"] ] }; end
|
478
|
+
map '/d' do; run lambda{ |_| [200, {}, ["d\n"] ] }; end
|
479
|
+
map '/e' do
|
480
|
+
map '/f' do; run lambda{ |_| [200, {}, ["e/f\n"]] }; end
|
481
|
+
map '/g' do; run lambda{ |_| [200, {}, ["e/g\n"]] }; end
|
482
|
+
map '/h' do; run lambda{ |_| [200, {}, ["e/h\n"]] }; end
|
483
|
+
map '/i' do; run lambda{ |_| [200, {}, ["e/i\n"]] }; end
|
484
|
+
map '/' do; run lambda{ |_| [200, {}, ["e\n"]] }; end
|
452
485
|
end
|
453
|
-
|
454
|
-
|
486
|
+
map '/j' do; run lambda{ |_| [200, {}, ["j\n"] ] }; end
|
487
|
+
map '/k' do; run lambda{ |_| [200, {}, ["k\n"] ] }; end
|
488
|
+
map '/l' do; run lambda{ |_| [200, {}, ["l\n"] ] }; end
|
489
|
+
map '/m' do
|
490
|
+
map '/g' do; run lambda{ |_| [200, {}, ["m/g\n"]] }; end
|
491
|
+
run lambda{ |_| [200, {}, ["m\n"] ] }
|
455
492
|
end
|
456
|
-
|
457
|
-
use Rack::ContentLength
|
458
|
-
|
459
|
-
|
493
|
+
|
494
|
+
use Rack::ContentLength
|
495
|
+
run lambda{ |_| [200, {}, ["/\n"]] }
|
496
|
+
}
|
460
497
|
```
|
461
498
|
|
462
499
|
<!---
|
463
|
-
GET /
|
464
|
-
[200,
|
465
|
-
|
466
|
-
|
500
|
+
GET /a
|
501
|
+
[200, {}, ["a\n"]]
|
502
|
+
|
503
|
+
GET /a/x
|
504
|
+
[200, {}, ["a\n"]]
|
505
|
+
|
506
|
+
GET /b
|
507
|
+
[200, {}, ["b\n"]]
|
508
|
+
|
509
|
+
GET /c
|
510
|
+
[200, {}, ["c\n"]]
|
511
|
+
|
512
|
+
GET /d
|
513
|
+
[200, {}, ["d\n"]]
|
514
|
+
|
515
|
+
GET /e/f
|
516
|
+
[200, {}, ["e/f\n"]]
|
517
|
+
|
518
|
+
GET /e/g
|
519
|
+
[200, {}, ["e/g\n"]]
|
520
|
+
|
521
|
+
GET /e/h
|
522
|
+
[200, {}, ["e/h\n"]]
|
523
|
+
|
524
|
+
GET /e/i
|
525
|
+
[200, {}, ["e/i\n"]]
|
526
|
+
|
527
|
+
GET /e/
|
528
|
+
[200, {}, ["e\n"]]
|
529
|
+
|
530
|
+
GET /e
|
531
|
+
[200, {}, ["e\n"]]
|
532
|
+
|
533
|
+
GET /e/x
|
534
|
+
[200, {}, ["e\n"]]
|
535
|
+
|
536
|
+
GET /j
|
537
|
+
[200, {}, ["j\n"]]
|
538
|
+
|
539
|
+
GET /k
|
540
|
+
[200, {}, ["k\n"]]
|
541
|
+
|
542
|
+
GET /l
|
543
|
+
[200, {}, ["l\n"]]
|
544
|
+
|
545
|
+
GET /m/g
|
546
|
+
[200, {}, ["m/g\n"]]
|
547
|
+
|
548
|
+
GET /m
|
549
|
+
[200, {}, ["m\n"]]
|
550
|
+
|
551
|
+
GET /m/
|
552
|
+
[200, {}, ["m\n"]]
|
553
|
+
|
554
|
+
GET /m/x
|
555
|
+
[200, {}, ["m\n"]]
|
556
|
+
|
557
|
+
GET /
|
558
|
+
[200, {'Content-Length' => '2'}, ["/\n"]]
|
559
|
+
|
560
|
+
GET /x
|
561
|
+
[200, {'Content-Length' => '2'}, ["/\n"]]
|
562
|
+
|
563
|
+
GET /ab
|
564
|
+
[200, {'Content-Length' => '2'}, ["/\n"]]
|
467
565
|
-->
|
468
566
|
|
567
|
+
You could try a stupid benchmark yourself:
|
568
|
+
|
569
|
+
ruby -Ilib bench/bench_builder.rb
|
570
|
+
|
571
|
+
For a 1000 routes app, here's my result:
|
572
|
+
|
573
|
+
```
|
574
|
+
Calculating -------------------------------------
|
575
|
+
Jellyfish::URLMap 5.726k i/100ms
|
576
|
+
Rack::URLMap 167.000 i/100ms
|
577
|
+
-------------------------------------------------
|
578
|
+
Jellyfish::URLMap 62.397k (± 1.2%) i/s - 314.930k
|
579
|
+
Rack::URLMap 1.702k (± 1.5%) i/s - 8.517k
|
580
|
+
|
581
|
+
Comparison:
|
582
|
+
Jellyfish::URLMap: 62397.3 i/s
|
583
|
+
Rack::URLMap: 1702.0 i/s - 36.66x slower
|
584
|
+
```
|
585
|
+
|
469
586
|
### Extension: NormalizedParams (with force_encoding)
|
470
587
|
|
471
588
|
``` ruby
|
@@ -514,39 +631,6 @@ GET /%E5%9B%A7
|
|
514
631
|
["/%E5%9B%A7=/\u{56e7}\n"]]
|
515
632
|
-->
|
516
633
|
|
517
|
-
### Extension: Sinatra flavoured controller
|
518
|
-
|
519
|
-
It's an extension collection contains:
|
520
|
-
|
521
|
-
* MultiActions
|
522
|
-
* NormalizedParams
|
523
|
-
* NormalizedPath
|
524
|
-
|
525
|
-
``` ruby
|
526
|
-
require 'jellyfish'
|
527
|
-
class Tank
|
528
|
-
include Jellyfish
|
529
|
-
controller_include Jellyfish::Sinatra
|
530
|
-
|
531
|
-
get do # wildcard before filter
|
532
|
-
@state = 'jumps'
|
533
|
-
end
|
534
|
-
get %r{^/(?<id>\d+)$} do
|
535
|
-
"Jelly ##{params[:id]} #{@state}.\n"
|
536
|
-
end
|
537
|
-
end
|
538
|
-
use Rack::ContentLength
|
539
|
-
use Rack::ContentType, 'text/plain'
|
540
|
-
run Tank.new
|
541
|
-
```
|
542
|
-
|
543
|
-
<!---
|
544
|
-
GET /123
|
545
|
-
[200,
|
546
|
-
{'Content-Length' => '18', 'Content-Type' => 'text/plain'},
|
547
|
-
["Jelly #123 jumps.\n"]]
|
548
|
-
-->
|
549
|
-
|
550
634
|
### Extension: NewRelic
|
551
635
|
|
552
636
|
``` ruby
|
@@ -577,7 +661,6 @@ GET /
|
|
577
661
|
|
578
662
|
### Extension: Using multiple extensions with custom controller
|
579
663
|
|
580
|
-
This is effectively the same as using Jellyfish::Sinatra extension.
|
581
664
|
Note that the controller should be assigned lastly in order to include
|
582
665
|
modules remembered in controller_include.
|
583
666
|
|
@@ -586,16 +669,13 @@ require 'jellyfish'
|
|
586
669
|
class Tank
|
587
670
|
include Jellyfish
|
588
671
|
class MyController < Jellyfish::Controller
|
589
|
-
include Jellyfish::
|
672
|
+
include Jellyfish::WebSocket
|
590
673
|
end
|
591
674
|
controller_include NormalizedParams, NormalizedPath
|
592
675
|
controller MyController
|
593
676
|
|
594
|
-
get do # wildcard before filter
|
595
|
-
@state = 'jumps'
|
596
|
-
end
|
597
677
|
get %r{^/(?<id>\d+)$} do
|
598
|
-
"Jelly ##{params[:id]}
|
678
|
+
"Jelly ##{params[:id]} jumps.\n"
|
599
679
|
end
|
600
680
|
end
|
601
681
|
use Rack::ContentLength
|
@@ -761,35 +841,6 @@ GET /status
|
|
761
841
|
["30\u{2103}\n"]]
|
762
842
|
-->
|
763
843
|
|
764
|
-
### Halt in before action
|
765
|
-
|
766
|
-
``` ruby
|
767
|
-
require 'jellyfish'
|
768
|
-
class Tank
|
769
|
-
include Jellyfish
|
770
|
-
controller_include Jellyfish::MultiActions
|
771
|
-
|
772
|
-
get do # wildcard before filter
|
773
|
-
body "Done!\n"
|
774
|
-
halt
|
775
|
-
end
|
776
|
-
get '/' do
|
777
|
-
"Never reach.\n"
|
778
|
-
end
|
779
|
-
end
|
780
|
-
|
781
|
-
use Rack::ContentLength
|
782
|
-
use Rack::ContentType, 'text/plain'
|
783
|
-
run Tank.new
|
784
|
-
```
|
785
|
-
|
786
|
-
<!---
|
787
|
-
GET /status
|
788
|
-
[200,
|
789
|
-
{'Content-Length' => '6', 'Content-Type' => 'text/plain'},
|
790
|
-
["Done!\n"]]
|
791
|
-
-->
|
792
|
-
|
793
844
|
### One huge tank
|
794
845
|
|
795
846
|
``` ruby
|
@@ -912,6 +963,57 @@ GET /chunked
|
|
912
963
|
"2\r\n3\n\r\n", "2\r\n4\n\r\n", "0\r\n\r\n"]]
|
913
964
|
-->
|
914
965
|
|
966
|
+
### Server Sent Event (SSE)
|
967
|
+
|
968
|
+
``` ruby
|
969
|
+
class Tank
|
970
|
+
include Jellyfish
|
971
|
+
class Body
|
972
|
+
def each
|
973
|
+
(0..4).each{ |i| yield "data: #{i}\n\n" }
|
974
|
+
end
|
975
|
+
end
|
976
|
+
get '/sse' do
|
977
|
+
headers_merge('Content-Type' => 'text/event-stream')
|
978
|
+
Body.new
|
979
|
+
end
|
980
|
+
end
|
981
|
+
run Tank.new
|
982
|
+
```
|
983
|
+
|
984
|
+
<!---
|
985
|
+
GET /sse
|
986
|
+
[200,
|
987
|
+
{'Content-Type' => 'text/event-stream'},
|
988
|
+
["data: 0\n\n", "data: 1\n\n", "data: 2\n\n", "data: 3\n\n", "data: 4\n\n"]]
|
989
|
+
-->
|
990
|
+
|
991
|
+
### Server Sent Event (SSE) with Rack Hijacking
|
992
|
+
|
993
|
+
``` ruby
|
994
|
+
class Tank
|
995
|
+
include Jellyfish
|
996
|
+
get '/sse' do
|
997
|
+
headers_merge(
|
998
|
+
'Content-Type' => 'text/event-stream',
|
999
|
+
'rack.hijack' => lambda do |sock|
|
1000
|
+
(0..4).each do |i|
|
1001
|
+
sock.write("data: #{i}\n\n")
|
1002
|
+
end
|
1003
|
+
sock.close
|
1004
|
+
end)
|
1005
|
+
end
|
1006
|
+
end
|
1007
|
+
run Tank.new
|
1008
|
+
```
|
1009
|
+
|
1010
|
+
<!---
|
1011
|
+
GET /sse
|
1012
|
+
[200,
|
1013
|
+
{'Content-Type' => 'text/event-stream'},
|
1014
|
+
["data: 0\n\n", "data: 1\n\n", "data: 2\n\n", "data: 3\n\n", "data: 4\n\n"]]
|
1015
|
+
-->
|
1016
|
+
|
915
1017
|
### Using WebSocket
|
916
1018
|
|
917
1019
|
Note that this only works for Rack servers which support [hijack][].
|
@@ -956,36 +1058,9 @@ HTTP
|
|
956
1058
|
[200, {}, ['']]
|
957
1059
|
-->
|
958
1060
|
|
959
|
-
### Use Swagger to generate API documentation
|
960
|
-
|
961
|
-
For a complete example, checkout [config.ru](config.ru).
|
962
|
-
|
963
|
-
``` ruby
|
964
|
-
require 'jellyfish'
|
965
|
-
class Tank
|
966
|
-
include Jellyfish
|
967
|
-
get %r{^/(?<id>\d+)$}, :notes => 'This is an API note' do |match|
|
968
|
-
"Jelly ##{match[:id]}\n"
|
969
|
-
end
|
970
|
-
end
|
971
|
-
use Rack::ContentLength
|
972
|
-
use Rack::ContentType, 'text/plain'
|
973
|
-
map '/swagger' do
|
974
|
-
run Jellyfish::Swagger.new('', Tank)
|
975
|
-
end
|
976
|
-
run Tank.new
|
977
|
-
```
|
978
|
-
|
979
|
-
<!---
|
980
|
-
GET /swagger
|
981
|
-
[200,
|
982
|
-
{'Content-Type' => 'application/json; charset=utf-8',
|
983
|
-
'Content-Length' => '81'},
|
984
|
-
['{"swaggerVersion":"1.2","info":{},"apiVersion":"0.1.0","apis":[{"path":"/{id}"}]}']]
|
985
|
-
-->
|
986
|
-
|
987
1061
|
## CONTRIBUTORS:
|
988
1062
|
|
1063
|
+
* Fumin (@fumin)
|
989
1064
|
* Jason R. Clark (@jasonrclark)
|
990
1065
|
* Lin Jen-Shin (@godfat)
|
991
1066
|
|
@@ -993,7 +1068,7 @@ GET /swagger
|
|
993
1068
|
|
994
1069
|
Apache License 2.0
|
995
1070
|
|
996
|
-
Copyright (c) 2012-
|
1071
|
+
Copyright (c) 2012-2015, Lin Jen-Shin (godfat)
|
997
1072
|
|
998
1073
|
Licensed under the Apache License, Version 2.0 (the "License");
|
999
1074
|
you may not use this file except in compliance with the License.
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
# Calculating -------------------------------------
|
3
|
+
# Jellyfish::URLMap 5.726k i/100ms
|
4
|
+
# Rack::URLMap 167.000 i/100ms
|
5
|
+
# -------------------------------------------------
|
6
|
+
# Jellyfish::URLMap 62.397k (± 1.2%) i/s - 314.930k
|
7
|
+
# Rack::URLMap 1.702k (± 1.5%) i/s - 8.517k
|
8
|
+
|
9
|
+
# Comparison:
|
10
|
+
# Jellyfish::URLMap: 62397.3 i/s
|
11
|
+
# Rack::URLMap: 1702.0 i/s - 36.66x slower
|
12
|
+
|
13
|
+
require 'jellyfish'
|
14
|
+
require 'rack'
|
15
|
+
|
16
|
+
require 'benchmark/ips'
|
17
|
+
|
18
|
+
num = 1000
|
19
|
+
app = lambda do |_|
|
20
|
+
ok = [200, {}, []]
|
21
|
+
rn = lambda{ |_| ok }
|
22
|
+
|
23
|
+
(0...num).each do |i|
|
24
|
+
map "/#{i}" do
|
25
|
+
run rn
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
jelly = Jellyfish::Builder.app(&app)
|
31
|
+
rack = Rack::Builder.app(&app)
|
32
|
+
path_info = 'PATH_INFO'
|
33
|
+
|
34
|
+
Benchmark.ips do |x|
|
35
|
+
x.report(jelly.class) do
|
36
|
+
jelly.call(path_info => rand(num).to_s)
|
37
|
+
end
|
38
|
+
|
39
|
+
x.report(rack.class) do
|
40
|
+
rack.call(path_info => rand(num).to_s)
|
41
|
+
end
|
42
|
+
|
43
|
+
x.compare!
|
44
|
+
end
|