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,82 +1,95 @@
1
- require 'action_dispatch/journey/router/utils'
2
- require 'action_dispatch/journey/router/strexp'
3
- require 'action_dispatch/journey/routes'
4
- require 'action_dispatch/journey/formatter'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "action_dispatch/journey/router/utils"
6
+ require "action_dispatch/journey/routes"
7
+ require "action_dispatch/journey/formatter"
5
8
 
6
9
  before = $-w
7
10
  $-w = false
8
- require 'action_dispatch/journey/parser'
11
+ require "action_dispatch/journey/parser"
9
12
  $-w = before
10
13
 
11
- require 'action_dispatch/journey/route'
12
- require 'action_dispatch/journey/path/pattern'
14
+ require "action_dispatch/journey/route"
15
+ require "action_dispatch/journey/path/pattern"
13
16
 
14
17
  module ActionDispatch
15
18
  module Journey # :nodoc:
16
19
  class Router # :nodoc:
17
- class RoutingError < ::StandardError # :nodoc:
18
- end
19
-
20
- # :nodoc:
21
- VERSION = '2.0.0'
22
-
23
20
  attr_accessor :routes
24
21
 
25
22
  def initialize(routes)
26
23
  @routes = routes
27
24
  end
28
25
 
26
+ def eager_load!
27
+ # Eagerly trigger the simulator's initialization so it doesn't happen during a
28
+ # request cycle.
29
+ simulator
30
+ nil
31
+ end
32
+
29
33
  def serve(req)
30
- find_routes(req).each do |match, parameters, route|
34
+ find_routes(req) do |match, parameters, route|
31
35
  set_params = req.path_parameters
32
36
  path_info = req.path_info
33
37
  script_name = req.script_name
34
38
 
35
39
  unless route.path.anchored
36
- req.script_name = (script_name.to_s + match.to_s).chomp('/')
40
+ req.script_name = (script_name.to_s + match.to_s).chomp("/")
37
41
  req.path_info = match.post_match
38
42
  req.path_info = "/" + req.path_info unless req.path_info.start_with? "/"
39
43
  end
40
44
 
41
- req.path_parameters = set_params.merge parameters
45
+ tmp_params = set_params.merge route.defaults
46
+ parameters.each_pair { |key, val|
47
+ tmp_params[key] = val.force_encoding(::Encoding::UTF_8)
48
+ }
49
+
50
+ req.path_parameters = tmp_params
51
+ req.route_uri_pattern = route.path.spec.to_s
42
52
 
43
- status, headers, body = route.app.serve(req)
53
+ _, headers, _ = response = route.app.serve(req)
44
54
 
45
- if 'pass' == headers['X-Cascade']
55
+ if "pass" == headers[Constants::X_CASCADE]
46
56
  req.script_name = script_name
47
57
  req.path_info = path_info
48
58
  req.path_parameters = set_params
49
59
  next
50
60
  end
51
61
 
52
- return [status, headers, body]
62
+ return response
53
63
  end
54
64
 
55
- return [404, {'X-Cascade' => 'pass'}, ['Not Found']]
65
+ [404, { Constants::X_CASCADE => "pass" }, ["Not Found"]]
56
66
  end
57
67
 
58
68
  def recognize(rails_req)
59
- find_routes(rails_req).each do |match, parameters, route|
69
+ find_routes(rails_req) do |match, parameters, route|
60
70
  unless route.path.anchored
61
71
  rails_req.script_name = match.to_s
62
- rails_req.path_info = match.post_match.sub(/^([^\/])/, '/\1')
72
+ rails_req.path_info = match.post_match
73
+ rails_req.path_info = "/" + rails_req.path_info unless rails_req.path_info.start_with? "/"
63
74
  end
64
75
 
76
+ parameters = route.defaults.merge parameters
65
77
  yield(route, parameters)
66
78
  end
67
79
  end
68
80
 
69
81
  def visualizer
70
82
  tt = GTG::Builder.new(ast).transition_table
71
- groups = partitioned_routes.first.map(&:ast).group_by { |a| a.to_s }
72
- asts = groups.values.map { |v| v.first }
83
+ groups = partitioned_routes.first.map(&:ast).group_by(&:to_s)
84
+ asts = groups.values.map(&:first)
73
85
  tt.visualizer(asts)
