actionpack 4.2.10 → 5.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +553 -401
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/abstract_controller/base.rb +28 -38
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
- data/lib/abstract_controller/caching.rb +62 -0
- data/lib/abstract_controller/callbacks.rb +52 -19
- data/lib/abstract_controller/collector.rb +4 -9
- data/lib/abstract_controller/error.rb +4 -0
- data/lib/abstract_controller/helpers.rb +4 -3
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
- data/lib/abstract_controller/rendering.rb +28 -18
- data/lib/abstract_controller/translation.rb +8 -7
- data/lib/abstract_controller.rb +6 -2
- data/lib/action_controller/api/api_rendering.rb +14 -0
- data/lib/action_controller/api.rb +147 -0
- data/lib/action_controller/base.rb +10 -13
- data/lib/action_controller/caching.rb +13 -58
- data/lib/action_controller/form_builder.rb +48 -0
- data/lib/action_controller/log_subscriber.rb +3 -10
- data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
- data/lib/action_controller/metal/conditional_get.rb +106 -34
- data/lib/action_controller/metal/cookies.rb +1 -3
- data/lib/action_controller/metal/data_streaming.rb +11 -32
- data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +11 -6
- data/lib/action_controller/metal/force_ssl.rb +10 -10
- data/lib/action_controller/metal/head.rb +14 -8
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +44 -35
- data/lib/action_controller/metal/implicit_render.rb +61 -6
- data/lib/action_controller/metal/instrumentation.rb +5 -5
- data/lib/action_controller/metal/live.rb +66 -88
- data/lib/action_controller/metal/mime_responds.rb +27 -42
- data/lib/action_controller/metal/params_wrapper.rb +8 -8
- data/lib/action_controller/metal/redirecting.rb +32 -9
- data/lib/action_controller/metal/renderers.rb +85 -40
- data/lib/action_controller/metal/rendering.rb +38 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
- data/lib/action_controller/metal/rescue.rb +3 -12
- data/lib/action_controller/metal/streaming.rb +4 -4
- data/lib/action_controller/metal/strong_parameters.rb +293 -90
- data/lib/action_controller/metal/testing.rb +1 -12
- data/lib/action_controller/metal/url_for.rb +12 -5
- data/lib/action_controller/metal.rb +88 -63
- data/lib/action_controller/renderer.rb +111 -0
- data/lib/action_controller/template_assertions.rb +9 -0
- data/lib/action_controller/test_case.rb +288 -368
- data/lib/action_controller.rb +12 -9
- data/lib/action_dispatch/http/cache.rb +73 -34
- data/lib/action_dispatch/http/filter_parameters.rb +15 -11
- data/lib/action_dispatch/http/filter_redirect.rb +7 -8
- data/lib/action_dispatch/http/headers.rb +44 -13
- data/lib/action_dispatch/http/mime_negotiation.rb +41 -23
- data/lib/action_dispatch/http/mime_type.rb +126 -90
- data/lib/action_dispatch/http/mime_types.rb +3 -4
- data/lib/action_dispatch/http/parameter_filter.rb +18 -8
- data/lib/action_dispatch/http/parameters.rb +54 -41
- data/lib/action_dispatch/http/request.rb +149 -82
- data/lib/action_dispatch/http/response.rb +206 -102
- data/lib/action_dispatch/http/url.rb +117 -8
- data/lib/action_dispatch/journey/formatter.rb +39 -28
- data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
- data/lib/action_dispatch/journey/nodes/node.rb +14 -4
- data/lib/action_dispatch/journey/parser_extras.rb +4 -0
- data/lib/action_dispatch/journey/path/pattern.rb +38 -42
- data/lib/action_dispatch/journey/route.rb +74 -19
- data/lib/action_dispatch/journey/router/utils.rb +5 -5
- data/lib/action_dispatch/journey/router.rb +5 -9
- data/lib/action_dispatch/journey/routes.rb +14 -15
- data/lib/action_dispatch/journey/visitors.rb +86 -43
- data/lib/action_dispatch/middleware/callbacks.rb +10 -1
- data/lib/action_dispatch/middleware/cookies.rb +189 -135
- data/lib/action_dispatch/middleware/debug_exceptions.rb +124 -49
- data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
- data/lib/action_dispatch/middleware/executor.rb +19 -0
- data/lib/action_dispatch/middleware/flash.rb +66 -45
- data/lib/action_dispatch/middleware/params_parser.rb +32 -46
- data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
- data/lib/action_dispatch/middleware/reloader.rb +14 -58
- data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
- data/lib/action_dispatch/middleware/request_id.rb +11 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
- data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
- data/lib/action_dispatch/middleware/ssl.rb +115 -36
- data/lib/action_dispatch/middleware/stack.rb +44 -40
- data/lib/action_dispatch/middleware/static.rb +51 -35
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
- data/lib/action_dispatch/railtie.rb +2 -2
- data/lib/action_dispatch/request/session.rb +69 -33
- data/lib/action_dispatch/request/utils.rb +51 -19
- data/lib/action_dispatch/routing/inspector.rb +32 -43
- data/lib/action_dispatch/routing/mapper.rb +491 -338
- data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
- data/lib/action_dispatch/routing/redirection.rb +3 -3
- data/lib/action_dispatch/routing/route_set.rb +145 -238
- data/lib/action_dispatch/routing/url_for.rb +27 -10
- data/lib/action_dispatch/routing.rb +17 -13
- data/lib/action_dispatch/testing/assertion_response.rb +45 -0
- data/lib/action_dispatch/testing/assertions/response.rb +38 -20
- data/lib/action_dispatch/testing/assertions/routing.rb +11 -10
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +368 -97
- data/lib/action_dispatch/testing/test_process.rb +5 -6
- data/lib/action_dispatch/testing/test_request.rb +22 -31
- data/lib/action_dispatch/testing/test_response.rb +7 -4
- data/lib/action_dispatch.rb +3 -1
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack.rb +1 -1
- metadata +30 -34
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
- /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'action_controller/metal/exceptions'
|
|
2
|
-
require 'active_support/deprecation'
|
|
3
2
|
|
|
4
3
|
module ActionDispatch
|
|
5
4
|
module Journey
|
|
@@ -15,7 +14,7 @@ module ActionDispatch
|
|
|
15
14
|
|
|
16
15
|
def generate(name, options, path_parameters, parameterize = nil)
|
|
17
16
|
constraints = path_parameters.merge(options)
|
|
18
|
-
missing_keys =
|
|
17
|
+
missing_keys = nil # need for variable scope
|
|
19
18
|
|
|
20
19
|
match_route(name, constraints) do |route|
|
|
21
20
|
parameterized_parts = extract_parameterized_parts(route, options, path_parameters, parameterize)
|
|
@@ -26,22 +25,27 @@ module ActionDispatch
|
|
|
26
25
|
next unless name || route.dispatcher?
|
|
27
26
|
|
|
28
27
|
missing_keys = missing_keys(route, parameterized_parts)
|
|
29
|
-
next
|
|
28
|
+
next if missing_keys && !missing_keys.empty?
|
|
30
29
|
params = options.dup.delete_if do |key, _|
|
|
31
30
|
parameterized_parts.key?(key) || route.defaults.key?(key)
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
defaults = route.defaults
|
|
35
34
|
required_parts = route.required_parts
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
|
|
36
|
+
route.parts.reverse_each do |key|
|
|
37
|
+
break if defaults[key].nil? && parameterized_parts[key].present?
|
|
38
|
+
break if parameterized_parts[key].to_s != defaults[key].to_s
|
|
39
|
+
break if required_parts.include?(key)
|
|
40
|
+
|
|
41
|
+
parameterized_parts.delete(key)
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
return [route.format(parameterized_parts), params]
|
|
41
45
|
end
|
|
42
46
|
|
|
43
47
|
message = "No route matches #{Hash[constraints.sort_by{|k,v| k.to_s}].inspect}"
|
|
44
|
-
message << " missing required keys: #{missing_keys.sort.inspect}"
|
|
48
|
+
message << " missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
|
|
45
49
|
|
|
46
50
|
raise ActionController::UrlGenerationError, message
|
|
47
51
|
end
|
|
@@ -55,12 +59,12 @@ module ActionDispatch
|
|
|
55
59
|
def extract_parameterized_parts(route, options, recall, parameterize = nil)
|
|
56
60
|
parameterized_parts = recall.merge(options)
|
|
57
61
|
|
|
58
|
-
keys_to_keep = route.parts.
|
|
62
|
+
keys_to_keep = route.parts.reverse_each.drop_while { |part|
|
|
59
63
|
!options.key?(part) || (options[part] || recall[part]).nil?
|
|
60
64
|
} | route.required_parts
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
parameterized_parts.delete_if do |bad_key, _|
|
|
67
|
+
!keys_to_keep.include?(bad_key)
|
|
64
68
|
end
|
|
65
69
|
|
|
66
70
|
if parameterize
|
|
@@ -81,9 +85,6 @@ module ActionDispatch
|
|
|
81
85
|
if named_routes.key?(name)
|
|
82
86
|
yield named_routes[name]
|
|
83
87
|
else
|
|
84
|
-
# Make sure we don't show the deprecation warning more than once
|
|
85
|
-
warned = false
|
|
86
|
-
|
|
87
88
|
routes = non_recursive(cache, options)
|
|
88
89
|
|
|
89
90
|
hash = routes.group_by { |_, r| r.score(options) }
|
|
@@ -92,17 +93,6 @@ module ActionDispatch
|
|
|
92
93
|
break if score < 0
|
|
93
94
|
|
|
94
95
|
hash[score].sort_by { |i, _| i }.each do |_, route|
|
|
95
|
-
if name && !warned
|
|
96
|
-
ActiveSupport::Deprecation.warn <<-MSG.squish
|
|
97
|
-
You are trying to generate the URL for a named route called
|
|
98
|
-
#{name.inspect} but no such route was found. In the future,
|
|
99
|
-
this will result in an `ActionController::UrlGenerationError`
|
|
100
|
-
exception.
|
|
101
|
-
MSG
|
|
102
|
-
|
|
103
|
-
warned = true
|
|
104
|
-
end
|
|
105
|
-
|
|
106
96
|
yield route
|
|
107
97
|
end
|
|
108
98
|
end
|
|
@@ -125,15 +115,36 @@ module ActionDispatch
|
|
|
125
115
|
routes
|
|
126
116
|
end
|
|
127
117
|
|
|
118
|
+
module RegexCaseComparator
|
|
119
|
+
DEFAULT_INPUT = /[-_.a-zA-Z0-9]+\/[-_.a-zA-Z0-9]+/
|
|
120
|
+
DEFAULT_REGEX = /\A#{DEFAULT_INPUT}\Z/
|
|
121
|
+
|
|
122
|
+
def self.===(regex)
|
|
123
|
+
DEFAULT_INPUT == regex
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
128
127
|
# Returns an array populated with missing keys if any are present.
|
|
129
128
|
def missing_keys(route, parts)
|
|
130
|
-
missing_keys =
|
|
129
|
+
missing_keys = nil
|
|
131
130
|
tests = route.path.requirements
|
|
132
131
|
route.required_parts.each { |key|
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
case tests[key]
|
|
133
|
+
when nil
|
|
134
|
+
unless parts[key]
|
|
135
|
+
missing_keys ||= []
|
|
136
|
+
missing_keys << key
|
|
137
|
+
end
|
|
138
|
+
when RegexCaseComparator
|
|
139
|
+
unless RegexCaseComparator::DEFAULT_REGEX === parts[key]
|
|
140
|
+
missing_keys ||= []
|
|
141
|
+
missing_keys << key
|
|
142
|
+
end
|
|
135
143
|
else
|
|
136
|
-
|
|
144
|
+
unless /\A#{tests[key]}\Z/ === parts[key]
|
|
145
|
+
missing_keys ||= []
|
|
146
|
+
missing_keys << key
|
|
147
|
+
end
|
|
137
148
|
end
|
|
138
149
|
}
|
|
139
150
|
missing_keys
|
|
@@ -149,7 +160,7 @@ module ActionDispatch
|
|
|
149
160
|
|
|
150
161
|
def build_cache
|
|
151
162
|
root = { ___routes: [] }
|
|
152
|
-
routes.each_with_index do |route, i|
|
|
163
|
+
routes.routes.each_with_index do |route, i|
|
|
153
164
|
leaf = route.required_defaults.inject(root) do |h, tuple|
|
|
154
165
|
h[tuple] ||= {}
|
|
155
166
|
end
|
|
@@ -45,51 +45,6 @@ module ActionDispatch
|
|
|
45
45
|
(@table.keys + @table.values.flat_map(&:keys)).uniq
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
# Returns a generalized transition graph with reduced states. The states
|
|
49
|
-
# are reduced like a DFA, but the table must be simulated like an NFA.
|
|
50
|
-
#
|
|
51
|
-
# Edges of the GTG are regular expressions.
|
|
52
|
-
def generalized_table
|
|
53
|
-
gt = GTG::TransitionTable.new
|
|
54
|
-
marked = {}
|
|
55
|
-
state_id = Hash.new { |h,k| h[k] = h.length }
|
|
56
|
-
alphabet = self.alphabet
|
|
57
|
-
|
|
58
|
-
stack = [eclosure(0)]
|
|
59
|
-
|
|
60
|
-
until stack.empty?
|
|
61
|
-
state = stack.pop
|
|
62
|
-
next if marked[state] || state.empty?
|
|
63
|
-
|
|
64
|
-
marked[state] = true
|
|
65
|
-
|
|
66
|
-
alphabet.each do |alpha|
|
|
67
|
-
next_state = eclosure(following_states(state, alpha))
|
|
68
|
-
next if next_state.empty?
|
|
69
|
-
|
|
70
|
-
gt[state_id[state], state_id[next_state]] = alpha
|
|
71
|
-
stack << next_state
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
final_groups = state_id.keys.find_all { |s|
|
|
76
|
-
s.sort.last == accepting
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
final_groups.each do |states|
|
|
80
|
-
id = state_id[states]
|
|
81
|
-
|
|
82
|
-
gt.add_accepting(id)
|
|
83
|
-
save = states.find { |s|
|
|
84
|
-
@memos.key?(s) && eclosure(s).sort.last == accepting
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
gt.add_memo(id, memo(save))
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
gt
|
|
91
|
-
end
|
|
92
|
-
|
|
93
48
|
# Returns set of NFA states to which there is a transition on ast symbol
|
|
94
49
|
# +a+ from some state +s+ in +t+.
|
|
95
50
|
def following_states(t, a)
|
|
@@ -107,7 +62,7 @@ module ActionDispatch
|
|
|
107
62
|
end
|
|
108
63
|
|
|
109
64
|
def alphabet
|
|
110
|
-
inverted.values.flat_map(&:keys).compact.uniq.sort_by
|
|
65
|
+
inverted.values.flat_map(&:keys).compact.uniq.sort_by(&:to_s)
|
|
111
66
|
end
|
|
112
67
|
|
|
113
68
|
# Returns a set of NFA states reachable from some NFA state +s+ in set
|
|
@@ -14,15 +14,15 @@ module ActionDispatch
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def each(&block)
|
|
17
|
-
Visitors::Each.
|
|
17
|
+
Visitors::Each::INSTANCE.accept(self, block)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def to_s
|
|
21
|
-
Visitors::String.
|
|
21
|
+
Visitors::String::INSTANCE.accept(self, '')
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def to_dot
|
|
25
|
-
Visitors::Dot.
|
|
25
|
+
Visitors::Dot::INSTANCE.accept(self)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def to_sym
|
|
@@ -30,7 +30,7 @@ module ActionDispatch
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def name
|
|
33
|
-
left.tr '*:', ''
|
|
33
|
+
left.tr '*:'.freeze, ''.freeze
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def type
|
|
@@ -39,10 +39,15 @@ module ActionDispatch
|
|
|
39
39
|
|
|
40
40
|
def symbol?; false; end
|
|
41
41
|
def literal?; false; end
|
|
42
|
+
def terminal?; false; end
|
|
43
|
+
def star?; false; end
|
|
44
|
+
def cat?; false; end
|
|
45
|
+
def group?; false; end
|
|
42
46
|
end
|
|
43
47
|
|
|
44
48
|
class Terminal < Node # :nodoc:
|
|
45
49
|
alias :symbol :left
|
|
50
|
+
def terminal?; true; end
|
|
46
51
|
end
|
|
47
52
|
|
|
48
53
|
class Literal < Terminal # :nodoc:
|
|
@@ -69,11 +74,13 @@ module ActionDispatch
|
|
|
69
74
|
class Symbol < Terminal # :nodoc:
|
|
70
75
|
attr_accessor :regexp
|
|
71
76
|
alias :symbol :regexp
|
|
77
|
+
attr_reader :name
|
|
72
78
|
|
|
73
79
|
DEFAULT_EXP = /[^\.\/\?]+/
|
|
74
80
|
def initialize(left)
|
|
75
81
|
super
|
|
76
82
|
@regexp = DEFAULT_EXP
|
|
83
|
+
@name = left.tr '*:'.freeze, ''.freeze
|
|
77
84
|
end
|
|
78
85
|
|
|
79
86
|
def default_regexp?
|
|
@@ -89,9 +96,11 @@ module ActionDispatch
|
|
|
89
96
|
|
|
90
97
|
class Group < Unary # :nodoc:
|
|
91
98
|
def type; :GROUP; end
|
|
99
|
+
def group?; true; end
|
|
92
100
|
end
|
|
93
101
|
|
|
94
102
|
class Star < Unary # :nodoc:
|
|
103
|
+
def star?; true; end
|
|
95
104
|
def type; :STAR; end
|
|
96
105
|
|
|
97
106
|
def name
|
|
@@ -111,6 +120,7 @@ module ActionDispatch
|
|
|
111
120
|
end
|
|
112
121
|
|
|
113
122
|
class Cat < Binary # :nodoc:
|
|
123
|
+
def cat?; true; end
|
|
114
124
|
def type; :CAT; end
|
|
115
125
|
end
|
|
116
126
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require 'action_dispatch/journey/router/strexp'
|
|
2
|
-
|
|
3
1
|
module ActionDispatch
|
|
4
2
|
module Journey # :nodoc:
|
|
5
3
|
module Path # :nodoc:
|
|
@@ -7,14 +5,20 @@ module ActionDispatch
|
|
|
7
5
|
attr_reader :spec, :requirements, :anchored
|
|
8
6
|
|
|
9
7
|
def self.from_string string
|
|
10
|
-
|
|
8
|
+
build(string, {}, "/.?", true)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.build(path, requirements, separators, anchored)
|
|
12
|
+
parser = Journey::Parser.new
|
|
13
|
+
ast = parser.parse path
|
|
14
|
+
new ast, requirements, separators, anchored
|
|
11
15
|
end
|
|
12
16
|
|
|
13
|
-
def initialize(
|
|
14
|
-
@spec =
|
|
15
|
-
@requirements =
|
|
16
|
-
@separators =
|
|
17
|
-
@anchored =
|
|
17
|
+
def initialize(ast, requirements, separators, anchored)
|
|
18
|
+
@spec = ast
|
|
19
|
+
@requirements = requirements
|
|
20
|
+
@separators = separators
|
|
21
|
+
@anchored = anchored
|
|
18
22
|
|
|
19
23
|
@names = nil
|
|
20
24
|
@optional_names = nil
|
|
@@ -28,12 +32,12 @@ module ActionDispatch
|
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
def ast
|
|
31
|
-
@spec.
|
|
35
|
+
@spec.find_all(&:symbol?).each do |node|
|
|
32
36
|
re = @requirements[node.to_sym]
|
|
33
37
|
node.regexp = re if re
|
|
34
38
|
end
|
|
35
39
|
|
|
36
|
-
@spec.
|
|
40
|
+
@spec.find_all(&:star?).each do |node|
|
|
37
41
|
node = node.left
|
|
38
42
|
node.regexp = @requirements[node.to_sym] || /(.+)/
|
|
39
43
|
end
|
|
@@ -42,7 +46,7 @@ module ActionDispatch
|
|
|
42
46
|
end
|
|
43
47
|
|
|
44
48
|
def names
|
|
45
|
-
@names ||= spec.
|
|
49
|
+
@names ||= spec.find_all(&:symbol?).map(&:name)
|
|
46
50
|
end
|
|
47
51
|
|
|
48
52
|
def required_names
|
|
@@ -50,34 +54,9 @@ module ActionDispatch
|
|
|
50
54
|
end
|
|
51
55
|
|
|
52
56
|
def optional_names
|
|
53
|
-
@optional_names ||= spec.
|
|
54
|
-
group.
|
|
55
|
-
}.map
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
class RegexpOffsets < Journey::Visitors::Visitor # :nodoc:
|
|
59
|
-
attr_reader :offsets
|
|
60
|
-
|
|
61
|
-
def initialize(matchers)
|
|
62
|
-
@matchers = matchers
|
|
63
|
-
@capture_count = [0]
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def visit(node)
|
|
67
|
-
super
|
|
68
|
-
@capture_count
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def visit_SYMBOL(node)
|
|
72
|
-
node = node.to_sym
|
|
73
|
-
|
|
74
|
-
if @matchers.key?(node)
|
|
75
|
-
re = /#{@matchers[node]}|/
|
|
76
|
-
@capture_count.push((re.match('').length - 1) + (@capture_count.last || 0))
|
|
77
|
-
else
|
|
78
|
-
@capture_count << (@capture_count.last || 0)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
57
|
+
@optional_names ||= spec.find_all(&:group?).flat_map { |group|
|
|
58
|
+
group.find_all(&:symbol?)
|
|
59
|
+
}.map(&:name).uniq
|
|
81
60
|
end
|
|
82
61
|
|
|
83
62
|
class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc:
|
|
@@ -122,6 +101,11 @@ module ActionDispatch
|
|
|
122
101
|
re = @matchers[node.left.to_sym] || '.+'
|
|
123
102
|
"(#{re})"
|
|
124
103
|
end
|
|
104
|
+
|
|
105
|
+
def visit_OR(node)
|
|
106
|
+
children = node.children.map { |n| visit n }
|
|
107
|
+
"(?:#{children.join(?|)})"
|
|
108
|
+
end
|
|
125
109
|
end
|
|
126
110
|
|
|
127
111
|
class UnanchoredRegexp < AnchoredRegexp # :nodoc:
|
|
@@ -140,7 +124,7 @@ module ActionDispatch
|
|
|
140
124
|
end
|
|
141
125
|
|
|
142
126
|
def captures
|
|
143
|
-
(length - 1)
|
|
127
|
+
Array.new(length - 1) { |i| self[i + 1] }
|
|
144
128
|
end
|
|
145
129
|
|
|
146
130
|
def [](x)
|
|
@@ -184,8 +168,20 @@ module ActionDispatch
|
|
|
184
168
|
def offsets
|
|
185
169
|
return @offsets if @offsets
|
|
186
170
|
|
|
187
|
-
|
|
188
|
-
|
|
171
|
+
@offsets = [0]
|
|
172
|
+
|
|
173
|
+
spec.find_all(&:symbol?).each do |node|
|
|
174
|
+
node = node.to_sym
|
|
175
|
+
|
|
176
|
+
if @requirements.key?(node)
|
|
177
|
+
re = /#{@requirements[node]}|/
|
|
178
|
+
@offsets.push((re.match('').length - 1) + @offsets.last)
|
|
179
|
+
else
|
|
180
|
+
@offsets << @offsets.last
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
@offsets
|
|
189
185
|
end
|
|
190
186
|
end
|
|
191
187
|
end
|
|
@@ -1,41 +1,88 @@
|
|
|
1
1
|
module ActionDispatch
|
|
2
2
|
module Journey # :nodoc:
|
|
3
3
|
class Route # :nodoc:
|
|
4
|
-
attr_reader :app, :path, :defaults, :name
|
|
4
|
+
attr_reader :app, :path, :defaults, :name, :precedence
|
|
5
5
|
|
|
6
|
-
attr_reader :constraints
|
|
6
|
+
attr_reader :constraints, :internal
|
|
7
7
|
alias :conditions :constraints
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
module VerbMatchers
|
|
10
|
+
VERBS = %w{ DELETE GET HEAD OPTIONS LINK PATCH POST PUT TRACE UNLINK }
|
|
11
|
+
VERBS.each do |v|
|
|
12
|
+
class_eval <<-eoc
|
|
13
|
+
class #{v}
|
|
14
|
+
def self.verb; name.split("::").last; end
|
|
15
|
+
def self.call(req); req.#{v.downcase}?; end
|
|
16
|
+
end
|
|
17
|
+
eoc
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class Unknown
|
|
21
|
+
attr_reader :verb
|
|
22
|
+
|
|
23
|
+
def initialize(verb)
|
|
24
|
+
@verb = verb
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def call(request); @verb === request.request_method; end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class All
|
|
31
|
+
def self.call(_); true; end
|
|
32
|
+
def self.verb; ''; end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
VERB_TO_CLASS = VERBS.each_with_object({ :all => All }) do |verb, hash|
|
|
36
|
+
klass = const_get verb
|
|
37
|
+
hash[verb] = klass
|
|
38
|
+
hash[verb.downcase] = klass
|
|
39
|
+
hash[verb.downcase.to_sym] = klass
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.verb_matcher(verb)
|
|
45
|
+
VerbMatchers::VERB_TO_CLASS.fetch(verb) do
|
|
46
|
+
VerbMatchers::Unknown.new verb.to_s.dasherize.upcase
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.build(name, app, path, constraints, required_defaults, defaults)
|
|
51
|
+
request_method_match = verb_matcher(constraints.delete(:request_method))
|
|
52
|
+
new name, app, path, constraints, required_defaults, defaults, request_method_match, 0
|
|
53
|
+
end
|
|
10
54
|
|
|
11
55
|
##
|
|
12
56
|
# +path+ is a path constraint.
|
|
13
57
|
# +constraints+ is a hash of constraints to be applied to this route.
|
|
14
|
-
def initialize(name, app, path, constraints, defaults =
|
|
58
|
+
def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence, internal = false)
|
|
15
59
|
@name = name
|
|
16
60
|
@app = app
|
|
17
61
|
@path = path
|
|
18
62
|
|
|
63
|
+
@request_method_match = request_method_match
|
|
19
64
|
@constraints = constraints
|
|
20
65
|
@defaults = defaults
|
|
21
66
|
@required_defaults = nil
|
|
67
|
+
@_required_defaults = required_defaults
|
|
22
68
|
@required_parts = nil
|
|
23
69
|
@parts = nil
|
|
24
70
|
@decorated_ast = nil
|
|
25
|
-
@precedence =
|
|
71
|
+
@precedence = precedence
|
|
26
72
|
@path_formatter = @path.build_formatter
|
|
73
|
+
@internal = internal
|
|
27
74
|
end
|
|
28
75
|
|
|
29
76
|
def ast
|
|
30
77
|
@decorated_ast ||= begin
|
|
31
78
|
decorated_ast = path.ast
|
|
32
|
-
decorated_ast.
|
|
79
|
+
decorated_ast.find_all(&:terminal?).each { |n| n.memo = self }
|
|
33
80
|
decorated_ast
|
|
34
81
|
end
|
|
35
82
|
end
|
|
36
83
|
|
|
37
84
|
def requirements # :nodoc:
|
|
38
|
-
# needed for rails `
|
|
85
|
+
# needed for rails `rails routes`
|
|
39
86
|
@defaults.merge(path.requirements).delete_if { |_,v|
|
|
40
87
|
/.+?/ == v
|
|
41
88
|
}
|
|
@@ -60,7 +107,7 @@ module ActionDispatch
|
|
|
60
107
|
end
|
|
61
108
|
|
|
62
109
|
def parts
|
|
63
|
-
@parts ||= segments.map
|
|
110
|
+
@parts ||= segments.map(&:to_sym)
|
|
64
111
|
end
|
|
65
112
|
alias :segment_keys :parts
|
|
66
113
|
|
|
@@ -68,16 +115,12 @@ module ActionDispatch
|
|
|
68
115
|
@path_formatter.evaluate path_options
|
|
69
116
|
end
|
|
70
117
|
|
|
71
|
-
def optional_parts
|
|
72
|
-
path.optional_names.map { |n| n.to_sym }
|
|
73
|
-
end
|
|
74
|
-
|
|
75
118
|
def required_parts
|
|
76
|
-
@required_parts ||= path.required_names.map
|
|
119
|
+
@required_parts ||= path.required_names.map(&:to_sym)
|
|
77
120
|
end
|
|
78
121
|
|
|
79
122
|
def required_default?(key)
|
|
80
|
-
|
|
123
|
+
@_required_defaults.include?(key)
|
|
81
124
|
end
|
|
82
125
|
|
|
83
126
|
def required_defaults
|
|
@@ -95,9 +138,8 @@ module ActionDispatch
|
|
|
95
138
|
end
|
|
96
139
|
|
|
97
140
|
def matches?(request)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
141
|
+
match_verb(request) &&
|
|
142
|
+
constraints.all? { |method, value|
|
|
101
143
|
case value
|
|
102
144
|
when Regexp, String
|
|
103
145
|
value === request.send(method).to_s
|
|
@@ -110,15 +152,28 @@ module ActionDispatch
|
|
|
110
152
|
else
|
|
111
153
|
value === request.send(method)
|
|
112
154
|
end
|
|
113
|
-
|
|
155
|
+
}
|
|
114
156
|
end
|
|
115
157
|
|
|
116
158
|
def ip
|
|
117
159
|
constraints[:ip] || //
|
|
118
160
|
end
|
|
119
161
|
|
|
162
|
+
def requires_matching_verb?
|
|
163
|
+
!@request_method_match.all? { |x| x == VerbMatchers::All }
|
|
164
|
+
end
|
|
165
|
+
|
|
120
166
|
def verb
|
|
121
|
-
|
|
167
|
+
verbs.join('|')
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
def verbs
|
|
172
|
+
@request_method_match.map(&:verb)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def match_verb(request)
|
|
176
|
+
@request_method_match.any? { |m| m.call request }
|
|
122
177
|
end
|
|
123
178
|
end
|
|
124
179
|
end
|
|
@@ -13,11 +13,11 @@ module ActionDispatch
|
|
|
13
13
|
# normalize_path("") # => "/"
|
|
14
14
|
# normalize_path("/%ab") # => "/%AB"
|
|
15
15
|
def self.normalize_path(path)
|
|
16
|
-
path = "/#{path}"
|
|
17
|
-
path.squeeze!('/')
|
|
18
|
-
path.sub!(%r{/+\Z}, '')
|
|
16
|
+
path = "/#{path}"
|
|
17
|
+
path.squeeze!('/'.freeze)
|
|
18
|
+
path.sub!(%r{/+\Z}, ''.freeze)
|
|
19
19
|
path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
|
|
20
|
-
path = '/' if path == ''
|
|
20
|
+
path = '/' if path == ''.freeze
|
|
21
21
|
path
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -55,7 +55,7 @@ module ActionDispatch
|
|
|
55
55
|
|
|
56
56
|
def unescape_uri(uri)
|
|
57
57
|
encoding = uri.encoding == US_ASCII ? UTF_8 : uri.encoding
|
|
58
|
-
uri.gsub(ESCAPED) { [
|
|
58
|
+
uri.gsub(ESCAPED) { |match| [match[1, 2].hex].pack('C') }.force_encoding(encoding)
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
protected
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'action_dispatch/journey/router/utils'
|
|
2
|
-
require 'action_dispatch/journey/router/strexp'
|
|
3
2
|
require 'action_dispatch/journey/routes'
|
|
4
3
|
require 'action_dispatch/journey/formatter'
|
|
5
4
|
|
|
@@ -17,9 +16,6 @@ module ActionDispatch
|
|
|
17
16
|
class RoutingError < ::StandardError # :nodoc:
|
|
18
17
|
end
|
|
19
18
|
|
|
20
|
-
# :nodoc:
|
|
21
|
-
VERSION = '2.0.0'
|
|
22
|
-
|
|
23
19
|
attr_accessor :routes
|
|
24
20
|
|
|
25
21
|
def initialize(routes)
|
|
@@ -68,8 +64,8 @@ module ActionDispatch
|
|
|
68
64
|
|
|
69
65
|
def visualizer
|
|
70
66
|
tt = GTG::Builder.new(ast).transition_table
|
|
71
|
-
groups = partitioned_routes.first.map(&:ast).group_by
|
|
72
|
-
asts = groups.values.map
|
|
67
|
+
groups = partitioned_routes.first.map(&:ast).group_by(&:to_s)
|
|
68
|
+
asts = groups.values.map(&:first)
|
|
73
69
|
tt.visualizer(asts)
|
|
74
70
|
end
|
|
75
71
|
|
|
@@ -88,7 +84,7 @@ module ActionDispatch
|
|
|
88
84
|
end
|
|
89
85
|
|
|
90
86
|
def custom_routes
|
|
91
|
-
|
|
87
|
+
routes.custom_routes
|
|
92
88
|
end
|
|
93
89
|
|
|
94
90
|
def filter_routes(path)
|
|
@@ -102,7 +98,7 @@ module ActionDispatch
|
|
|
102
98
|
}
|
|
103
99
|
|
|
104
100
|
routes =
|
|
105
|
-
if req.
|
|
101
|
+
if req.head?
|
|
106
102
|
match_head_routes(routes, req)
|
|
107
103
|
else
|
|
108
104
|
match_routes(routes, req)
|
|
@@ -121,7 +117,7 @@ module ActionDispatch
|
|
|
121
117
|
end
|
|
122
118
|
|
|
123
119
|
def match_head_routes(routes, req)
|
|
124
|
-
verb_specific_routes = routes.
|
|
120
|
+
verb_specific_routes = routes.select(&:requires_matching_verb?)
|
|
125
121
|
head_routes = match_routes(verb_specific_routes, req)
|
|
126
122
|
|
|
127
123
|
if head_routes.empty?
|