rack-mount 0.4.7 → 0.5.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.
- 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
|