actionpack 4.2.10 → 7.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (202) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +86 -600
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -14
  5. data/lib/abstract_controller/asset_paths.rb +5 -1
  6. data/lib/abstract_controller/base.rb +166 -136
  7. data/lib/abstract_controller/caching/fragments.rb +149 -0
  8. data/lib/abstract_controller/caching.rb +68 -0
  9. data/lib/abstract_controller/callbacks.rb +126 -57
  10. data/lib/abstract_controller/collector.rb +13 -15
  11. data/lib/abstract_controller/deprecator.rb +9 -0
  12. data/lib/abstract_controller/error.rb +8 -0
  13. data/lib/abstract_controller/helpers.rb +181 -132
  14. data/lib/abstract_controller/logger.rb +5 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
  16. data/lib/abstract_controller/rendering.rb +56 -56
  17. data/lib/abstract_controller/translation.rb +29 -15
  18. data/lib/abstract_controller/url_for.rb +15 -11
  19. data/lib/abstract_controller.rb +21 -5
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +154 -0
  22. data/lib/action_controller/base.rb +219 -155
  23. data/lib/action_controller/caching.rb +28 -68
  24. data/lib/action_controller/deprecator.rb +9 -0
  25. data/lib/action_controller/form_builder.rb +55 -0
  26. data/lib/action_controller/log_subscriber.rb +35 -22
  27. data/lib/action_controller/metal/allow_browser.rb +119 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +259 -122
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +9 -5
  32. data/lib/action_controller/metal/data_streaming.rb +87 -104
  33. data/lib/action_controller/metal/default_headers.rb +21 -0
  34. data/lib/action_controller/metal/etag_with_flash.rb +22 -0
  35. data/lib/action_controller/metal/etag_with_template_digest.rb +35 -26
  36. data/lib/action_controller/metal/exceptions.rb +71 -24
  37. data/lib/action_controller/metal/flash.rb +26 -19
  38. data/lib/action_controller/metal/head.rb +45 -36
  39. data/lib/action_controller/metal/helpers.rb +80 -64
  40. data/lib/action_controller/metal/http_authentication.rb +297 -244
  41. data/lib/action_controller/metal/implicit_render.rb +57 -9
  42. data/lib/action_controller/metal/instrumentation.rb +76 -64
  43. data/lib/action_controller/metal/live.rb +238 -176
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +177 -166
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +145 -118
  48. data/lib/action_controller/metal/permissions_policy.rb +38 -0
  49. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  50. data/lib/action_controller/metal/redirecting.rb +203 -64
  51. data/lib/action_controller/metal/renderers.rb +108 -65
  52. data/lib/action_controller/metal/rendering.rb +216 -56
  53. data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
  54. data/lib/action_controller/metal/rescue.rb +19 -21
  55. data/lib/action_controller/metal/streaming.rb +179 -138
  56. data/lib/action_controller/metal/strong_parameters.rb +1058 -382
  57. data/lib/action_controller/metal/testing.rb +11 -17
  58. data/lib/action_controller/metal/url_for.rb +37 -21
  59. data/lib/action_controller/metal.rb +236 -138
  60. data/lib/action_controller/railtie.rb +89 -11
  61. data/lib/action_controller/railties/helpers.rb +5 -1
  62. data/lib/action_controller/renderer.rb +161 -0
  63. data/lib/action_controller/template_assertions.rb +13 -0
  64. data/lib/action_controller/test_case.rb +425 -497
  65. data/lib/action_controller.rb +44 -22
  66. data/lib/action_dispatch/constants.rb +34 -0
  67. data/lib/action_dispatch/deprecator.rb +9 -0
  68. data/lib/action_dispatch/http/cache.rb +119 -63
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +364 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +36 -34
  72. data/lib/action_dispatch/http/filter_redirect.rb +24 -12
  73. data/lib/action_dispatch/http/headers.rb +66 -31
  74. data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
  75. data/lib/action_dispatch/http/mime_type.rb +196 -136
  76. data/lib/action_dispatch/http/mime_types.rb +25 -7
  77. data/lib/action_dispatch/http/parameters.rb +97 -45
  78. data/lib/action_dispatch/http/permissions_policy.rb +187 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +6 -0
  80. data/lib/action_dispatch/http/request.rb +299 -170
  81. data/lib/action_dispatch/http/response.rb +311 -160
  82. data/lib/action_dispatch/http/upload.rb +52 -23
  83. data/lib/action_dispatch/http/url.rb +201 -125
  84. data/lib/action_dispatch/journey/formatter.rb +110 -50
  85. data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
  88. data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
  89. data/lib/action_dispatch/journey/nodes/node.rb +100 -20
  90. data/lib/action_dispatch/journey/parser.rb +19 -17
  91. data/lib/action_dispatch/journey/parser.y +4 -3
  92. data/lib/action_dispatch/journey/parser_extras.rb +14 -4
  93. data/lib/action_dispatch/journey/path/pattern.rb +79 -63
  94. data/lib/action_dispatch/journey/route.rb +108 -44
  95. data/lib/action_dispatch/journey/router/utils.rb +41 -29
  96. data/lib/action_dispatch/journey/router.rb +64 -57
  97. data/lib/action_dispatch/journey/routes.rb +23 -21
  98. data/lib/action_dispatch/journey/scanner.rb +28 -17
  99. data/lib/action_dispatch/journey/visitors.rb +100 -54
  100. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  101. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  102. data/lib/action_dispatch/journey.rb +7 -5
  103. data/lib/action_dispatch/log_subscriber.rb +25 -0
  104. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  105. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  106. data/lib/action_dispatch/middleware/callbacks.rb +7 -6
  107. data/lib/action_dispatch/middleware/cookies.rb +471 -328
  108. data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
  109. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  110. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  111. data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
  112. data/lib/action_dispatch/middleware/executor.rb +32 -0
  113. data/lib/action_dispatch/middleware/flash.rb +143 -101
  114. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  115. data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
  116. data/lib/action_dispatch/middleware/reloader.rb +10 -92
  117. data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
  118. data/lib/action_dispatch/middleware/request_id.rb +29 -15
  119. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  120. data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
  121. data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
  122. data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
  123. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
  124. data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
  125. data/lib/action_dispatch/middleware/ssl.rb +134 -36
  126. data/lib/action_dispatch/middleware/stack.rb +109 -44
  127. data/lib/action_dispatch/middleware/static.rb +159 -90
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +7 -24
  132. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
  136. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -7
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
  140. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +139 -15
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +6 -6
  146. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
  147. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
  148. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  149. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  150. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  151. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +7 -4
  152. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
  153. data/lib/action_dispatch/railtie.rb +44 -16
  154. data/lib/action_dispatch/request/session.rb +159 -69
  155. data/lib/action_dispatch/request/utils.rb +97 -23
  156. data/lib/action_dispatch/routing/endpoint.rb +11 -2
  157. data/lib/action_dispatch/routing/inspector.rb +195 -106
  158. data/lib/action_dispatch/routing/mapper.rb +1338 -955
  159. data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
  160. data/lib/action_dispatch/routing/redirection.rb +78 -51
  161. data/lib/action_dispatch/routing/route_set.rb +460 -374
  162. data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
  163. data/lib/action_dispatch/routing/url_for.rb +172 -124
  164. data/lib/action_dispatch/routing.rb +159 -158
  165. data/lib/action_dispatch/system_test_case.rb +206 -0
  166. data/lib/action_dispatch/system_testing/browser.rb +84 -0
  167. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  168. data/lib/action_dispatch/system_testing/server.rb +33 -0
  169. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  170. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  171. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  172. data/lib/action_dispatch/testing/assertions/response.rb +71 -39
  173. data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
  174. data/lib/action_dispatch/testing/assertions.rb +9 -6
  175. data/lib/action_dispatch/testing/integration.rb +486 -306
  176. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  177. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  178. data/lib/action_dispatch/testing/test_process.rb +35 -22
  179. data/lib/action_dispatch/testing/test_request.rb +29 -34
  180. data/lib/action_dispatch/testing/test_response.rb +48 -15
  181. data/lib/action_dispatch.rb +82 -40
  182. data/lib/action_pack/gem_version.rb +8 -4
  183. data/lib/action_pack/version.rb +6 -2
  184. data/lib/action_pack.rb +21 -18
  185. metadata +146 -56
  186. data/lib/action_controller/caching/fragments.rb +0 -103
  187. data/lib/action_controller/metal/force_ssl.rb +0 -97
  188. data/lib/action_controller/metal/hide_actions.rb +0 -40
  189. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  190. data/lib/action_controller/middleware.rb +0 -39
  191. data/lib/action_controller/model_naming.rb +0 -12
  192. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  193. data/lib/action_dispatch/journey/backwards.rb +0 -5
  194. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  195. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  196. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  197. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  199. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +0 -27
  200. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  201. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  202. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,22 +1,21 @@
