actionpack 5.2.8.1 → 6.0.6

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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +270 -347
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/abstract_controller/base.rb +4 -3
  6. data/lib/abstract_controller/caching/fragments.rb +6 -22
  7. data/lib/abstract_controller/caching.rb +1 -1
  8. data/lib/abstract_controller/callbacks.rb +12 -0
  9. data/lib/abstract_controller/collector.rb +1 -2
  10. data/lib/abstract_controller/helpers.rb +7 -6
  11. data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
  12. data/lib/abstract_controller/translation.rb +4 -4
  13. data/lib/action_controller/api.rb +2 -1
  14. data/lib/action_controller/base.rb +2 -7
  15. data/lib/action_controller/caching.rb +1 -2
  16. data/lib/action_controller/log_subscriber.rb +8 -5
  17. data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
  18. data/lib/action_controller/metal/conditional_get.rb +9 -3
  19. data/lib/action_controller/metal/content_security_policy.rb +0 -1
  20. data/lib/action_controller/metal/data_streaming.rb +5 -6
  21. data/lib/action_controller/metal/default_headers.rb +17 -0
  22. data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
  23. data/lib/action_controller/metal/exceptions.rb +23 -2
  24. data/lib/action_controller/metal/flash.rb +5 -5
  25. data/lib/action_controller/metal/force_ssl.rb +15 -56
  26. data/lib/action_controller/metal/head.rb +1 -1
  27. data/lib/action_controller/metal/helpers.rb +3 -4
  28. data/lib/action_controller/metal/http_authentication.rb +20 -21
  29. data/lib/action_controller/metal/implicit_render.rb +4 -14
  30. data/lib/action_controller/metal/instrumentation.rb +3 -6
  31. data/lib/action_controller/metal/live.rb +29 -31
  32. data/lib/action_controller/metal/mime_responds.rb +13 -2
  33. data/lib/action_controller/metal/params_wrapper.rb +18 -14
  34. data/lib/action_controller/metal/redirecting.rb +5 -5
  35. data/lib/action_controller/metal/renderers.rb +4 -4
  36. data/lib/action_controller/metal/rendering.rb +2 -3
  37. data/lib/action_controller/metal/request_forgery_protection.rb +25 -48
  38. data/lib/action_controller/metal/streaming.rb +0 -1
  39. data/lib/action_controller/metal/strong_parameters.rb +65 -44
  40. data/lib/action_controller/metal/url_for.rb +1 -1
  41. data/lib/action_controller/metal.rb +8 -6
  42. data/lib/action_controller/railties/helpers.rb +1 -1
  43. data/lib/action_controller/renderer.rb +17 -3
  44. data/lib/action_controller/template_assertions.rb +1 -1
  45. data/lib/action_controller/test_case.rb +7 -8
  46. data/lib/action_controller.rb +5 -1
  47. data/lib/action_dispatch/http/cache.rb +14 -11
  48. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  49. data/lib/action_dispatch/http/content_security_policy.rb +28 -17
  50. data/lib/action_dispatch/http/filter_parameters.rb +8 -7
  51. data/lib/action_dispatch/http/filter_redirect.rb +1 -2
  52. data/lib/action_dispatch/http/headers.rb +1 -2
  53. data/lib/action_dispatch/http/mime_negotiation.rb +13 -6
  54. data/lib/action_dispatch/http/mime_type.rb +14 -8
  55. data/lib/action_dispatch/http/parameter_filter.rb +5 -79
  56. data/lib/action_dispatch/http/parameters.rb +15 -6
  57. data/lib/action_dispatch/http/request.rb +21 -14
  58. data/lib/action_dispatch/http/response.rb +40 -21
  59. data/lib/action_dispatch/http/upload.rb +9 -1
  60. data/lib/action_dispatch/http/url.rb +81 -82
  61. data/lib/action_dispatch/journey/formatter.rb +2 -3
  62. data/lib/action_dispatch/journey/gtg/builder.rb +0 -1
  63. data/lib/action_dispatch/journey/gtg/transition_table.rb +0 -1
  64. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
  65. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -1
  66. data/lib/action_dispatch/journey/nodes/node.rb +9 -8
  67. data/lib/action_dispatch/journey/path/pattern.rb +6 -3
  68. data/lib/action_dispatch/journey/route.rb +5 -4
  69. data/lib/action_dispatch/journey/router/utils.rb +10 -10
  70. data/lib/action_dispatch/journey/router.rb +0 -4
  71. data/lib/action_dispatch/journey/routes.rb +0 -2
  72. data/lib/action_dispatch/journey/scanner.rb +10 -4
  73. data/lib/action_dispatch/journey/visitors.rb +1 -4
  74. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  75. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  76. data/lib/action_dispatch/middleware/cookies.rb +62 -78
  77. data/lib/action_dispatch/middleware/debug_exceptions.rb +45 -61
  78. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  79. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  80. data/lib/action_dispatch/middleware/exception_wrapper.rb +49 -16
  81. data/lib/action_dispatch/middleware/flash.rb +1 -1
  82. data/lib/action_dispatch/middleware/host_authorization.rb +121 -0
  83. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
  84. data/lib/action_dispatch/middleware/remote_ip.rb +9 -12
  85. data/lib/action_dispatch/middleware/request_id.rb +2 -2
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +0 -1
  87. data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -7
  88. data/lib/action_dispatch/middleware/show_exceptions.rb +1 -2
  89. data/lib/action_dispatch/middleware/ssl.rb +8 -8
  90. data/lib/action_dispatch/middleware/stack.rb +38 -2
  91. data/lib/action_dispatch/middleware/static.rb +6 -7
  92. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  93. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  94. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
  95. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  96. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  97. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  98. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  99. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -4
  101. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  102. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +7 -4
  103. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +5 -2
  104. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -0
  105. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  106. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  107. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  108. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  109. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  110. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
  111. data/lib/action_dispatch/railtie.rb +7 -2
  112. data/lib/action_dispatch/request/session.rb +9 -2
  113. data/lib/action_dispatch/routing/inspector.rb +97 -50
  114. data/lib/action_dispatch/routing/mapper.rb +63 -42
  115. data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -6
  116. data/lib/action_dispatch/routing/route_set.rb +25 -31
  117. data/lib/action_dispatch/routing/url_for.rb +2 -2
  118. data/lib/action_dispatch/routing.rb +21 -20
  119. data/lib/action_dispatch/system_test_case.rb +44 -6
  120. data/lib/action_dispatch/system_testing/browser.rb +38 -7
  121. data/lib/action_dispatch/system_testing/driver.rb +11 -2
  122. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +6 -5
  123. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +7 -6
  124. data/lib/action_dispatch/testing/assertion_response.rb +0 -1
  125. data/lib/action_dispatch/testing/assertions/response.rb +2 -3
  126. data/lib/action_dispatch/testing/assertions/routing.rb +15 -3
  127. data/lib/action_dispatch/testing/assertions.rb +1 -1
  128. data/lib/action_dispatch/testing/integration.rb +33 -12
  129. data/lib/action_dispatch/testing/request_encoder.rb +2 -2
  130. data/lib/action_dispatch/testing/test_process.rb +2 -2
  131. data/lib/action_dispatch/testing/test_response.rb +4 -32
  132. data/lib/action_dispatch.rb +7 -2
  133. data/lib/action_pack/gem_version.rb +4 -4
  134. data/lib/action_pack.rb +1 -1
  135. metadata +29 -15
  136. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -67,7 +67,7 @@ module ActionDispatch
