jellyfish 1.3.0 → 1.4.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/.gitlab-ci.yml +41 -0
- data/.gitmodules +1 -1
- data/CHANGES.md +21 -0
- data/Gemfile +0 -4
- data/README.md +45 -49
- data/config.ru +1 -2
- data/jellyfish.gemspec +5 -5
- data/lib/jellyfish/builder.rb +3 -3
- data/lib/jellyfish/version.rb +1 -1
- data/lib/jellyfish.rb +30 -28
- data/task/README.md +4 -4
- data/task/gemgem.rb +3 -1
- data/test/rack/test_builder.rb +6 -6
- data/test/rack/test_urlmap.rb +86 -86
- data/test/sinatra/test_base.rb +6 -6
- data/test/sinatra/test_routing.rb +3 -3
- data/test/test_from_readme.rb +1 -1
- data/test/test_log.rb +2 -1
- data/test/test_rewrite.rb +14 -0
- metadata +7 -8
- data/.travis.yml +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2ac4c1c5b9beff9266516aa120e637e7c1b3d09010e0f0cf0df7b8728e26646
|
4
|
+
data.tar.gz: 8cd5fff611eea7b8fc454aa8a4d2a844225cf2904d6fcf46dacfc118f9090d84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87587d0f02f1896aa15fb8b72daf2e1cd22b129a89f277680f56ada903778ff2d23c86765273a6199e97c85563e458225a8079bd97e660b4085103b548d99a78
|
7
|
+
data.tar.gz: 35071bfc020d030e952c2023a3bfb7c8cdcf9723194bb8f70212e4cb01b0dc74105a9ccfd93e4c7921f94b37c10049e74e42322cc0532447b87c8e90ceacfd38
|
data/.gitlab-ci.yml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
stages:
|
3
|
+
- test
|
4
|
+
|
5
|
+
.test:
|
6
|
+
stage: test
|
7
|
+
image: ruby:${RUBY_VERSION}-bullseye
|
8
|
+
variables:
|
9
|
+
GIT_DEPTH: "1"
|
10
|
+
GIT_SUBMODULE_STRATEGY: recursive
|
11
|
+
GIT_SUBMODULE_PATHS: task
|
12
|
+
# websocket_parser does not work with frozen string literal
|
13
|
+
# RUBYOPT: --enable-frozen-string-literal
|
14
|
+
before_script:
|
15
|
+
- bundle install --retry=3
|
16
|
+
- unset CI # Coverage doesn't work well with frozen literal
|
17
|
+
script:
|
18
|
+
- ruby -vr bundler/setup -S rake test
|
19
|
+
|
20
|
+
ruby:3.0:
|
21
|
+
extends:
|
22
|
+
- .test
|
23
|
+
variables:
|
24
|
+
RUBY_VERSION: '3.0'
|
25
|
+
|
26
|
+
ruby:3.1:
|
27
|
+
extends:
|
28
|
+
- .test
|
29
|
+
variables:
|
30
|
+
RUBY_VERSION: '3.1'
|
31
|
+
|
32
|
+
ruby:3.2:
|
33
|
+
extends:
|
34
|
+
- .test
|
35
|
+
variables:
|
36
|
+
RUBY_VERSION: '3.2'
|
37
|
+
|
38
|
+
jruby:latest:
|
39
|
+
extends:
|
40
|
+
- .test
|
41
|
+
image: jruby:latest
|
data/.gitmodules
CHANGED
data/CHANGES.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## Jellyfish 1.4.0 -- 2023-02-22
|
4
|
+
|
5
|
+
### Incompatible changes
|
6
|
+
|
7
|
+
* Adopted Rack 3. Technically, just lower the cases for headers.
|
8
|
+
* Internal strings are all frozen now.
|
9
|
+
* `log` and `log_error` now takes the `env` for the second argument,
|
10
|
+
rather than the error stream.
|
11
|
+
* `handle` now takes the `env` for the third argument,
|
12
|
+
rather than the error stream.
|
13
|
+
|
14
|
+
### Enhancements
|
15
|
+
|
16
|
+
* The default error logging will now also show `env['PATH_INFO']`.
|
17
|
+
|
18
|
+
## Jellyfish 1.3.1 -- 2018-11-11
|
19
|
+
|
20
|
+
### Bugs fixed
|
21
|
+
|
22
|
+
* Fixed `Jellyfish::Rewrite` for SCRIPT_NAME when host is also used.
|
23
|
+
|
3
24
|
## Jellyfish 1.3.0 -- 2018-11-11
|
4
25
|
|
5
26
|
### Incompatible changes
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# Jellyfish [](https://gitlab.com/godfat/jellyfish/-/pipelines)
|
2
2
|
|
3
|
-
by Lin Jen-Shin ([godfat](
|
3
|
+
by Lin Jen-Shin ([godfat](https://godfat.org))
|
4
4
|
|
5
5
|

|
6
6
|
|
@@ -8,7 +8,7 @@ by Lin Jen-Shin ([godfat](http://godfat.org))
|
|
8
8
|
|
9
9
|
* [github](https://github.com/godfat/jellyfish)
|
10
10
|
* [rubygems](https://rubygems.org/gems/jellyfish)
|
11
|
-
* [rdoc](
|
11
|
+
* [rdoc](https://rubydoc.info/github/godfat/jellyfish)
|
12
12
|
* [issues](https://github.com/godfat/jellyfish/issues) (feel free to ask for support)
|
13
13
|
|
14
14
|
## DESCRIPTION:
|
@@ -42,7 +42,7 @@ Check [jellyfish-contrib][] for extra extensions.
|
|
42
42
|
* Simple
|
43
43
|
* Modular
|
44
44
|
* No templates (You could use [tilt](https://github.com/rtomayko/tilt))
|
45
|
-
* No ORM (You could use [sequel](
|
45
|
+
* No ORM (You could use [sequel](https://sequel.jeremyevans.net))
|
46
46
|
* No `dup` in `call`
|
47
47
|
* Regular expression routes, e.g. `get %r{^/(?<id>\d+)$}`
|
48
48
|
* String routes, e.g. `get '/'`
|
@@ -56,7 +56,7 @@ Because Sinatra is too complex and inconsistent for me.
|
|
56
56
|
|
57
57
|
## REQUIREMENTS:
|
58
58
|
|
59
|
-
* Tested with MRI (official CRuby)
|
59
|
+
* Tested with MRI (official CRuby) and JRuby.
|
60
60
|
|
61
61
|
## INSTALLATION:
|
62
62
|
|
@@ -84,7 +84,7 @@ run Tank.new
|
|
84
84
|
<!---
|
85
85
|
GET /
|
86
86
|
[200,
|
87
|
-
{'
|
87
|
+
{'content-length' => '12', 'content-type' => 'text/plain'},
|
88
88
|
["Jelly Kelly\n"]]
|
89
89
|
-->
|
90
90
|
|
@@ -106,7 +106,7 @@ run Tank.new
|
|
106
106
|
<!---
|
107
107
|
GET /123
|
108
108
|
[200,
|
109
|
-
{'
|
109
|
+
{'content-length' => '11', 'content-type' => 'text/plain'},
|
110
110
|
["Jelly #123\n"]]
|
111
111
|
-->
|
112
112
|
|
@@ -133,7 +133,7 @@ run Tank.new
|
|
133
133
|
<!---
|
134
134
|
GET /hctam
|
135
135
|
[200,
|
136
|
-
{'
|
136
|
+
{'content-length' => '5', 'content-type' => 'text/plain'},
|
137
137
|
["true\n"]]
|
138
138
|
-->
|
139
139
|
|
@@ -159,7 +159,7 @@ run Tank.new
|
|
159
159
|
<!---
|
160
160
|
POST /
|
161
161
|
[201,
|
162
|
-
{'
|
162
|
+
{'content-length' => '18', 'content-type' => 'text/plain',
|
163
163
|
'X-Jellyfish-Life' => '100', 'X-Jellyfish-Mana' => '200'},
|
164
164
|
["Jellyfish 100/200\n"]]
|
165
165
|
-->
|
@@ -185,8 +185,8 @@ body = File.read("#{File.dirname(
|
|
185
185
|
File.expand_path(__FILE__))}/../lib/jellyfish/public/302.html").
|
186
186
|
gsub('VAR_URL', 'http://host/')
|
187
187
|
[302,
|
188
|
-
{'
|
189
|
-
'
|
188
|
+
{'content-length' => body.bytesize.to_s, 'content-type' => 'text/html',
|
189
|
+
'location' => 'http://host/'},
|
190
190
|
[body]]
|
191
191
|
-->
|
192
192
|
|
@@ -210,7 +210,7 @@ GET /crash
|
|
210
210
|
body = File.read("#{File.dirname(
|
211
211
|
File.expand_path(__FILE__))}/../lib/jellyfish/public/500.html")
|
212
212
|
[500,
|
213
|
-
{'
|
213
|
+
{'content-length' => body.bytesize.to_s, 'content-type' => 'text/html'},
|
214
214
|
[body]]
|
215
215
|
-->
|
216
216
|
|
@@ -244,7 +244,7 @@ body = case RUBY_ENGINE
|
|
244
244
|
"No one hears you: (eval):9:in `block in <class:Tank>'\n"
|
245
245
|
end
|
246
246
|
[403,
|
247
|
-
{'
|
247
|
+
{'content-length' => body.bytesize.to_s, 'content-type' => 'text/plain'},
|
248
248
|
[body]]
|
249
249
|
-->
|
250
250
|
|
@@ -267,7 +267,7 @@ run Tank.new
|
|
267
267
|
<!---
|
268
268
|
GET /
|
269
269
|
[404,
|
270
|
-
{'
|
270
|
+
{'content-length' => '18', 'content-type' => 'text/plain'},
|
271
271
|
["You found nothing."]]
|
272
272
|
-->
|
273
273
|
|
@@ -293,7 +293,7 @@ run Tank.new
|
|
293
293
|
<!---
|
294
294
|
GET /
|
295
295
|
[404,
|
296
|
-
{'
|
296
|
+
{'content-length' => '18', 'content-type' => 'text/plain'},
|
297
297
|
["You found nothing."]]
|
298
298
|
-->
|
299
299
|
|
@@ -315,7 +315,7 @@ run Tank.new
|
|
315
315
|
<!---
|
316
316
|
GET /report?name=godfat
|
317
317
|
[200,
|
318
|
-
{'
|
318
|
+
{'content-length' => '20', 'content-type' => 'text/plain'},
|
319
319
|
["Your name is godfat\n"]]
|
320
320
|
-->
|
321
321
|
|
@@ -341,7 +341,7 @@ run Tank.new
|
|
341
341
|
<!---
|
342
342
|
GET /report
|
343
343
|
[200,
|
344
|
-
{'
|
344
|
+
{'content-length' => '3', 'content-type' => 'text/plain'},
|
345
345
|
["OK\n"]]
|
346
346
|
-->
|
347
347
|
|
@@ -374,7 +374,7 @@ run Heater.new
|
|
374
374
|
<!---
|
375
375
|
GET /status
|
376
376
|
[200,
|
377
|
-
{'
|
377
|
+
{'content-length' => '6', 'content-type' => 'text/plain'},
|
378
378
|
["30\u{2103}\n"]]
|
379
379
|
-->
|
380
380
|
|
@@ -406,7 +406,7 @@ run Heater.new
|
|
406
406
|
<!---
|
407
407
|
GET /status
|
408
408
|
[200,
|
409
|
-
{'
|
409
|
+
{'content-length' => '6', 'content-type' => 'text/plain'},
|
410
410
|
["30\u{2103}\n"]]
|
411
411
|
-->
|
412
412
|
|
@@ -438,7 +438,7 @@ run Tank.new
|
|
438
438
|
<!---
|
439
439
|
GET /123
|
440
440
|
[200,
|
441
|
-
{'
|
441
|
+
{'content-length' => '13', 'content-type' => 'text/plain'},
|
442
442
|
["Jelly jumps.\n"]]
|
443
443
|
-->
|
444
444
|
|
@@ -554,13 +554,13 @@ GET /m/x
|
|
554
554
|
[200, {}, ["m\n"]]
|
555
555
|
|
556
556
|
GET /
|
557
|
-
[200, {'
|
557
|
+
[200, {'content-length' => '2'}, ["/\n"]]
|
558
558
|
|
559
559
|
GET /x
|
560
|
-
[200, {'
|
560
|
+
[200, {'content-length' => '2'}, ["/\n"]]
|
561
561
|
|
562
562
|
GET /ab
|
563
|
-
[200, {'
|
563
|
+
[200, {'content-length' => '2'}, ["/\n"]]
|
564
564
|
-->
|
565
565
|
|
566
566
|
You could try a stupid benchmark yourself:
|
@@ -807,7 +807,7 @@ run Tank.new
|
|
807
807
|
<!---
|
808
808
|
GET /123
|
809
809
|
[200,
|
810
|
-
{'
|
810
|
+
{'content-length' => '11', 'content-type' => 'text/plain'},
|
811
811
|
["Jelly #123\n"]]
|
812
812
|
-->
|
813
813
|
|
@@ -831,7 +831,7 @@ run Tank.new
|
|
831
831
|
<!---
|
832
832
|
GET /%E5%9B%A7
|
833
833
|
[200,
|
834
|
-
{'
|
834
|
+
{'content-length' => '16', 'content-type' => 'text/plain'},
|
835
835
|
["/%E5%9B%A7=/\u{56e7}\n"]]
|
836
836
|
-->
|
837
837
|
|
@@ -862,7 +862,7 @@ run Tank.new
|
|
862
862
|
<!---
|
863
863
|
GET /123
|
864
864
|
[200,
|
865
|
-
{'
|
865
|
+
{'content-length' => '18', 'content-type' => 'text/plain'},
|
866
866
|
["Jelly #123 jumps.\n"]]
|
867
867
|
-->
|
868
868
|
|
@@ -896,7 +896,7 @@ run Tank.new
|
|
896
896
|
<!---
|
897
897
|
GET /
|
898
898
|
[200,
|
899
|
-
{'
|
899
|
+
{'content-length' => '12', 'content-type' => 'text/plain'},
|
900
900
|
["Jelly Kelly\n"]]
|
901
901
|
-->
|
902
902
|
|
@@ -934,7 +934,7 @@ run Tank.new
|
|
934
934
|
<!---
|
935
935
|
GET /status
|
936
936
|
[200,
|
937
|
-
{'
|
937
|
+
{'content-length' => '25', 'content-type' => 'text/plain',
|
938
938
|
'X-Temperature' => "30\u{2103}"},
|
939
939
|
["See header X-Temperature\n"]]
|
940
940
|
-->
|
@@ -980,7 +980,7 @@ run Tank.new
|
|
980
980
|
<!---
|
981
981
|
GET /status
|
982
982
|
[200,
|
983
|
-
{'
|
983
|
+
{'content-length' => '1', 'content-type' => 'text/plain',
|
984
984
|
'X-Temperature' => "35\u{2103}"},
|
985
985
|
["\n"]]
|
986
986
|
-->
|
@@ -1013,7 +1013,7 @@ run Tank.new
|
|
1013
1013
|
<!---
|
1014
1014
|
GET /status
|
1015
1015
|
[200,
|
1016
|
-
{'
|
1016
|
+
{'content-length' => '6', 'content-type' => 'text/plain'},
|
1017
1017
|
["30\u{2103}\n"]]
|
1018
1018
|
-->
|
1019
1019
|
|
@@ -1048,7 +1048,7 @@ run HugeTank
|
|
1048
1048
|
<!---
|
1049
1049
|
GET /status
|
1050
1050
|
[200,
|
1051
|
-
{'
|
1051
|
+
{'content-length' => '6', 'content-type' => 'text/plain'},
|
1052
1052
|
["30\u{2103}\n"]]
|
1053
1053
|
-->
|
1054
1054
|
|
@@ -1081,7 +1081,7 @@ run Tank.new
|
|
1081
1081
|
<!---
|
1082
1082
|
GET /
|
1083
1083
|
[200,
|
1084
|
-
{'
|
1084
|
+
{'content-length' => '29', 'content-type' => 'text/plain'},
|
1085
1085
|
["Protected: Oops, tank broken\n"]]
|
1086
1086
|
-->
|
1087
1087
|
|
@@ -1099,7 +1099,6 @@ class Tank
|
|
1099
1099
|
}
|
1100
1100
|
end
|
1101
1101
|
end
|
1102
|
-
use Rack::Chunked
|
1103
1102
|
use Rack::ContentType, 'text/plain'
|
1104
1103
|
run Tank.new
|
1105
1104
|
```
|
@@ -1107,9 +1106,8 @@ run Tank.new
|
|
1107
1106
|
<!---
|
1108
1107
|
GET /chunked
|
1109
1108
|
[200,
|
1110
|
-
{'
|
1111
|
-
["
|
1112
|
-
"2\r\n3\n\r\n", "2\r\n4\n\r\n", "0\r\n\r\n"]]
|
1109
|
+
{'content-type' => 'text/plain'},
|
1110
|
+
["0\n", "1\n", "2\n", "3\n", "4\n"]]
|
1113
1111
|
-->
|
1114
1112
|
|
1115
1113
|
### Chunked transfer encoding (streaming) with custom body
|
@@ -1126,7 +1124,6 @@ class Tank
|
|
1126
1124
|
Body.new
|
1127
1125
|
end
|
1128
1126
|
end
|
1129
|
-
use Rack::Chunked
|
1130
1127
|
use Rack::ContentType, 'text/plain'
|
1131
1128
|
run Tank.new
|
1132
1129
|
```
|
@@ -1134,9 +1131,8 @@ run Tank.new
|
|
1134
1131
|
<!---
|
1135
1132
|
GET /chunked
|
1136
1133
|
[200,
|
1137
|
-
{'
|
1138
|
-
["
|
1139
|
-
"2\r\n3\n\r\n", "2\r\n4\n\r\n", "0\r\n\r\n"]]
|
1134
|
+
{'content-type' => 'text/plain'},
|
1135
|
+
["0\n", "1\n", "2\n", "3\n", "4\n"]]
|
1140
1136
|
-->
|
1141
1137
|
|
1142
1138
|
### Server Sent Event (SSE)
|
@@ -1150,7 +1146,7 @@ class Tank
|
|
1150
1146
|
end
|
1151
1147
|
end
|
1152
1148
|
get '/sse' do
|
1153
|
-
headers_merge('
|
1149
|
+
headers_merge('content-type' => 'text/event-stream')
|
1154
1150
|
Body.new
|
1155
1151
|
end
|
1156
1152
|
end
|
@@ -1160,7 +1156,7 @@ run Tank.new
|
|
1160
1156
|
<!---
|
1161
1157
|
GET /sse
|
1162
1158
|
[200,
|
1163
|
-
{'
|
1159
|
+
{'content-type' => 'text/event-stream'},
|
1164
1160
|
["data: 0\n\n", "data: 1\n\n", "data: 2\n\n", "data: 3\n\n", "data: 4\n\n"]]
|
1165
1161
|
-->
|
1166
1162
|
|
@@ -1171,7 +1167,7 @@ class Tank
|
|
1171
1167
|
include Jellyfish
|
1172
1168
|
get '/sse' do
|
1173
1169
|
headers_merge(
|
1174
|
-
'
|
1170
|
+
'content-type' => 'text/event-stream',
|
1175
1171
|
'rack.hijack' => lambda do |sock|
|
1176
1172
|
(0..4).each do |i|
|
1177
1173
|
sock.write("data: #{i}\n\n")
|
@@ -1186,7 +1182,7 @@ run Tank.new
|
|
1186
1182
|
<!---
|
1187
1183
|
GET /sse
|
1188
1184
|
[200,
|
1189
|
-
{'
|
1185
|
+
{'content-type' => 'text/event-stream'},
|
1190
1186
|
["data: 0\n\n", "data: 1\n\n", "data: 2\n\n", "data: 3\n\n", "data: 4\n\n"]]
|
1191
1187
|
-->
|
1192
1188
|
|
@@ -1201,9 +1197,9 @@ EventMachine is basically dead, we could see if there would be a
|
|
1201
1197
|
[Celluloid-IO][] based web server production ready in the future,
|
1202
1198
|
so that we could take the advantage of event based approach.
|
1203
1199
|
|
1204
|
-
[hijack]:
|
1205
|
-
[Rainbows!]:
|
1206
|
-
[Puma]:
|
1200
|
+
[hijack]: https://github.com/rack/rack/blob/main/SPEC.rdoc#label-Hijacking
|
1201
|
+
[Rainbows!]: https://yhbt.net/rainbows/
|
1202
|
+
[Puma]: https://puma.io
|
1207
1203
|
[Celluloid-IO]: https://github.com/celluloid/celluloid-io
|
1208
1204
|
|
1209
1205
|
``` ruby
|
@@ -1244,13 +1240,13 @@ HTTP
|
|
1244
1240
|
|
1245
1241
|
Apache License 2.0 (Apache-2.0)
|
1246
1242
|
|
1247
|
-
Copyright (c) 2012-
|
1243
|
+
Copyright (c) 2012-2023, Lin Jen-Shin (godfat)
|
1248
1244
|
|
1249
1245
|
Licensed under the Apache License, Version 2.0 (the "License");
|
1250
1246
|
you may not use this file except in compliance with the License.
|
1251
1247
|
You may obtain a copy of the License at
|
1252
1248
|
|
1253
|
-
<
|
1249
|
+
<https://www.apache.org/licenses/LICENSE-2.0>
|
1254
1250
|
|
1255
1251
|
Unless required by applicable law or agreed to in writing, software
|
1256
1252
|
distributed under the License is distributed on an "AS IS" BASIS,
|
data/config.ru
CHANGED
@@ -7,7 +7,7 @@ class Jelly
|
|
7
7
|
|
8
8
|
controller_include Module.new{
|
9
9
|
def dispatch
|
10
|
-
headers_merge '
|
10
|
+
headers_merge 'content-type' => 'application/json; charset=utf-8',
|
11
11
|
'Access-Control-Allow-Origin' => '*'
|
12
12
|
super
|
13
13
|
end
|
@@ -59,7 +59,6 @@ end
|
|
59
59
|
|
60
60
|
App = Jellyfish::Builder.app do
|
61
61
|
use Rack::CommonLogger
|
62
|
-
use Rack::Chunked
|
63
62
|
use Rack::ContentLength
|
64
63
|
use Rack::Deflater
|
65
64
|
|
data/jellyfish.gemspec
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# stub: jellyfish 1.
|
2
|
+
# stub: jellyfish 1.4.0 ruby lib
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "jellyfish".freeze
|
6
|
-
s.version = "1.
|
6
|
+
s.version = "1.4.0"
|
7
7
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
9
9
|
s.require_paths = ["lib".freeze]
|
10
10
|
s.authors = ["Lin Jen-Shin (godfat)".freeze]
|
11
|
-
s.date = "
|
11
|
+
s.date = "2023-02-22"
|
12
12
|
s.description = "Pico web framework for building API-centric web applications.\nFor Rack applications or Rack middleware. Around 250 lines of code.\n\nCheck [jellyfish-contrib][] for extra extensions.\n\n[jellyfish-contrib]: https://github.com/godfat/jellyfish-contrib".freeze
|
13
13
|
s.email = ["godfat (XD) godfat.org".freeze]
|
14
14
|
s.files = [
|
15
15
|
".gitignore".freeze,
|
16
|
+
".gitlab-ci.yml".freeze,
|
16
17
|
".gitmodules".freeze,
|
17
|
-
".travis.yml".freeze,
|
18
18
|
"CHANGES.md".freeze,
|
19
19
|
"Gemfile".freeze,
|
20
20
|
"LICENSE".freeze,
|
@@ -56,7 +56,7 @@ Gem::Specification.new do |s|
|
|
56
56
|
"test/test_websocket.rb".freeze]
|
57
57
|
s.homepage = "https://github.com/godfat/jellyfish".freeze
|
58
58
|
s.licenses = ["Apache-2.0".freeze]
|
59
|
-
s.rubygems_version = "
|
59
|
+
s.rubygems_version = "3.4.3".freeze
|
60
60
|
s.summary = "Pico web framework for building API-centric web applications.".freeze
|
61
61
|
s.test_files = [
|
62
62
|
"test/rack/test_builder.rb".freeze,
|
data/lib/jellyfish/builder.rb
CHANGED
@@ -31,7 +31,7 @@ module Jellyfish
|
|
31
31
|
|
32
32
|
def map path, to: nil, host: nil, &block
|
33
33
|
key = if host then "http://#{File.join(host, path)}" else path end
|
34
|
-
(@map ||= {})[key] = [block, to]
|
34
|
+
(@map ||= {})[key] = [block, path, to]
|
35
35
|
end
|
36
36
|
|
37
37
|
def listen host, &block
|
@@ -56,8 +56,8 @@ module Jellyfish
|
|
56
56
|
private
|
57
57
|
def generate_map current_map, app
|
58
58
|
mapped = if app then {'' => app} else {} end
|
59
|
-
current_map.each do |path, (block, to)|
|
60
|
-
mapped[path] = self.class.app(app,
|
59
|
+
current_map.each do |path, (block, from, to)|
|
60
|
+
mapped[path] = self.class.app(app, from, to, &block)
|
61
61
|
end
|
62
62
|
URLMap.new(mapped)
|
63
63
|
end
|
data/lib/jellyfish/version.rb
CHANGED
data/lib/jellyfish.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Jellyfish
|
3
4
|
autoload :VERSION , 'jellyfish/version'
|
@@ -15,7 +16,7 @@ module Jellyfish
|
|
15
16
|
|
16
17
|
class Response < RuntimeError
|
17
18
|
def headers
|
18
|
-
@headers ||= {'
|
19
|
+
@headers ||= {'content-type' => 'text/html'}
|
19
20
|
end
|
20
21
|
|
21
22
|
def body
|
@@ -29,7 +30,7 @@ module Jellyfish
|
|
29
30
|
attr_reader :url
|
30
31
|
def initialize url; @url = url ; end
|
31
32
|
def status ; 302 ; end
|
32
|
-
def headers ; super.merge('
|
33
|
+
def headers ; super.merge('location' => url) ; end
|
33
34
|
def body ; super.map{ |b| b.gsub('VAR_URL', url) }; end
|
34
35
|
end
|
35
36
|
|
@@ -55,8 +56,8 @@ module Jellyfish
|
|
55
56
|
raise
|
56
57
|
end
|
57
58
|
|
58
|
-
def log message; jellyfish.log( message, env
|
59
|
-
def log_error error; jellyfish.log_error(error, env
|
59
|
+
def log message; jellyfish.log( message, env); end
|
60
|
+
def log_error error; jellyfish.log_error(error, env); end
|
60
61
|
def request ; @request ||= Rack::Request.new(env); end
|
61
62
|
def halt *args; throw(:halt, *args) ; end
|
62
63
|
def cascade ; halt(Jellyfish::Cascade) ; end
|
@@ -190,7 +191,7 @@ module Jellyfish
|
|
190
191
|
when Cascade
|
191
192
|
cascade(ctrl, env)
|
192
193
|
when Response
|
193
|
-
handle(ctrl, res, env
|
194
|
+
handle(ctrl, res, env)
|
194
195
|
when Array
|
195
196
|
res
|
196
197
|
when NilClass # make sure we return rack triple
|
@@ -199,47 +200,48 @@ module Jellyfish
|
|
199
200
|
raise TypeError.new("Expect the response to be a Jellyfish::Response" \
|
200
201
|
" or Rack triple (Array), but got: #{res.inspect}")
|
201
202
|
end
|
202
|
-
rescue =>
|
203
|
-
handle(ctrl,
|
203
|
+
rescue => error
|
204
|
+
handle(ctrl, error, env)
|
204
205
|
end
|
205
206
|
|
206
|
-
def log_error
|
207
|
-
|
208
|
-
|
207
|
+
def log_error error, env
|
208
|
+
env['rack.errors']&.
|
209
|
+
puts("[#{self.class.name}] #{error.inspect}" \
|
210
|
+
" for #{env['PATH_INFO'] || '/'} #{error.backtrace}")
|
209
211
|
end
|
210
212
|
|
211
|
-
def log msg,
|
212
|
-
|
213
|
-
|
213
|
+
def log msg, env
|
214
|
+
env['rack.errors']&.
|
215
|
+
puts("[#{self.class.name}] #{msg}")
|
214
216
|
end
|
215
217
|
|
216
218
|
private
|
217
219
|
def cascade ctrl, env
|
218
220
|
app.call(env)
|
219
|
-
rescue =>
|
220
|
-
handle(ctrl,
|
221
|
+
rescue => error
|
222
|
+
handle(ctrl, error, env)
|
221
223
|
end
|
222
224
|
|
223
|
-
def handle ctrl,
|
224
|
-
if handler = best_handler(
|
225
|
-
ctrl.block_call(
|
225
|
+
def handle ctrl, error, env
|
226
|
+
if handler = best_handler(error)
|
227
|
+
ctrl.block_call(error, handler)
|
226
228
|
elsif !self.class.handle_exceptions
|
227
|
-
raise
|
228
|
-
elsif
|
229
|
-
[
|
229
|
+
raise error
|
230
|
+
elsif error.kind_of?(Response) # InternalError ends up here if no handlers
|
231
|
+
[error.status, error.headers, error.body]
|
230
232
|
else # fallback and see if there's any InternalError handler
|
231
|
-
log_error(
|
232
|
-
handle(ctrl, InternalError.new)
|
233
|
+
log_error(error, env)
|
234
|
+
handle(ctrl, InternalError.new, env)
|
233
235
|
end
|
234
236
|
end
|
235
237
|
|
236
|
-
def best_handler
|
238
|
+
def best_handler error
|
237
239
|
handlers = self.class.handlers
|
238
|
-
if handlers.key?(
|
239
|
-
handlers[
|
240
|
+
if handlers.key?(error.class)
|
241
|
+
handlers[error.class]
|
240
242
|
else # or find the nearest match and cache it
|
241
|
-
ancestors
|
242
|
-
handlers[
|
243
|
+
ancestors = error.class.ancestors
|
244
|
+
handlers[error.class] = handlers.dup. # thread safe iteration
|
243
245
|
inject([nil, Float::INFINITY]){ |(handler, val), (klass, block)|
|
244
246
|
idx = ancestors.index(klass) || Float::INFINITY # lower is better
|
245
247
|
if idx < val then [block, idx] else [handler, val] end }.first
|
data/task/README.md
CHANGED
@@ -13,11 +13,11 @@ Provided tasks:
|
|
13
13
|
|
14
14
|
## REQUIREMENTS:
|
15
15
|
|
16
|
-
* Tested with MRI (official CRuby)
|
16
|
+
* Tested with MRI (official CRuby) and JRuby.
|
17
17
|
|
18
18
|
## INSTALLATION:
|
19
19
|
|
20
|
-
git submodule add
|
20
|
+
git submodule add https://github.com/godfat/gemgem.git task
|
21
21
|
|
22
22
|
And in Rakefile:
|
23
23
|
|
@@ -39,13 +39,13 @@ end
|
|
39
39
|
|
40
40
|
Apache License 2.0 (Apache-2.0)
|
41
41
|
|
42
|
-
Copyright (c) 2011-
|
42
|
+
Copyright (c) 2011-2023, Lin Jen-Shin (godfat)
|
43
43
|
|
44
44
|
Licensed under the Apache License, Version 2.0 (the "License");
|
45
45
|
you may not use this file except in compliance with the License.
|
46
46
|
You may obtain a copy of the License at
|
47
47
|
|
48
|
-
<
|
48
|
+
<https://www.apache.org/licenses/LICENSE-2.0>
|
49
49
|
|
50
50
|
Unless required by applicable law or agreed to in writing, software
|
51
51
|
distributed under the License is distributed on an "AS IS" BASIS,
|
data/task/gemgem.rb
CHANGED
@@ -42,6 +42,7 @@ module Gemgem
|
|
42
42
|
|
43
43
|
def gem_install
|
44
44
|
require 'rubygems/commands/install_command'
|
45
|
+
require 'rubygems/package'
|
45
46
|
# read ~/.gemrc
|
46
47
|
Gem.use_paths(Gem.configuration[:gemhome], Gem.configuration[:gempath])
|
47
48
|
Gem::Command.extra_args = Gem.configuration[:gem]
|
@@ -51,7 +52,8 @@ module Gemgem
|
|
51
52
|
cmd.handle_options([])
|
52
53
|
|
53
54
|
# install
|
54
|
-
|
55
|
+
gem_package = Gem::Package.new(gem_path)
|
56
|
+
install = Gem::Installer.new(gem_package, cmd.options)
|
55
57
|
install.install
|
56
58
|
puts "\e[35mGem installed: \e[33m#{strip_path(install.gem_dir)}\e[0m"
|
57
59
|
end
|
data/test/rack/test_builder.rb
CHANGED
@@ -28,10 +28,10 @@ describe Jellyfish::Builder do
|
|
28
28
|
would "supports mapping" do
|
29
29
|
app = builder_to_app do
|
30
30
|
map '/' do |outer_env|
|
31
|
-
run lambda { |inner_env| [200, {"
|
31
|
+
run lambda { |inner_env| [200, {"content-type" => "text/plain"}, ['root']] }
|
32
32
|
end
|
33
33
|
map '/sub' do
|
34
|
-
run lambda { |inner_env| [200, {"
|
34
|
+
run lambda { |inner_env| [200, {"content-type" => "text/plain"}, ['sub']] }
|
35
35
|
end
|
36
36
|
end
|
37
37
|
Rack::MockRequest.new(app).get("/").body.to_s.should.eq 'root'
|
@@ -56,7 +56,7 @@ describe Jellyfish::Builder do
|
|
56
56
|
'secret' == password
|
57
57
|
end
|
58
58
|
|
59
|
-
run lambda { |env| [200, {"
|
59
|
+
run lambda { |env| [200, {"content-type" => "text/plain"}, ['Hi Boss']] }
|
60
60
|
end
|
61
61
|
|
62
62
|
response = Rack::MockRequest.new(app).get("/")
|
@@ -84,9 +84,9 @@ describe Jellyfish::Builder do
|
|
84
84
|
would "can mix map and run for endpoints" do
|
85
85
|
app = builder_to_app do
|
86
86
|
map '/sub' do
|
87
|
-
run lambda { |inner_env| [200, {"
|
87
|
+
run lambda { |inner_env| [200, {"content-type" => "text/plain"}, ['sub']] }
|
88
88
|
end
|
89
|
-
run lambda { |inner_env| [200, {"
|
89
|
+
run lambda { |inner_env| [200, {"content-type" => "text/plain"}, ['root']] }
|
90
90
|
end
|
91
91
|
|
92
92
|
Rack::MockRequest.new(app).get("/").body.to_s.should.eq 'root'
|
@@ -123,7 +123,7 @@ describe Jellyfish::Builder do
|
|
123
123
|
def call(env)
|
124
124
|
raise "bzzzt" if @called > 0
|
125
125
|
@called += 1
|
126
|
-
[200, {'
|
126
|
+
[200, {'content-type' => 'text/plain'}, ['OK']]
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
data/test/rack/test_urlmap.rb
CHANGED
@@ -8,9 +8,9 @@ describe Jellyfish::URLMap do
|
|
8
8
|
would "dispatches paths correctly" do
|
9
9
|
app = lambda { |env|
|
10
10
|
[200, {
|
11
|
-
'
|
12
|
-
'
|
13
|
-
'
|
11
|
+
'x-scriptname' => env['SCRIPT_NAME'],
|
12
|
+
'x-pathinfo' => env['PATH_INFO'],
|
13
|
+
'content-type' => 'text/plain'
|
14
14
|
}, [""]]
|
15
15
|
}
|
16
16
|
map = Rack::Lint.new(Jellyfish::URLMap.new({
|
@@ -27,102 +27,102 @@ describe Jellyfish::URLMap do
|
|
27
27
|
|
28
28
|
res = Rack::MockRequest.new(map).get("/foo")
|
29
29
|
res.should.ok?
|
30
|
-
res["
|
31
|
-
res["
|
30
|
+
res["x-scriptname"].should.eq "/foo"
|
31
|
+
res["x-pathinfo"].should.eq ""
|
32
32
|
|
33
33
|
res = Rack::MockRequest.new(map).get("/foo/")
|
34
34
|
res.should.ok?
|
35
|
-
res["
|
36
|
-
res["
|
35
|
+
res["x-scriptname"].should.eq "/foo"
|
36
|
+
res["x-pathinfo"].should.eq "/"
|
37
37
|
|
38
38
|
res = Rack::MockRequest.new(map).get("/foo/bar")
|
39
39
|
res.should.ok?
|
40
|
-
res["
|
41
|
-
res["
|
40
|
+
res["x-scriptname"].should.eq "/foo/bar"
|
41
|
+
res["x-pathinfo"].should.eq ""
|
42
42
|
|
43
43
|
res = Rack::MockRequest.new(map).get("/foo/bar/")
|
44
44
|
res.should.ok?
|
45
|
-
res["
|
46
|
-
res["
|
45
|
+
res["x-scriptname"].should.eq "/foo/bar"
|
46
|
+
res["x-pathinfo"].should.eq "/"
|
47
47
|
|
48
48
|
res = Rack::MockRequest.new(map).get("/foo///bar//quux")
|
49
49
|
res.status.should.eq 200
|
50
50
|
res.should.ok?
|
51
|
-
res["
|
52
|
-
res["
|
51
|
+
res["x-scriptname"].should.eq "/foo/bar"
|
52
|
+
res["x-pathinfo"].should.eq "//quux"
|
53
53
|
|
54
54
|
res = Rack::MockRequest.new(map).get("/foo/quux", "SCRIPT_NAME" => "/bleh")
|
55
55
|
res.should.ok?
|
56
|
-
res["
|
57
|
-
res["
|
56
|
+
res["x-scriptname"].should.eq "/bleh/foo"
|
57
|
+
res["x-pathinfo"].should.eq "/quux"
|
58
58
|
|
59
59
|
res = Rack::MockRequest.new(map).get("/bar", 'HTTP_HOST' => 'foo.org')
|
60
60
|
res.should.ok?
|
61
|
-
res["
|
62
|
-
res["
|
61
|
+
res["x-scriptname"].should.eq "/bar"
|
62
|
+
res["x-pathinfo"].should.empty?
|
63
63
|
|
64
64
|
res = Rack::MockRequest.new(map).get("/bar/", 'HTTP_HOST' => 'foo.org')
|
65
65
|
res.should.ok?
|
66
|
-
res["
|
67
|
-
res["
|
66
|
+
res["x-scriptname"].should.eq "/bar"
|
67
|
+
res["x-pathinfo"].should.eq '/'
|
68
68
|
end
|
69
69
|
|
70
70
|
would "dispatches hosts correctly" do
|
71
71
|
map = Rack::Lint.new(Jellyfish::URLMap.new("http://foo.org/" => lambda { |env|
|
72
72
|
[200,
|
73
|
-
{ "
|
74
|
-
"
|
75
|
-
"
|
73
|
+
{ "content-type" => "text/plain",
|
74
|
+
"x-position" => "foo.org",
|
75
|
+
"x-host" => env["HTTP_HOST"] || env["SERVER_NAME"],
|
76
76
|
}, [""]]},
|
77
77
|
"http://subdomain.foo.org/" => lambda { |env|
|
78
78
|
[200,
|
79
|
-
{ "
|
80
|
-
"
|
81
|
-
"
|
79
|
+
{ "content-type" => "text/plain",
|
80
|
+
"x-position" => "subdomain.foo.org",
|
81
|
+
"x-host" => env["HTTP_HOST"] || env["SERVER_NAME"],
|
82
82
|
}, [""]]},
|
83
83
|
"http://bar.org/" => lambda { |env|
|
84
84
|
[200,
|
85
|
-
{ "
|
86
|
-
"
|
87
|
-
"
|
85
|
+
{ "content-type" => "text/plain",
|
86
|
+
"x-position" => "bar.org",
|
87
|
+
"x-host" => env["HTTP_HOST"] || env["SERVER_NAME"],
|
88
88
|
}, [""]]},
|
89
89
|
"/" => lambda { |env|
|
90
90
|
[200,
|
91
|
-
{ "
|
92
|
-
"
|
93
|
-
"
|
91
|
+
{ "content-type" => "text/plain",
|
92
|
+
"x-position" => "default.org",
|
93
|
+
"x-host" => env["HTTP_HOST"] || env["SERVER_NAME"],
|
94
94
|
}, [""]]}
|
95
95
|
))
|
96
96
|
|
97
97
|
res = Rack::MockRequest.new(map).get("/")
|
98
98
|
res.should.ok?
|
99
|
-
res["
|
99
|
+
res["x-position"].should.eq "default.org"
|
100
100
|
|
101
101
|
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "bar.org")
|
102
102
|
res.should.ok?
|
103
|
-
res["
|
103
|
+
res["x-position"].should.eq "bar.org"
|
104
104
|
|
105
105
|
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "foo.org")
|
106
106
|
res.should.ok?
|
107
|
-
res["
|
107
|
+
res["x-position"].should.eq "foo.org"
|
108
108
|
|
109
109
|
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "subdomain.foo.org", "SERVER_NAME" => "foo.org")
|
110
110
|
res.should.ok?
|
111
|
-
res["
|
111
|
+
res["x-position"].should.eq "subdomain.foo.org"
|
112
112
|
|
113
113
|
res = Rack::MockRequest.new(map).get("http://foo.org/")
|
114
114
|
res.should.ok?
|
115
|
-
res["
|
115
|
+
res["x-position"].should.eq "foo.org"
|
116
116
|
|
117
117
|
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "example.org")
|
118
118
|
res.should.ok?
|
119
|
-
res["
|
119
|
+
res["x-position"].should.eq "default.org"
|
120
120
|
|
121
121
|
res = Rack::MockRequest.new(map).get("/",
|
122
122
|
"HTTP_HOST" => "example.org:9292",
|
123
123
|
"SERVER_PORT" => "9292")
|
124
124
|
res.should.ok?
|
125
|
-
res["
|
125
|
+
res["x-position"].should.eq "default.org"
|
126
126
|
end
|
127
127
|
|
128
128
|
would "be nestable" do
|
@@ -130,10 +130,10 @@ describe Jellyfish::URLMap do
|
|
130
130
|
Jellyfish::URLMap.new("/bar" =>
|
131
131
|
Jellyfish::URLMap.new("/quux" => lambda { |env|
|
132
132
|
[200,
|
133
|
-
{ "
|
134
|
-
"
|
135
|
-
"
|
136
|
-
"
|
133
|
+
{ "content-type" => "text/plain",
|
134
|
+
"x-position" => "/foo/bar/quux",
|
135
|
+
"x-pathinfo" => env["PATH_INFO"],
|
136
|
+
"x-scriptname" => env["SCRIPT_NAME"],
|
137
137
|
}, [""]]}
|
138
138
|
))))
|
139
139
|
|
@@ -142,97 +142,97 @@ describe Jellyfish::URLMap do
|
|
142
142
|
|
143
143
|
res = Rack::MockRequest.new(map).get("/foo/bar/quux")
|
144
144
|
res.should.ok?
|
145
|
-
res["
|
146
|
-
res["
|
147
|
-
res["
|
145
|
+
res["x-position"].should.eq "/foo/bar/quux"
|
146
|
+
res["x-pathinfo"].should.eq ""
|
147
|
+
res["x-scriptname"].should.eq "/foo/bar/quux"
|
148
148
|
end
|
149
149
|
|
150
150
|
would "route root apps correctly" do
|
151
151
|
map = Rack::Lint.new(Jellyfish::URLMap.new("/" => lambda { |env|
|
152
152
|
[200,
|
153
|
-
{ "
|
154
|
-
"
|
155
|
-
"
|
156
|
-
"
|
153
|
+
{ "content-type" => "text/plain",
|
154
|
+
"x-position" => "root",
|
155
|
+
"x-pathinfo" => env["PATH_INFO"],
|
156
|
+
"x-scriptname" => env["SCRIPT_NAME"]
|
157
157
|
}, [""]]},
|
158
158
|
"/foo" => lambda { |env|
|
159
159
|
[200,
|
160
|
-
{ "
|
161
|
-
"
|
162
|
-
"
|
163
|
-
"
|
160
|
+
{ "content-type" => "text/plain",
|
161
|
+
"x-position" => "foo",
|
162
|
+
"x-pathinfo" => env["PATH_INFO"],
|
163
|
+
"x-scriptname" => env["SCRIPT_NAME"]
|
164
164
|
}, [""]]}
|
165
165
|
))
|
166
166
|
|
167
167
|
res = Rack::MockRequest.new(map).get("/foo/bar")
|
168
168
|
res.should.ok?
|
169
|
-
res["
|
170
|
-
res["
|
171
|
-
res["
|
169
|
+
res["x-position"].should.eq "foo"
|
170
|
+
res["x-pathinfo"].should.eq "/bar"
|
171
|
+
res["x-scriptname"].should.eq "/foo"
|
172
172
|
|
173
173
|
res = Rack::MockRequest.new(map).get("/foo")
|
174
174
|
res.should.ok?
|
175
|
-
res["
|
176
|
-
res["
|
177
|
-
res["
|
175
|
+
res["x-position"].should.eq "foo"
|
176
|
+
res["x-pathinfo"].should.eq ""
|
177
|
+
res["x-scriptname"].should.eq "/foo"
|
178
178
|
|
179
179
|
res = Rack::MockRequest.new(map).get("/bar")
|
180
180
|
res.should.ok?
|
181
|
-
res["
|
182
|
-
res["
|
183
|
-
res["
|
181
|
+
res["x-position"].should.eq "root"
|
182
|
+
res["x-pathinfo"].should.eq "/bar"
|
183
|
+
res["x-scriptname"].should.eq ""
|
184
184
|
|
185
185
|
res = Rack::MockRequest.new(map).get("")
|
186
186
|
res.should.ok?
|
187
|
-
res["
|
188
|
-
res["
|
189
|
-
res["
|
187
|
+
res["x-position"].should.eq "root"
|
188
|
+
res["x-pathinfo"].should.eq "/"
|
189
|
+
res["x-scriptname"].should.eq ""
|
190
190
|
end
|
191
191
|
|
192
192
|
would "not squeeze slashes" do
|
193
193
|
map = Rack::Lint.new(Jellyfish::URLMap.new("/" => lambda { |env|
|
194
194
|
[200,
|
195
|
-
{ "
|
196
|
-
"
|
197
|
-
"
|
198
|
-
"
|
195
|
+
{ "content-type" => "text/plain",
|
196
|
+
"x-position" => "root",
|
197
|
+
"x-pathinfo" => env["PATH_INFO"],
|
198
|
+
"x-scriptname" => env["SCRIPT_NAME"]
|
199
199
|
}, [""]]},
|
200
200
|
"/foo" => lambda { |env|
|
201
201
|
[200,
|
202
|
-
{ "
|
203
|
-
"
|
204
|
-
"
|
205
|
-
"
|
202
|
+
{ "content-type" => "text/plain",
|
203
|
+
"x-position" => "foo",
|
204
|
+
"x-pathinfo" => env["PATH_INFO"],
|
205
|
+
"x-scriptname" => env["SCRIPT_NAME"]
|
206
206
|
}, [""]]}
|
207
207
|
))
|
208
208
|
|
209
209
|
res = Rack::MockRequest.new(map).get("/http://example.org/bar")
|
210
210
|
res.should.ok?
|
211
|
-
res["
|
212
|
-
res["
|
213
|
-
res["
|
211
|
+
res["x-position"].should.eq "root"
|
212
|
+
res["x-pathinfo"].should.eq "/http://example.org/bar"
|
213
|
+
res["x-scriptname"].should.eq ""
|
214
214
|
end
|
215
215
|
|
216
216
|
would "not be case sensitive with hosts" do
|
217
217
|
map = Rack::Lint.new(Jellyfish::URLMap.new("http://example.org/" => lambda { |env|
|
218
218
|
[200,
|
219
|
-
{ "
|
220
|
-
"
|
221
|
-
"
|
222
|
-
"
|
219
|
+
{ "content-type" => "text/plain",
|
220
|
+
"x-position" => "root",
|
221
|
+
"x-pathinfo" => env["PATH_INFO"],
|
222
|
+
"x-scriptname" => env["SCRIPT_NAME"]
|
223
223
|
}, [""]]}
|
224
224
|
))
|
225
225
|
|
226
226
|
res = Rack::MockRequest.new(map).get("http://example.org/")
|
227
227
|
res.should.ok?
|
228
|
-
res["
|
229
|
-
res["
|
230
|
-
res["
|
228
|
+
res["x-position"].should.eq "root"
|
229
|
+
res["x-pathinfo"].should.eq "/"
|
230
|
+
res["x-scriptname"].should.eq ""
|
231
231
|
|
232
232
|
res = Rack::MockRequest.new(map).get("http://EXAMPLE.ORG/")
|
233
233
|
res.should.ok?
|
234
|
-
res["
|
235
|
-
res["
|
236
|
-
res["
|
234
|
+
res["x-position"].should.eq "root"
|
235
|
+
res["x-pathinfo"].should.eq "/"
|
236
|
+
res["x-scriptname"].should.eq ""
|
237
237
|
end
|
238
238
|
end
|
data/test/sinatra/test_base.rb
CHANGED
@@ -38,7 +38,7 @@ describe 'Sinatra base_test.rb' do
|
|
38
38
|
|
39
39
|
describe 'Jellyfish as a Rack middleware' do
|
40
40
|
inner_app ||= lambda{ |env|
|
41
|
-
[210, {'
|
41
|
+
[210, {'x-downstream' => 'true'}, ['Hello from downstream']]
|
42
42
|
}
|
43
43
|
|
44
44
|
app = Class.new{
|
@@ -55,7 +55,7 @@ describe 'Sinatra base_test.rb' do
|
|
55
55
|
end
|
56
56
|
|
57
57
|
get '/explicit-forward' do
|
58
|
-
headers_merge '
|
58
|
+
headers_merge 'x-middleware' => 'true'
|
59
59
|
status, headers, _ = jellyfish.app.call(env)
|
60
60
|
self.status status
|
61
61
|
self.headers headers
|
@@ -80,14 +80,14 @@ describe 'Sinatra base_test.rb' do
|
|
80
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
|
-
headers['
|
83
|
+
headers['x-downstream'].should.eq 'true'
|
84
84
|
body .should.eq ['Hello from downstream']
|
85
85
|
end
|
86
86
|
|
87
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
|
-
headers['
|
90
|
+
headers['x-downstream'].should.eq 'true'
|
91
91
|
body .should.eq ['Hello from downstream']
|
92
92
|
end
|
93
93
|
|
@@ -96,8 +96,8 @@ describe 'Sinatra base_test.rb' do
|
|
96
96
|
get('/explicit-forward', Rack::ContentLength.new(app))
|
97
97
|
|
98
98
|
status .should.eq 210
|
99
|
-
headers['
|
100
|
-
headers['
|
99
|
+
headers['x-downstream'] .should.eq 'true'
|
100
|
+
headers['content-length'].should.eq '28'
|
101
101
|
body.to_a .should.eq ['Hello after explicit forward']
|
102
102
|
end
|
103
103
|
end
|
@@ -70,7 +70,7 @@ describe 'Sinatra routing_test.rb' do
|
|
70
70
|
app = Class.new{
|
71
71
|
include Jellyfish
|
72
72
|
get{
|
73
|
-
self.headers '
|
73
|
+
self.headers 'content-type' => 'text/plain'
|
74
74
|
status, headers, body = jellyfish.app.call(env)
|
75
75
|
self.status status
|
76
76
|
self.body body
|
@@ -79,7 +79,7 @@ describe 'Sinatra routing_test.rb' do
|
|
79
79
|
}.new(Class.new{
|
80
80
|
include Jellyfish
|
81
81
|
handle Jellyfish::NotFound do
|
82
|
-
headers_merge '
|
82
|
+
headers_merge 'content-type' => 'text/html'
|
83
83
|
status 404
|
84
84
|
'<h1>Not Found</h1>'
|
85
85
|
end
|
@@ -87,7 +87,7 @@ describe 'Sinatra routing_test.rb' do
|
|
87
87
|
|
88
88
|
status, headers, body = get('/foo', app)
|
89
89
|
status .should.eq 404
|
90
|
-
headers['
|
90
|
+
headers['content-type'].should.eq 'text/html'
|
91
91
|
body .should.eq ['<h1>Not Found</h1>']
|
92
92
|
end
|
93
93
|
|
data/test/test_from_readme.rb
CHANGED
@@ -30,7 +30,7 @@ describe 'from README.md' do
|
|
30
30
|
sock = nil
|
31
31
|
status, headers, body = File.open(File::NULL) do |input|
|
32
32
|
app.call(
|
33
|
-
'
|
33
|
+
'SERVER_PROTOCOL'=> 'HTTP/1.1',
|
34
34
|
'REQUEST_METHOD' => method,
|
35
35
|
'HTTP_HOST' => host,
|
36
36
|
'PATH_INFO' => pinfo,
|
data/test/test_log.rb
CHANGED
@@ -22,6 +22,7 @@ describe Jellyfish do
|
|
22
22
|
|
23
23
|
def mock_log
|
24
24
|
log = []
|
25
|
+
log.singleton_class.send(:public, :puts)
|
25
26
|
mock(log).puts(is_a(String)){ |msg| log << msg }
|
26
27
|
log
|
27
28
|
end
|
@@ -35,6 +36,6 @@ describe Jellyfish do
|
|
35
36
|
would "log_error to env['rack.errors']" do
|
36
37
|
log = mock_log
|
37
38
|
get('/log_error', app, 'rack.errors' => log)
|
38
|
-
log.should.eq ['[Name] #<RuntimeError: RuntimeError> ["backtrace"]']
|
39
|
+
log.should.eq ['[Name] #<RuntimeError: RuntimeError> for /log_error ["backtrace"]']
|
39
40
|
end
|
40
41
|
end
|
data/test/test_rewrite.rb
CHANGED
@@ -41,4 +41,18 @@ describe Jellyfish::Rewrite do
|
|
41
41
|
expect(call(app, '/top/from/inner')).eq '/top!/to/inner'
|
42
42
|
expect(call(app, '/top/from/outer')).eq '/top!/to/outer'
|
43
43
|
end
|
44
|
+
|
45
|
+
would 'map to with host and handle SCRIPT_NAME properly' do
|
46
|
+
app = Jellyfish::Builder.app do
|
47
|
+
map '/path', to: '/path', host: 'host' do
|
48
|
+
run lambda{ |env|
|
49
|
+
[200, {},
|
50
|
+
["#{env['HTTP_HOST']} #{env['SCRIPT_NAME']} #{env['PATH_INFO']}"]]
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
expect(get('/path', app, 'HTTP_HOST' => 'host').dig(-1, -1)).
|
56
|
+
eq 'host /path'
|
57
|
+
end
|
44
58
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jellyfish
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lin Jen-Shin (godfat)
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-02-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |-
|
14
14
|
Pico web framework for building API-centric web applications.
|
@@ -24,8 +24,8 @@ extensions: []
|
|
24
24
|
extra_rdoc_files: []
|
25
25
|
files:
|
26
26
|
- ".gitignore"
|
27
|
+
- ".gitlab-ci.yml"
|
27
28
|
- ".gitmodules"
|
28
|
-
- ".travis.yml"
|
29
29
|
- CHANGES.md
|
30
30
|
- Gemfile
|
31
31
|
- LICENSE
|
@@ -69,7 +69,7 @@ homepage: https://github.com/godfat/jellyfish
|
|
69
69
|
licenses:
|
70
70
|
- Apache-2.0
|
71
71
|
metadata: {}
|
72
|
-
post_install_message:
|
72
|
+
post_install_message:
|
73
73
|
rdoc_options: []
|
74
74
|
require_paths:
|
75
75
|
- lib
|
@@ -84,9 +84,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
84
|
- !ruby/object:Gem::Version
|
85
85
|
version: '0'
|
86
86
|
requirements: []
|
87
|
-
|
88
|
-
|
89
|
-
signing_key:
|
87
|
+
rubygems_version: 3.4.3
|
88
|
+
signing_key:
|
90
89
|
specification_version: 4
|
91
90
|
summary: Pico web framework for building API-centric web applications.
|
92
91
|
test_files:
|
data/.travis.yml
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
sudo: false
|
2
|
-
language: ruby
|
3
|
-
|
4
|
-
install: 'gem install bundler; bundle install --retry=3'
|
5
|
-
script: 'ruby -vr bundler/setup -S rake test'
|
6
|
-
|
7
|
-
matrix:
|
8
|
-
include:
|
9
|
-
- rvm: 2.3
|
10
|
-
- rvm: 2.4
|
11
|
-
- rvm: 2.5
|
12
|
-
- rvm: ruby-head
|
13
|
-
- rvm: jruby
|
14
|
-
env: JRUBY_OPTS=--debug
|
15
|
-
- rvm: rbx
|
16
|
-
|
17
|
-
allow_failures:
|
18
|
-
- rvm: rbx
|