rack-mount 0.4.7 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/rack/mount/analysis/splitting.rb +7 -7
- data/lib/rack/mount/mixover.rb +21 -8
- data/lib/rack/mount/prefix.rb +16 -10
- data/lib/rack/mount/recognition/code_generation.rb +10 -8
- data/lib/rack/mount/recognition/route.rb +19 -4
- data/lib/rack/mount/recognition/route_set.rb +19 -5
- data/lib/rack/mount/strexp.rb +2 -1
- data/lib/rack/mount/strexp/parser.rb +2 -2
- data/lib/rack/mount/utils.rb +11 -0
- metadata +1 -1
@@ -5,11 +5,7 @@ module Rack::Mount
|
|
5
5
|
module Splitting
|
6
6
|
NULL = "\0".freeze
|
7
7
|
|
8
|
-
class Key <
|
9
|
-
def initialize(method, index, separators)
|
10
|
-
replace([method, index, separators])
|
11
|
-
end
|
12
|
-
|
8
|
+
class Key < Struct.new(:method, :index, :separators)
|
13
9
|
def self.split(value, separator_pattern)
|
14
10
|
keys = value.split(separator_pattern)
|
15
11
|
keys.shift if keys[0] == ''
|
@@ -18,11 +14,15 @@ module Rack::Mount
|
|
18
14
|
end
|
19
15
|
|
20
16
|
def call(cache, obj)
|
21
|
-
(cache[
|
17
|
+
(cache[method] ||= self.class.split(obj.send(method), separators))[index]
|
22
18
|
end
|
23
19
|
|
24
20
|
def call_source(cache, obj)
|
25
|
-
"(#{cache}[:#{
|
21
|
+
"(#{cache}[:#{method}] ||= Analysis::Splitting::Key.split(#{obj}.#{method}, #{separators.inspect}))[#{index}]"
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
"#{method}[#{index}]"
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
data/lib/rack/mount/mixover.rb
CHANGED
@@ -4,6 +4,12 @@ module Rack::Mount
|
|
4
4
|
# metaclass. This allows mixins to be stacked ontop of the instance
|
5
5
|
# methods.
|
6
6
|
module Mixover
|
7
|
+
def self.extended(klass)
|
8
|
+
klass.instance_eval do
|
9
|
+
@extended_modules = []
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
7
13
|
module InstanceMethods #:nodoc:
|
8
14
|
def dup
|
9
15
|
obj = super
|
@@ -15,33 +21,40 @@ module Rack::Mount
|
|
15
21
|
|
16
22
|
# Replaces include with a lazy version.
|
17
23
|
def include(*mod)
|
18
|
-
|
24
|
+
extended_modules.push(*mod)
|
25
|
+
end
|
26
|
+
|
27
|
+
def extended_modules
|
28
|
+
Thread.current[extended_modules_thread_local_key] || @extended_modules
|
19
29
|
end
|
20
30
|
|
21
31
|
def new(*args, &block) #:nodoc:
|
22
32
|
obj = allocate
|
23
33
|
obj.extend(InstanceMethods)
|
24
|
-
|
34
|
+
extended_modules.each { |mod| obj.extend(mod) }
|
25
35
|
obj.send(:initialize, *args, &block)
|
26
36
|
obj
|
27
37
|
end
|
28
38
|
|
29
39
|
# Create a new class without an included module.
|
30
40
|
def new_without_module(mod, *args, &block)
|
31
|
-
|
32
|
-
@included_modules.delete(mod)
|
41
|
+
(Thread.current[extended_modules_thread_local_key] = extended_modules.dup).delete(mod)
|
33
42
|
new(*args, &block)
|
34
43
|
ensure
|
35
|
-
|
44
|
+
Thread.current[extended_modules_thread_local_key] = nil
|
36
45
|
end
|
37
46
|
|
38
47
|
# Create a new class temporarily with a module.
|
39
48
|
def new_with_module(mod, *args, &block)
|
40
|
-
|
41
|
-
include(mod)
|
49
|
+
(Thread.current[extended_modules_thread_local_key] = extended_modules.dup).push(*mod)
|
42
50
|
new(*args, &block)
|
43
51
|
ensure
|
44
|
-
|
52
|
+
Thread.current[extended_modules_thread_local_key] = nil
|
45
53
|
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def extended_modules_thread_local_key
|
57
|
+
"mixover_extended_modules_#{object_id}"
|
58
|
+
end
|
46
59
|
end
|
47
60
|
end
|
data/lib/rack/mount/prefix.rb
CHANGED
@@ -7,23 +7,29 @@ module Rack::Mount
|
|
7
7
|
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
8
8
|
SLASH = '/'.freeze
|
9
9
|
|
10
|
-
|
10
|
+
KEY = 'rack.mount.prefix'.freeze
|
11
|
+
|
12
|
+
def initialize(app, prefix = nil)
|
11
13
|
@app, @prefix = app, prefix.freeze
|
12
14
|
freeze
|
13
15
|
end
|
14
16
|
|
15
17
|
def call(env)
|
16
|
-
|
17
|
-
|
18
|
+
if prefix = env[KEY] || @prefix
|
19
|
+
old_path_info = env[PATH_INFO].dup
|
20
|
+
old_script_name = env[SCRIPT_NAME].dup
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
begin
|
23
|
+
env[PATH_INFO] = Utils.normalize_path(env[PATH_INFO].sub(prefix, EMPTY_STRING))
|
24
|
+
env[PATH_INFO] = EMPTY_STRING if env[PATH_INFO] == SLASH
|
25
|
+
env[SCRIPT_NAME] = Utils.normalize_path(env[SCRIPT_NAME].to_s + prefix)
|
26
|
+
@app.call(env)
|
27
|
+
ensure
|
28
|
+
env[PATH_INFO] = old_path_info
|
29
|
+
env[SCRIPT_NAME] = old_script_name
|
30
|
+
end
|
31
|
+
else
|
23
32
|
@app.call(env)
|
24
|
-
ensure
|
25
|
-
env[PATH_INFO] = old_path_info
|
26
|
-
env[SCRIPT_NAME] = old_script_name
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
@@ -39,6 +39,7 @@ module Rack::Mount
|
|
39
39
|
|
40
40
|
container.each_with_index { |route, i|
|
41
41
|
body << "route = self[#{i}]"
|
42
|
+
body << 'matches = {}'
|
42
43
|
body << 'params = route.defaults.dup'
|
43
44
|
|
44
45
|
conditions = []
|
@@ -46,10 +47,11 @@ module Rack::Mount
|
|
46
47
|
b = []
|
47
48
|
if condition.is_a?(Regexp)
|
48
49
|
b << "if m = obj.#{method}.match(#{condition.inspect})"
|
50
|
+
b << "matches[:#{method}] = m"
|
49
51
|
if (named_captures = route.named_captures[method]) && named_captures.any?
|
50
|
-
b << '
|
52
|
+
b << 'captures = m.captures'
|
51
53
|
b << 'p = nil'
|
52
|
-
b << named_captures.map { |k, j| "params[#{k.inspect}] = p if p =
|
54
|
+
b << named_captures.map { |k, j| "params[#{k.inspect}] = p if p = captures[#{j}]" }.join('; ')
|
53
55
|
end
|
54
56
|
else
|
55
57
|
b << "if m = obj.#{method} == route.conditions[:#{method}]"
|
@@ -61,7 +63,7 @@ module Rack::Mount
|
|
61
63
|
|
62
64
|
body << <<-RUBY
|
63
65
|
if #{conditions.join(' && ')}
|
64
|
-
yield route, params
|
66
|
+
yield route, matches, params
|
65
67
|
end
|
66
68
|
RUBY
|
67
69
|
}
|
@@ -76,7 +78,7 @@ module Rack::Mount
|
|
76
78
|
|
77
79
|
def optimize_recognize!
|
78
80
|
keys = @recognition_keys.map { |key|
|
79
|
-
if key.
|
81
|
+
if key.respond_to?(:call_source)
|
80
82
|
key.call_source(:cache, :obj)
|
81
83
|
else
|
82
84
|
"obj.#{key}"
|
@@ -94,12 +96,12 @@ module Rack::Mount
|
|
94
96
|
optimize_container_iterator(container) unless container.respond_to?(:optimized_each)
|
95
97
|
|
96
98
|
if block_given?
|
97
|
-
container.optimized_each(obj) do |route, params|
|
98
|
-
yield route, params
|
99
|
+
container.optimized_each(obj) do |route, matches, params|
|
100
|
+
yield route, matches, params
|
99
101
|
end
|
100
102
|
else
|
101
|
-
container.optimized_each(obj) do |route, params|
|
102
|
-
return route, params
|
103
|
+
container.optimized_each(obj) do |route, matches, params|
|
104
|
+
return route, matches, params
|
103
105
|
end
|
104
106
|
end
|
105
107
|
|
@@ -17,16 +17,31 @@ module Rack::Mount
|
|
17
17
|
}.freeze
|
18
18
|
}
|
19
19
|
@named_captures.freeze
|
20
|
+
|
21
|
+
if @conditions.has_key?(:path_info) &&
|
22
|
+
!Utils.regexp_anchored?(@conditions[:path_info])
|
23
|
+
@prefix = true
|
24
|
+
@app = Prefix.new(@app)
|
25
|
+
else
|
26
|
+
@prefix = false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def prefix?
|
31
|
+
@prefix
|
20
32
|
end
|
21
33
|
|
22
34
|
def recognize(obj)
|
23
|
-
|
35
|
+
matches = {}
|
36
|
+
params = @defaults.dup
|
37
|
+
|
24
38
|
if @conditions.all? { |method, condition|
|
25
39
|
value = obj.send(method)
|
26
40
|
if condition.is_a?(Regexp) && (m = value.match(condition))
|
27
|
-
matches = m
|
41
|
+
matches[method] = m
|
42
|
+
captures = m.captures
|
28
43
|
@named_captures[method].each { |k, i|
|
29
|
-
if v =
|
44
|
+
if v = captures[i]
|
30
45
|
params[k] = v
|
31
46
|
end
|
32
47
|
}
|
@@ -37,7 +52,7 @@ module Rack::Mount
|
|
37
52
|
false
|
38
53
|
end
|
39
54
|
}
|
40
|
-
params
|
55
|
+
return matches, params
|
41
56
|
else
|
42
57
|
nil
|
43
58
|
end
|
@@ -26,18 +26,19 @@ module Rack::Mount
|
|
26
26
|
|
27
27
|
cache = {}
|
28
28
|
keys = @recognition_keys.map { |key|
|
29
|
-
if key.
|
29
|
+
if key.respond_to?(:call_source)
|
30
30
|
key.call(cache, obj)
|
31
31
|
else
|
32
32
|
obj.send(key)
|
33
33
|
end
|
34
34
|
}
|
35
35
|
@recognition_graph[*keys].each do |route|
|
36
|
-
|
36
|
+
matches, params = route.recognize(obj)
|
37
|
+
if matches && params
|
37
38
|
if block_given?
|
38
|
-
yield route, params
|
39
|
+
yield route, matches, params
|
39
40
|
else
|
40
|
-
return route, params
|
41
|
+
return route, matches, params
|
41
42
|
end
|
42
43
|
end
|
43
44
|
end
|
@@ -62,10 +63,14 @@ module Rack::Mount
|
|
62
63
|
|
63
64
|
request = nil
|
64
65
|
req = @request_class.new(env)
|
65
|
-
recognize(req) do |route, params|
|
66
|
+
recognize(req) do |route, matches, params|
|
66
67
|
# TODO: We only want to unescape params from uri related methods
|
67
68
|
params.each { |k, v| params[k] = Utils.unescape_uri(v) if v.is_a?(String) }
|
68
69
|
|
70
|
+
if route.prefix?
|
71
|
+
env[Prefix::KEY] = matches[:path_info].to_s
|
72
|
+
end
|
73
|
+
|
69
74
|
env[@parameters_key] = params
|
70
75
|
result = route.app.call(env)
|
71
76
|
return result unless result[1][X_CASCADE] == PASS
|
@@ -81,6 +86,15 @@ module Rack::Mount
|
|
81
86
|
super
|
82
87
|
end
|
83
88
|
|
89
|
+
protected
|
90
|
+
def recognition_stats
|
91
|
+
{ :keys => @recognition_keys,
|
92
|
+
:keys_size => @recognition_keys.size,
|
93
|
+
:graph_size => @recognition_graph.size,
|
94
|
+
:graph_height => @recognition_graph.height,
|
95
|
+
:graph_average_height => @recognition_graph.average_height }
|
96
|
+
end
|
97
|
+
|
84
98
|
private
|
85
99
|
def expire!
|
86
100
|
@recognition_keys = @recognition_graph = nil
|
data/lib/rack/mount/strexp.rb
CHANGED
@@ -21,13 +21,14 @@ module Rack::Mount
|
|
21
21
|
#
|
22
22
|
# Strexp.compile('src/*files')
|
23
23
|
# # => %r{\Asrc/(?<files>.+)\Z}
|
24
|
-
def initialize(str, requirements = {}, separators = [])
|
24
|
+
def initialize(str, requirements = {}, separators = [], anchor = true)
|
25
25
|
return super(str) if str.is_a?(Regexp)
|
26
26
|
|
27
27
|
requirements = requirements ? requirements.dup : {}
|
28
28
|
normalize_requirements!(requirements, separators)
|
29
29
|
|
30
30
|
parser = StrexpParser.new
|
31
|
+
parser.anchor = anchor
|
31
32
|
parser.requirements = requirements
|
32
33
|
|
33
34
|
begin
|
@@ -20,7 +20,7 @@ else
|
|
20
20
|
REGEXP_NAMED_CAPTURE = '(?:<%s>%s)'.freeze
|
21
21
|
end
|
22
22
|
|
23
|
-
attr_accessor :requirements
|
23
|
+
attr_accessor :anchor, :requirements
|
24
24
|
##### State transition tables begin ###
|
25
25
|
|
26
26
|
racc_action_table = [
|
@@ -114,7 +114,7 @@ Racc_debug_parser = false
|
|
114
114
|
# reduce 0 omitted
|
115
115
|
|
116
116
|
def _reduce_1(val, _values, result)
|
117
|
-
result = "\\A#{val.join}\\Z"
|
117
|
+
result = anchor ? "\\A#{val.join}\\Z" : "\\A#{val.join}"
|
118
118
|
result
|
119
119
|
end
|
120
120
|
|
data/lib/rack/mount/utils.rb
CHANGED
@@ -88,6 +88,17 @@ module Rack::Mount
|
|
88
88
|
end
|
89
89
|
module_function :build_nested_query
|
90
90
|
|
91
|
+
# Determines whether the regexp must match the entire string.
|
92
|
+
#
|
93
|
+
# regexp_anchored?(/^foo$/) # => true
|
94
|
+
# regexp_anchored?(/foo/) # => false
|
95
|
+
# regexp_anchored?(/^foo/) # => false
|
96
|
+
# regexp_anchored?(/foo$/) # => false
|
97
|
+
def regexp_anchored?(regexp)
|
98
|
+
regexp.source =~ /\A(\\A|\^).*(\\Z|\$)\Z/ ? true : false
|
99
|
+
end
|
100
|
+
module_function :regexp_anchored?
|
101
|
+
|
91
102
|
def normalize_extended_expression(regexp)
|
92
103
|
return regexp unless regexp.options & Regexp::EXTENDED != 0
|
93
104
|
source = regexp.source
|