67
67
  end
68
68
 
69
69
  def path_for(options)
70
- path = options[:script_name].to_s.chomp("/".freeze)
70
+ path = options[:script_name].to_s.chomp("/")
71
71
  path << options[:path] if options.key?(:path)
72
72
 
73
73
  add_trailing_slash(path) if options[:trailing_slash]
@@ -78,109 +78,108 @@ module ActionDispatch
78
78
  end
79
79
 
80
80
  private
81
-
82
- def add_params(path, params)
83
- params = { params: params } unless params.is_a?(Hash)
84
- params.reject! { |_, v| v.to_param.nil? }
85
- query = params.to_query
86
- path << "?#{query}" unless query.empty?
87
- end
88
-
89
- def add_anchor(path, anchor)
90
- if anchor
91
- path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param)}"
81
+ def add_params(path, params)
82
+ params = { params: params } unless params.is_a?(Hash)
83
+ params.reject! { |_, v| v.to_param.nil? }
84
+ query = params.to_query
85
+ path << "?#{query}" unless query.empty?
92
86
  end
93
- end
94
87
 
95
- def extract_domain_from(host, tld_length)
96
- host.split(".").last(1 + tld_length).join(".")
97
- end
88
+ def add_anchor(path, anchor)
89
+ if anchor
90
+ path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param)}"
91
+ end
92
+ end
98
93
 
