jellyfish 1.1.1 → 1.2.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 +5 -5
- data/.travis.yml +14 -11
- data/CHANGES.md +21 -0
- data/README.md +125 -60
- data/Rakefile +4 -4
- data/TODO.md +2 -0
- data/jellyfish.gemspec +69 -69
- data/lib/jellyfish.rb +10 -8
- data/lib/jellyfish/builder.rb +11 -6
- data/lib/jellyfish/test.rb +10 -2
- data/lib/jellyfish/urlmap.rb +74 -9
- data/lib/jellyfish/version.rb +1 -1
- data/task/README.md +8 -8
- data/task/gemgem.rb +29 -7
- data/test/rack/test_builder.rb +1 -1
- data/test/rack/test_urlmap.rb +67 -9
- data/test/test_from_readme.rb +13 -10
- data/test/test_listen.rb +64 -0
- data/test/test_rewrite.rb +8 -6
- data/test/test_websocket.rb +6 -3
- metadata +6 -6
- data/jellyfish.png +0 -0
- data/lib/jellyfish/newrelic.rb +0 -24
data/lib/jellyfish.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
|
2
2
|
module Jellyfish
|
3
3
|
autoload :VERSION , 'jellyfish/version'
|
4
|
-
autoload :NewRelic, 'jellyfish/newrelic'
|
5
4
|
|
6
5
|
autoload :NormalizedParams, 'jellyfish/normalized_params'
|
7
6
|
autoload :NormalizedPath , 'jellyfish/normalized_path'
|
@@ -159,13 +158,16 @@ module Jellyfish
|
|
159
158
|
end
|
160
159
|
|
161
160
|
%w[options get head post put delete patch].each do |method|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
161
|
+
define_method(method) do |route=//, meta={}, &block|
|
162
|
+
define(method, route, meta, &block)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def define(method, route=//, meta={}, &block)
|
167
|
+
raise TypeError.new("Route #{route} should respond to :match") \
|
168
|
+
unless route.respond_to?(:match)
|
169
|
+
|
170
|
+
(routes[method.to_s] ||= []) << [route, block || lambda{|_|}, meta]
|
169
171
|
end
|
170
172
|
|
171
173
|
def inherited sub
|
data/lib/jellyfish/builder.rb
CHANGED
@@ -28,8 +28,13 @@ module Jellyfish
|
|
28
28
|
@warmup = lam || block
|
29
29
|
end
|
30
30
|
|
31
|
-
def map path, to: nil, &block
|
32
|
-
|
31
|
+
def map path, to: nil, host: nil, &block
|
32
|
+
key = if host then "http://#{host}/#{path}" else path end
|
33
|
+
(@map ||= {})[key] = [block, to]
|
34
|
+
end
|
35
|
+
|
36
|
+
def listen host, &block
|
37
|
+
map('', host: host, &block)
|
33
38
|
end
|
34
39
|
|
35
40
|
def rewrite rules, &block
|
@@ -42,16 +47,16 @@ module Jellyfish
|
|
42
47
|
run = if @map then generate_map(@map, @run) else @run end
|
43
48
|
fail 'missing run or map statement' unless run
|
44
49
|
app = @use.inject(run){ |a, m| m.call(a) }
|
45
|
-
|
46
|
-
@warmup.call(
|
47
|
-
|
50
|
+
result = if to then Rewrite.new(app, to) else app end
|
51
|
+
@warmup.call(result) if @warmup
|
52
|
+
result
|
48
53
|
end
|
49
54
|
|
50
55
|
private
|
51
56
|
def generate_map current_map, app
|
52
57
|
mapped = if app then {'' => app} else {} end
|
53
58
|
current_map.each do |path, (block, to)|
|
54
|
-
mapped[path
|
59
|
+
mapped[path] = self.class.app(app, to, &block)
|
55
60
|
end
|
56
61
|
URLMap.new(mapped)
|
57
62
|
end
|
data/lib/jellyfish/test.rb
CHANGED
@@ -4,7 +4,7 @@ require 'muack'
|
|
4
4
|
require 'jellyfish'
|
5
5
|
require 'rack'
|
6
6
|
|
7
|
-
Pork::
|
7
|
+
Pork::Suite.include(Muack::API)
|
8
8
|
|
9
9
|
copy :jellyfish do
|
10
10
|
module_eval(%w[options get head post put delete patch].map{ |method|
|
@@ -15,7 +15,7 @@ copy :jellyfish do
|
|
15
15
|
'REQUEST_METHOD' => '#{method}'.upcase,
|
16
16
|
'SCRIPT_NAME' => '' ,
|
17
17
|
'rack.input' => input ,
|
18
|
-
'rack.url_scheme'=> '
|
18
|
+
'rack.url_scheme'=> 'http' ,
|
19
19
|
'SERVER_NAME' => 'localhost' ,
|
20
20
|
'SERVER_PORT' => '8080'}.merge(env))
|
21
21
|
end
|
@@ -23,3 +23,11 @@ copy :jellyfish do
|
|
23
23
|
RUBY
|
24
24
|
}.join("\n"))
|
25
25
|
end
|
26
|
+
|
27
|
+
copy :stringio do
|
28
|
+
def new_stringio
|
29
|
+
sock = StringIO.new
|
30
|
+
sock.set_encoding('ASCII-8BIT')
|
31
|
+
sock
|
32
|
+
end
|
33
|
+
end
|
data/lib/jellyfish/urlmap.rb
CHANGED
@@ -1,26 +1,91 @@
|
|
1
1
|
|
2
2
|
module Jellyfish
|
3
3
|
class URLMap
|
4
|
-
def initialize
|
5
|
-
|
6
|
-
|
4
|
+
def initialize mapped_not_chomped
|
5
|
+
mapped = transform_keys(mapped_not_chomped){ |k| k.sub(%r{/+\z}, '') }
|
6
|
+
keys = mapped.keys
|
7
|
+
@no_host = !keys.any?{ |k| match?(k, %r{\Ahttps?://}) }
|
8
|
+
|
9
|
+
string = keys.sort_by{ |k| -k.size }.
|
10
|
+
map{ |k| build_regexp(k) }.
|
7
11
|
join('|')
|
8
12
|
|
9
13
|
@mapped = mapped
|
10
|
-
@routes =
|
14
|
+
@routes = %r{\A(?:#{string})(?:/|\z)}
|
11
15
|
end
|
12
16
|
|
13
17
|
def call env
|
14
18
|
path_info = env['PATH_INFO']
|
15
|
-
matched = @routes.match(path_info).to_s.chomp('/')
|
16
|
-
squeezed = matched.squeeze('/')
|
17
19
|
|
18
|
-
if
|
19
|
-
|
20
|
-
|
20
|
+
if @no_host
|
21
|
+
if matched = @routes.match(path_info)
|
22
|
+
cut_path = matched.to_s.chomp('/')
|
23
|
+
script_name = key = cut_path.squeeze('/')
|
24
|
+
end
|
25
|
+
else
|
26
|
+
host = (env['HTTP_HOST'] || env['SERVER_NAME']).to_s.downcase
|
27
|
+
if matched = @routes.match("#{host}/#{path_info}")
|
28
|
+
cut_path = matched.to_s[host.size + 1..-1].chomp('/')
|
29
|
+
script_name = cut_path.squeeze('/')
|
30
|
+
|
31
|
+
key =
|
32
|
+
if matched[:host]
|
33
|
+
host_with_path =
|
34
|
+
if script_name.empty?
|
35
|
+
host
|
36
|
+
else
|
37
|
+
File.join(host, script_name)
|
38
|
+
end
|
39
|
+
"#{env['rack.url_scheme']}://#{host_with_path}"
|
40
|
+
else
|
41
|
+
script_name
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if app = @mapped[key]
|
47
|
+
app.call(env.merge('PATH_INFO' => path_info[cut_path.size..-1],
|
48
|
+
'SCRIPT_NAME' => env['SCRIPT_NAME'] + script_name))
|
21
49
|
else
|
22
50
|
[404, {}, []]
|
23
51
|
end
|
24
52
|
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def build_regexp path
|
57
|
+
if @no_host
|
58
|
+
regexp_path(path)
|
59
|
+
elsif matched = path.match(%r{\Ahttps?://([^/]+)(/?.*)})
|
60
|
+
# We only need to know if we're matching against a host,
|
61
|
+
# therefore just an empty group is sufficient.
|
62
|
+
"(?<host>)#{matched[1]}/#{regexp_path(matched[2])}"
|
63
|
+
else
|
64
|
+
"[^/]*/#{regexp_path(path)}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def regexp_path path
|
69
|
+
Regexp.escape(path).gsub('/', '/+')
|
70
|
+
end
|
71
|
+
|
72
|
+
def transform_keys hash, &block
|
73
|
+
if hash.respond_to?(:transform_keys)
|
74
|
+
hash.transform_keys(&block)
|
75
|
+
else
|
76
|
+
hash.inject({}) do |result, (key, value)|
|
77
|
+
result[yield(key)] = value
|
78
|
+
result
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def match? string, regexp
|
84
|
+
if string.respond_to?(:match?)
|
85
|
+
string.match?(regexp)
|
86
|
+
else
|
87
|
+
string =~ regexp
|
88
|
+
end
|
89
|
+
end
|
25
90
|
end
|
26
91
|
end
|
data/lib/jellyfish/version.rb
CHANGED
data/task/README.md
CHANGED
@@ -4,16 +4,16 @@
|
|
4
4
|
|
5
5
|
Provided tasks:
|
6
6
|
|
7
|
-
rake clean #
|
7
|
+
rake clean # Trash ignored files
|
8
8
|
rake gem:build # Build gem
|
9
9
|
rake gem:install # Install gem
|
10
10
|
rake gem:release # Release gem
|
11
11
|
rake gem:spec # Generate gemspec
|
12
|
-
rake test # Run tests
|
12
|
+
rake test # Run tests
|
13
13
|
|
14
14
|
## REQUIREMENTS:
|
15
15
|
|
16
|
-
* Tested with MRI (official CRuby)
|
16
|
+
* Tested with MRI (official CRuby), Rubinius and JRuby.
|
17
17
|
|
18
18
|
## INSTALLATION:
|
19
19
|
|
@@ -23,13 +23,13 @@ And in Rakefile:
|
|
23
23
|
|
24
24
|
``` ruby
|
25
25
|
begin
|
26
|
-
require "#{
|
26
|
+
require "#{__dir__}/task/gemgem"
|
27
27
|
rescue LoadError
|
28
|
-
sh 'git submodule update --init'
|
28
|
+
sh 'git submodule update --init --recursive'
|
29
29
|
exec Gem.ruby, '-S', $PROGRAM_NAME, *ARGV
|
30
30
|
end
|
31
31
|
|
32
|
-
Gemgem.init(
|
32
|
+
Gemgem.init(__dir__, :submodules => %w[your-dep]) do |s|
|
33
33
|
s.name = 'your-gem'
|
34
34
|
s.version = '0.1.0'
|
35
35
|
end
|
@@ -37,9 +37,9 @@ end
|
|
37
37
|
|
38
38
|
## LICENSE:
|
39
39
|
|
40
|
-
Apache License 2.0
|
40
|
+
Apache License 2.0 (Apache-2.0)
|
41
41
|
|
42
|
-
Copyright (c) 2011-
|
42
|
+
Copyright (c) 2011-2017, 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.
|
data/task/gemgem.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
module Gemgem
|
3
3
|
class << self
|
4
|
-
attr_accessor :dir, :spec, :spec_create
|
4
|
+
attr_accessor :dir, :spec, :submodules, :spec_create
|
5
5
|
end
|
6
6
|
|
7
7
|
module_function
|
@@ -11,12 +11,14 @@ module Gemgem
|
|
11
11
|
def pkg_dir ; "#{dir}/pkg" ; end
|
12
12
|
def escaped_dir; @escaped_dir ||= Regexp.escape(dir); end
|
13
13
|
|
14
|
-
def init dir, &block
|
14
|
+
def init dir, options={}, &block
|
15
15
|
self.dir = dir
|
16
|
-
$LOAD_PATH.unshift("#{dir}/lib")
|
17
16
|
ENV['RUBYLIB'] = "#{dir}/lib:#{ENV['RUBYLIB']}"
|
18
17
|
ENV['PATH'] = "#{dir}/bin:#{ENV['PATH']}"
|
18
|
+
self.submodules = options[:submodules] || []
|
19
19
|
self.spec_create = block
|
20
|
+
|
21
|
+
$LOAD_PATH.unshift("#{dir}/lib", *submodules_libs)
|
20
22
|
end
|
21
23
|
|
22
24
|
def create
|
@@ -26,7 +28,7 @@ module Gemgem
|
|
26
28
|
|
27
29
|
s.description = description.join
|
28
30
|
s.summary = description.first
|
29
|
-
s.license =
|
31
|
+
s.license = license
|
30
32
|
|
31
33
|
s.date = Time.now.strftime('%Y-%m-%d')
|
32
34
|
s.files = gem_files
|
@@ -115,6 +117,7 @@ module Gemgem
|
|
115
117
|
SimpleCov.start do
|
116
118
|
add_filter('test/')
|
117
119
|
add_filter('test.rb')
|
120
|
+
submodules_libs.each(&method(:add_filter))
|
118
121
|
end
|
119
122
|
end
|
120
123
|
|
@@ -159,11 +162,15 @@ module Gemgem
|
|
159
162
|
end
|
160
163
|
|
161
164
|
def strip_home_path path
|
162
|
-
path.sub(ENV['HOME']
|
165
|
+
path.sub(/\A#{Regexp.escape(ENV['HOME'])}\//, '~/')
|
163
166
|
end
|
164
167
|
|
165
168
|
def strip_cwd_path path
|
166
|
-
path.sub(Dir.pwd
|
169
|
+
path.sub(/\A#{Regexp.escape(Dir.pwd)}\//, '')
|
170
|
+
end
|
171
|
+
|
172
|
+
def submodules_libs
|
173
|
+
submodules.map{ |path| "#{dir}/#{path}/lib" }
|
167
174
|
end
|
168
175
|
|
169
176
|
def git *args
|
@@ -201,6 +208,11 @@ module Gemgem
|
|
201
208
|
@description ||= (readme['DESCRIPTION']||'').sub(/.+\n\n/, '').lines.to_a
|
202
209
|
end
|
203
210
|
|
211
|
+
def license
|
212
|
+
readme['LICENSE'].sub(/.+\n\n/, '').lines.first.
|
213
|
+
split(/[()]/).map(&:strip).reject(&:empty?).last
|
214
|
+
end
|
215
|
+
|
204
216
|
def all_files
|
205
217
|
@all_files ||= fold_files(glob).sort
|
206
218
|
end
|
@@ -221,7 +233,8 @@ module Gemgem
|
|
221
233
|
|
222
234
|
def gem_files
|
223
235
|
@gem_files ||= all_files.reject{ |f|
|
224
|
-
f =~
|
236
|
+
f =~ submodules_pattern ||
|
237
|
+
(f =~ ignored_pattern && !git_files.include?(f))
|
225
238
|
}
|
226
239
|
end
|
227
240
|
|
@@ -253,6 +266,15 @@ module Gemgem
|
|
253
266
|
end
|
254
267
|
end
|
255
268
|
|
269
|
+
def submodules_pattern
|
270
|
+
@submodules_pattern ||= if submodules.empty?
|
271
|
+
/^$/
|
272
|
+
else
|
273
|
+
Regexp.new(submodules.map{ |path|
|
274
|
+
"^#{Regexp.escape(path)}/" }.join('|'))
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
256
278
|
def expand_patterns pathes
|
257
279
|
# http://git-scm.com/docs/gitignore
|
258
280
|
pathes.flat_map{ |path|
|
data/test/rack/test_builder.rb
CHANGED
data/test/rack/test_urlmap.rb
CHANGED
@@ -14,7 +14,7 @@ describe Jellyfish::URLMap do
|
|
14
14
|
}, [""]]
|
15
15
|
}
|
16
16
|
map = Rack::Lint.new(Jellyfish::URLMap.new({
|
17
|
-
'/bar' => app,
|
17
|
+
'http://foo.org/bar' => app,
|
18
18
|
'/foo' => app,
|
19
19
|
'/foo/bar' => app
|
20
20
|
}))
|
@@ -67,10 +67,68 @@ describe Jellyfish::URLMap do
|
|
67
67
|
res["X-PathInfo"].should.eq '/'
|
68
68
|
end
|
69
69
|
|
70
|
+
would "dispatches hosts correctly" do
|
71
|
+
map = Rack::Lint.new(Jellyfish::URLMap.new("http://foo.org/" => lambda { |env|
|
72
|
+
[200,
|
73
|
+
{ "Content-Type" => "text/plain",
|
74
|
+
"X-Position" => "foo.org",
|
75
|
+
"X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"],
|
76
|
+
}, [""]]},
|
77
|
+
"http://subdomain.foo.org/" => lambda { |env|
|
78
|
+
[200,
|
79
|
+
{ "Content-Type" => "text/plain",
|
80
|
+
"X-Position" => "subdomain.foo.org",
|
81
|
+
"X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"],
|
82
|
+
}, [""]]},
|
83
|
+
"http://bar.org/" => lambda { |env|
|
84
|
+
[200,
|
85
|
+
{ "Content-Type" => "text/plain",
|
86
|
+
"X-Position" => "bar.org",
|
87
|
+
"X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"],
|
88
|
+
}, [""]]},
|
89
|
+
"/" => lambda { |env|
|
90
|
+
[200,
|
91
|
+
{ "Content-Type" => "text/plain",
|
92
|
+
"X-Position" => "default.org",
|
93
|
+
"X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"],
|
94
|
+
}, [""]]}
|
95
|
+
))
|
96
|
+
|
97
|
+
res = Rack::MockRequest.new(map).get("/")
|
98
|
+
res.should.ok?
|
99
|
+
res["X-Position"].should.eq "default.org"
|
100
|
+
|
101
|
+
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "bar.org")
|
102
|
+
res.should.ok?
|
103
|
+
res["X-Position"].should.eq "bar.org"
|
104
|
+
|
105
|
+
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "foo.org")
|
106
|
+
res.should.ok?
|
107
|
+
res["X-Position"].should.eq "foo.org"
|
108
|
+
|
109
|
+
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "subdomain.foo.org", "SERVER_NAME" => "foo.org")
|
110
|
+
res.should.ok?
|
111
|
+
res["X-Position"].should.eq "subdomain.foo.org"
|
112
|
+
|
113
|
+
res = Rack::MockRequest.new(map).get("http://foo.org/")
|
114
|
+
res.should.ok?
|
115
|
+
res["X-Position"].should.eq "foo.org"
|
116
|
+
|
117
|
+
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "example.org")
|
118
|
+
res.should.ok?
|
119
|
+
res["X-Position"].should.eq "default.org"
|
120
|
+
|
121
|
+
res = Rack::MockRequest.new(map).get("/",
|
122
|
+
"HTTP_HOST" => "example.org:9292",
|
123
|
+
"SERVER_PORT" => "9292")
|
124
|
+
res.should.ok?
|
125
|
+
res["X-Position"].should.eq "default.org"
|
126
|
+
end
|
127
|
+
|
70
128
|
would "be nestable" do
|
71
|
-
map = Rack::Lint.new(
|
72
|
-
|
73
|
-
|
129
|
+
map = Rack::Lint.new(Jellyfish::URLMap.new("/foo" =>
|
130
|
+
Jellyfish::URLMap.new("/bar" =>
|
131
|
+
Jellyfish::URLMap.new("/quux" => lambda { |env|
|
74
132
|
[200,
|
75
133
|
{ "Content-Type" => "text/plain",
|
76
134
|
"X-Position" => "/foo/bar/quux",
|
@@ -90,7 +148,7 @@ describe Jellyfish::URLMap do
|
|
90
148
|
end
|
91
149
|
|
92
150
|
would "route root apps correctly" do
|
93
|
-
map = Rack::Lint.new(
|
151
|
+
map = Rack::Lint.new(Jellyfish::URLMap.new("/" => lambda { |env|
|
94
152
|
[200,
|
95
153
|
{ "Content-Type" => "text/plain",
|
96
154
|
"X-Position" => "root",
|
@@ -132,7 +190,7 @@ describe Jellyfish::URLMap do
|
|
132
190
|
end
|
133
191
|
|
134
192
|
would "not squeeze slashes" do
|
135
|
-
map = Rack::Lint.new(
|
193
|
+
map = Rack::Lint.new(Jellyfish::URLMap.new("/" => lambda { |env|
|
136
194
|
[200,
|
137
195
|
{ "Content-Type" => "text/plain",
|
138
196
|
"X-Position" => "root",
|
@@ -156,7 +214,7 @@ describe Jellyfish::URLMap do
|
|
156
214
|
end
|
157
215
|
|
158
216
|
would "not be case sensitive with hosts" do
|
159
|
-
map = Rack::Lint.new(
|
217
|
+
map = Rack::Lint.new(Jellyfish::URLMap.new("http://example.org/" => lambda { |env|
|
160
218
|
[200,
|
161
219
|
{ "Content-Type" => "text/plain",
|
162
220
|
"X-Position" => "root",
|
@@ -165,13 +223,13 @@ describe Jellyfish::URLMap do
|
|
165
223
|
}, [""]]}
|
166
224
|
))
|
167
225
|
|
168
|
-
res = Rack::MockRequest.new(map).get("/")
|
226
|
+
res = Rack::MockRequest.new(map).get("http://example.org/")
|
169
227
|
res.should.ok?
|
170
228
|
res["X-Position"].should.eq "root"
|
171
229
|
res["X-PathInfo"].should.eq "/"
|
172
230
|
res["X-ScriptName"].should.eq ""
|
173
231
|
|
174
|
-
res = Rack::MockRequest.new(map).get("/")
|
232
|
+
res = Rack::MockRequest.new(map).get("http://EXAMPLE.ORG/")
|
175
233
|
res.should.ok?
|
176
234
|
res["X-Position"].should.eq "root"
|
177
235
|
res["X-PathInfo"].should.eq "/"
|