rack-mount 0.0.1 → 0.0.2
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.
- data/README.rdoc +1 -1
- data/lib/rack/mount.rb +0 -2
- data/lib/rack/mount/analysis/frequency.rb +10 -3
- data/lib/rack/mount/analysis/splitting.rb +79 -62
- data/lib/rack/mount/generatable_regexp.rb +48 -45
- data/lib/rack/mount/generation/route.rb +10 -5
- data/lib/rack/mount/generation/route_set.rb +23 -31
- data/lib/rack/mount/prefix.rb +14 -15
- data/lib/rack/mount/recognition/code_generation.rb +51 -61
- data/lib/rack/mount/recognition/route.rb +7 -22
- data/lib/rack/mount/recognition/route_set.rb +40 -16
- data/lib/rack/mount/regexp_with_named_groups.rb +33 -7
- data/lib/rack/mount/route_set.rb +7 -5
- data/lib/rack/mount/strexp.rb +11 -40
- data/lib/rack/mount/strexp/parser.rb +158 -0
- data/lib/rack/mount/strexp/tokenizer.rb +84 -0
- data/lib/rack/mount/utils.rb +26 -163
- data/lib/rack/mount/vendor/multimap/multimap.rb +1 -0
- data/lib/rack/mount/vendor/reginald/reginald.rb +55 -0
- data/lib/rack/mount/vendor/reginald/reginald/alternation.rb +50 -0
- data/lib/rack/mount/vendor/reginald/reginald/anchor.rb +20 -0
- data/lib/rack/mount/vendor/reginald/reginald/character.rb +53 -0
- data/lib/rack/mount/vendor/reginald/reginald/character_class.rb +61 -0
- data/lib/rack/mount/vendor/reginald/reginald/expression.rb +75 -0
- data/lib/rack/mount/vendor/reginald/reginald/group.rb +61 -0
- data/lib/rack/mount/vendor/reginald/reginald/parser.rb +306 -0
- data/lib/rack/mount/vendor/reginald/reginald/tokenizer.rb +141 -0
- metadata +14 -15
- data/lib/rack/mount/const.rb +0 -45
- data/lib/rack/mount/meta_method.rb +0 -104
data/lib/rack/mount/prefix.rb
CHANGED
@@ -2,29 +2,28 @@ require 'rack/mount/utils'
|
|
2
2
|
|
3
3
|
module Rack::Mount
|
4
4
|
class Prefix #:nodoc:
|
5
|
-
|
5
|
+
EMPTY_STRING = ''.freeze
|
6
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
7
|
+
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
8
|
+
SLASH = '/'.freeze
|
6
9
|
|
7
|
-
def initialize(app, prefix
|
10
|
+
def initialize(app, prefix)
|
8
11
|
@app, @prefix = app, prefix.freeze
|
9
12
|
freeze
|
10
13
|
end
|
11
14
|
|
12
15
|
def call(env)
|
13
|
-
|
14
|
-
|
15
|
-
old_script_name = env[Const::SCRIPT_NAME].dup
|
16
|
+
old_path_info = env[PATH_INFO].dup
|
17
|
+
old_script_name = env[SCRIPT_NAME].dup
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@app.call(env)
|
22
|
-
ensure
|
23
|
-
env[Const::PATH_INFO] = old_path_info
|
24
|
-
env[Const::SCRIPT_NAME] = old_script_name
|
25
|
-
end
|
26
|
-
else
|
19
|
+
begin
|
20
|
+
env[PATH_INFO] = Utils.normalize_path(env[PATH_INFO].sub(@prefix, EMPTY_STRING))
|
21
|
+
env[PATH_INFO] = EMPTY_STRING if env[PATH_INFO] == SLASH
|
22
|
+
env[SCRIPT_NAME] = Utils.normalize_path(env[SCRIPT_NAME].to_s + @prefix)
|
27
23
|
@app.call(env)
|
24
|
+
ensure
|
25
|
+
env[PATH_INFO] = old_path_info
|
26
|
+
env[SCRIPT_NAME] = old_script_name
|
28
27
|
end
|
29
28
|
end
|
30
29
|
end
|
@@ -1,98 +1,88 @@
|
|
1
|
-
require 'rack/mount/meta_method'
|
2
|
-
|
3
1
|
module Rack::Mount
|
4
2
|
module Recognition
|
5
3
|
module CodeGeneration #:nodoc:
|
6
|
-
def
|
4
|
+
def _expired_recognize(env) #:nodoc:
|
7
5
|
raise 'route set not finalized'
|
8
6
|
end
|
9
7
|
|
10
8
|
def rehash
|
11
9
|
super
|
12
|
-
|
10
|
+
optimize_recognize!
|
13
11
|
end
|
14
12
|
|
15
13
|
private
|
16
14
|
def expire!
|
17
15
|
class << self
|
18
|
-
|
16
|
+
undef :recognize
|
17
|
+
alias_method :recognize, :_expired_recognize
|
19
18
|
end
|
20
19
|
|
21
20
|
super
|
22
21
|
end
|
23
22
|
|
24
23
|
def optimize_container_iterator(container)
|
25
|
-
|
26
|
-
m << 'env = req.env'
|
24
|
+
body = []
|
27
25
|
|
28
26
|
container.each_with_index { |route, i|
|
29
|
-
|
30
|
-
|
31
|
-
m << "route = self[#{i}]"
|
32
|
-
m << 'routing_args = route.defaults.dup'
|
27
|
+
body << "route = self[#{i}]"
|
28
|
+
body << 'params = route.defaults.dup'
|
33
29
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
30
|
+
conditions = []
|
31
|
+
route.conditions.each do |method, condition|
|
32
|
+
b = []
|
33
|
+
b << "if m = obj.#{method}.match(#{condition.inspect})"
|
34
|
+
b << 'matches = m.captures' if route.named_captures[method].any?
|
35
|
+
b << 'p = nil' if route.named_captures[method].any?
|
36
|
+
b << route.named_captures[method].map { |k, j| "params[#{k.inspect}] = p if p = matches[#{j}]" }.join('; ')
|
37
|
+
b << 'true'
|
38
|
+
b << 'end'
|
39
|
+
conditions << "(#{b.join('; ')})"
|
38
40
|
end
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
b << "matches = m.captures" if route.named_captures[method].any?
|
44
|
-
route.named_captures[method].each do |k, i|
|
45
|
-
b << MetaMethod::Condition.new("p = matches[#{i}]") do |c2|
|
46
|
-
c2 << "routing_args[#{k.inspect}] = Utils.unescape_uri(p)"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
if method == :path_info && !Utils.regexp_anchored?(condition)
|
50
|
-
b << "env[Prefix::KEY] = m.to_s"
|
51
|
-
end
|
52
|
-
b << "true"
|
53
|
-
end
|
54
|
-
c.else = MetaMethod::Block.new("false")
|
42
|
+
body << <<-RUBY
|
43
|
+
if #{conditions.join(' && ')}
|
44
|
+
yield route, params
|
55
45
|
end
|
56
|
-
|
46
|
+
RUBY
|
57
47
|
}
|
58
48
|
|
59
|
-
|
60
|
-
|
61
|
-
|
49
|
+
container.instance_eval(<<-RUBY, __FILE__, __LINE__)
|
50
|
+
def optimized_each(obj)
|
51
|
+
#{body.join("\n")}
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
RUBY
|
62
55
|
end
|
63
56
|
|
64
|
-
def
|
65
|
-
|
57
|
+
def optimize_recognize!
|
58
|
+
keys = @recognition_keys.map { |key|
|
59
|
+
if key.is_a?(Array)
|
60
|
+
key.call_source(:cache, :obj)
|
61
|
+
else
|
62
|
+
"obj.#{key}"
|
63
|
+
end
|
64
|
+
}.join(', ')
|
66
65
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
66
|
+
instance_eval(<<-RUBY, __FILE__, __LINE__)
|
67
|
+
undef :recognize
|
68
|
+
def recognize(obj, &block)
|
69
|
+
cache = {}
|
70
|
+
container = @recognition_graph[#{keys}]
|
71
|
+
optimize_container_iterator(container) unless container.respond_to?(:optimized_each)
|
73
72
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
if key.is_a?(Array)
|
79
|
-
cache = true
|
80
|
-
key.call_source(:cache, :req)
|
73
|
+
if block_given?
|
74
|
+
container.optimized_each(obj) do |route, params|
|
75
|
+
yield route, params
|
76
|
+
end
|
81
77
|
else
|
82
|
-
|
78
|
+
container.optimized_each(obj) do |route, params|
|
79
|
+
return route, params
|
80
|
+
end
|
83
81
|
end
|
84
|
-
}.join(', ')
|
85
|
-
method << 'cache = {}' if cache
|
86
|
-
method << "container = @recognition_graph[#{keys}]"
|
87
|
-
method << "optimize_container_iterator(container) unless container.respond_to?(:optimized_each)"
|
88
|
-
method << "container.optimized_each(req) || (set_expectation ? Const::NOT_FOUND_RESPONSE : Const::EXPECTATION_FAILED_RESPONSE)"
|
89
|
-
method << 'ensure'
|
90
|
-
method << 'env.delete(Const::EXPECT) if set_expectation'
|
91
|
-
method << 'end'
|
92
|
-
end
|
93
82
|
|
94
|
-
|
95
|
-
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
RUBY
|
96
86
|
end
|
97
87
|
end
|
98
88
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'rack/mount/
|
1
|
+
require 'rack/mount/utils'
|
2
2
|
|
3
3
|
module Rack::Mount
|
4
4
|
module Recognition
|
@@ -8,12 +8,6 @@ module Rack::Mount
|
|
8
8
|
def initialize(*args)
|
9
9
|
super
|
10
10
|
|
11
|
-
# TODO: Don't explict check for :path_info condition
|
12
|
-
if @conditions.has_key?(:path_info) &&
|
13
|
-
!Utils.regexp_anchored?(@conditions[:path_info])
|
14
|
-
@app = Prefix.new(@app)
|
15
|
-
end
|
16
|
-
|
17
11
|
@named_captures = {}
|
18
12
|
@conditions.map { |method, condition|
|
19
13
|
@named_captures[method] = condition.named_captures.inject({}) { |named_captures, (k, v)|
|
@@ -24,34 +18,25 @@ module Rack::Mount
|
|
24
18
|
@named_captures.freeze
|
25
19
|
end
|
26
20
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
routing_args = @defaults.dup
|
21
|
+
def recognize(obj)
|
22
|
+
params = @defaults.dup
|
31
23
|
if @conditions.all? { |method, condition|
|
32
|
-
value =
|
24
|
+
value = obj.send(method)
|
33
25
|
if m = value.match(condition)
|
34
26
|
matches = m.captures
|
35
27
|
@named_captures[method].each { |k, i|
|
36
28
|
if v = matches[i]
|
37
|
-
|
38
|
-
# uri related methods
|
39
|
-
routing_args[k] = Utils.unescape_uri(v)
|
29
|
+
params[k] = v
|
40
30
|
end
|
41
31
|
}
|
42
|
-
# TODO: Don't explict check for :path_info condition
|
43
|
-
if method == :path_info && !Utils.regexp_anchored?(condition)
|
44
|
-
env[Prefix::KEY] = m.to_s
|
45
|
-
end
|
46
32
|
true
|
47
33
|
else
|
48
34
|
false
|
49
35
|
end
|
50
36
|
}
|
51
|
-
|
52
|
-
@app.call(env)
|
37
|
+
params
|
53
38
|
else
|
54
|
-
|
39
|
+
nil
|
55
40
|
end
|
56
41
|
end
|
57
42
|
end
|
@@ -7,7 +7,7 @@ module Rack::Mount
|
|
7
7
|
|
8
8
|
# Adds recognition related concerns to RouteSet.new.
|
9
9
|
def initialize(options = {})
|
10
|
-
@parameters_key = options.delete(:parameters_key) ||
|
10
|
+
@parameters_key = options.delete(:parameters_key) || 'rack.routing_args'
|
11
11
|
@parameters_key.freeze
|
12
12
|
@recognition_key_analyzer = Analysis::Frequency.new_with_module(Analysis::Splitting)
|
13
13
|
|
@@ -21,6 +21,33 @@ module Rack::Mount
|
|
21
21
|
route
|
22
22
|
end
|
23
23
|
|
24
|
+
def recognize(obj)
|
25
|
+
raise 'route set not finalized' unless @recognition_graph
|
26
|
+
|
27
|
+
cache = {}
|
28
|
+
keys = @recognition_keys.map { |key|
|
29
|
+
if key.is_a?(Array)
|
30
|
+
key.call(cache, obj)
|
31
|
+
else
|
32
|
+
obj.send(key)
|
33
|
+
end
|
34
|
+
}
|
35
|
+
@recognition_graph[*keys].each do |route|
|
36
|
+
if params = route.recognize(obj)
|
37
|
+
if block_given?
|
38
|
+
yield route, params
|
39
|
+
else
|
40
|
+
return route, params
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
EXPECT = 'Expect'.freeze
|
49
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
50
|
+
|
24
51
|
# Rack compatible recognition and dispatching method. Routes are
|
25
52
|
# tried until one returns a non-catch status code. If no routes
|
26
53
|
# match, the catch status code is returned.
|
@@ -30,27 +57,24 @@ module Rack::Mount
|
|
30
57
|
def call(env)
|
31
58
|
raise 'route set not finalized' unless @recognition_graph
|
32
59
|
|
33
|
-
set_expectation = env[
|
34
|
-
env[
|
60
|
+
set_expectation = env[EXPECT] != '100-continue'
|
61
|
+
env[EXPECT] = '100-continue' if set_expectation
|
35
62
|
|
36
|
-
env[
|
63
|
+
env[PATH_INFO] = Utils.normalize_path(env[PATH_INFO])
|
37
64
|
|
38
|
-
cache = {}
|
39
65
|
req = @request_class.new(env)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
}
|
47
|
-
@recognition_graph[*keys].each do |route|
|
48
|
-
result = route.call(req)
|
66
|
+
recognize(req) do |route, params|
|
67
|
+
# TODO: We only want to unescape params from uri related methods
|
68
|
+
params.each { |k, v| params[k] = Utils.unescape_uri(v) if v.is_a?(String) }
|
69
|
+
|
70
|
+
env[@parameters_key] = params
|
71
|
+
result = route.app.call(env)
|
49
72
|
return result unless result[0].to_i == 417
|
50
73
|
end
|
51
|
-
|
74
|
+
|
75
|
+
set_expectation ? [404, {'Content-Type' => 'text/html'}, ['Not Found']] : [417, {'Content-Type' => 'text/html'}, ['Expectation failed']]
|
52
76
|
ensure
|
53
|
-
env.delete(
|
77
|
+
env.delete(EXPECT) if set_expectation
|
54
78
|
end
|
55
79
|
|
56
80
|
def rehash #:nodoc:
|
@@ -1,7 +1,15 @@
|
|
1
|
-
require 'rack/mount/utils'
|
2
|
-
|
3
1
|
module Rack::Mount
|
4
|
-
|
2
|
+
begin
|
3
|
+
eval('/(?<foo>.*)/').named_captures
|
4
|
+
|
5
|
+
class RegexpWithNamedGroups < Regexp
|
6
|
+
def self.supports_named_captures?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
rescue SyntaxError, NoMethodError
|
11
|
+
require 'strscan'
|
12
|
+
|
5
13
|
# A wrapper that adds shim named capture support to older
|
6
14
|
# versions of Ruby.
|
7
15
|
#
|
@@ -16,6 +24,10 @@ module Rack::Mount
|
|
16
24
|
#
|
17
25
|
# /(?:<foo>[a-z]+)/
|
18
26
|
class RegexpWithNamedGroups < Regexp
|
27
|
+
def self.supports_named_captures?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
19
31
|
def self.new(regexp) #:nodoc:
|
20
32
|
if regexp.is_a?(RegexpWithNamedGroups)
|
21
33
|
regexp
|
@@ -26,9 +38,25 @@ module Rack::Mount
|
|
26
38
|
|
27
39
|
# Wraps Regexp with named capture support.
|
28
40
|
def initialize(regexp)
|
29
|
-
regexp
|
41
|
+
regexp = Regexp.compile(regexp) unless regexp.is_a?(Regexp)
|
42
|
+
source, options = regexp.source, regexp.options
|
43
|
+
@names, scanner = [], StringScanner.new(source)
|
44
|
+
|
45
|
+
while scanner.skip_until(/\(/)
|
46
|
+
if scanner.scan(/\?:<([^>]+)>/)
|
47
|
+
@names << scanner[1]
|
48
|
+
elsif scanner.scan(/\?:/)
|
49
|
+
# ignore
|
50
|
+
else
|
51
|
+
@names << nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
source.gsub!(/\?:<([^>]+)>/, '')
|
55
|
+
|
56
|
+
@names = [] unless @names.any?
|
30
57
|
@names.freeze
|
31
|
-
|
58
|
+
|
59
|
+
super(source, options)
|
32
60
|
end
|
33
61
|
|
34
62
|
def names
|
@@ -43,7 +71,5 @@ module Rack::Mount
|
|
43
71
|
named_captures
|
44
72
|
end
|
45
73
|
end
|
46
|
-
else
|
47
|
-
RegexpWithNamedGroups = Regexp
|
48
74
|
end
|
49
75
|
end
|
data/lib/rack/mount/route_set.rb
CHANGED
@@ -40,7 +40,7 @@ module Rack::Mount
|
|
40
40
|
# Conditions may be expressed as strings or
|
41
41
|
# regexps to match against.
|
42
42
|
# <tt>defaults</tt>:: A hash of values that always gets merged in
|
43
|
-
# <tt>name</tt>:: Symbol identifier for the route used with named
|
43
|
+
# <tt>name</tt>:: Symbol identifier for the route used with named
|
44
44
|
# route generations
|
45
45
|
def add_route(app, conditions = {}, defaults = {}, name = nil)
|
46
46
|
route = Route.new(self, app, conditions, defaults, name)
|
@@ -98,10 +98,12 @@ module Rack::Mount
|
|
98
98
|
def build_nested_route_set(keys, &block)
|
99
99
|
graph = Multimap.new
|
100
100
|
@routes.each_with_index do |route, index|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
101
|
+
catch(:skip) do
|
102
|
+
k = keys.map { |key| block.call(key, index) }
|
103
|
+
Utils.pop_trailing_nils!(k)
|
104
|
+
k.map! { |key| key || /.+/ }
|
105
|
+
graph[*k] = route
|
106
|
+
end
|
105
107
|
end
|
106
108
|
graph
|
107
109
|
end
|
data/lib/rack/mount/strexp.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'rack/mount/strexp/parser'
|
2
2
|
|
3
3
|
module Rack::Mount
|
4
4
|
class Strexp < Regexp
|
@@ -24,14 +24,19 @@ module Rack::Mount
|
|
24
24
|
def initialize(str, requirements = {}, separators = [])
|
25
25
|
return super(str) if str.is_a?(Regexp)
|
26
26
|
|
27
|
-
re = Regexp.escape(str)
|
28
27
|
requirements = requirements ? requirements.dup : {}
|
29
|
-
|
30
28
|
normalize_requirements!(requirements, separators)
|
31
|
-
parse_dynamic_segments!(re, requirements)
|
32
|
-
parse_optional_segments!(re)
|
33
29
|
|
34
|
-
|
30
|
+
parser = StrexpParser.new
|
31
|
+
parser.requirements = requirements
|
32
|
+
|
33
|
+
begin
|
34
|
+
re = parser.scan_str(str)
|
35
|
+
rescue Racc::ParseError => e
|
36
|
+
raise RegexpError, e.message
|
37
|
+
end
|
38
|
+
|
39
|
+
super(re)
|
35
40
|
end
|
36
41
|
|
37
42
|
private
|
@@ -52,40 +57,6 @@ module Rack::Mount
|
|
52
57
|
requirements
|
53
58
|
end
|
54
59
|
|
55
|
-
def parse_dynamic_segments!(str, requirements)
|
56
|
-
re, pos, scanner = '', 0, StringScanner.new(str)
|
57
|
-
while scanner.scan_until(/(:|\\\*)([a-zA-Z_]\w*)/)
|
58
|
-
pre, pos = scanner.pre_match[pos..-1], scanner.pos
|
59
|
-
if pre =~ /(.*)\\\\\Z/
|
60
|
-
re << $1 + scanner.matched
|
61
|
-
else
|
62
|
-
name = scanner[2].to_sym
|
63
|
-
requirement = scanner[1] == ':' ?
|
64
|
-
requirements[name] : '.+'
|
65
|
-
re << pre + Const::REGEXP_NAMED_CAPTURE % [name, requirement]
|
66
|
-
end
|
67
|
-
end
|
68
|
-
re << scanner.rest
|
69
|
-
str.replace(re)
|
70
|
-
end
|
71
|
-
|
72
|
-
def parse_optional_segments!(str)
|
73
|
-
re, pos, scanner = '', 0, StringScanner.new(str)
|
74
|
-
while scanner.scan_until(/\\\(|\\\)/)
|
75
|
-
pre, pos = scanner.pre_match[pos..-1], scanner.pos
|
76
|
-
if pre =~ /(.*)\\\\\Z/
|
77
|
-
re << $1 + scanner.matched
|
78
|
-
elsif scanner.matched == '\\('
|
79
|
-
# re << pre + '(?:'
|
80
|
-
re << pre + '('
|
81
|
-
elsif scanner.matched == '\\)'
|
82
|
-
re << pre + ')?'
|
83
|
-
end
|
84
|
-
end
|
85
|
-
re << scanner.rest
|
86
|
-
str.replace(re)
|
87
|
-
end
|
88
|
-
|
89
60
|
def regexp_has_modifiers?(regexp)
|
90
61
|
regexp.options & (Regexp::IGNORECASE | Regexp::EXTENDED) != 0
|
91
62
|
end
|