99
- def extract_subdomains_from(host, tld_length)
100
- parts = host.split(".")
101
- parts[0..-(tld_length + 2)]
102
- end
94
+ def extract_domain_from(host, tld_length)
95
+ host.split(".").last(1 + tld_length).join(".")
96
+ end
103
97
 
104
- def add_trailing_slash(path)
105
- if path.include?("?")
106
- path.sub!(/\?/, '/\&')
107
- elsif !path.include?(".")
108
- path.sub!(/[^\/]\z|\A\z/, '\&/')
98
+ def extract_subdomains_from(host, tld_length)
99
+ parts = host.split(".")
100
+ parts[0..-(tld_length + 2)]
109
101
  end
110
- end
111
102
 
112
- def build_host_url(host, port, protocol, options, path)
113
- if match = host.match(HOST_REGEXP)
114
- protocol ||= match[1] unless protocol == false
115
- host = match[2]
116
- port = match[3] unless options.key? :port
103
+ def add_trailing_slash(path)
104
+ if path.include?("?")
105
+ path.sub!(/\?/, '/\&')
106
+ elsif !path.include?(".")
107
+ path.sub!(/[^\/]\z|\A\z/, '\&/')
108
+ end
117
109
  end
118
110
 
119
- protocol = normalize_protocol protocol
120
- host = normalize_host(host, options)
111
+ def build_host_url(host, port, protocol, options, path)
112
+ if match = host.match(HOST_REGEXP)
113
+ protocol ||= match[1] unless protocol == false
114
+ host = match[2]
115
+ port = match[3] unless options.key? :port
116
+ end
121
117
 
122
- result = protocol.dup
118
+ protocol = normalize_protocol protocol
119
+ host = normalize_host(host, options)
123
120
 
124
- if options[:user] && options[:password]
125
- result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
126
- end
121
+ result = protocol.dup
127
122
 
128
- result << host
129
- normalize_port(port, protocol) { |normalized_port|
130
- result << ":#{normalized_port}"
131
- }
123
+ if options[:user] && options[:password]
124
+ result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
125
+ end
132
126
 
133
- result.concat path
134
- end
127
+ result << host
128
+ normalize_port(port, protocol) { |normalized_port|
129
+ result << ":#{normalized_port}"
130
+ }
135
131
 
136
- def named_host?(host)
137
- IP_HOST_REGEXP !~ host
138
- end
132
+ result.concat path
133
+ end
139
134
 
140
- def normalize_protocol(protocol)
141
- case protocol
142
- when nil
143
- "http://"
144
- when false, "//"
145
- "//"
146
- when PROTOCOL_REGEXP
147
- "#{$1}://"
148
- else
149
- raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
135
+ def named_host?(host)
136
+ IP_HOST_REGEXP !~ host
150
137
  end
151
- end
152
138
 
153
- def normalize_host(_host, options)
154
- return _host unless named_host?(_host)
139
+ def normalize_protocol(protocol)
140
+ case protocol
141
+ when nil
142
+ "http://"
143
+ when false, "//"
144
+ "//"
145
+ when PROTOCOL_REGEXP
146
+ "#{$1}://"
147
+ else
148
+ raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
149
+ end
150
+ end
151
+
152
+ def normalize_host(_host, options)
153
+ return _host unless named_host?(_host)
155
154
 
