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.
@@ -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 "/"