1
- require 'action_dispatch/journey/router/strexp'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
2
4
 
3
5
  module ActionDispatch
4
6
  module Journey # :nodoc:
5
7
  module Path # :nodoc:
6
8
  class Pattern # :nodoc:
7
- attr_reader :spec, :requirements, :anchored
8
-
9
- def self.from_string string
10
- new Journey::Router::Strexp.build(string, {}, ["/.?"], true)
11
- end
9
+ attr_reader :ast, :names, :requirements, :anchored, :spec
12
10
 
13
- def initialize(strexp)
14
- @spec = strexp.ast
15
- @requirements = strexp.requirements
16
- @separators = strexp.separators.join
17
- @anchored = strexp.anchor
11
+ def initialize(ast, requirements, separators, anchored)
12
+ @ast = ast
13
+ @spec = ast.root
14
+ @requirements = requirements
15
+ @separators = separators
16
+ @anchored = anchored
18
17
 
19
- @names = nil
18
+ @names = ast.names
20
19
  @optional_names = nil
21
20
  @required_names = nil
22
21
  @re = nil
@@ -27,22 +26,33 @@ module ActionDispatch
27
26
  Visitors::FormatBuilder.new.accept(spec)
28
27
  end
29
28
 
30
- def ast
31
- @spec.grep(Nodes::Symbol).each do |node|
32
- re = @requirements[node.to_sym]
33
- node.regexp = re if re
34
- end
29
+ def eager_load!
30
+ required_names
31
+ offsets
32
+ to_regexp
33
+ @ast = nil
34
+ end
35
35
 