156
- tld_length = options[:tld_length] || @@tld_length
157
- subdomain = options.fetch :subdomain, true
158
- domain = options[:domain]
155
+ tld_length = options[:tld_length] || @@tld_length
156
+ subdomain = options.fetch :subdomain, true
157
+ domain = options[:domain]
159
158
 
160
- host = "".dup
161
- if subdomain == true
162
- return _host if domain.nil?
159
+ host = +""
160
+ if subdomain == true
161
+ return _host if domain.nil?
163
162
 
164
- host << extract_subdomains_from(_host, tld_length).join(".")
165
- elsif subdomain
166
- host << subdomain.to_param
163
+ host << extract_subdomains_from(_host, tld_length).join(".")
164
+ elsif subdomain
165
+ host << subdomain.to_param
166
+ end
167
+ host << "." unless host.empty?
168
+ host << (domain || extract_domain_from(_host, tld_length))
169
+ host
167
170
  end
168
- host << "." unless host.empty?
169
- host << (domain || extract_domain_from(_host, tld_length))
170
- host
171
- end
172
171
 
173
- def normalize_port(port, protocol)
174
- return unless port
172
+ def normalize_port(port, protocol)
173
+ return unless port
175
174
 
176
- case protocol
177
- when "//" then yield port
178
- when "https://"
179
- yield port unless port.to_i == 443
180
- else
181
- yield port unless port.to_i == 80
175
+ case protocol
176
+ when "//" then yield port
177
+ when "https://"
178
+ yield port unless port.to_i == 443
179
+ else
180
+ yield port unless port.to_i == 80
181
+ end
182
182
  end
183
- end
184
183
  end
185
184
 
186
185
  def initialize
@@ -231,7 +230,7 @@ module ActionDispatch
231
230
  # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
232
231
  # req.host # => "example.com"
233
232
  def host
234
- raw_host_with_port.sub(/:\d+$/, "".freeze)
233
+ raw_host_with_port.sub(/:\d+$/, "")
235
234
  end
236
235
 
237
236
  # Returns a \host:\port string for this request, such as "example.com" or
@@ -50,7 +50,7 @@ module ActionDispatch
50
50
  unmatched_keys = (missing_keys || []) & constraints.keys
51
51
  missing_keys = (missing_keys || []) - unmatched_keys
52
52
 
53
- message = "No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}".dup
53
+ message = +"No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}"
54
54
  message << ", missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
55
55
  message << ", possible unmatched constraints: #{unmatched_keys.sort.inspect}" if unmatched_keys && !unmatched_keys.empty?
56
56
 
@@ -62,12 +62,11 @@ module ActionDispatch
62
62
  end
63
63
 
64
64
  private
65
-
66
65
  def extract_parameterized_parts(route, options, recall, parameterize = nil)
67
66
  parameterized_parts = recall.merge(options)
68
67
 
69
68
  keys_to_keep = route.parts.reverse_each.drop_while { |part|
70
- !options.key?(part) || (options[part] || recall[part]).nil?
69
+ !(options.key?(part) || route.scope_options.key?(part)) || (options[part] || recall[part]).nil?
71
70
  } | route.required_parts
72
71
 
73
72
  parameterized_parts.delete_if do |bad_key, _|
@@ -128,7 +128,6 @@ module ActionDispatch
128
128
  end
129
129
 
130
130
  private
131
-
132
131
  def followpos_table
133
132
  @followpos ||= build_followpos
134
133
  end
@@ -141,7 +141,6 @@ module ActionDispatch
141
141
  end
142
142
 
143
143
  private
144
-
145
144
  def states_hash_for(sym)
146
145
  case sym
147
146
  when String
@@ -25,8 +25,6 @@ module ActionDispatch
25
25
  state = tt.eclosure(0)
26
26
  until input.eos?
