actionpack 4.2.10 → 5.2.4.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 (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +300 -466
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller/asset_paths.rb +2 -0
  6. data/lib/abstract_controller/base.rb +45 -49
  7. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +47 -31
  10. data/lib/abstract_controller/collector.rb +8 -11
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +25 -25
  13. data/lib/abstract_controller/logger.rb +2 -0
  14. data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
  15. data/lib/abstract_controller/rendering.rb +42 -41
  16. data/lib/abstract_controller/translation.rb +10 -7
  17. data/lib/abstract_controller/url_for.rb +2 -0
  18. data/lib/abstract_controller.rb +12 -5
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/base.rb +27 -19
  22. data/lib/action_controller/caching.rb +14 -57
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +10 -15
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +118 -44
  27. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  28. data/lib/action_controller/metal/cookies.rb +3 -3
  29. data/lib/action_controller/metal/data_streaming.rb +27 -46
  30. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  31. data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
  32. data/lib/action_controller/metal/exceptions.rb +8 -14
  33. data/lib/action_controller/metal/flash.rb +4 -3
  34. data/lib/action_controller/metal/force_ssl.rb +23 -21
  35. data/lib/action_controller/metal/head.rb +21 -19
  36. data/lib/action_controller/metal/helpers.rb +24 -14
  37. data/lib/action_controller/metal/http_authentication.rb +65 -58
  38. data/lib/action_controller/metal/implicit_render.rb +62 -8
  39. data/lib/action_controller/metal/instrumentation.rb +19 -21
  40. data/lib/action_controller/metal/live.rb +90 -106
  41. data/lib/action_controller/metal/mime_responds.rb +33 -46
  42. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  43. data/lib/action_controller/metal/params_wrapper.rb +61 -53
  44. data/lib/action_controller/metal/redirecting.rb +49 -28
  45. data/lib/action_controller/metal/renderers.rb +87 -44
  46. data/lib/action_controller/metal/rendering.rb +72 -50
  47. data/lib/action_controller/metal/request_forgery_protection.rb +229 -93
  48. data/lib/action_controller/metal/rescue.rb +9 -16
  49. data/lib/action_controller/metal/streaming.rb +12 -10
  50. data/lib/action_controller/metal/strong_parameters.rb +583 -164
  51. data/lib/action_controller/metal/testing.rb +2 -17
  52. data/lib/action_controller/metal/url_for.rb +19 -10
  53. data/lib/action_controller/metal.rb +98 -83
  54. data/lib/action_controller/railtie.rb +28 -10
  55. data/lib/action_controller/railties/helpers.rb +2 -0
  56. data/lib/action_controller/renderer.rb +117 -0
  57. data/lib/action_controller/template_assertions.rb +11 -0
  58. data/lib/action_controller/test_case.rb +280 -411
  59. data/lib/action_controller.rb +29 -21
  60. data/lib/action_dispatch/http/cache.rb +93 -47
  61. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  62. data/lib/action_dispatch/http/filter_parameters.rb +26 -20
  63. data/lib/action_dispatch/http/filter_redirect.rb +10 -11
  64. data/lib/action_dispatch/http/headers.rb +55 -22
  65. data/lib/action_dispatch/http/mime_negotiation.rb +60 -41
  66. data/lib/action_dispatch/http/mime_type.rb +134 -121
  67. data/lib/action_dispatch/http/mime_types.rb +20 -6
  68. data/lib/action_dispatch/http/parameter_filter.rb +25 -11
  69. data/lib/action_dispatch/http/parameters.rb +98 -39
  70. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  71. data/lib/action_dispatch/http/request.rb +200 -118
  72. data/lib/action_dispatch/http/response.rb +225 -110
  73. data/lib/action_dispatch/http/upload.rb +12 -6
  74. data/lib/action_dispatch/http/url.rb +110 -28
  75. data/lib/action_dispatch/journey/formatter.rb +55 -32
  76. data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
  79. data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
  80. data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
  81. data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
  82. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
  83. data/lib/action_dispatch/journey/nodes/node.rb +18 -6
  84. data/lib/action_dispatch/journey/parser.rb +23 -22
  85. data/lib/action_dispatch/journey/parser.y +3 -2
  86. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  87. data/lib/action_dispatch/journey/path/pattern.rb +50 -44
  88. data/lib/action_dispatch/journey/route.rb +106 -28
  89. data/lib/action_dispatch/journey/router/utils.rb +20 -11
  90. data/lib/action_dispatch/journey/router.rb +35 -23
  91. data/lib/action_dispatch/journey/routes.rb +18 -16
  92. data/lib/action_dispatch/journey/scanner.rb +18 -15
  93. data/lib/action_dispatch/journey/visitors.rb +99 -52
  94. data/lib/action_dispatch/journey.rb +7 -5
  95. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  96. data/lib/action_dispatch/middleware/cookies.rb +304 -193
  97. data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
  98. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  99. data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
  100. data/lib/action_dispatch/middleware/executor.rb +21 -0
  101. data/lib/action_dispatch/middleware/flash.rb +78 -54
  102. data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
  103. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  104. data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
  105. data/lib/action_dispatch/middleware/request_id.rb +17 -9
  106. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
  107. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  108. data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
  109. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  110. data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
  111. data/lib/action_dispatch/middleware/ssl.rb +114 -36
  112. data/lib/action_dispatch/middleware/stack.rb +31 -44
  113. data/lib/action_dispatch/middleware/static.rb +57 -50
  114. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  115. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  116. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  121. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  123. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -64
  124. data/lib/action_dispatch/railtie.rb +19 -11
  125. data/lib/action_dispatch/request/session.rb +106 -59
  126. data/lib/action_dispatch/request/utils.rb +67 -24
  127. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  128. data/lib/action_dispatch/routing/inspector.rb +58 -67
  129. data/lib/action_dispatch/routing/mapper.rb +733 -447
  130. data/lib/action_dispatch/routing/polymorphic_routes.rb +166 -140
  131. data/lib/action_dispatch/routing/redirection.rb +36 -26
  132. data/lib/action_dispatch/routing/route_set.rb +321 -291
  133. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  134. data/lib/action_dispatch/routing/url_for.rb +65 -25
  135. data/lib/action_dispatch/routing.rb +17 -18
  136. data/lib/action_dispatch/system_test_case.rb +147 -0
  137. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  138. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  139. data/lib/action_dispatch/system_testing/server.rb +31 -0
  140. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  143. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  144. data/lib/action_dispatch/testing/assertions/response.rb +45 -20
  145. data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
  146. data/lib/action_dispatch/testing/assertions.rb +6 -4
  147. data/lib/action_dispatch/testing/integration.rb +347 -209
  148. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  149. data/lib/action_dispatch/testing/test_process.rb +28 -22
  150. data/lib/action_dispatch/testing/test_request.rb +27 -34
  151. data/lib/action_dispatch/testing/test_response.rb +35 -7
  152. data/lib/action_dispatch.rb +27 -19
  153. data/lib/action_pack/gem_version.rb +5 -3
  154. data/lib/action_pack/version.rb +3 -1
  155. data/lib/action_pack.rb +4 -2
  156. metadata +59 -42
  157. data/lib/action_controller/metal/hide_actions.rb +0 -40
  158. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  159. data/lib/action_controller/middleware.rb +0 -39
  160. data/lib/action_controller/model_naming.rb +0 -12
  161. data/lib/action_dispatch/journey/backwards.rb +0 -5
  162. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  163. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  164. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  165. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  166. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "action_dispatch"
4
+ require "active_support/messages/rotation_configuration"
2
5
 
3
6
  module ActionDispatch
4
7
  class Railtie < Rails::Railtie # :nodoc:
@@ -8,22 +11,29 @@ module ActionDispatch
8
11
  config.action_dispatch.show_exceptions = true
9
12
  config.action_dispatch.tld_length = 1
10
13
  config.action_dispatch.ignore_accept_header = false
11
- config.action_dispatch.rescue_templates = { }
12
- config.action_dispatch.rescue_responses = { }
14
+ config.action_dispatch.rescue_templates = {}
15
+ config.action_dispatch.rescue_responses = {}
13
16
  config.action_dispatch.default_charset = nil
14
17
  config.action_dispatch.rack_cache = false
15
- config.action_dispatch.http_auth_salt = 'http authentication'
16
- config.action_dispatch.signed_cookie_salt = 'signed cookie'
17
- config.action_dispatch.encrypted_cookie_salt = 'encrypted cookie'
18
- config.action_dispatch.encrypted_signed_cookie_salt = 'signed encrypted cookie'
18
+ config.action_dispatch.http_auth_salt = "http authentication"
19
+ config.action_dispatch.signed_cookie_salt = "signed cookie"
20
+ config.action_dispatch.encrypted_cookie_salt = "encrypted cookie"
21
+ config.action_dispatch.encrypted_signed_cookie_salt = "signed encrypted cookie"
22
+ config.action_dispatch.authenticated_encrypted_cookie_salt = "authenticated encrypted cookie"
23
+ config.action_dispatch.use_authenticated_cookie_encryption = false
19
24
  config.action_dispatch.perform_deep_munge = true
20
25
 
21
26
  config.action_dispatch.default_headers = {
22
- 'X-Frame-Options' => 'SAMEORIGIN',
23
- 'X-XSS-Protection' => '1; mode=block',
24
- 'X-Content-Type-Options' => 'nosniff'
27
+ "X-Frame-Options" => "SAMEORIGIN",
28
+ "X-XSS-Protection" => "1; mode=block",
29
+ "X-Content-Type-Options" => "nosniff",
30
+ "X-Download-Options" => "noopen",
31
+ "X-Permitted-Cross-Domain-Policies" => "none",
32
+ "Referrer-Policy" => "strict-origin-when-cross-origin"
25
33
  }
26
34
 
35
+ config.action_dispatch.cookies_rotations = ActiveSupport::Messages::RotationConfiguration.new
36
+
27
37
  config.eager_load_namespaces << ActionDispatch
28
38
 
29
39
  initializer "action_dispatch.configure" do |app|
@@ -40,8 +50,6 @@ module ActionDispatch
40
50
  ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie
41
51
 
42
52
  ActionDispatch.test_app = app
43
-
44
- ActionDispatch::Routing::RouteSet.relative_url_root = app.config.relative_url_root
45
53
  end
46
54
  end
47
55
  end
@@ -1,95 +1,105 @@
1
- require 'rack/session/abstract/id'
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/session/abstract/id"
2
4
 
3
5
  module ActionDispatch
4
- class Request < Rack::Request
6
+ class Request
5
7
  # Session is responsible for lazily loading the session from store.
6
8
  class Session # :nodoc:
7
- ENV_SESSION_KEY = Rack::Session::Abstract::ENV_SESSION_KEY # :nodoc:
8
- ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY # :nodoc:
9
+ ENV_SESSION_KEY = Rack::RACK_SESSION # :nodoc:
10
+ ENV_SESSION_OPTIONS_KEY = Rack::RACK_SESSION_OPTIONS # :nodoc:
9
11
 
10
- # Singleton object used to determine if an optional param wasn't specified
12
+ # Singleton object used to determine if an optional param wasn't specified.
11
13
  Unspecified = Object.new
12
14
 
13
- def self.create(store, env, default_options)
14
- session_was = find env
15
- session = Request::Session.new(store, env)
15
+ # Creates a session hash, merging the properties of the previous session if any.
16
+ def self.create(store, req, default_options)
17
+ session_was = find req
18
+ session = Request::Session.new(store, req)
16
19
  session.merge! session_was if session_was
17
20
 
18
- set(env, session)
19
- Options.set(env, Request::Session::Options.new(store, env, default_options))
21
+ set(req, session)
22
+ Options.set(req, Request::Session::Options.new(store, default_options))
20
23
  session
21
24
  end
22
25
 
23
- def self.find(env)
24
- env[ENV_SESSION_KEY]
26
+ def self.find(req)
27
+ req.get_header ENV_SESSION_KEY
25
28
  end
26
29
 
27
- def self.set(env, session)
28
- env[ENV_SESSION_KEY] = session
30
+ def self.set(req, session)
31
+ req.set_header ENV_SESSION_KEY, session
29
32
  end
30
33
 
31
34
  class Options #:nodoc:
32
- def self.set(env, options)
33
- env[ENV_SESSION_OPTIONS_KEY] = options
35
+ def self.set(req, options)
36
+ req.set_header ENV_SESSION_OPTIONS_KEY, options
34
37
  end
35
38
 
36
- def self.find(env)
37
- env[ENV_SESSION_OPTIONS_KEY]
39
+ def self.find(req)
40
+ req.get_header ENV_SESSION_OPTIONS_KEY
38
41
  end
39
42
 
40
- def initialize(by, env, default_options)
43
+ def initialize(by, default_options)
41
44
  @by = by
42
- @env = env
43
45
  @delegate = default_options.dup
44
46
  end
45
47
 
46
48
  def [](key)
47
- if key == :id
48
- @delegate.fetch(key) {
49
- @delegate[:id] = @by.send(:extract_session_id, @env)
50
- }
51
- else
52
- @delegate[key]
53
- end
49
+ @delegate[key]
50
+ end
51
+
52
+ def id(req)
53
+ @delegate.fetch(:id) {
54
+ @by.send(:extract_session_id, req)
55
+ }
54
56
  end
55
57
 
56
- def []=(k,v); @delegate[k] = v; end
58
+ def []=(k, v); @delegate[k] = v; end
57
59
  def to_hash; @delegate.dup; end
58
60
  def values_at(*args); @delegate.values_at(*args); end
59
61
  end
60
62
 
61
- def initialize(by, env)
63
+ def initialize(by, req)
62
64
  @by = by
63
- @env = env
65
+ @req = req
64
66
  @delegate = {}
65
67
  @loaded = false
66
- @exists = nil # we haven't checked yet
68
+ @exists = nil # We haven't checked yet.
67
69
  end
68
70
 
69
71
  def id
70
- options[:id]
72
+ options.id(@req)
71
73
  end
72
74
 
73
75
  def options
74
- Options.find @env
76
+ Options.find @req
75
77
  end
76
78
 
77
79
  def destroy
78
80
  clear
79
81
  options = self.options || {}
80
- new_sid = @by.send(:destroy_session, @env, options[:id], options)
81
- options[:id] = new_sid # Reset session id with a new value or nil
82
+ @by.send(:delete_session, @req, options.id(@req), options)
82
83
 
83
- # Load the new sid to be written with the response
84
+ # Load the new sid to be written with the response.
84
85
  @loaded = false
85
86
  load_for_write!
86
87
  end
87
88
 
89
+ # Returns value of the key stored in the session or
90
+ # +nil+ if the given key is not found in the session.
88
91
  def [](key)
89
92
  load_for_read!
90
- @delegate[key.to_s]
93
+ key = key.to_s
94
+
95
+ if key == "session_id"
96
+ id&.public_id
97
+ else
98
+ @delegate[key]
99
+ end
91
100
  end
92
101
 
102
+ # Returns true if the session has the given key or false.
93
103
  def has_key?(key)
94
104
  load_for_read!
95
105
  @delegate.key?(key.to_s)
@@ -97,40 +107,73 @@ module ActionDispatch
97
107
  alias :key? :has_key?
98
108
  alias :include? :has_key?
99
109
 
110
+ # Returns keys of the session as Array.
100
111
  def keys
112
+ load_for_read!
101
113
  @delegate.keys
102
114
  end
103
115
 
116
+ # Returns values of the session as Array.
104
117
  def values
118
+ load_for_read!
105
119
  @delegate.values
106
120
  end
107
121
 
122
+ # Writes given value to given key of the session.
108
123
  def []=(key, value)
109
124
  load_for_write!
110
125
  @delegate[key.to_s] = value
111
126
  end
112
127
 
128
+ # Clears the session.
113
129
  def clear
114
130
  load_for_write!
115
131
  @delegate.clear
116
132
  end
117
133
 
134
+ # Returns the session as Hash.
118
135
  def to_hash
119
136
  load_for_read!
120
- @delegate.dup.delete_if { |_,v| v.nil? }
121
- end
122
-
137
+ @delegate.dup.delete_if { |_, v| v.nil? }
138
+ end
139
+ alias :to_h :to_hash
140
+
141
+ # Updates the session with given Hash.
142
+ #
143
+ # session.to_hash
144
+ # # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2"}
145
+ #
146
+ # session.update({ "foo" => "bar" })
147
+ # # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
148
+ #
149
+ # session.to_hash
150
+ # # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
123
151
  def update(hash)
124
152
  load_for_write!
125
153
  @delegate.update stringify_keys(hash)
126
154
  end
127
155
 
156
+ # Deletes given key from the session.
128
157
  def delete(key)
129
158
  load_for_write!
130
159
  @delegate.delete key.to_s
131
160
  end
132
161
 
133
- def fetch(key, default=Unspecified, &block)
162
+ # Returns value of the given key from the session, or raises +KeyError+
163
+ # if can't find the given key and no default value is set.
164
+ # Returns default value if specified.
165
+ #
166
+ # session.fetch(:foo)
167
+ # # => KeyError: key not found: "foo"
168
+ #
169
+ # session.fetch(:foo, :bar)
170
+ # # => :bar
171
+ #
172
+ # session.fetch(:foo) do
173
+ # :bar
174
+ # end
175
+ # # => :bar
176
+ def fetch(key, default = Unspecified, &block)
134
177
  load_for_read!
135
178
  if default == Unspecified
136
179
  @delegate.fetch(key.to_s, &block)
@@ -149,7 +192,7 @@ module ActionDispatch
149
192
 
150
193
  def exists?
151
194
  return @exists unless @exists.nil?
152
- @exists = @by.send(:session_exists?, @env)
195
+ @exists = @by.send(:session_exists?, @req)
153
196
  end
154
197
 
155
198
  def loaded?
@@ -166,28 +209,32 @@ module ActionDispatch
166
209
  @delegate.merge!(other)
167
210
  end
168
211
 
212
+ def each(&block)
213
+ to_hash.each(&block)
214
+ end
215
+
169
216
  private
170
217
 
171
- def load_for_read!
172
- load! if !loaded? && exists?
173
- end
218
+ def load_for_read!
219
+ load! if !loaded? && exists?
220
+ end
174
221
 
175
- def load_for_write!
176
- load! unless loaded?
177
- end
222
+ def load_for_write!
223
+ load! unless loaded?
224
+ end
178
225
 
179
- def load!
180
- id, session = @by.load_session @env
181
- options[:id] = id
182
- @delegate.replace(stringify_keys(session))
183
- @loaded = true
184
- end
226
+ def load!
227
+ id, session = @by.load_session @req
228
+ options[:id] = id
229
+ @delegate.replace(stringify_keys(session))
230
+ @loaded = true
231
+ end
185
232
 
186
- def stringify_keys(other)
187
- other.each_with_object({}) { |(key, value), hash|
188
- hash[key.to_s] = value
189
- }
190
- end
233
+ def stringify_keys(other)
234
+ other.each_with_object({}) { |(key, value), hash|
235
+ hash[key.to_s] = value
236
+ }
237
+ end
191
238
  end
192
239
  end
193
240
  end
@@ -1,35 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/indifferent_access"
4
+
1
5
  module ActionDispatch
2
- class Request < Rack::Request
6
+ class Request
3
7
  class Utils # :nodoc:
8
+ mattr_accessor :perform_deep_munge, default: true
9
+
10
+ def self.each_param_value(params, &block)
11
+ case params
12
+ when Array
13
+ params.each { |element| each_param_value(element, &block) }
14
+ when Hash
15
+ params.each_value { |value| each_param_value(value, &block) }
16
+ when String
17
+ block.call params
18
+ end
19
+ end
4
20
 
5
- mattr_accessor :perform_deep_munge
6
- self.perform_deep_munge = true
7
-
8
- class << self
9
- # Remove nils from the params hash
10
- def deep_munge(hash, keys = [])
11
- return hash unless perform_deep_munge
12
-
13
- hash.each do |k, v|
14
- keys << k
15
- case v
16
- when Array
17
- v.grep(Hash) { |x| deep_munge(x, keys) }
18
- v.compact!
19
- if v.empty?
20
- hash[k] = nil
21
- ActiveSupport::Notifications.instrument("deep_munge.action_controller", keys: keys)
22
- end
23
- when Hash
24
- deep_munge(v, keys)
21
+ def self.normalize_encode_params(params)
22
+ if perform_deep_munge
23
+ NoNilParamEncoder.normalize_encode_params params
24
+ else
25
+ ParamEncoder.normalize_encode_params params
26
+ end
27
+ end
28
+
29
+ def self.check_param_encoding(params)
30
+ case params
31
+ when Array
32
+ params.each { |element| check_param_encoding(element) }
33
+ when Hash
34
+ params.each_value { |value| check_param_encoding(value) }
35
+ when String
36
+ unless params.valid_encoding?
37
+ # Raise Rack::Utils::InvalidParameterError for consistency with Rack.
38
+ # ActionDispatch::Request#GET will re-raise as a BadRequest error.
39
+ raise Rack::Utils::InvalidParameterError, "Invalid encoding for parameter: #{params.scrub}"
40
+ end
41
+ end
42
+ end
43
+
44
+ class ParamEncoder # :nodoc:
45
+ # Convert nested Hash to HashWithIndifferentAccess.
46
+ def self.normalize_encode_params(params)
47
+ case params
48
+ when Array
49
+ handle_array params
50
+ when Hash
51
+ if params.has_key?(:tempfile)
52
+ ActionDispatch::Http::UploadedFile.new(params)
53
+ else
54
+ params.each_with_object({}) do |(key, val), new_hash|
55
+ new_hash[key] = normalize_encode_params(val)
56
+ end.with_indifferent_access
25
57
  end
26
- keys.pop
58
+ else
59
+ params
27
60
  end
61
+ end
28
62
 
29
- hash
63
+ def self.handle_array(params)
64
+ params.map! { |el| normalize_encode_params(el) }
65
+ end
66
+ end
67
+
68
+ # Remove nils from the params hash.
69
+ class NoNilParamEncoder < ParamEncoder # :nodoc:
70
+ def self.handle_array(params)
71
+ list = super
72
+ list.compact!
73
+ list
30
74
  end
31
75
  end
32
76
  end
33
77
  end
34
78
  end
35
-
@@ -1,10 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionDispatch
2
4
  module Routing
3
5
  class Endpoint # :nodoc:
4
6
  def dispatcher?; false; end
5
7
  def redirect?; false; end
6
- def matches?(req); true; end
7
- def app; self; end
8
+ def matches?(req); true; end
9
+ def app; self; end
10
+ def rack_app; app; end
11
+
12
+ def engine?
13
+ rack_app.is_a?(Class) && rack_app < Rails::Engine
14
+ end
8
15
  end
9
16
  end
10
17
  end
@@ -1,5 +1,7 @@
1
- require 'delegate'
2
- require 'active_support/core_ext/string/strip'
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+ require "active_support/core_ext/string/strip"
3
5
 
4
6
  module ActionDispatch
5
7
  module Routing
@@ -13,11 +15,7 @@ module ActionDispatch
13
15
  end
14
16
 
15
17
  def rack_app
16
- app.app
17
- end
18
-
19
- def verb
20
- super.source.gsub(/[$^]/, '')
18
+ app.rack_app
21
19
  end
22
20
 
23
21
  def path
@@ -28,23 +26,6 @@ module ActionDispatch
28
26
  super.to_s
29
27
  end
30
28
 
31
- def regexp
32
- __getobj__.path.to_regexp
33
- end
34
-
35
- def json_regexp
36
- str = regexp.inspect.
37
- sub('\\A' , '^').
38
- sub('\\Z' , '$').
39
- sub('\\z' , '$').
40
- sub(/^\// , '').
41
- sub(/\/[a-z]*$/ , '').
42
- gsub(/\(\?#.+\)/ , '').
43
- gsub(/\(\?-\w+:/ , '(').
44
- gsub(/\s/ , '')
45
- Regexp.new(str).source
46
- end
47
-
48
29
  def reqs
49
30
  @reqs ||= begin
50
31
  reqs = endpoint
@@ -54,25 +35,25 @@ module ActionDispatch
54
35
  end
55
36
 
56
37
  def controller
57
- requirements[:controller] || ':controller'
38
+ parts.include?(:controller) ? ":controller" : requirements[:controller]
58
39
  end
59
40
 
60
41
  def action
61
- requirements[:action] || ':action'
42
+ parts.include?(:action) ? ":action" : requirements[:action]
62
43
  end
63
44
 
64
45
  def internal?
65
- controller.to_s =~ %r{\Arails/(info|mailers|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}\z}
46
+ internal
66
47
  end
67
48
 
68
49
  def engine?
69
- rack_app.respond_to?(:routes)
50
+ app.engine?
70
51
  end
71
52
  end
72
53
 
73
54
  ##
74
55
  # This class is just used for displaying route information when someone
75
- # executes `rake routes` or looks at the RoutingError page.
56
+ # executes `rails routes` or looks at the RoutingError page.
76
57
  # People should not use this class.
77
58
  class RoutesInspector # :nodoc:
78
59
  def initialize(routes)
@@ -81,12 +62,10 @@ module ActionDispatch
81
62
  end
82
63
 
83
64
  def format(formatter, filter = nil)
84
- routes_to_display = filter_routes(filter)
85
-
65
+ routes_to_display = filter_routes(normalize_filter(filter))
86
66
  routes = collect_routes(routes_to_display)
87
-
88
67
  if routes.none?
89
- formatter.no_routes
68
+ formatter.no_routes(collect_routes(@routes))
90
69
  return formatter.result
91
70
  end
92
71
 
@@ -103,40 +82,48 @@ module ActionDispatch
103
82
 
104
83
  private
105
84
 
106
- def filter_routes(filter)
107
- if filter
108
- @routes.select { |route| route.defaults[:controller] == filter }
109
- else
110
- @routes
85
+ def normalize_filter(filter)
86
+ if filter.is_a?(Hash) && filter[:controller]
87
+ { controller: /#{filter[:controller].underscore.sub(/_?controller\z/, "")}/ }
88
+ elsif filter
89
+ { controller: /#{filter}/, action: /#{filter}/, verb: /#{filter}/, name: /#{filter}/, path: /#{filter}/ }
90
+ end
111
91
  end
112
- end
113
92
 
114
- def collect_routes(routes)
115
- routes.collect do |route|
116
- RouteWrapper.new(route)
117
- end.reject do |route|
118
- route.internal?
119
- end.collect do |route|
120
- collect_engine_routes(route)
93
+ def filter_routes(filter)
94
+ if filter
95
+ @routes.select do |route|
96
+ route_wrapper = RouteWrapper.new(route)
97
+ filter.any? { |default, value| route_wrapper.send(default) =~ value }
98
+ end
99
+ else
100
+ @routes
101
+ end
102
+ end
103
+
104
+ def collect_routes(routes)
105
+ routes.collect do |route|
106
+ RouteWrapper.new(route)
107
+ end.reject(&:internal?).collect do |route|
108
+ collect_engine_routes(route)
121
109
 
122
- { name: route.name,
123
- verb: route.verb,
124
- path: route.path,
125
- reqs: route.reqs,
126
- regexp: route.json_regexp }
110
+ { name: route.name,
111
+ verb: route.verb,
112
+ path: route.path,
113
+ reqs: route.reqs }
114
+ end
127
115
  end
128
- end
129
116
 
130
- def collect_engine_routes(route)
131
- name = route.endpoint
132
- return unless route.engine?
133
- return if @engines[name]
117
+ def collect_engine_routes(route)
118
+ name = route.endpoint
119
+ return unless route.engine?
120
+ return if @engines[name]
134
121
 
135
- routes = route.rack_app.routes
136
- if routes.is_a?(ActionDispatch::Routing::RouteSet)
137
- @engines[name] = collect_routes(routes.routes)
122
+ routes = route.rack_app.routes
123
+ if routes.is_a?(ActionDispatch::Routing::RouteSet)
124
+ @engines[name] = collect_routes(routes.routes)
125
+ end
138
126
  end
139
- end
140
127
  end
141
128
 
142
129
  class ConsoleFormatter
@@ -160,19 +147,23 @@ module ActionDispatch
160
147
  @buffer << draw_header(routes)
161
148
  end
162
149
 
163
- def no_routes
164
- @buffer << <<-MESSAGE.strip_heredoc
150
+ def no_routes(routes)
151
+ @buffer <<
152
+ if routes.none?
153
+ <<-MESSAGE.strip_heredoc
165
154
  You don't have any routes defined!
166
155
 
167
156
  Please add some routes in config/routes.rb.
168
-
169
- For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html.
170
157
  MESSAGE
158
+ else
159
+ "No routes were found for this controller"
160
+ end
161
+ @buffer << "For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
171
162
  end
172
163
 
173
164
  private
174
165
  def draw_section(routes)
175
- header_lengths = ['Prefix', 'Verb', 'URI Pattern'].map(&:length)
166
+ header_lengths = ["Prefix", "Verb", "URI Pattern"].map(&:length)
176
167
  name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max)
177
168
 
178
169
  routes.map do |r|
@@ -207,11 +198,11 @@ module ActionDispatch
207
198
  @buffer << @view.render(partial: "routes/route", collection: routes)
208
199
  end
209
200
 
210
- # the header is part of the HTML page, so we don't construct it here.
201
+ # The header is part of the HTML page, so we don't construct it here.
211
202
  def header(routes)
212
203
  end
213
204
 
214
- def no_routes
205
+ def no_routes(*)
215
206
  @buffer << <<-MESSAGE.strip_heredoc
216
207
  <p>You don't have any routes defined!</p>
217
208
  <ul>