36
- @spec.grep(Nodes::Star).each do |node|
37
- node = node.left
38
- node.regexp = @requirements[node.to_sym] || /(.+)/
39
- end
36
+ def requirements_anchored?
37
+ # each required param must not be surrounded by a literal, otherwise it isn't
38
+ # simple to chunk-match the url piecemeal
39
+ terminals = ast.terminals
40
40
 
41
- @spec
42
- end
41
+ terminals.each_with_index { |s, index|
42
+ next if index < 1
43
+ next if s.type == :DOT || s.type == :SLASH
44
+
45
+ back = terminals[index - 1]
46
+ fwd = terminals[index + 1]
47
+
48
+ # we also don't support this yet, constraints must be regexps
49
+ return false if s.symbol? && s.regexp.is_a?(Array)
43
50
 
44
- def names
45
- @names ||= spec.grep(Nodes::Symbol).map { |n| n.name }
51
+ return false if back.literal?
52
+ return false if !fwd.nil? && fwd.literal?
53
+ }
54
+
55
+ true
46
56
  end
47
57
 
48
58
  def required_names
@@ -50,34 +60,9 @@ module ActionDispatch
50
60
  end
51
61
 
52
62
  def optional_names
53
- @optional_names ||= spec.grep(Nodes::Group).flat_map { |group|
54
- group.grep(Nodes::Symbol)
55
- }.map { |n| n.name }.uniq
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
63
+ @optional_names ||= spec.find_all(&:group?).flat_map { |group|
64
+ group.find_all(&:symbol?)
65
+ }.map(&:name).uniq
81
66
  end