27
27
  sym = input.scan(%r([/.?]|[^/.?]+))
28
-
29
- # FIXME: tt.eclosure is not needed for the GTG
30
28
  state = tt.eclosure(tt.move(state, sym))
31
29
  end
32
30
 
@@ -94,7 +94,6 @@ module ActionDispatch
94
94
  end
95
95
 
96
96
  private
97
-
98
97
  def inverted
99
98
  return @inverted if @inverted
100
99
 
@@ -32,7 +32,7 @@ module ActionDispatch
32
32
  end
33
33
 
34
34
  def name
35
- left.tr "*:".freeze, "".freeze
35
+ -left.tr("*:", "")
36
36
  end
37
37
 
38
38
  def type
@@ -65,12 +65,12 @@ module ActionDispatch
65
65
  def literal?; false; end
66
66
  end
67
67
 
68
- %w{ Symbol Slash Dot }.each do |t|
69
- class_eval <<-eoruby, __FILE__, __LINE__ + 1
70
- class #{t} < Terminal;
71
- def type; :#{t.upcase}; end
72
- end
73
- eoruby
68
+ class Slash < Terminal # :nodoc:
69
+ def type; :SLASH; end
70
+ end
71
+
72
+ class Dot < Terminal # :nodoc:
73
+ def type; :DOT; end
74
74
  end
75
75
 
76
76
  class Symbol < Terminal # :nodoc:
@@ -82,13 +82,14 @@ module ActionDispatch
82
82
  def initialize(left)
83
83
  super
84
84
  @regexp = DEFAULT_EXP
85
- @name = left.tr "*:".freeze, "".freeze
85
+ @name = -left.tr("*:", "")
86
86
  end
87
87
 
88
88
  def default_regexp?
89
89
  regexp == DEFAULT_EXP
90
90
  end
91
91
 
92
+ def type; :SYMBOL; end
92
93
  def symbol?; true; end
93
94
  end
94
95
 
@@ -90,7 +90,7 @@ module ActionDispatch
90
90
  return @separator_re unless @matchers.key?(node)
91
91
 
92
92
  re = @matchers[node]
93
- "(#{re})"
93
+ "(#{Regexp.union(re)})"
94
94
  end
95
95
 
96
96
  def visit_GROUP(node)
@@ -137,6 +137,10 @@ module ActionDispatch
137
137
  Array.new(length - 1) { |i| self[i + 1] }
138
138
  end
139
139
 
140
+ def named_captures
141
+ @names.zip(captures).to_h
142
+ end
143
+
140
144
  def [](x)
141
145
  idx = @offsets[x - 1] + x
142
146
  @match[idx]
@@ -170,7 +174,6 @@ module ActionDispatch
170
174
  end
171
175
 
172
176
  private
173
-
174
177
  def regexp_visitor
175
178
  @anchored ? AnchoredRegexp : UnanchoredRegexp
176
179
  end
@@ -184,7 +187,7 @@ module ActionDispatch
184
187
  node = node.to_sym
185
188
 
186
189
  if @requirements.key?(node)
187
- re = /#{@requirements[node]}|/
190
+ re = /#{Regexp.union(@requirements[node])}|/
188
191
  @offsets.push((re.match("").length - 1) + @offsets.last)
189
192
  else
190
193
  @offsets << @offsets.last
@@ -4,9 +4,9 @@ module ActionDispatch
4
4
  # :stopdoc:
5
5
  module Journey
6
6
  class Route
7
- attr_reader :app, :path, :defaults, :name, :precedence
7
+ attr_reader :app, :path, :defaults, :name, :precedence, :constraints,
8
+ :internal, :scope_options
8
9
 
9
- attr_reader :constraints, :internal
10
10
  alias :conditions :constraints
11
11
 
12
12
  module VerbMatchers
@@ -51,13 +51,13 @@ module ActionDispatch
51
51
 
52
52
  def self.build(name, app, path, constraints, required_defaults, defaults)
53
53
  request_method_match = verb_matcher(constraints.delete(:request_method))
