jellyfish 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- module_eval <<-RUBY
163
- def #{method} route=//, meta={}, &block
164
- raise TypeError.new("Route \#{route} should respond to :match") \
165
- unless route.respond_to?(:match)
166
- (routes['#{method}'] ||= []) << [route, block || lambda{|_|}, meta]
167
- end
168
- RUBY
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
@@ -28,8 +28,13 @@ module Jellyfish
28
28
  @warmup = lam || block
29
29
  end
30
30
 
31
- def map path, to: nil, &block
32
- (@map ||= {})[path] = [block, to]
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
- rewrite = if to then Rewrite.new(app, to) else app end
46
- @warmup.call(rewrite) if @warmup
47
- rewrite
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.chomp('/')] = self.class.app(app, to, &block)
59
+ mapped[path] = self.class.app(app, to, &block)
55
60
  end
56
61
  URLMap.new(mapped)
57
62
  end
@@ -4,7 +4,7 @@ require 'muack'
4
4
  require 'jellyfish'
5
5
  require 'rack'
6
6
 
7
- Pork::Executor.__send__(:include, Muack::API)
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'=> 'https' ,
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
@@ -1,26 +1,91 @@
1
1
 
2
2
  module Jellyfish
3
3
  class URLMap
4
- def initialize mapped
5
- string = mapped.keys.sort_by{ |k| -k.size }.
6
- map{ |k| Regexp.escape(k).gsub('/', '/+') }.
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 = Regexp.new("\\A(?:#{string})(?:/|\\z)", 'i', 'n')
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 app = @mapped[squeezed]
19
- app.call(env.merge('PATH_INFO' => path_info[matched.size..-1],
20
- 'SCRIPT_NAME' => env['SCRIPT_NAME'] + squeezed))
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
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Jellyfish
3
- VERSION = '1.1.1'
3
+ VERSION = '1.2.0'
4
4
  end
@@ -4,16 +4,16 @@
4
4
 
5
5
  Provided tasks:
6
6
 
7
- rake clean # Remove ignored files
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 in memory
12
+ rake test # Run tests
13
13
 
14
14
  ## REQUIREMENTS:
15
15
 
16
- * Tested with MRI (official CRuby) 1.9.3, 2.0.0, Rubinius and JRuby.
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 "#{dir = File.dirname(__FILE__)}/task/gemgem"
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(dir) do |s|
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-2013, Lin Jen-Shin (godfat)
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.
@@ -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 = readme['LICENSE'].sub(/.+\n\n/, '').lines.first.strip
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 =~ ignored_pattern && !git_files.include?(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|
@@ -3,7 +3,7 @@ require 'jellyfish/test'
3
3
 
4
4
  require 'rack/lint'
5
5
  require 'rack/mock'
6
- require 'rack/showexceptions'
6
+ require 'rack/show_exceptions'
7
7
  require 'rack/urlmap'
8
8
 
9
9
  describe Jellyfish::Builder do
@@ -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(Rack::URLMap.new("/foo" =>
72
- Rack::URLMap.new("/bar" =>
73
- Rack::URLMap.new("/quux" => lambda { |env|
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(Rack::URLMap.new("/" => lambda { |env|
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(Rack::URLMap.new("/" => lambda { |env|
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(Rack::URLMap.new("/" => lambda { |env|
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 "/"