jellyfish 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 "/"
|