82
67
 
83
68
  class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc:
@@ -93,7 +78,7 @@ module ActionDispatch
93
78
  end
94
79
 
95
80
  def visit_CAT(node)
96
- [visit(node.left), visit(node.right)].join
81
+ "#{visit(node.left)}#{visit(node.right)}"
97
82
  end
98
83
 
99
84
  def visit_SYMBOL(node)
@@ -102,7 +87,7 @@ module ActionDispatch
102
87
  return @separator_re unless @matchers.key?(node)
103
88
 
104
89
  re = @matchers[node]
105
- "(#{re})"
90
+ "(#{Regexp.union(re)})"
106
91
  end
107
92
 
108
93
  def visit_GROUP(node)
@@ -119,14 +104,20 @@ module ActionDispatch
119
104
  end
120
105
 
121
106
  def visit_STAR(node)
122
- re = @matchers[node.left.to_sym] || '.+'
123
- "(#{re})"
107
+ re = @matchers[node.left.to_sym]
108
+ re ? "(#{re})" : "(.+)"
109
+ end
110
+
111
+ def visit_OR(node)
112
+ children = node.children.map { |n| visit n }
113
+ "(?:#{children.join(?|)})"
124
114
  end
125
115
  end
126
116
 
127
117
  class UnanchoredRegexp < AnchoredRegexp # :nodoc:
128
118
  def accept(node)