54
- new name, app, path, constraints, required_defaults, defaults, request_method_match, 0
54
+ new name, app, path, constraints, required_defaults, defaults, request_method_match, 0, {}
55
55
  end
56
56
 
57
57
  ##
58
58
  # +path+ is a path constraint.
59
59
  # +constraints+ is a hash of constraints to be applied to this route.
60
- def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence, internal = false)
60
+ def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence, scope_options, internal = false)
61
61
  @name = name
62
62
  @app = app
63
63
  @path = path
@@ -72,6 +72,7 @@ module ActionDispatch
72
72
  @decorated_ast = nil
73
73
  @precedence = precedence
74
74
  @path_formatter = @path.build_formatter
75
+ @scope_options = scope_options
75
76
  @internal = internal
76
77
  end
77
78
 
@@ -17,11 +17,11 @@ module ActionDispatch
17
17
  def self.normalize_path(path)
18
18
  path ||= ""
19
19
  encoding = path.encoding
20
- path = "/#{path}".dup
21
- path.squeeze!("/".freeze)
22
- path.sub!(%r{/+\Z}, "".freeze)
20
+ path = +"/#{path}"
21
+ path.squeeze!("/")
22
+ path.sub!(%r{/+\Z}, "")
23
23
  path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
24
- path = "/".dup if path == "".freeze
24
+ path = +"/" if path == ""
25
25
  path.force_encoding(encoding)
26
26
  path
27
27
  end
@@ -29,16 +29,16 @@ module ActionDispatch
29
29
  # URI path and fragment escaping
30
30
  # https://tools.ietf.org/html/rfc3986
31
31
  class UriEncoder # :nodoc:
32
- ENCODE = "%%%02X".freeze
32
+ ENCODE = "%%%02X"
33
33
  US_ASCII = Encoding::US_ASCII
34
34
  UTF_8 = Encoding::UTF_8
35
- EMPTY = "".dup.force_encoding(US_ASCII).freeze
35
+ EMPTY = (+"").force_encoding(US_ASCII).freeze
36
36
  DEC2HEX = (0..255).to_a.map { |i| ENCODE % i }.map { |s| s.force_encoding(US_ASCII) }
37
37
 
38
- ALPHA = "a-zA-Z".freeze
39
- DIGIT = "0-9".freeze
40
- UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~".freeze
41
- SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;=".freeze
38
+ ALPHA = "a-zA-Z"
39
+ DIGIT = "0-9"
40
+ UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
41
+ SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
42
42
 
43
43
  ESCAPED = /%[a-zA-Z0-9]{2}/.freeze
44
44
 
@@ -15,9 +15,6 @@ require "action_dispatch/journey/path/pattern"
15
15
  module ActionDispatch
16
16
  module Journey # :nodoc:
17
17
  class Router # :nodoc:
18
- class RoutingError < ::StandardError # :nodoc:
19
- end
20
-
21
18
  attr_accessor :routes
22
19
 
23
20
  def initialize(routes)
@@ -84,7 +81,6 @@ module ActionDispatch
84
81
  end
85
82
 
86
83
  private
87
-
88
84
  def partitioned_routes
89
85
  routes.partition { |r|
90
86
  r.path.anchored && r.ast.grep(Nodes::Symbol).all? { |n| n.default_regexp? }
@@ -56,7 +56,6 @@ module ActionDispatch
56
56
  end
57
57
 
58
58
  def simulator
59
- return if ast.nil?
60
59
  @simulator ||= begin
61
60
  gtg = GTG::Builder.new(ast).transition_table
62
61
  GTG::Simulator.new(gtg)
@@ -72,7 +71,6 @@ module ActionDispatch
72
71
  end
73
72
 
74
73
  private
75
-
76
74
  def clear_cache!
77
75
  @ast = nil
78
76
  @simulator = nil
@@ -33,6 +33,12 @@ module ActionDispatch
33
33
  end
34
34
 
35
35
  private
36
+ # takes advantage of String @- deduping capabilities in Ruby 2.5 upwards
37
+ # see: https://bugs.ruby-lang.org/issues/13077
38
+ def dedup_scan(regex)
39
+ r = @ss.scan(regex)
40
+ r ? -r : nil
41
+ end
36
42
 
37
43
  def scan
38
44
  case
@@ -47,15 +53,15 @@ module ActionDispatch
47
53
  [:OR, "|"]
48
54
  when @ss.skip(/\./)
49
55
  [:DOT, "."]
50
- when text = @ss.scan(/:\w+/)
56
+ when text = dedup_scan(/:\w+/)
51
57
  [:SYMBOL, text]
52
- when text = @ss.scan(/\*\w+/)
58
+ when text = dedup_scan(/\*\w+/)
53
59
  [:STAR, text]
54
60
  when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\[:()])+/)
