rack-mount 0.6.1 → 0.6.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/lib/rack/mount.rb +3 -13
- data/lib/rack/mount/analysis/frequency.rb +0 -2
- data/lib/rack/mount/analysis/splitting.rb +2 -2
- data/lib/rack/mount/code_generation.rb +113 -0
- data/lib/rack/mount/generatable_regexp.rb +1 -1
- data/lib/rack/mount/route.rb +69 -5
- data/lib/rack/mount/route_set.rb +289 -46
- data/lib/rack/mount/strexp.rb +53 -50
- data/lib/rack/mount/utils.rb +7 -7
- data/lib/rack/mount/vendor/reginald/reginald.rb +2 -1
- data/lib/rack/mount/vendor/reginald/reginald/alternation.rb +11 -1
- data/lib/rack/mount/vendor/reginald/reginald/atom.rb +17 -5
- data/lib/rack/mount/vendor/reginald/reginald/character.rb +11 -2
- data/lib/rack/mount/vendor/reginald/reginald/character_class.rb +8 -20
- data/lib/rack/mount/vendor/reginald/reginald/collection.rb +10 -6
- data/lib/rack/mount/vendor/reginald/reginald/expression.rb +30 -20
- data/lib/rack/mount/vendor/reginald/reginald/group.rb +22 -8
- data/lib/rack/mount/vendor/reginald/reginald/options.rb +47 -0
- data/lib/rack/mount/vendor/reginald/reginald/parser.rb +137 -273
- data/lib/rack/mount/vendor/reginald/reginald/tokenizer.rb +1 -1
- data/lib/rack/mount/vendor/reginald/reginald/version.rb +3 -0
- data/lib/rack/mount/version.rb +1 -1
- metadata +6 -10
- data/lib/rack/mount/exceptions.rb +0 -3
- data/lib/rack/mount/generation/route.rb +0 -59
- data/lib/rack/mount/generation/route_set.rb +0 -200
- data/lib/rack/mount/mixover.rb +0 -60
- data/lib/rack/mount/recognition/code_generation.rb +0 -124
- data/lib/rack/mount/recognition/route.rb +0 -62
- data/lib/rack/mount/recognition/route_set.rb +0 -121
data/lib/rack/mount.rb
CHANGED
@@ -11,32 +11,22 @@ module Rack #:nodoc:
|
|
11
11
|
# other request attributes, session information, or even data dynamically
|
12
12
|
# pulled from a database.
|
13
13
|
module Mount
|
14
|
+
autoload :CodeGeneration, 'rack/mount/code_generation'
|
14
15
|
autoload :GeneratableRegexp, 'rack/mount/generatable_regexp'
|
15
|
-
autoload :Mixover, 'rack/mount/mixover'
|
16
16
|
autoload :Multimap, 'rack/mount/multimap'
|
17
17
|
autoload :Prefix, 'rack/mount/prefix'
|
18
18
|
autoload :RegexpWithNamedGroups, 'rack/mount/regexp_with_named_groups'
|
19
19
|
autoload :Route, 'rack/mount/route'
|
20
20
|
autoload :RouteSet, 'rack/mount/route_set'
|
21
|
-
autoload :RoutingError, 'rack/mount/
|
21
|
+
autoload :RoutingError, 'rack/mount/route_set'
|
22
22
|
autoload :Strexp, 'rack/mount/strexp'
|
23
23
|
autoload :Utils, 'rack/mount/utils'
|
24
|
+
autoload :Version, 'rack/mount/version'
|
24
25
|
|
25
26
|
module Analysis #:nodoc:
|
26
27
|
autoload :Frequency, 'rack/mount/analysis/frequency'
|
27
28
|
autoload :Histogram, 'rack/mount/analysis/histogram'
|
28
29
|
autoload :Splitting, 'rack/mount/analysis/splitting'
|
29
30
|
end
|
30
|
-
|
31
|
-
module Generation #:nodoc:
|
32
|
-
autoload :Route, 'rack/mount/generation/route'
|
33
|
-
autoload :RouteSet, 'rack/mount/generation/route_set'
|
34
|
-
end
|
35
|
-
|
36
|
-
module Recognition #:nodoc:
|
37
|
-
autoload :CodeGeneration, 'rack/mount/recognition/code_generation'
|
38
|
-
autoload :Route, 'rack/mount/recognition/route'
|
39
|
-
autoload :RouteSet, 'rack/mount/recognition/route_set'
|
40
|
-
end
|
41
31
|
end
|
42
32
|
end
|
@@ -2,7 +2,7 @@ require 'rack/mount/utils'
|
|
2
2
|
|
3
3
|
module Rack::Mount
|
4
4
|
module Analysis
|
5
|
-
|
5
|
+
class Splitting < Frequency
|
6
6
|
NULL = "\0".freeze
|
7
7
|
|
8
8
|
class Key < Struct.new(:method, :index, :separators)
|
@@ -67,7 +67,7 @@ module Rack::Mount
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
if inside = part[0]
|
70
|
+
if inside = part.expression[0]
|
71
71
|
if inside.is_a?(Reginald::Character) && inside.literal?
|
72
72
|
boundaries << inside.to_s
|
73
73
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Rack::Mount
|
2
|
+
module CodeGeneration #:nodoc:
|
3
|
+
def _expired_recognize(env) #:nodoc:
|
4
|
+
raise 'route set not finalized'
|
5
|
+
end
|
6
|
+
|
7
|
+
def rehash
|
8
|
+
super
|
9
|
+
optimize_recognize!
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def expire!
|
14
|
+
if @optimized_recognize_defined
|
15
|
+
remove_metaclass_method :recognize
|
16
|
+
|
17
|
+
class << self
|
18
|
+
alias_method :recognize, :_expired_recognize
|
19
|
+
end
|
20
|
+
|
21
|
+
@optimized_recognize_defined = false
|
22
|
+
end
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def optimize_container_iterator(container)
|
28
|
+
body = []
|
29
|
+
|
30
|
+
container.each_with_index { |route, i|
|
31
|
+
body << "route = self[#{i}]"
|
32
|
+
body << 'matches = {}'
|
33
|
+
body << 'params = route.defaults.dup'
|
34
|
+
|
35
|
+
conditions = []
|
36
|
+
route.conditions.each do |method, condition|
|
37
|
+
b = []
|
38
|
+
if condition.is_a?(Regexp)
|
39
|
+
b << "if m = obj.#{method}.match(#{condition.inspect})"
|
40
|
+
b << "matches[:#{method}] = m"
|
41
|
+
if (named_captures = route.named_captures[method]) && named_captures.any?
|
42
|
+
b << 'captures = m.captures'
|
43
|
+
b << 'p = nil'
|
44
|
+
b << named_captures.map { |k, j| "params[#{k.inspect}] = p if p = captures[#{j}]" }.join('; ')
|
45
|
+
end
|
46
|
+
else
|
47
|
+
b << "if m = obj.#{method} == route.conditions[:#{method}]"
|
48
|
+
end
|
49
|
+
b << 'true'
|
50
|
+
b << 'end'
|
51
|
+
conditions << "(#{b.join('; ')})"
|
52
|
+
end
|
53
|
+
|
54
|
+
body << <<-RUBY
|
55
|
+
if #{conditions.join(' && ')}
|
56
|
+
yield route, matches, params
|
57
|
+
end
|
58
|
+
RUBY
|
59
|
+
}
|
60
|
+
|
61
|
+
container.instance_eval(<<-RUBY, __FILE__, __LINE__)
|
62
|
+
def optimized_each(obj)
|
63
|
+
#{body.join("\n")}
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
RUBY
|
67
|
+
end
|
68
|
+
|
69
|
+
def optimize_recognize!
|
70
|
+
keys = @recognition_keys.map { |key|
|
71
|
+
if key.respond_to?(:call_source)
|
72
|
+
key.call_source(:cache, :obj)
|
73
|
+
else
|
74
|
+
"obj.#{key}"
|
75
|
+
end
|
76
|
+
}.join(', ')
|
77
|
+
|
78
|
+
@optimized_recognize_defined = true
|
79
|
+
|
80
|
+
remove_metaclass_method :recognize
|
81
|
+
|
82
|
+
instance_eval(<<-RUBY, __FILE__, __LINE__)
|
83
|
+
def recognize(obj)
|
84
|
+
cache = {}
|
85
|
+
container = @recognition_graph[#{keys}]
|
86
|
+
optimize_container_iterator(container) unless container.respond_to?(:optimized_each)
|
87
|
+
|
88
|
+
if block_given?
|
89
|
+
container.optimized_each(obj) do |route, matches, params|
|
90
|
+
yield route, matches, params
|
91
|
+
end
|
92
|
+
else
|
93
|
+
container.optimized_each(obj) do |route, matches, params|
|
94
|
+
return route, matches, params
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
RUBY
|
101
|
+
end
|
102
|
+
|
103
|
+
# method_defined? can't distinguish between instance
|
104
|
+
# and meta methods. So we have to rescue if the method
|
105
|
+
# has not been defined in the metaclass yet.
|
106
|
+
def remove_metaclass_method(symbol)
|
107
|
+
metaclass = class << self; self; end
|
108
|
+
metaclass.send(:remove_method, symbol)
|
109
|
+
rescue NameError => e
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/rack/mount/route.rb
CHANGED
@@ -9,11 +9,6 @@ module Rack::Mount
|
|
9
9
|
# new Route objects. Instead use the factory method, RouteSet#add_route
|
10
10
|
# to create new routes and add them to the set.
|
11
11
|
class Route
|
12
|
-
extend Mixover
|
13
|
-
|
14
|
-
# Include generation and recognition concerns
|
15
|
-
include Generation::Route, Recognition::Route
|
16
|
-
|
17
12
|
# Valid rack application to call if conditions are met
|
18
13
|
attr_reader :app
|
19
14
|
|
@@ -27,6 +22,8 @@ module Rack::Mount
|
|
27
22
|
# Symbol identifier for the route used with named route generations
|
28
23
|
attr_reader :name
|
29
24
|
|
25
|
+
attr_reader :named_captures
|
26
|
+
|
30
27
|
def initialize(app, conditions, defaults, name)
|
31
28
|
unless app.respond_to?(:call)
|
32
29
|
raise ArgumentError, 'app must be a valid rack application' \
|
@@ -54,9 +51,76 @@ module Rack::Mount
|
|
54
51
|
@conditions[method] = pattern.freeze
|
55
52
|
end
|
56
53
|
|
54
|
+
@named_captures = {}
|
55
|
+
@conditions.map { |method, condition|
|
56
|
+
next unless condition.respond_to?(:named_captures)
|
57
|
+
@named_captures[method] = condition.named_captures.inject({}) { |named_captures, (k, v)|
|
58
|
+
named_captures[k.to_sym] = v.last - 1
|
59
|
+
named_captures
|
60
|
+
}.freeze
|
61
|
+
}
|
62
|
+
@named_captures.freeze
|
63
|
+
|
64
|
+
if @conditions.has_key?(:path_info) &&
|
65
|
+
!Utils.regexp_anchored?(@conditions[:path_info])
|
66
|
+
@prefix = true
|
67
|
+
@app = Prefix.new(@app)
|
68
|
+
else
|
69
|
+
@prefix = false
|
70
|
+
end
|
71
|
+
|
57
72
|
@conditions.freeze
|
58
73
|
end
|
59
74
|
|
75
|
+
def prefix?
|
76
|
+
@prefix
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def generation_keys
|
81
|
+
@conditions.inject({}) { |keys, (method, condition)|
|
82
|
+
if condition.respond_to?(:required_defaults)
|
83
|
+
keys.merge!(condition.required_defaults)
|
84
|
+
else
|
85
|
+
keys
|
86
|
+
end
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def significant_params?
|
91
|
+
@has_significant_params ||= @conditions.any? { |method, condition|
|
92
|
+
(condition.respond_to?(:required_params) && condition.required_params.any?) ||
|
93
|
+
(condition.respond_to?(:required_defaults) && condition.required_defaults.any?)
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
def generate(method, params = {}, recall = {}, options = {})
|
98
|
+
if method.nil?
|
99
|
+
result = @conditions.inject({}) { |h, (m, condition)|
|
100
|
+
if condition.respond_to?(:generate)
|
101
|
+
h[m] = condition.generate(params, recall, options)
|
102
|
+
end
|
103
|
+
h
|
104
|
+
}
|
105
|
+
return nil if result.values.compact.empty?
|
106
|
+
else
|
107
|
+
if condition = @conditions[method]
|
108
|
+
if condition.respond_to?(:generate)
|
109
|
+
result = condition.generate(params, recall, options)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
if result
|
115
|
+
@defaults.each do |key, value|
|
116
|
+
params.delete(key) if params[key] == value
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
result
|
121
|
+
end
|
122
|
+
|
123
|
+
|
60
124
|
def inspect #:nodoc:
|
61
125
|
"#<#{self.class.name} @app=#{@app.inspect} @conditions=#{@conditions.inspect} @defaults=#{@defaults.inspect} @name=#{@name.inspect}>"
|
62
126
|
end
|
data/lib/rack/mount/route_set.rb
CHANGED
@@ -3,16 +3,12 @@ require 'rack/mount/route'
|
|
3
3
|
require 'rack/mount/utils'
|
4
4
|
|
5
5
|
module Rack::Mount
|
6
|
-
class
|
7
|
-
extend Mixover
|
8
|
-
|
9
|
-
# Include generation and recognition concerns
|
10
|
-
include Generation::RouteSet, Recognition::RouteSet
|
11
|
-
include Recognition::CodeGeneration
|
6
|
+
class RoutingError < StandardError; end
|
12
7
|
|
8
|
+
class RouteSet
|
13
9
|
# Initialize a new RouteSet without optimizations
|
14
|
-
def self.new_without_optimizations(
|
15
|
-
|
10
|
+
def self.new_without_optimizations(options = {}, &block)
|
11
|
+
new(options.merge(:_optimize => false), &block)
|
16
12
|
end
|
17
13
|
|
18
14
|
# Basic RouteSet initializer.
|
@@ -23,12 +19,19 @@ module Rack::Mount
|
|
23
19
|
# - <tt>Generation::RouteSet.new</tt>
|
24
20
|
# - <tt>Recognition::RouteSet.new</tt>
|
25
21
|
def initialize(options = {}, &block)
|
22
|
+
@parameters_key = options.delete(:parameters_key) || 'rack.routing_args'
|
23
|
+
@parameters_key.freeze
|
24
|
+
|
25
|
+
@named_routes = {}
|
26
|
+
|
27
|
+
@recognition_key_analyzer = Analysis::Splitting.new
|
28
|
+
@generation_key_analyzer = Analysis::Frequency.new
|
29
|
+
|
26
30
|
@request_class = options.delete(:request_class) || Rack::Request
|
27
|
-
@valid_conditions =
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
31
|
+
@valid_conditions = @request_class.public_instance_methods.map! { |m| m.to_sym }
|
32
|
+
|
33
|
+
extend CodeGeneration unless options[:_optimize] == false
|
34
|
+
@optimized_recognize_defined = false
|
32
35
|
|
33
36
|
@routes = []
|
34
37
|
expire!
|
@@ -49,26 +52,191 @@ module Rack::Mount
|
|
49
52
|
# <tt>name</tt>:: Symbol identifier for the route used with named
|
50
53
|
# route generations
|
51
54
|
def add_route(app, conditions = {}, defaults = {}, name = nil)
|
52
|
-
|
55
|
+
unless conditions.is_a?(Hash)
|
56
|
+
raise ArgumentError, 'conditions must be a Hash'
|
57
|
+
end
|
58
|
+
|
59
|
+
unless conditions.all? { |method, pattern|
|
60
|
+
@valid_conditions.include?(method)
|
61
|
+
}
|
62
|
+
raise ArgumentError, 'conditions may only include ' +
|
63
|
+
@valid_conditions.inspect
|
64
|
+
end
|
65
|
+
|
53
66
|
route = Route.new(app, conditions, defaults, name)
|
54
67
|
@routes << route
|
68
|
+
|
69
|
+
@recognition_key_analyzer << route.conditions
|
70
|
+
|
71
|
+
@named_routes[route.name] = route if route.name
|
72
|
+
@generation_key_analyzer << route.generation_keys
|
73
|
+
|
55
74
|
expire!
|
56
75
|
route
|
57
76
|
end
|
58
77
|
|
59
|
-
|
78
|
+
def recognize(obj)
|
79
|
+
raise 'route set not finalized' unless @recognition_graph
|
80
|
+
|
81
|
+
cache = {}
|
82
|
+
keys = @recognition_keys.map { |key|
|
83
|
+
if key.respond_to?(:call)
|
84
|
+
key.call(cache, obj)
|
85
|
+
else
|
86
|
+
obj.send(key)
|
87
|
+
end
|
88
|
+
}
|
89
|
+
|
90
|
+
@recognition_graph[*keys].each do |route|
|
91
|
+
matches = {}
|
92
|
+
params = route.defaults.dup
|
93
|
+
|
94
|
+
if route.conditions.all? { |method, condition|
|
95
|
+
value = obj.send(method)
|
96
|
+
if condition.is_a?(Regexp) && (m = value.match(condition))
|
97
|
+
matches[method] = m
|
98
|
+
captures = m.captures
|
99
|
+
route.named_captures[method].each do |k, i|
|
100
|
+
if v = captures[i]
|
101
|
+
params[k] = v
|
102
|
+
end
|
103
|
+
end
|
104
|
+
true
|
105
|
+
elsif value == condition
|
106
|
+
true
|
107
|
+
else
|
108
|
+
false
|
109
|
+
end
|
110
|
+
}
|
111
|
+
if block_given?
|
112
|
+
yield route, matches, params
|
113
|
+
else
|
114
|
+
return route, matches, params
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
|
122
|
+
X_CASCADE = 'X-Cascade'.freeze
|
123
|
+
PASS = 'pass'.freeze
|
124
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
125
|
+
|
126
|
+
# Rack compatible recognition and dispatching method. Routes are
|
127
|
+
# tried until one returns a non-catch status code. If no routes
|
128
|
+
# match, the catch status code is returned.
|
129
|
+
#
|
130
|
+
# This method can only be invoked after the RouteSet has been
|
131
|
+
# finalized.
|
60
132
|
def call(env)
|
61
|
-
raise
|
133
|
+
raise 'route set not finalized' unless @recognition_graph
|
134
|
+
|
135
|
+
env[PATH_INFO] = Utils.normalize_path(env[PATH_INFO])
|
136
|
+
|
137
|
+
request = nil
|
138
|
+
req = @request_class.new(env)
|
139
|
+
recognize(req) do |route, matches, params|
|
140
|
+
# TODO: We only want to unescape params from uri related methods
|
141
|
+
params.each { |k, v| params[k] = Utils.unescape_uri(v) if v.is_a?(String) }
|
142
|
+
|
143
|
+
if route.prefix?
|
144
|
+
env[Prefix::KEY] = matches[:path_info].to_s
|
145
|
+
end
|
146
|
+
|
147
|
+
env[@parameters_key] = params
|
148
|
+
result = route.app.call(env)
|
149
|
+
return result unless result[1][X_CASCADE] == PASS
|
150
|
+
end
|
151
|
+
|
152
|
+
request || [404, {'Content-Type' => 'text/html', 'X-Cascade' => 'pass'}, ['Not Found']]
|
62
153
|
end
|
63
154
|
|
64
|
-
#
|
65
|
-
|
66
|
-
|
155
|
+
# Generates a url from Rack env and identifiers or significant keys.
|
156
|
+
#
|
157
|
+
# To generate a url by named route, pass the name in as a +Symbol+.
|
158
|
+
# url(env, :dashboard) # => "/dashboard"
|
159
|
+
#
|
160
|
+
# Additional parameters can be passed in as a hash
|
161
|
+
# url(env, :people, :id => "1") # => "/people/1"
|
162
|
+
#
|
163
|
+
# If no name route is given, it will fall back to a slower
|
164
|
+
# generation search.
|
165
|
+
# url(env, :controller => "people", :action => "show", :id => "1")
|
166
|
+
# # => "/people/1"
|
167
|
+
def url(env, *args)
|
168
|
+
named_route, params = nil, {}
|
169
|
+
|
170
|
+
case args.length
|
171
|
+
when 2
|
172
|
+
named_route, params = args[0], args[1].dup
|
173
|
+
when 1
|
174
|
+
if args[0].is_a?(Hash)
|
175
|
+
params = args[0].dup
|
176
|
+
else
|
177
|
+
named_route = args[0]
|
178
|
+
end
|
179
|
+
else
|
180
|
+
raise ArgumentError
|
181
|
+
end
|
182
|
+
|
183
|
+
only_path = params.delete(:only_path)
|
184
|
+
recall = env[@parameters_key] || {}
|
185
|
+
|
186
|
+
unless result = generate(:all, named_route, params, recall,
|
187
|
+
:parameterize => lambda { |name, param| Utils.escape_uri(param) })
|
188
|
+
return
|
189
|
+
end
|
190
|
+
|
191
|
+
parts, params = result
|
192
|
+
return unless parts
|
193
|
+
|
194
|
+
params.each do |k, v|
|
195
|
+
if v
|
196
|
+
params[k] = v
|
197
|
+
else
|
198
|
+
params.delete(k)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
req = stubbed_request_class.new(env)
|
203
|
+
req._stubbed_values = parts.merge(:query_string => Utils.build_nested_query(params))
|
204
|
+
only_path ? req.fullpath : req.url
|
67
205
|
end
|
68
206
|
|
69
|
-
|
70
|
-
|
71
|
-
|
207
|
+
def generate(method, *args) #:nodoc:
|
208
|
+
raise 'route set not finalized' unless @generation_graph
|
209
|
+
|
210
|
+
method = nil if method == :all
|
211
|
+
named_route, params, recall, options = extract_params!(*args)
|
212
|
+
merged = recall.merge(params)
|
213
|
+
route = nil
|
214
|
+
|
215
|
+
if named_route
|
216
|
+
if route = @named_routes[named_route.to_sym]
|
217
|
+
recall = route.defaults.merge(recall)
|
218
|
+
url = route.generate(method, params, recall, options)
|
219
|
+
[url, params]
|
220
|
+
else
|
221
|
+
raise RoutingError, "#{named_route} failed to generate from #{params.inspect}"
|
222
|
+
end
|
223
|
+
else
|
224
|
+
keys = @generation_keys.map { |key|
|
225
|
+
if k = merged[key]
|
226
|
+
k.to_s
|
227
|
+
else
|
228
|
+
nil
|
229
|
+
end
|
230
|
+
}
|
231
|
+
@generation_graph[*keys].each do |r|
|
232
|
+
next unless r.significant_params?
|
233
|
+
if url = r.generate(method, params, recall, options)
|
234
|
+
return [url, params]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
raise RoutingError, "No route matches #{params.inspect}"
|
239
|
+
end
|
72
240
|
end
|
73
241
|
|
74
242
|
# Number of routes in the set
|
@@ -77,6 +245,10 @@ module Rack::Mount
|
|
77
245
|
end
|
78
246
|
|
79
247
|
def rehash #:nodoc:
|
248
|
+
@recognition_keys = build_recognition_keys
|
249
|
+
@recognition_graph = build_recognition_graph
|
250
|
+
@generation_keys = build_generation_keys
|
251
|
+
@generation_graph = build_generation_graph
|
80
252
|
end
|
81
253
|
|
82
254
|
# Finalizes the set and builds optimized data structures. You *must*
|
@@ -85,7 +257,11 @@ module Rack::Mount
|
|
85
257
|
def freeze
|
86
258
|
unless frozen?
|
87
259
|
rehash
|
88
|
-
|
260
|
+
|
261
|
+
@recognition_key_analyzer = nil
|
262
|
+
@generation_key_analyzer = nil
|
263
|
+
@valid_conditions = nil
|
264
|
+
|
89
265
|
@routes.each { |route| route.freeze }
|
90
266
|
@routes.freeze
|
91
267
|
end
|
@@ -104,44 +280,35 @@ module Rack::Mount
|
|
104
280
|
hash[:@recognition_graph] = graph.dup
|
105
281
|
end
|
106
282
|
|
107
|
-
included_modules = (class << self; included_modules; end)
|
108
|
-
included_modules.reject! { |mod| mod == Kernel }
|
109
|
-
hash[:included_modules] = included_modules
|
110
|
-
|
111
283
|
hash
|
112
284
|
end
|
113
285
|
|
114
286
|
def marshal_load(hash) #:nodoc:
|
115
|
-
hash.delete(:included_modules).reverse.each { |mod| extend(mod) }
|
116
|
-
|
117
287
|
hash.each do |ivar, value|
|
118
288
|
instance_variable_set(ivar, value)
|
119
289
|
end
|
120
290
|
end
|
121
291
|
|
292
|
+
protected
|
293
|
+
def recognition_stats
|
294
|
+
{ :keys => @recognition_keys,
|
295
|
+
:keys_size => @recognition_keys.size,
|
296
|
+
:graph_size => @recognition_graph.size,
|
297
|
+
:graph_height => @recognition_graph.height,
|
298
|
+
:graph_average_height => @recognition_graph.average_height }
|
299
|
+
end
|
300
|
+
|
122
301
|
private
|
123
302
|
def expire! #:nodoc:
|
124
|
-
|
303
|
+
@recognition_keys = @recognition_graph = nil
|
304
|
+
@recognition_key_analyzer.expire!
|
125
305
|
|
126
|
-
|
127
|
-
@
|
306
|
+
@generation_keys = @generation_graph = nil
|
307
|
+
@generation_key_analyzer.expire!
|
128
308
|
end
|
129
309
|
|
130
310
|
def instance_variables_to_serialize
|
131
|
-
instance_variables.map { |ivar| ivar.to_sym }
|
132
|
-
end
|
133
|
-
|
134
|
-
def validate_conditions!(conditions)
|
135
|
-
unless conditions.is_a?(Hash)
|
136
|
-
raise ArgumentError, 'conditions must be a Hash'
|
137
|
-
end
|
138
|
-
|
139
|
-
unless conditions.all? { |method, pattern|
|
140
|
-
@valid_conditions.include?(method)
|
141
|
-
}
|
142
|
-
raise ArgumentError, 'conditions may only include ' +
|
143
|
-
@valid_conditions.inspect
|
144
|
-
end
|
311
|
+
instance_variables.map { |ivar| ivar.to_sym } - [:@stubbed_request_class, :@optimized_recognize_defined]
|
145
312
|
end
|
146
313
|
|
147
314
|
# An internal helper method for constructing a nested set from
|
@@ -162,5 +329,81 @@ module Rack::Mount
|
|
162
329
|
end
|
163
330
|
graph
|
164
331
|
end
|
332
|
+
|
333
|
+
def build_recognition_graph
|
334
|
+
build_nested_route_set(@recognition_keys) { |k, i|
|
335
|
+
@recognition_key_analyzer.possible_keys[i][k]
|
336
|
+
}
|
337
|
+
end
|
338
|
+
|
339
|
+
def build_recognition_keys
|
340
|
+
@recognition_key_analyzer.report
|
341
|
+
end
|
342
|
+
|
343
|
+
def build_generation_graph
|
344
|
+
build_nested_route_set(@generation_keys) { |k, i|
|
345
|
+
throw :skip unless @routes[i].significant_params?
|
346
|
+
|
347
|
+
if k = @generation_key_analyzer.possible_keys[i][k]
|
348
|
+
k.to_s
|
349
|
+
else
|
350
|
+
nil
|
351
|
+
end
|
352
|
+
}
|
353
|
+
end
|
354
|
+
|
355
|
+
def build_generation_keys
|
356
|
+
@generation_key_analyzer.report
|
357
|
+
end
|
358
|
+
|
359
|
+
def extract_params!(*args)
|
360
|
+
case args.length
|
361
|
+
when 4
|
362
|
+
named_route, params, recall, options = args
|
363
|
+
when 3
|
364
|
+
if args[0].is_a?(Hash)
|
365
|
+
params, recall, options = args
|
366
|
+
else
|
367
|
+
named_route, params, recall = args
|
368
|
+
end
|
369
|
+
when 2
|
370
|
+
if args[0].is_a?(Hash)
|
371
|
+
params, recall = args
|
372
|
+
else
|
373
|
+
named_route, params = args
|
374
|
+
end
|
375
|
+
when 1
|
376
|
+
if args[0].is_a?(Hash)
|
377
|
+
params = args[0]
|
378
|
+
else
|
379
|
+
named_route = args[0]
|
380
|
+
end
|
381
|
+
else
|
382
|
+
raise ArgumentError
|
383
|
+
end
|
384
|
+
|
385
|
+
named_route ||= nil
|
386
|
+
params ||= {}
|
387
|
+
recall ||= {}
|
388
|
+
options ||= {}
|
389
|
+
|
390
|
+
[named_route, params.dup, recall.dup, options.dup]
|
391
|
+
end
|
392
|
+
|
393
|
+
def stubbed_request_class
|
394
|
+
@stubbed_request_class ||= begin
|
395
|
+
klass = Class.new(@request_class)
|
396
|
+
klass.public_instance_methods.each do |method|
|
397
|
+
next if method =~ /^__|object_id/
|
398
|
+
klass.class_eval <<-RUBY
|
399
|
+
def #{method}(*args, &block)
|
400
|
+
@_stubbed_values[:#{method}] || super
|
401
|
+
end
|
402
|
+
RUBY
|
403
|
+
end
|
404
|
+
klass.class_eval { attr_accessor :_stubbed_values }
|
405
|
+
klass
|
406
|
+
end
|
407
|
+
end
|
165
408
|
end
|
166
409
|
end
|