129
- %r{\A#{visit node}}
119
+ path = visit node
120
+ path == "/" ? %r{\A/} : %r{\A#{path}(?:\b|\Z|/)}
130
121
  end
131
122
  end
132
123
 
@@ -140,7 +131,11 @@ module ActionDispatch
140
131
  end
141
132
 
142
133
  def captures
143
- (length - 1).times.map { |i| self[i + 1] }
134
+ Array.new(length - 1) { |i| self[i + 1] }
135
+ end
136
+
137
+ def named_captures
138
+ @names.zip(captures).to_h
144
139
  end
145
140
 
146
141
  def [](x)
@@ -167,6 +162,10 @@ module ActionDispatch
167
162
  end
168
163
  alias :=~ :match
169
164
 
165
+ def match?(other)
166
+ to_regexp.match?(other)
167
+ end
168
+
170
169
  def source
171
170
  to_regexp.source
172
171
  end
@@ -175,17 +174,34 @@ module ActionDispatch
175
174
  @re ||= regexp_visitor.new(@separators, @requirements).accept spec
176
175
  end
177
176
 
178
- private
177
+ def requirements_for_missing_keys_check
178
+ @requirements_for_missing_keys_check ||= requirements.transform_values do |regex|
179
+ /\A#{regex}\Z/
180
+ end
181
+ end
179
182
 
183
+ private
180
184
  def regexp_visitor
181
185
  @anchored ? AnchoredRegexp : UnanchoredRegexp
182
186
  end
183
187
 
184
188
  def offsets
185
- return @offsets if @offsets
189
+ @offsets ||= begin
190
+ offsets = [0]
191
+
192
+ spec.find_all(&:symbol?).each do |node|
193
+ node = node.to_sym
186
194
 
187
- viz = RegexpOffsets.new(@requirements)
188
- @offsets = viz.accept(spec)
195
+ if @requirements.key?(node)
196
+ re = /#{Regexp.union(@requirements[node])}|/
197
+ offsets.push((re.match("").length - 1) + offsets.last)
198
+ else
199
+ offsets << offsets.last
200
+ end
201
+ end
202
+
203
+ offsets
204
+ end
189
205
  end
190
206
  end
191
207
  end
@@ -1,43 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
1
5
  module ActionDispatch
2
- module Journey # :nodoc:
3
- class Route # :nodoc:
4
- attr_reader :app, :path, :defaults, :name
6
+ # :stopdoc:
7
+ module Journey
8
+ class Route
9
+ attr_reader :app, :path, :defaults, :name, :precedence, :constraints,
10
+ :internal, :scope_options, :ast, :source_location
5
11
 
6
- attr_reader :constraints
7
12
  alias :conditions :constraints
8
13
 
9
- attr_accessor :precedence
14
+ module VerbMatchers
15
+ VERBS = %w{ DELETE GET HEAD OPTIONS LINK PATCH POST PUT TRACE UNLINK }
16
+ VERBS.each do |v|
17
+ class_eval <<-eoc, __FILE__, __LINE__ + 1
18
+ # frozen_string_literal: true
19
+ class #{v}
20
+ def self.verb; name.split("::").last; end
21
+ def self.call(req); req.#{v.downcase}?; end
22
+ end
23
+ eoc
24
+ end
25
+
26
+ class Unknown
27
+ attr_reader :verb
28
+
29
+ def initialize(verb)
30
+ @verb = verb
31
+ end
32
+
33
+ def call(request); @verb == request.request_method; end
34
+ end
35
+
36
+ class All
37
+ def self.call(_); true; end
38
+ def self.verb; ""; end
39
+ end
40
+
41
+ VERB_TO_CLASS = VERBS.each_with_object(all: All) do |verb, hash|
42
+ klass = const_get verb
43
+ hash[verb] = klass
44
+ hash[verb.downcase] = klass
45
+ hash[verb.downcase.to_sym] = klass
46
+ end
47
+ end
48
+
49
+ def self.verb_matcher(verb)
50
+ VerbMatchers::VERB_TO_CLASS.fetch(verb) do
51
+ VerbMatchers::Unknown.new verb.to_s.dasherize.upcase
52
+ end
53
+ end
10
54
 
11
55
  ##
12
56
  # +path+ is a path constraint.
13
- # +constraints+ is a hash of constraints to be applied to this route.
14
- def initialize(name, app, path, constraints, defaults = {})
57
+ # `constraints` is a hash of constraints to be applied to this route.
58
+ def initialize(name:, app: nil, path:, constraints: {}, required_defaults: [], defaults: {}, request_method_match: nil, precedence: 0, scope_options: {}, internal: false, source_location: nil)
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
- @decorated_ast = nil
25
- @precedence = 0
70
+ @precedence = precedence
26
71
  @path_formatter = @path.build_formatter
27
- end
28
-
29
- def ast
30
- @decorated_ast ||= begin
31
- decorated_ast = path.ast
32
- decorated_ast.grep(Nodes::Terminal).each { |n| n.memo = self }
33
- decorated_ast
34
- end
35
- end
36
-
37
- def requirements # :nodoc:
38
- # needed for rails `rake routes`
39
- @defaults.merge(path.requirements).delete_if { |_,v|
40
- /.+?/ == v
72
+ @scope_options = scope_options
73
+ @internal = internal
74
+ @source_location = source_location
75
+
76
+ @ast = @path.ast.root
77
+ @path.ast.route = self
78
+ end
79
+
80
+ def eager_load!
81
+ path.eager_load!
82
+ parts
83
+ required_defaults
84
+ nil
85
+ end
86
+
87
+ # Needed for `bin/rails routes`. Picks up succinctly defined requirements for a
88
+ # route, for example route
89
+ #
90
+ # get 'photo/:id', :controller => 'photos', :action => 'show',
91
+ # :id => /[A-Z]\d{5}/
92
+ #
93
+ # will have {:controller=>"photos", :action=>"show", :[id=>/](A-Z){5}/} as
94
+ # requirements.
95
+ def requirements
96
+ @defaults.merge(path.requirements).delete_if { |_, v|
97
+ /.+?/m == v
41
98
  }
42
99
  end
43
100
 
@@ -49,18 +106,16 @@ module ActionDispatch
49
106
  required_parts + required_defaults.keys
50
107
  end
51
108
 
52
- def score(constraints)
53
- required_keys = path.required_names
54
- supplied_keys = constraints.map { |k,v| v && k.to_s }.compact
55
-
56
- return -1 unless (required_keys - supplied_keys).empty?
109
+ def score(supplied_keys)
110
+ path.required_names.each do |k|
111
+ return -1 unless supplied_keys.include?(k)
112
+ end
57
113
 
58
- score = (supplied_keys & path.names).length
59
- score + (required_defaults.length * 2)
114
+ (required_defaults.length * 2) + path.names.count { |k| supplied_keys.include?(k) }
60
115
  end
61
116
 
62
117
  def parts
63
- @parts ||= segments.map { |n| n.to_sym }
118
+ @parts ||= segments.map(&:to_sym)
64
119
  end
65
120
  alias :segment_keys :parts
66
121
 
@@ -68,26 +123,22 @@ module ActionDispatch
68
123
  @path_formatter.evaluate path_options
69
124
  end
70
125
 
71
- def optional_parts
72
- path.optional_names.map { |n| n.to_sym }
73
- end
74
-
75
126
  def required_parts
76
- @required_parts ||= path.required_names.map { |n| n.to_sym }
127
+ @required_parts ||= path.required_names.map(&:to_sym)
77
128
  end
78
129
 
79
130
  def required_default?(key)
80
- (constraints[:required_defaults] || []).include?(key)
131
+ @_required_defaults.include?(key)
81
132
  end
82
133
 
83
134
  def required_defaults
84
- @required_defaults ||= @defaults.dup.delete_if do |k,_|
135
+ @required_defaults ||= @defaults.dup.delete_if do |k, _|
85
136
  parts.include?(k) || !required_default?(k)
86
137
  end
87
138
  end
88
139
 
89
140
  def glob?
90
- !path.spec.grep(Nodes::Star).empty?
141
+ path.ast.glob?
91
142
  end
92
143
 
93
144
  def dispatcher?
@@ -95,9 +146,8 @@ module ActionDispatch
95
146
  end
96
147
 
97
148
  def matches?(request)
98
- constraints.all? do |method, value|
99
- next true unless request.respond_to?(method)
100
-
149
+ match_verb(request) &&
150
+ constraints.all? { |method, value|
101
151
  case value
102
152
  when Regexp, String
103
153
  value === request.send(method).to_s
@@ -110,16 +160,30 @@ module ActionDispatch
110
160
  else
111
161
  value === request.send(method)
112
162
  end
113
- end
163
+ }
114
164
  end
115
165
 
116
166
  def ip
117
167
  constraints[:ip] || //
118
168
  end
119
169
 
170
+ def requires_matching_verb?
171
+ !@request_method_match.all? { |x| x == VerbMatchers::All }
172
+ end
173
+
120
174
  def verb
121
- constraints[:request_method] || //
175
+ verbs.join("|")
122
176
  end
177
+
178
+ private
179
+ def verbs
180
+ @request_method_match.map(&:verb)
181
+ end
182
+
183
+ def match_verb(request)
184
+ @request_method_match.any? { |m| m.call request }
185
+ end
123
186
  end
124
187
  end
188
+ # :startdoc:
125
189
  end
@@ -1,45 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
1
5
  module ActionDispatch
2
6
  module Journey # :nodoc:
3
7
  class Router # :nodoc:
4
8
  class Utils # :nodoc:
5
9
  # Normalizes URI path.
6
10
  #
7
- # Strips off trailing slash and ensures there is a leading slash.
8
- # Also converts downcase url encoded string to uppercase.
11
+ # Strips off trailing slash and ensures there is a leading slash. Also converts
12
+ # downcase URL encoded string to uppercase.
9
13
  #
10
- # normalize_path("/foo") # => "/foo"
11
- # normalize_path("/foo/") # => "/foo"
12
- # normalize_path("foo") # => "/foo"
13
- # normalize_path("") # => "/"
14
- # normalize_path("/%ab") # => "/%AB"
14
+ # normalize_path("/foo") # => "/foo"
15
+ # normalize_path("/foo/") # => "/foo"
16
+ # normalize_path("foo") # => "/foo"
17
+ # normalize_path("") # => "/"
18
+ # normalize_path("/%ab") # => "/%AB"
15
19
  def self.normalize_path(path)
16
- path = "/#{path}".force_encoding(Encoding::UTF_8)
17
- path.squeeze!('/')
18
- path.sub!(%r{/+\Z}, '')
19
- path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
20
- path = '/' if path == ''
21
- path
20
+ path ||= ""
21
+ encoding = path.encoding
22
+ path = +"/#{path}"
23
+ path.squeeze!("/")
24
+
25
+ unless path == "/"
26
+ path.delete_suffix!("/")
27
+ path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
28
+ end
29
+
30
+ path.force_encoding(encoding)
22
31
  end
23
32
 
24
- # URI path and fragment escaping
25
- # http://tools.ietf.org/html/rfc3986
33
+ # URI path and fragment escaping https://tools.ietf.org/html/rfc3986
26
34
  class UriEncoder # :nodoc:
27
- ENCODE = "%%%02X".freeze
35
+ ENCODE = "%%%02X"
28
36
  US_ASCII = Encoding::US_ASCII
29
37
  UTF_8 = Encoding::UTF_8
30
- EMPTY = "".force_encoding(US_ASCII).freeze
31
- DEC2HEX = (0..255).to_a.map{ |i| ENCODE % i }.map{ |s| s.force_encoding(US_ASCII) }
38
+ EMPTY = (+"").force_encoding(US_ASCII).freeze
39
+ DEC2HEX = (0..255).map { |i| (ENCODE % i).force_encoding(US_ASCII) }
32
40
 
33
- ALPHA = "a-zA-Z".freeze
34
- DIGIT = "0-9".freeze
35
- UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~".freeze
36
- SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;=".freeze
41
+ ALPHA = "a-zA-Z"
42
+ DIGIT = "0-9"
43
+ UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
44
+ SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
37
45
 
38
- ESCAPED = /%[a-zA-Z0-9]{2}/.freeze
46
+ ESCAPED = /%[a-zA-Z0-9]{2}/
39
47
 
40
- FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/\?]/.freeze
41
- SEGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@]/.freeze
42
- PATH = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/]/.freeze
48
+ FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/?]/
49
+ SEGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@]/
50
+ PATH = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/]/
43
51
 
