omg-actionpack 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +129 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller/asset_paths.rb +14 -0
  6. data/lib/abstract_controller/base.rb +299 -0
  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 +265 -0
  10. data/lib/abstract_controller/collector.rb +44 -0
  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 +243 -0
  14. data/lib/abstract_controller/logger.rb +16 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
  16. data/lib/abstract_controller/rendering.rb +126 -0
  17. data/lib/abstract_controller/translation.rb +42 -0
  18. data/lib/abstract_controller/url_for.rb +37 -0
  19. data/lib/abstract_controller.rb +36 -0
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +155 -0
  22. data/lib/action_controller/base.rb +332 -0
  23. data/lib/action_controller/caching.rb +49 -0
  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 +96 -0
  27. data/lib/action_controller/metal/allow_browser.rb +123 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +341 -0
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +20 -0
  32. data/lib/action_controller/metal/data_streaming.rb +154 -0
  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 +59 -0
  36. data/lib/action_controller/metal/exceptions.rb +106 -0
  37. data/lib/action_controller/metal/flash.rb +67 -0
  38. data/lib/action_controller/metal/head.rb +67 -0
  39. data/lib/action_controller/metal/helpers.rb +129 -0
  40. data/lib/action_controller/metal/http_authentication.rb +565 -0
  41. data/lib/action_controller/metal/implicit_render.rb +67 -0
  42. data/lib/action_controller/metal/instrumentation.rb +120 -0
  43. data/lib/action_controller/metal/live.rb +398 -0
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +337 -0
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +312 -0
  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 +251 -0
  51. data/lib/action_controller/metal/renderers.rb +181 -0
  52. data/lib/action_controller/metal/rendering.rb +260 -0
  53. data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
  54. data/lib/action_controller/metal/rescue.rb +33 -0
  55. data/lib/action_controller/metal/streaming.rb +183 -0
  56. data/lib/action_controller/metal/strong_parameters.rb +1546 -0
  57. data/lib/action_controller/metal/testing.rb +25 -0
  58. data/lib/action_controller/metal/url_for.rb +65 -0
  59. data/lib/action_controller/metal.rb +339 -0
  60. data/lib/action_controller/railtie.rb +149 -0
  61. data/lib/action_controller/railties/helpers.rb +26 -0
  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 +691 -0
  65. data/lib/action_controller.rb +80 -0
  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 +249 -0
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +365 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +80 -0
  72. data/lib/action_dispatch/http/filter_redirect.rb +50 -0
  73. data/lib/action_dispatch/http/headers.rb +134 -0
  74. data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
  75. data/lib/action_dispatch/http/mime_type.rb +389 -0
  76. data/lib/action_dispatch/http/mime_types.rb +54 -0
  77. data/lib/action_dispatch/http/parameters.rb +119 -0
  78. data/lib/action_dispatch/http/permissions_policy.rb +189 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +67 -0
  80. data/lib/action_dispatch/http/request.rb +498 -0
  81. data/lib/action_dispatch/http/response.rb +556 -0
  82. data/lib/action_dispatch/http/upload.rb +107 -0
  83. data/lib/action_dispatch/http/url.rb +344 -0
  84. data/lib/action_dispatch/journey/formatter.rb +226 -0
  85. data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
  88. data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
  89. data/lib/action_dispatch/journey/nodes/node.rb +208 -0
  90. data/lib/action_dispatch/journey/parser.rb +103 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +209 -0
  92. data/lib/action_dispatch/journey/route.rb +189 -0
  93. data/lib/action_dispatch/journey/router/utils.rb +105 -0
  94. data/lib/action_dispatch/journey/router.rb +151 -0
  95. data/lib/action_dispatch/journey/routes.rb +82 -0
  96. data/lib/action_dispatch/journey/scanner.rb +70 -0
  97. data/lib/action_dispatch/journey/visitors.rb +267 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/journey.rb +7 -0
  102. data/lib/action_dispatch/log_subscriber.rb +25 -0
  103. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  104. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  105. data/lib/action_dispatch/middleware/callbacks.rb +38 -0
  106. data/lib/action_dispatch/middleware/cookies.rb +719 -0
  107. data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
  108. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  109. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  110. data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
  111. data/lib/action_dispatch/middleware/executor.rb +32 -0
  112. data/lib/action_dispatch/middleware/flash.rb +318 -0
  113. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  114. data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
  115. data/lib/action_dispatch/middleware/reloader.rb +16 -0
  116. data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
  117. data/lib/action_dispatch/middleware/request_id.rb +50 -0
  118. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  119. data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
  120. data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
  121. data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
  122. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
  123. data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
  124. data/lib/action_dispatch/middleware/ssl.rb +180 -0
  125. data/lib/action_dispatch/middleware/stack.rb +194 -0
  126. data/lib/action_dispatch/middleware/static.rb +192 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +17 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  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 +35 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  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 +284 -0
  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 +11 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  148. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  149. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  150. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  151. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  152. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  153. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
  154. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
  155. data/lib/action_dispatch/railtie.rb +77 -0
  156. data/lib/action_dispatch/request/session.rb +283 -0
  157. data/lib/action_dispatch/request/utils.rb +109 -0
  158. data/lib/action_dispatch/routing/endpoint.rb +19 -0
  159. data/lib/action_dispatch/routing/inspector.rb +323 -0
  160. data/lib/action_dispatch/routing/mapper.rb +2372 -0
  161. data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
  162. data/lib/action_dispatch/routing/redirection.rb +218 -0
  163. data/lib/action_dispatch/routing/route_set.rb +958 -0
  164. data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
  165. data/lib/action_dispatch/routing/url_for.rb +244 -0
  166. data/lib/action_dispatch/routing.rb +262 -0
  167. data/lib/action_dispatch/system_test_case.rb +206 -0
  168. data/lib/action_dispatch/system_testing/browser.rb +75 -0
  169. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  170. data/lib/action_dispatch/system_testing/server.rb +33 -0
  171. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  172. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  173. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  174. data/lib/action_dispatch/testing/assertions/response.rb +114 -0
  175. data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
  176. data/lib/action_dispatch/testing/assertions.rb +25 -0
  177. data/lib/action_dispatch/testing/integration.rb +694 -0
  178. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  179. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  180. data/lib/action_dispatch/testing/test_process.rb +57 -0
  181. data/lib/action_dispatch/testing/test_request.rb +73 -0
  182. data/lib/action_dispatch/testing/test_response.rb +58 -0
  183. data/lib/action_dispatch.rb +147 -0
  184. data/lib/action_pack/gem_version.rb +19 -0
  185. data/lib/action_pack/version.rb +12 -0
  186. data/lib/action_pack.rb +27 -0
  187. metadata +375 -0
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_dispatch/journey/scanner"
4
+ require "action_dispatch/journey/nodes/node"
5
+
6
+ module ActionDispatch
7
+ module Journey # :nodoc:
8
+ class Parser # :nodoc:
9
+ include Journey::Nodes
10
+
11
+ def self.parse(string)
12
+ new.parse string
13
+ end
14
+
15
+ def initialize
16
+ @scanner = Scanner.new
17
+ @next_token = nil
18
+ end
19
+
20
+ def parse(string)
21
+ @scanner.scan_setup(string)
22
+ advance_token
23
+ do_parse
24
+ end
25
+
26
+ private
27
+ def advance_token
28
+ @next_token = @scanner.next_token
29
+ end
30
+
31
+ def do_parse
32
+ parse_expressions
33
+ end
34
+
35
+ def parse_expressions
36
+ node = parse_expression
37
+
38
+ while @next_token
39
+ case @next_token
40
+ when :RPAREN
41
+ break
42
+ when :OR
43
+ node = parse_or(node)
44
+ else
45
+ node = Cat.new(node, parse_expressions)
46
+ end
47
+ end
48
+
49
+ node
50
+ end
51
+
52
+ def parse_or(lhs)
53
+ advance_token
54
+ node = parse_expression
55
+ Or.new([lhs, node])
56
+ end
57
+
58
+ def parse_expression
59
+ if @next_token == :STAR
60
+ parse_star
61
+ elsif @next_token == :LPAREN
62
+ parse_group
63
+ else
64
+ parse_terminal
65
+ end
66
+ end
67
+
68
+ def parse_star
69
+ node = Star.new(Symbol.new(@scanner.last_string, Symbol::GREEDY_EXP))
70
+ advance_token
71
+ node
72
+ end
73
+
74
+ def parse_group
75
+ advance_token
76
+ node = parse_expressions
77
+ if @next_token == :RPAREN
78
+ node = Group.new(node)
79
+ advance_token
80
+ node
81
+ else
82
+ raise ArgumentError, "missing right parenthesis."
83
+ end
84
+ end
85
+
86
+ def parse_terminal
87
+ node = case @next_token
88
+ when :SYMBOL
89
+ Symbol.new(@scanner.last_string)
90
+ when :LITERAL
91
+ Literal.new(@scanner.last_literal)
92
+ when :SLASH
93
+ Slash.new("/")
94
+ when :DOT
95
+ Dot.new(".")
96
+ end
97
+
98
+ advance_token
99
+ node
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionDispatch
6
+ module Journey # :nodoc:
7
+ module Path # :nodoc:
8
+ class Pattern # :nodoc:
9
+ attr_reader :ast, :names, :requirements, :anchored, :spec
10
+
11
+ def initialize(ast, requirements, separators, anchored)
12
+ @ast = ast
13
+ @spec = ast.root
14
+ @requirements = requirements
15
+ @separators = separators
16
+ @anchored = anchored
17
+
18
+ @names = ast.names
19
+ @optional_names = nil
20
+ @required_names = nil
21
+ @re = nil
22
+ @offsets = nil
23
+ end
24
+
25
+ def build_formatter
26
+ Visitors::FormatBuilder.new.accept(spec)
27
+ end
28
+
29
+ def eager_load!
30
+ required_names
31
+ offsets
32
+ to_regexp
33
+ @ast = nil
34
+ end
35
+
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
+
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)
50
+
51
+ return false if back.literal?
52
+ return false if !fwd.nil? && fwd.literal?
53
+ }
54
+
55
+ true
56
+ end
57
+
58
+ def required_names
59
+ @required_names ||= names - optional_names
60
+ end
61
+
62
+ def optional_names
63
+ @optional_names ||= spec.find_all(&:group?).flat_map { |group|
64
+ group.find_all(&:symbol?)
65
+ }.map(&:name).uniq
66
+ end
67
+
68
+ class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc:
69
+ def initialize(separator, matchers)
70
+ @separator = separator
71
+ @matchers = matchers
72
+ @separator_re = "([^#{separator}]+)"
73
+ super()
74
+ end
75
+
76
+ def accept(node)
77
+ %r{\A#{visit node}\Z}
78
+ end
79
+
80
+ def visit_CAT(node)
81
+ "#{visit(node.left)}#{visit(node.right)}"
82
+ end
83
+
84
+ def visit_SYMBOL(node)
85
+ node = node.to_sym
86
+
87
+ return @separator_re unless @matchers.key?(node)
88
+
89
+ re = @matchers[node]
90
+ "(#{Regexp.union(re)})"
91
+ end
92
+
93
+ def visit_GROUP(node)
94
+ "(?:#{visit node.left})?"
95
+ end
96
+
97
+ def visit_LITERAL(node)
98
+ Regexp.escape(node.left)
99
+ end
100
+ alias :visit_DOT :visit_LITERAL
101
+
102
+ def visit_SLASH(node)
103
+ node.left
104
+ end
105
+
106
+ def visit_STAR(node)
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(?|)})"
114
+ end
115
+ end
116
+
117
+ class UnanchoredRegexp < AnchoredRegexp # :nodoc:
118
+ def accept(node)
119
+ path = visit node
120
+ path == "/" ? %r{\A/} : %r{\A#{path}(?:\b|\Z|/)}
121
+ end
122
+ end
123
+
124
+ class MatchData # :nodoc:
125
+ attr_reader :names
126
+
127
+ def initialize(names, offsets, match)
128
+ @names = names
129
+ @offsets = offsets
130
+ @match = match
131
+ end
132
+
133
+ def captures
134
+ Array.new(length - 1) { |i| self[i + 1] }
135
+ end
136
+
137
+ def named_captures
138
+ @names.zip(captures).to_h
139
+ end
140
+
141
+ def [](x)
142
+ idx = @offsets[x - 1] + x
143
+ @match[idx]
144
+ end
145
+
146
+ def length
147
+ @offsets.length
148
+ end
149
+
150
+ def post_match
151
+ @match.post_match
152
+ end
153
+
154
+ def to_s
155
+ @match.to_s
156
+ end
157
+ end
158
+
159
+ def match(other)
160
+ return unless match = to_regexp.match(other)
161
+ MatchData.new(names, offsets, match)
162
+ end
163
+ alias :=~ :match
164
+
165
+ def match?(other)
166
+ to_regexp.match?(other)
167
+ end
168
+
169
+ def source
170
+ to_regexp.source
171
+ end
172
+
173
+ def to_regexp
174
+ @re ||= regexp_visitor.new(@separators, @requirements).accept spec
175
+ end
176
+
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
182
+
183
+ private
184
+ def regexp_visitor
185
+ @anchored ? AnchoredRegexp : UnanchoredRegexp
186
+ end
187
+
188
+ def offsets
189
+ @offsets ||= begin
190
+ offsets = [0]
191
+
192
+ spec.find_all(&:symbol?).each do |node|
193
+ node = node.to_sym
194
+
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
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionDispatch
6
+ # :stopdoc:
7
+ module Journey
8
+ class Route
9
+ attr_reader :app, :path, :defaults, :name, :precedence, :constraints,
10
+ :internal, :scope_options, :ast, :source_location
11
+
12
+ alias :conditions :constraints
13
+
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
54
+
55
+ ##
56
+ # +path+ is a path constraint.
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)
59
+ @name = name
60
+ @app = app
61
+ @path = path
62
+
63
+ @request_method_match = request_method_match
64
+ @constraints = constraints
65
+ @defaults = defaults
66
+ @required_defaults = nil
67
+ @_required_defaults = required_defaults
68
+ @required_parts = nil
69
+ @parts = nil
70
+ @precedence = precedence
71
+ @path_formatter = @path.build_formatter
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
98
+ }
99
+ end
100
+
101
+ def segments
102
+ path.names
103
+ end
104
+
105
+ def required_keys
106
+ required_parts + required_defaults.keys
107
+ end
108
+
109
+ def score(supplied_keys)
110
+ path.required_names.each do |k|
111
+ return -1 unless supplied_keys.include?(k)
112
+ end
113
+
114
+ (required_defaults.length * 2) + path.names.count { |k| supplied_keys.include?(k) }
115
+ end
116
+
117
+ def parts
118
+ @parts ||= segments.map(&:to_sym)
119
+ end
120
+ alias :segment_keys :parts
121
+
122
+ def format(path_options)
123
+ @path_formatter.evaluate path_options
124
+ end
125
+
126
+ def required_parts
127
+ @required_parts ||= path.required_names.map(&:to_sym)
128
+ end
129
+
130
+ def required_default?(key)
131
+ @_required_defaults.include?(key)
132
+ end
133
+
134
+ def required_defaults
135
+ @required_defaults ||= @defaults.dup.delete_if do |k, _|
136
+ parts.include?(k) || !required_default?(k)
137
+ end
138
+ end
139
+
140
+ def glob?
141
+ path.ast.glob?
142
+ end
143
+
144
+ def dispatcher?
145
+ @app.dispatcher?
146
+ end
147
+
148
+ def matches?(request)
149
+ match_verb(request) &&
150
+ constraints.all? { |method, value|
151
+ case value
152
+ when Regexp, String
153
+ value === request.send(method).to_s
154
+ when Array
155
+ value.include?(request.send(method))
156
+ when TrueClass
157
+ request.send(method).present?
158
+ when FalseClass
159
+ request.send(method).blank?
160
+ else
161
+ value === request.send(method)
162
+ end
163
+ }
164
+ end
165
+
166
+ def ip
167
+ constraints[:ip] || //
168
+ end
169
+
170
+ def requires_matching_verb?
171
+ !@request_method_match.all? { |x| x == VerbMatchers::All }
172
+ end
173
+
174
+ def verb
175
+ verbs.join("|")
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
186
+ end
187
+ end
188
+ # :startdoc:
189
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionDispatch
6
+ module Journey # :nodoc:
7
+ class Router # :nodoc:
8
+ class Utils # :nodoc:
9
+ # Normalizes URI path.
10
+ #
11
+ # Strips off trailing slash and ensures there is a leading slash. Also converts
12
+ # downcase URL encoded string to uppercase.
13
+ #
14
+ # normalize_path("/foo") # => "/foo"
15
+ # normalize_path("/foo/") # => "/foo"
16
+ # normalize_path("foo") # => "/foo"
17
+ # normalize_path("") # => "/"
18
+ # normalize_path("/%ab") # => "/%AB"
19
+ def self.normalize_path(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)
31
+ end
32
+
33
+ # URI path and fragment escaping https://tools.ietf.org/html/rfc3986
34
+ class UriEncoder # :nodoc:
35
+ ENCODE = "%%%02X"
36
+ US_ASCII = Encoding::US_ASCII
37
+ UTF_8 = Encoding::UTF_8
38
+ EMPTY = (+"").force_encoding(US_ASCII).freeze
39
+ DEC2HEX = (0..255).map { |i| (ENCODE % i).force_encoding(US_ASCII) }
40
+
41
+ ALPHA = "a-zA-Z"
42
+ DIGIT = "0-9"
43
+ UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
44
+ SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
45
+
46
+ ESCAPED = /%[a-zA-Z0-9]{2}/
47
+
48
+ FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/?]/
49
+ SEGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@]/
50
+ PATH = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/]/
51
+
52
+ def escape_fragment(fragment)
53
+ escape(fragment, FRAGMENT)
54
+ end
55
+
56
+ def escape_path(path)
57
+ escape(path, PATH)
58
+ end
59
+
60
+ def escape_segment(segment)
61
+ escape(segment, SEGMENT)
62
+ end
63
+
64
+ def unescape_uri(uri)
65
+ encoding = uri.encoding == US_ASCII ? UTF_8 : uri.encoding
66
+ uri.gsub(ESCAPED) { |match| [match[1, 2].hex].pack("C") }.force_encoding(encoding)
67
+ end
68
+
69
+ private
70
+ def escape(component, pattern)
71
+ component.gsub(pattern) { |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
72
+ end
73
+
74
+ def percent_encode(unsafe)
75
+ safe = EMPTY.dup
76
+ unsafe.each_byte { |b| safe << DEC2HEX[b] }
77
+ safe
78
+ end
79
+ end
80
+
81
+ ENCODER = UriEncoder.new
82
+
83
+ def self.escape_path(path)
84
+ ENCODER.escape_path(path.to_s)
85
+ end
86
+
87
+ def self.escape_segment(segment)
88
+ ENCODER.escape_segment(segment.to_s)
89
+ end
90
+
91
+ def self.escape_fragment(fragment)
92
+ ENCODER.escape_fragment(fragment.to_s)
93
+ end
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"
99
+ def self.unescape_uri(uri)
100
+ ENCODER.unescape_uri(uri)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end