55
61
  text.tr! "\\", ""
56
- [:LITERAL, text]
62
+ [:LITERAL, -text]
57
63
  # any char
58
- when text = @ss.scan(/./)
64
+ when text = dedup_scan(/./)
59
65
  [:LITERAL, text]
60
66
  end
61
67
  end
@@ -40,7 +40,7 @@ module ActionDispatch
40
40
  @parameters.each do |index|
41
41
  param = parts[index]
42
42
  value = hash[param.name]
43
- return "".freeze unless value
43
+ return "" unless value
44
44
  parts[index] = param.escape value
45
45
  end
46
46
 
@@ -59,7 +59,6 @@ module ActionDispatch
59
59
  end
60
60
 
61
61
  private
62
-
63
62
  def visit(node)
64
63
  send(DISPATCH_CACHE[node.type], node)
65
64
  end
@@ -168,7 +167,6 @@ module ActionDispatch
168
167
 
169
168
  class String < FunctionalVisitor # :nodoc:
170
169
  private
171
-
172
170
  def binary(node, seed)
173
171
  visit(node.right, visit(node.left, seed))
174
172
  end
@@ -214,7 +212,6 @@ module ActionDispatch
214
212
  end
215
213
 
216
214
  private
217
-
218
215
  def binary(node, seed)
219
216
  seed.last.concat node.children.map { |c|
220
217
  "#{node.object_id} -> #{c.object_id};"
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "uri"
5
+ require "action_dispatch/http/request"
6
+ require "active_support/actionable_error"
7
+
8
+ module ActionDispatch
9
+ class ActionableExceptions # :nodoc:
10
+ cattr_accessor :endpoint, default: "/rails/actions"
11
+
12
+ def initialize(app)
13
+ @app = app
14
+ end
15
+
16
+ def call(env)
17
+ request = ActionDispatch::Request.new(env)
18
+ return @app.call(env) unless actionable_request?(request)
19
+
20
+ ActiveSupport::ActionableError.dispatch(request.params[:error].to_s.safe_constantize, request.params[:action])
21
+
22
+ redirect_to request.params[:location]
23
+ end
24
+
25
+ private
26
+ def actionable_request?(request)
27
+ request.get_header("action_dispatch.show_detailed_exceptions") && request.post? && request.path == endpoint
28
+ end
29
+
30
+ def redirect_to(location)
31
+ uri = URI.parse location
32
+
33
+ if uri.relative? || uri.scheme == "http" || uri.scheme == "https"
34
+ body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
35
+ else
36
+ return [400, { "Content-Type" => "text/plain" }, ["Invalid redirection URI"]]
37
+ end
38
+
39
+ [302, {
40
+ "Content-Type" => "text/html; charset=#{Response.default_charset}",
41
+ "Content-Length" => body.bytesize.to_s,
42
+ "Location" => location,
43
+ }, [body]]
44
+ end
45
+ end
46
+ end
@@ -24,10 +24,8 @@ module ActionDispatch
24
24
  def call(env)
25
25
  error = nil
26
26
  result = run_callbacks :call do
27
- begin
28
- @app.call(env)
29
- rescue => error
30
- end
27
+ @app.call(env)
28
+ rescue => error
31
29
  end
32
30
  raise error if error
33
31
  result