44
52
  def escape_fragment(fragment)
45
53
  escape(fragment, FRAGMENT)
@@ -55,12 +63,12 @@ module ActionDispatch
55
63
 
56
64
  def unescape_uri(uri)
57
65
  encoding = uri.encoding == US_ASCII ? UTF_8 : uri.encoding
58
- uri.gsub(ESCAPED) { [$&[1, 2].hex].pack('C') }.force_encoding(encoding)
66
+ uri.gsub(ESCAPED) { |match| [match[1, 2].hex].pack("C") }.force_encoding(encoding)
59
67
  end
60
68
 
61
- protected
69
+ private
62
70
  def escape(component, pattern)
63
- component.gsub(pattern){ |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
71
+ component.gsub(pattern) { |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
64
72
  end
65
73
 
66
74
  def percent_encode(unsafe)
@@ -84,6 +92,10 @@ module ActionDispatch
84
92
  ENCODER.escape_fragment(fragment.to_s)
85
93
  end
86
94
 
95
+ # Replaces any escaped sequences with their unescaped representations.
96
+ #
97
+ # uri = "/topics?title=Ruby%20on%20Rails"
98
+ # unescape_uri(uri) #=> "/topics?title=Ruby on Rails"
87
99
  def self.unescape_uri(uri)
88
100
  ENCODER.unescape_uri(uri)
89
101
  end