74
86
  end
75
87
 
76
88
  private
77
-
78
89
  def partitioned_routes
79
- routes.partitioned_routes
90
+ routes.partition { |r|
91
+ r.path.anchored && r.path.requirements_anchored?
92
+ }
80
93
  end
81
94
 
82
95
  def ast
@@ -88,7 +101,7 @@ module ActionDispatch
88
101
  end
89
102
 
90
103
  def custom_routes
91
- partitioned_routes.last
104
+ routes.custom_routes
92
105
  end
93
106
 
94
107
  def filter_routes(path)
@@ -96,49 +109,43 @@ module ActionDispatch
96
109
  simulator.memos(path) { [] }
97
110
  end
98
111
 
99
- def find_routes req
100
- routes = filter_routes(req.path_info).concat custom_routes.find_all { |r|
101
- r.path.match(req.path_info)
112
+ def find_routes(req)
113
+ path_info = req.path_info
114
+ routes = filter_routes(path_info).concat custom_routes.find_all { |r|
115
+ r.path.match?(path_info)
102
116
  }
103
117
 
104
- routes =
105
- if req.request_method == "HEAD"
106
- match_head_routes(routes, req)
107
- else
108
- match_routes(routes, req)
109
- end
118
+ if req.head?
119
+ routes = match_head_routes(routes, req)
120
+ else
121
+ routes.select! { |r| r.matches?(req) }
122
+ end
110
123
 
111
124
  routes.sort_by!(&:precedence)
112
125
 
113
- routes.map! { |r|
114
- match_data = r.path.match(req.path_info)
115
- path_parameters = r.defaults.dup
116
- match_data.names.zip(match_data.captures) { |name,val|
126
+ routes.each { |r|
127
+ match_data = r.path.match(path_info)
128
+ path_parameters = {}
129
+ match_data.names.each_with_index { |name, i|
130
+ val = match_data[i + 1]
117
131
  path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
118
132
  }
119
- [match_data, path_parameters, r]
133
+ yield [match_data, path_parameters, r]
120
134
  }
121
135
  end
122
136
 
123
137
  def match_head_routes(routes, req)
124
- verb_specific_routes = routes.reject { |route| route.verb == // }
125
- head_routes = match_routes(verb_specific_routes, req)
126
-
127
- if head_routes.empty?
128
- begin
129
- req.request_method = "GET"
130
- match_routes(routes, req)
131
- ensure
132
- req.request_method = "HEAD"
133
- end
134
- else
135
- head_routes
138
+ head_routes = routes.select { |r| r.requires_matching_verb? && r.matches?(req) }
139
+ return head_routes unless head_routes.empty?
140
+
141
+ begin
142
+ req.request_method = "GET"
143
+ routes.select! { |r| r.matches?(req) }
144
+ routes
145
+ ensure
146
+ req.request_method = "HEAD"
136
147
  end
137
148
  end
138
-
139
- def match_routes(routes, req)
140
- routes.select { |r| r.matches?(req) }
141
- end
142
149
  end
143
150
  end
144
151
  end
@@ -1,17 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
1
5
  module ActionDispatch
2
6
  module Journey # :nodoc:
3
- # The Routing table. Contains all routes for a system. Routes can be
4
- # added to the table by calling Routes#add_route.
7
+ # The Routing table. Contains all routes for a system. Routes can be added to
8
+ # the table by calling Routes#add_route.
5
9
  class Routes # :nodoc:
6
10
  include Enumerable
7
11
 
8
- attr_reader :routes, :named_routes
12
+ attr_reader :routes, :custom_routes, :anchored_routes
9
13
 
10
- def initialize
11
- @routes = []
12
- @named_routes = {}
14
+ def initialize(routes = [])
15
+ @routes = routes
13
16
  @ast = nil
14
- @partitioned_routes = nil
17
+ @anchored_routes = []
18
+ @custom_routes = []
15
19
  @simulator = nil
16
20
  end
17
21
 
@@ -34,19 +38,22 @@ module ActionDispatch
34
38
 
35
39
  def clear
36
40
  routes.clear
37
- named_routes.clear
41
+ anchored_routes.clear
42
+ custom_routes.clear
38
43
  end
39
44
 
40
- def partitioned_routes
41
- @partitioned_routes ||= routes.partition do |r|
42
- r.path.anchored && r.ast.grep(Nodes::Symbol).all?(&:default_regexp?)
45
+ def partition_route(route)
46
+ if route.path.anchored && route.path.requirements_anchored?
47
+ anchored_routes << route
48
+ else
49
+ custom_routes << route
43
50
  end
44
51
  end
45
52
 
46
53
  def ast
47
54
  @ast ||= begin
48
- asts = partitioned_routes.first.map(&:ast)
49
- Nodes::Or.new(asts) unless asts.empty?
55
+ nodes = anchored_routes.map(&:ast)
56
+ Nodes::Or.new(nodes)
50
57
  end
51
58
  end
52
59
 
@@ -57,22 +64,17 @@ module ActionDispatch
57
64
  end
58
65
  end
59
66
 
60
- # Add a route to the routing table.
61
- def add_route(app, path, conditions, defaults, name = nil)
62
- route = Route.new(name, app, path, conditions, defaults)
63
-
64
- route.precedence = routes.length
67
+ def add_route(name, mapping)
68
+ route = mapping.make_route name, routes.length
65
69
  routes << route
66
- named_routes[name] = route if name && !named_routes[name]
70
+ partition_route(route)
67
71
  clear_cache!
68
72
  route
69
73
  end
70
74
 
71
75
  private
72
-
73
76
  def clear_cache!
74
77
  @ast = nil
75
- @partitioned_routes = nil
76
78
  @simulator = nil
77
79
  end
78
80
  end
@@ -1,4 +1,8 @@
1
- require 'strscan'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "strscan"
2
6
 
3
7
  module ActionDispatch
4
8
  module Journey # :nodoc:
@@ -31,28 +35,35 @@ module ActionDispatch
31
35
  end
32
36
 
33
37
  private
38
+ # takes advantage of String @- deduping capabilities in Ruby 2.5 upwards see:
39
+ # https://bugs.ruby-lang.org/issues/13077
40
+ def dedup_scan(regex)
41
+ r = @ss.scan(regex)
42
+ r ? -r : nil
43
+ end
34
44
 
35
45
  def scan
36
46
  case
37
47
  # /
38
- when text = @ss.scan(/\//)
39
- [:SLASH, text]
40
- when text = @ss.scan(/\*\w+/)
41
- [:STAR, text]
42
- when text = @ss.scan(/(?<!\\)\(/)
43
- [:LPAREN, text]
44
- when text = @ss.scan(/(?<!\\)\)/)
45
- [:RPAREN, text]
46
- when text = @ss.scan(/\|/)
47
- [:OR, text]
48
- when text = @ss.scan(/\./)
49
- [:DOT, text]
50
- when text = @ss.scan(/(?<!\\):\w+/)
48
+ when @ss.skip(/\//)
49
+ [:SLASH, "/"]
50
+ when @ss.skip(/\(/)
51
+ [:LPAREN, "("]
52
+ when @ss.skip(/\)/)
53
+ [:RPAREN, ")"]
54
+ when @ss.skip(/\|/)
55
+ [:OR, "|"]
56
+ when @ss.skip(/\./)
57
+ [:DOT, "."]
58
+ when text = dedup_scan(/:\w+/)
51
59
  [:SYMBOL, text]
52
- when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\:|\\\(|\\\))+/)
53
- [:LITERAL, text.tr('\\', '')]
60
+ when text = dedup_scan(/\*\w+/)
61
+ [:STAR, text]
62
+ when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\[:()])+/)
63
+ text.tr! "\\", ""
64
+ [:LITERAL, -text]
54
65
  # any char
55
- when text = @ss.scan(/./)
66
+ when text = dedup_scan(/./)
56
67
  [:LITERAL, text]
57
68
  end
58
69
  end
@@ -1,12 +1,15 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
2
4
 
3
5
  module ActionDispatch
4
- module Journey # :nodoc:
6
+ # :stopdoc:
7
+ module Journey
5
8
  class Format
6
9
  ESCAPE_PATH = ->(value) { Router::Utils.escape_path(value) }
7
10
  ESCAPE_SEGMENT = ->(value) { Router::Utils.escape_segment(value) }
8
11
 
9
- class Parameter < Struct.new(:name, :escaper)
12
+ Parameter = Struct.new(:name, :escaper) do
10
13
  def escape(value); escaper.call value; end
11
14
  end
12
15
 
@@ -23,7 +26,7 @@ module ActionDispatch
23
26
  @children = []
24
27
  @parameters = []
25
28
 
26
- parts.each_with_index do |object,i|
29
+ parts.each_with_index do |object, i|
27
30
  case object
28
31
  when Journey::Format
29
32
  @children << i
@@ -39,7 +42,7 @@ module ActionDispatch
39
42
  @parameters.each do |index|
40
43
  param = parts[index]
41
44
  value = hash[param.name]
42
- return ''.freeze unless value
45
+ return "" if value.nil?
43
46
  parts[index] = param.escape value
44
47
  end
45
48
 
@@ -58,8 +61,7 @@ module ActionDispatch
58
61
  end
59
62
 
60
63
  private
61
-
62
- def visit node
64
+ def visit(node)
63
65
  send(DISPATCH_CACHE[node.type], node)
64
66
  end
65
67
 
@@ -92,6 +94,45 @@ module ActionDispatch
92
94
  end
93
95
  end
94
96
 
97
+ class FunctionalVisitor # :nodoc:
98
+ DISPATCH_CACHE = {}
99
+
100
+ def accept(node, seed)
101
+ visit(node, seed)
102
+ end
103
+
104
+ def visit(node, seed)
105
+ send(DISPATCH_CACHE[node.type], node, seed)
106
+ end
107
+
108
+ def binary(node, seed)
109
+ visit(node.right, visit(node.left, seed))
110
+ end
111
+ def visit_CAT(n, seed); binary(n, seed); end
112
+
113
+ def nary(node, seed)
114
+ node.children.inject(seed) { |s, c| visit(c, s) }
115
+ end
116
+ def visit_OR(n, seed); nary(n, seed); end
117
+
118
+ def unary(node, seed)
119
+ visit(node.left, seed)
120
+ end
121
+ def visit_GROUP(n, seed); unary(n, seed); end
122
+ def visit_STAR(n, seed); unary(n, seed); end
123
+
124
+ def terminal(node, seed); seed; end
125
+ def visit_LITERAL(n, seed); terminal(n, seed); end
126
+ def visit_SYMBOL(n, seed); terminal(n, seed); end
127
+ def visit_SLASH(n, seed); terminal(n, seed); end
128
+ def visit_DOT(n, seed); terminal(n, seed); end
129
+
130
+ instance_methods(false).each do |pim|
131
+ next unless pim =~ /^visit_(.*)$/
132
+ DISPATCH_CACHE[$1.to_sym] = pim
133
+ end
134
+ end
135
+
95
136
  class FormatBuilder < Visitor # :nodoc:
96
137
  def accept(node); Journey::Format.new(super); end
97
138
  def terminal(node); [node.left]; end
@@ -116,106 +157,111 @@ module ActionDispatch
116
157
  end
117
158
  end
118
159
 
119
- # Loop through the requirements AST
120
- class Each < Visitor # :nodoc:
121
- attr_reader :block
122
-
123
- def initialize(block)
124
- @block = block
125
- end
126
-
127
- def visit(node)
160
+ # Loop through the requirements AST.
161
+ class Each < FunctionalVisitor # :nodoc:
162
+ def visit(node, block)
128
163
  block.call(node)
129
164
  super
130
165
  end
166
+
167
+ INSTANCE = new
131
168
  end
132
169
 
133
- class String < Visitor # :nodoc:
170
+ class String < FunctionalVisitor # :nodoc:
134
171
  private
172
+ def binary(node, seed)
173
+ visit(node.right, visit(node.left, seed))
174
+ end
135
175
 
136
- def binary(node)
137
- [visit(node.left), visit(node.right)].join
138
- end
176
+ def nary(node, seed)
177
+ last_child = node.children.last
178
+ node.children.inject(seed) { |s, c|
179
+ string = visit(c, s)
180
+ string << "|" unless last_child == c
181
+ string
182
+ }
183
+ end
139
184
 
140
- def nary(node)
141
- node.children.map { |c| visit(c) }.join '|'
142
- end
185
+ def terminal(node, seed)
186
+ seed + node.left
187
+ end
143
188
 
144
- def terminal(node)
145
- node.left
146
- end
189
+ def visit_GROUP(node, seed)
190
+ visit(node.left, seed.dup << "(") << ")"
191
+ end
147
192
 
148
- def visit_GROUP(node)
149
- "(#{visit(node.left)})"
150
- end
193
+ INSTANCE = new
151
194
  end
152
195
 
153
- class Dot < Visitor # :nodoc:
196
+ class Dot < FunctionalVisitor # :nodoc:
154
197
  def initialize
155
198
  @nodes = []
156
199
  @edges = []
157
200
  end
158
201
 
159
- def accept(node)
202
+ def accept(node, seed = [[], []])
160
203
  super
204
+ nodes, edges = seed
161
205
  <<-eodot
162
206
  digraph parse_tree {
163
207
  size="8,5"
164
208
  node [shape = none];
165
209
  edge [dir = none];
166
- #{@nodes.join "\n"}
167
- #{@edges.join("\n")}
210
+ #{nodes.join "\n"}
211
+ #{edges.join("\n")}
168
212
  }
169
213
  eodot
170
214
  end
171
215
 
172
216
  private
173
-
174
- def binary(node)
175
- node.children.each do |c|
176
- @edges << "#{node.object_id} -> #{c.object_id};"
177
- end
217
+ def binary(node, seed)
218
+ seed.last.concat node.children.map { |c|
219
+ "#{node.object_id} -> #{c.object_id};"
220
+ }
178
221
  super
179
222
  end
180
223
 
181
- def nary(node)
182
- node.children.each do |c|
183
- @edges << "#{node.object_id} -> #{c.object_id};"
184
- end
224
+ def nary(node, seed)
225
+ seed.last.concat node.children.map { |c|
226
+ "#{node.object_id} -> #{c.object_id};"
227
+ }
185
228
  super
186
229
  end
187
230
 
188
- def unary(node)
189
- @edges << "#{node.object_id} -> #{node.left.object_id};"
231
+ def unary(node, seed)
232
+ seed.last << "#{node.object_id} -> #{node.left.object_id};"
190
233
  super
191
234
  end
192
235
 
193
- def visit_GROUP(node)
194
- @nodes << "#{node.object_id} [label=\"()\"];"
236
+ def visit_GROUP(node, seed)
237
+ seed.first << "#{node.object_id} [label=\"()\"];"
195
238
  super
196
239
  end
197
240
 
198
- def visit_CAT(node)
199
- @nodes << "#{node.object_id} [label=\"○\"];"
241
+ def visit_CAT(node, seed)
242
+ seed.first << "#{node.object_id} [label=\"○\"];"
200
243
  super
201
244
  end
202
245
 
203
- def visit_STAR(node)
204
- @nodes << "#{node.object_id} [label=\"*\"];"
246
+ def visit_STAR(node, seed)
247
+ seed.first << "#{node.object_id} [label=\"*\"];"
205
248
  super
206
249
  end
207
250
 
208
- def visit_OR(node)
209
- @nodes << "#{node.object_id} [label=\"|\"];"
251
+ def visit_OR(node, seed)
252
+ seed.first << "#{node.object_id} [label=\"|\"];"
210
253
  super
211
254
  end
212
255
 
213
- def terminal(node)
256
+ def terminal(node, seed)
214
257
  value = node.left
215
258
 
216
- @nodes << "#{node.object_id} [label=\"#{value}\"];"
259
+ seed.first << "#{node.object_id} [label=\"#{value}\"];"
260
+ seed
217
261
  end
262
+ INSTANCE = new
218
263
  end
219
264
  end
220
265
  end
266
+ # :startdoc:
221
267
  end