actionpack 4.2.8 → 5.2.4.2

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 +285 -444
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller.rb +12 -5
  6. data/lib/abstract_controller/asset_paths.rb +2 -0
  7. data/lib/abstract_controller/base.rb +45 -49
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
  10. data/lib/abstract_controller/callbacks.rb +47 -31
  11. data/lib/abstract_controller/collector.rb +8 -11
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +25 -25
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
  16. data/lib/abstract_controller/rendering.rb +42 -41
  17. data/lib/abstract_controller/translation.rb +10 -7
  18. data/lib/abstract_controller/url_for.rb +2 -0
  19. data/lib/action_controller.rb +29 -21
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +27 -19
  23. data/lib/action_controller/caching.rb +14 -57
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +10 -15
  26. data/lib/action_controller/metal.rb +98 -83
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +118 -44
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +3 -3
  31. data/lib/action_controller/metal/data_streaming.rb +27 -46
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
  34. data/lib/action_controller/metal/exceptions.rb +8 -14
  35. data/lib/action_controller/metal/flash.rb +4 -3
  36. data/lib/action_controller/metal/force_ssl.rb +23 -21
  37. data/lib/action_controller/metal/head.rb +21 -19
  38. data/lib/action_controller/metal/helpers.rb +24 -14
  39. data/lib/action_controller/metal/http_authentication.rb +64 -57
  40. data/lib/action_controller/metal/implicit_render.rb +62 -8
  41. data/lib/action_controller/metal/instrumentation.rb +19 -21
  42. data/lib/action_controller/metal/live.rb +90 -106
  43. data/lib/action_controller/metal/mime_responds.rb +33 -46
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +61 -53
  46. data/lib/action_controller/metal/redirecting.rb +49 -28
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +72 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +203 -92
  50. data/lib/action_controller/metal/rescue.rb +9 -16
  51. data/lib/action_controller/metal/streaming.rb +12 -10
  52. data/lib/action_controller/metal/strong_parameters.rb +582 -165
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/railtie.rb +28 -10
  56. data/lib/action_controller/railties/helpers.rb +2 -0
  57. data/lib/action_controller/renderer.rb +117 -0
  58. data/lib/action_controller/template_assertions.rb +11 -0
  59. data/lib/action_controller/test_case.rb +280 -411
  60. data/lib/action_dispatch.rb +27 -19
  61. data/lib/action_dispatch/http/cache.rb +93 -47
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +26 -20
  64. data/lib/action_dispatch/http/filter_redirect.rb +10 -11
  65. data/lib/action_dispatch/http/headers.rb +55 -22
  66. data/lib/action_dispatch/http/mime_negotiation.rb +60 -41
  67. data/lib/action_dispatch/http/mime_type.rb +134 -121
  68. data/lib/action_dispatch/http/mime_types.rb +20 -6
  69. data/lib/action_dispatch/http/parameter_filter.rb +25 -11
  70. data/lib/action_dispatch/http/parameters.rb +98 -39
  71. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  72. data/lib/action_dispatch/http/request.rb +200 -118
  73. data/lib/action_dispatch/http/response.rb +225 -110
  74. data/lib/action_dispatch/http/upload.rb +12 -6
  75. data/lib/action_dispatch/http/url.rb +110 -28
  76. data/lib/action_dispatch/journey.rb +7 -5
  77. data/lib/action_dispatch/journey/formatter.rb +55 -32
  78. data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
  81. data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
  82. data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
  85. data/lib/action_dispatch/journey/nodes/node.rb +18 -6
  86. data/lib/action_dispatch/journey/parser.rb +23 -22
  87. data/lib/action_dispatch/journey/parser.y +3 -2
  88. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  89. data/lib/action_dispatch/journey/path/pattern.rb +50 -44
  90. data/lib/action_dispatch/journey/route.rb +106 -28
  91. data/lib/action_dispatch/journey/router.rb +35 -23
  92. data/lib/action_dispatch/journey/router/utils.rb +20 -11
  93. data/lib/action_dispatch/journey/routes.rb +18 -16
  94. data/lib/action_dispatch/journey/scanner.rb +18 -15
  95. data/lib/action_dispatch/journey/visitors.rb +99 -52
  96. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  97. data/lib/action_dispatch/middleware/cookies.rb +304 -193
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
  101. data/lib/action_dispatch/middleware/executor.rb +21 -0
  102. data/lib/action_dispatch/middleware/flash.rb +78 -54
  103. data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
  104. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  105. data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
  106. data/lib/action_dispatch/middleware/request_id.rb +17 -9
  107. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
  108. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  109. data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
  110. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  111. data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
  112. data/lib/action_dispatch/middleware/ssl.rb +114 -36
  113. data/lib/action_dispatch/middleware/stack.rb +31 -44
  114. data/lib/action_dispatch/middleware/static.rb +57 -50
  115. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  116. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -64
  125. data/lib/action_dispatch/railtie.rb +19 -11
  126. data/lib/action_dispatch/request/session.rb +106 -59
  127. data/lib/action_dispatch/request/utils.rb +67 -24
  128. data/lib/action_dispatch/routing.rb +17 -18
  129. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  130. data/lib/action_dispatch/routing/inspector.rb +58 -67
  131. data/lib/action_dispatch/routing/mapper.rb +734 -447
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
  133. data/lib/action_dispatch/routing/redirection.rb +36 -26
  134. data/lib/action_dispatch/routing/route_set.rb +321 -291
  135. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  136. data/lib/action_dispatch/routing/url_for.rb +65 -25
  137. data/lib/action_dispatch/system_test_case.rb +147 -0
  138. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  139. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  140. data/lib/action_dispatch/system_testing/server.rb +31 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  143. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  145. data/lib/action_dispatch/testing/assertions.rb +6 -4
  146. data/lib/action_dispatch/testing/assertions/response.rb +45 -20
  147. data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
  148. data/lib/action_dispatch/testing/integration.rb +347 -209
  149. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  150. data/lib/action_dispatch/testing/test_process.rb +28 -22
  151. data/lib/action_dispatch/testing/test_request.rb +27 -34
  152. data/lib/action_dispatch/testing/test_response.rb +35 -7
  153. data/lib/action_pack.rb +4 -2
  154. data/lib/action_pack/gem_version.rb +5 -3
  155. data/lib/action_pack/version.rb +3 -1
  156. metadata +56 -39
  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,29 +1,9 @@
1
- require 'abstract_controller/collector'
1
+ # frozen_string_literal: true
2
+
3
+ require "abstract_controller/collector"
2
4
 
3
5
  module ActionController #:nodoc:
4
6
  module MimeResponds
5
- extend ActiveSupport::Concern
6
-
7
- # :stopdoc:
8
- module ClassMethods
9
- def respond_to(*)
10
- raise NoMethodError, "The controller-level `respond_to' feature has " \
11
- "been extracted to the `responders` gem. Add it to your Gemfile to " \
12
- "continue using this feature:\n" \
13
- " gem 'responders', '~> 2.0'\n" \
14
- "Consult the Rails upgrade guide for details."
15
- end
16
- end
17
-
18
- def respond_with(*)
19
- raise NoMethodError, "The `respond_with' feature has been extracted " \
20
- "to the `responders` gem. Add it to your Gemfile to continue using " \
21
- "this feature:\n" \
22
- " gem 'responders', '~> 2.0'\n" \
23
- "Consult the Rails upgrade guide for details."
24
- end
25
- # :startdoc:
26
-
27
7
  # Without web-service support, an action which collects the data for displaying a list of people
28
8
  # might look something like this:
29
9
  #
@@ -31,6 +11,13 @@ module ActionController #:nodoc:
31
11
  # @people = Person.all
32
12
  # end
33
13
  #
14
+ # That action implicitly responds to all formats, but formats can also be whitelisted:
15
+ #
16
+ # def index
17
+ # @people = Person.all
18
+ # respond_to :html, :js
19
+ # end
20
+ #
34
21
  # Here's the same action, with web-service support baked in:
35
22
  #
36
23
  # def index
@@ -38,11 +25,12 @@ module ActionController #:nodoc:
38
25
  #
39
26
  # respond_to do |format|
40
27
  # format.html
28
+ # format.js
41
29
  # format.xml { render xml: @people }
42
30
  # end
43
31
  # end
44
32
  #
45
- # What that says is, "if the client wants HTML in response to this action, just respond as we
33
+ # What that says is, "if the client wants HTML or JS in response to this action, just respond as we
46
34
  # would have before, but if the client wants XML, return them the list of people in XML format."
47
35
  # (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
48
36
  #
@@ -113,11 +101,11 @@ module ActionController #:nodoc:
113
101
  # and accept Rails' defaults, life will be much easier.
114
102
  #
115
103
  # If you need to use a MIME type which isn't supported by default, you can register your own handlers in
116
- # config/initializers/mime_types.rb as follows.
104
+ # +config/initializers/mime_types.rb+ as follows.
117
105
  #
118
106
  # Mime::Type.register "image/jpg", :jpg
119
107
  #
120
- # Respond to also allows you to specify a common block for different formats by using any:
108
+ # Respond to also allows you to specify a common block for different formats by using +any+:
121
109
  #
122
110
  # def index
123
111
  # @people = Person.all
@@ -173,21 +161,21 @@ module ActionController #:nodoc:
173
161
  # format.html.none { render "trash" }
174
162
  # end
175
163
  #
176
- # Variants also support common `any`/`all` block that formats have.
164
+ # Variants also support common +any+/+all+ block that formats have.
177
165
  #
178
166
  # It works for both inline:
179
167
  #
180
168
  # respond_to do |format|
181
- # format.html.any { render text: "any" }
182
- # format.html.phone { render text: "phone" }
169
+ # format.html.any { render html: "any" }
170
+ # format.html.phone { render html: "phone" }
183
171
  # end
184
172
  #
185
173
  # and block syntax:
186
174
  #
187
175
  # respond_to do |format|
188
176
  # format.html do |variant|
189
- # variant.any(:tablet, :phablet){ render text: "any" }
190
- # variant.phone { render text: "phone" }
177
+ # variant.any(:tablet, :phablet){ render html: "any" }
178
+ # variant.phone { render html: "phone" }
191
179
  # end
192
180
  # end
193
181
  #
@@ -195,16 +183,13 @@ module ActionController #:nodoc:
195
183
  #
196
184
  # request.variant = [:tablet, :phone]
197
185
  #
198
- # which will work similarly to formats and MIME types negotiation. If there will be no
199
- # :tablet variant declared, :phone variant will be picked:
186
+ # This will work similarly to formats and MIME types negotiation. If there
187
+ # is no +:tablet+ variant declared, the +:phone+ variant will be used:
200
188
  #
201
189
  # respond_to do |format|
202
190
  # format.html.none
203
191
  # format.html.phone # this gets rendered
204
192
  # end
205
- #
206
- # Be sure to check the documentation of <tt>ActionController::MimeResponds.respond_to</tt>
207
- # for more examples.
208
193
  def respond_to(*mimes)
209
194
  raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
210
195
 
@@ -213,8 +198,9 @@ module ActionController #:nodoc:
213
198
 
214
199
  if format = collector.negotiate_format(request)
215
200
  _process_format(format)
201
+ _set_rendered_content_type format
216
202
  response = collector.response
217
- response ? response.call : render({})
203
+ response.call if response
218
204
  else
219
205
  raise ActionController::UnknownFormat
220
206
  end
@@ -250,7 +236,7 @@ module ActionController #:nodoc:
250
236
  @responses = {}
251
237
  @variant = variant
252
238
 
253
- mimes.each { |mime| @responses["Mime::#{mime.upcase}".constantize] = nil }
239
+ mimes.each { |mime| @responses[Mime[mime]] = nil }
254
240
  end
255
241
 
256
242
  def any(*args, &block)
@@ -296,8 +282,8 @@ module ActionController #:nodoc:
296
282
 
297
283
  def any(*args, &block)
298
284
  if block_given?
299
- if args.any? && args.none?{ |a| a == @variant }
300
- args.each{ |v| @variants[v] = block }
285
+ if args.any? && args.none? { |a| a == @variant }
286
+ args.each { |v| @variants[v] = block }
301
287
  else
302
288
  @variants[:any] = block
303
289
  end
@@ -310,16 +296,17 @@ module ActionController #:nodoc:
310
296
  end
311
297
 
312
298
  def variant
313
- if @variant.nil?
299
+ if @variant.empty?
314
300
  @variants[:none] || @variants[:any]
315
- elsif (@variants.keys & @variant).any?
316
- @variant.each do |v|
317
- return @variants[v] if @variants.key?(v)
318
- end
319
301
  else
320
- @variants[:any]
302
+ @variants[variant_key]
321
303
  end
322
304
  end
305
+
306
+ private
307
+ def variant_key
308
+ @variant.find { |variant| @variants.key?(variant) } || :any
309
+ end
323
310
  end
324
311
  end
325
312
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionController
4
+ # Specify binary encoding for parameters for a given action.
5
+ module ParameterEncoding
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ def inherited(klass) # :nodoc:
10
+ super
11
+ klass.setup_param_encode
12
+ end
13
+
14
+ def setup_param_encode # :nodoc:
15
+ @_parameter_encodings = {}
16
+ end
17
+
18
+ def binary_params_for?(action) # :nodoc:
19
+ @_parameter_encodings[action.to_s]
20
+ end
21
+
22
+ # Specify that a given action's parameters should all be encoded as
23
+ # ASCII-8BIT (it "skips" the encoding default of UTF-8).
24
+ #
25
+ # For example, a controller would use it like this:
26
+ #
27
+ # class RepositoryController < ActionController::Base
28
+ # skip_parameter_encoding :show
29
+ #
30
+ # def show
31
+ # @repo = Repository.find_by_filesystem_path params[:file_path]
32
+ #
33
+ # # `repo_name` is guaranteed to be UTF-8, but was ASCII-8BIT, so
34
+ # # tag it as such
35
+ # @repo_name = params[:repo_name].force_encoding 'UTF-8'
36
+ # end
37
+ #
38
+ # def index
39
+ # @repositories = Repository.all
40
+ # end
41
+ # end
42
+ #
43
+ # The show action in the above controller would have all parameter values
44
+ # encoded as ASCII-8BIT. This is useful in the case where an application
45
+ # must handle data but encoding of the data is unknown, like file system data.
46
+ def skip_parameter_encoding(action)
47
+ @_parameter_encodings[action.to_s] = true
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,16 +1,16 @@
1
- require 'active_support/core_ext/hash/slice'
2
- require 'active_support/core_ext/hash/except'
3
- require 'active_support/core_ext/module/anonymous'
4
- require 'active_support/core_ext/struct'
5
- require 'action_dispatch/http/mime_type'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/slice"
4
+ require "active_support/core_ext/hash/except"
5
+ require "active_support/core_ext/module/anonymous"
6
+ require "action_dispatch/http/mime_type"
6
7
 
7
8
  module ActionController
8
9
  # Wraps the parameters hash into a nested hash. This will allow clients to
9
10
  # submit requests without having to specify any root elements.
10
11
  #
11
12
  # This functionality is enabled in +config/initializers/wrap_parameters.rb+
12
- # and can be customized. If you are upgrading to \Rails 3.1, this file will
13
- # need to be created for the functionality to be enabled.
13
+ # and can be customized.
14
14
  #
15
15
  # You could also turn it on per controller by setting the format array to
16
16
  # a non-empty array:
@@ -42,7 +42,7 @@ module ActionController
42
42
  # wrap_parameters :person, include: [:username, :password]
43
43
  # end
44
44
  #
45
- # On ActiveRecord models with no +:include+ or +:exclude+ option set,
45
+ # On Active Record models with no +:include+ or +:exclude+ option set,
46
46
  # it will only wrap the parameters returned by the class method
47
47
  # <tt>attribute_names</tt>.
48
48
  #
@@ -73,7 +73,7 @@ module ActionController
73
73
 
74
74
  EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
75
75
 
76
- require 'mutex_m'
76
+ require "mutex_m"
77
77
 
78
78
  class Options < Struct.new(:name, :format, :include, :exclude, :klass, :model) # :nodoc:
79
79
  include Mutex_m
@@ -86,14 +86,14 @@ module ActionController
86
86
  new name, format, include, exclude, nil, nil
87
87
  end
88
88
 
89
- def initialize(name, format, include, exclude, klass, model) # nodoc
89
+ def initialize(name, format, include, exclude, klass, model) # :nodoc:
90
90
  super
91
91
  @include_set = include
92
92
  @name_set = name
93
93
  end
94
94
 
95
95
  def model
96
- super || synchronize { super || self.model = _default_wrap_model }
96
+ super || self.model = _default_wrap_model
97
97
  end
98
98
 
99
99
  def include
@@ -107,7 +107,19 @@ module ActionController
107
107
 
108
108
  unless super || exclude
109
109
  if m.respond_to?(:attribute_names) && m.attribute_names.any?
110
- self.include = m.attribute_names
110
+ if m.respond_to?(:stored_attributes) && !m.stored_attributes.empty?
111
+ self.include = m.attribute_names + m.stored_attributes.values.flatten.map(&:to_s)
112
+ else
113
+ self.include = m.attribute_names
114
+ end
115
+
116
+ if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
117
+ self.include += m.nested_attributes_options.keys.map do |key|
118
+ key.to_s.dup.concat("_attributes")
119
+ end
120
+ end
121
+
122
+ self.include
111
123
  end
112
124
  end
113
125
  end
@@ -130,35 +142,34 @@ module ActionController
130
142
  end
131
143
 
132
144
  private
133
- # Determine the wrapper model from the controller's name. By convention,
134
- # this could be done by trying to find the defined model that has the
135
- # same singularize name as the controller. For example, +UsersController+
136
- # will try to find if the +User+ model exists.
137
- #
138
- # This method also does namespace lookup. Foo::Bar::UsersController will
139
- # try to find Foo::Bar::User, Foo::User and finally User.
140
- def _default_wrap_model #:nodoc:
141
- return nil if klass.anonymous?
142
- model_name = klass.name.sub(/Controller$/, '').classify
143
-
144
- begin
145
- if model_klass = model_name.safe_constantize
146
- model_klass
147
- else
148
- namespaces = model_name.split("::")
149
- namespaces.delete_at(-2)
150
- break if namespaces.last == model_name
151
- model_name = namespaces.join("::")
152
- end
153
- end until model_klass
145
+ # Determine the wrapper model from the controller's name. By convention,
146
+ # this could be done by trying to find the defined model that has the
147
+ # same singular name as the controller. For example, +UsersController+
148
+ # will try to find if the +User+ model exists.
149
+ #
150
+ # This method also does namespace lookup. Foo::Bar::UsersController will
151
+ # try to find Foo::Bar::User, Foo::User and finally User.
152
+ def _default_wrap_model
153
+ return nil if klass.anonymous?
154
+ model_name = klass.name.sub(/Controller$/, "").classify
155
+
156
+ begin
157
+ if model_klass = model_name.safe_constantize
158
+ model_klass
159
+ else
160
+ namespaces = model_name.split("::")
161
+ namespaces.delete_at(-2)
162
+ break if namespaces.last == model_name
163
+ model_name = namespaces.join("::")
164
+ end
165
+ end until model_klass
154
166
 
155
- model_klass
156
- end
167
+ model_klass
168
+ end
157
169
  end
158
170
 
159
171
  included do
160
- class_attribute :_wrapper_options
161
- self._wrapper_options = Options.from_hash(format: [])
172
+ class_attribute :_wrapper_options, default: Options.from_hash(format: [])
162
173
  end
163
174
 
164
175
  module ClassMethods
@@ -200,14 +211,14 @@ module ActionController
200
211
  when Hash
201
212
  options = name_or_model_or_options
202
213
  when false
203
- options = options.merge(:format => [])
214
+ options = options.merge(format: [])
204
215
  when Symbol, String
205
- options = options.merge(:name => name_or_model_or_options)
216
+ options = options.merge(name: name_or_model_or_options)
206
217
  else
207
218
  model = name_or_model_or_options
208
219
  end
209
220
 
210
- opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options)
221
+ opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options)
211
222
  opts.model = model
212
223
  opts.klass = self
213
224
 
@@ -215,7 +226,7 @@ module ActionController
215
226
  end
216
227
 
217
228
  # Sets the default wrapper key or model which will be used to determine
218
- # wrapper key and attribute names. Will be called automatically when the
229
+ # wrapper key and attribute names. Called automatically when the
219
230
  # module is inherited.
220
231
  def inherited(klass)
221
232
  if klass._wrapper_options.format.any?
@@ -227,24 +238,19 @@ module ActionController
227
238
  end
228
239
  end
229
240
 
230
- # Performs parameters wrapping upon the request. Will be called automatically
241
+ # Performs parameters wrapping upon the request. Called automatically
231
242
  # by the metal call stack.
232
243
  def process_action(*args)
233
244
  if _wrapper_enabled?
234
- if request.parameters[_wrapper_key].present?
235
- wrapped_hash = _extract_parameters(request.parameters)
236
- else
237
- wrapped_hash = _wrap_parameters request.request_parameters
238
- end
239
-
245
+ wrapped_hash = _wrap_parameters request.request_parameters
240
246
  wrapped_keys = request.request_parameters.keys
241
247
  wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
242
248
 
243
- # This will make the wrapped hash accessible from controller and view
249
+ # This will make the wrapped hash accessible from controller and view.
244
250
  request.parameters.merge! wrapped_hash
245
251
  request.request_parameters.merge! wrapped_hash
246
252
 
247
- # This will display the wrapped hash in the log file
253
+ # This will display the wrapped hash in the log file.
248
254
  request.filtered_parameters.merge! wrapped_filtered_hash
249
255
  end
250
256
  super
@@ -252,7 +258,7 @@ module ActionController
252
258
 
253
259
  private
254
260
 
255
- # Returns the wrapper key which will be used to stored wrapped parameters.
261
+ # Returns the wrapper key which will be used to store wrapped parameters.
256
262
  def _wrapper_key
257
263
  _wrapper_options.name
258
264
  end
@@ -278,8 +284,10 @@ module ActionController
278
284
 
279
285
  # Checks if we should perform parameters wrapping.
280
286
  def _wrapper_enabled?
281
- ref = request.content_mime_type.try(:ref)
282
- _wrapper_formats.include?(ref) && _wrapper_key && !request.request_parameters[_wrapper_key]
287
+ return false unless request.has_content_type?
288
+
289
+ ref = request.content_mime_type.ref
290
+ _wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key)
283
291
  end
284
292
  end
285
293
  end
@@ -1,17 +1,10 @@
1
- module ActionController
2
- class RedirectBackError < AbstractController::Error #:nodoc:
3
- DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
4
-
5
- def initialize(message = nil)
6
- super(message || DEFAULT_MESSAGE)
7
- end
8
- end
1
+ # frozen_string_literal: true
9
2
 
3
+ module ActionController
10
4
  module Redirecting
11
5
  extend ActiveSupport::Concern
12
6
 
13
7
  include AbstractController::Logger
14
- include ActionController::RackDelegation
15
8
  include ActionController::UrlFor
16
9
 
17
10
  # Redirects the browser to the target specified in +options+. This parameter can be any one of:
@@ -21,34 +14,31 @@ module ActionController
21
14
  # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) or a protocol relative reference (like <tt>//</tt>) - Is passed straight through as the target for redirection.
22
15
  # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
23
16
  # * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
24
- # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
25
- # Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
26
17
  #
27
18
  # === Examples:
28
19
  #
29
20
  # redirect_to action: "show", id: 5
30
- # redirect_to post
21
+ # redirect_to @post
31
22
  # redirect_to "http://www.rubyonrails.org"
32
23
  # redirect_to "/images/screenshot.jpg"
33
- # redirect_to articles_url
34
- # redirect_to :back
24
+ # redirect_to posts_url
35
25
  # redirect_to proc { edit_post_url(@post) }
36
26
  #
37
- # The redirection happens as a "302 Found" header unless otherwise specified using the <tt>:status</tt> option:
27
+ # The redirection happens as a <tt>302 Found</tt> header unless otherwise specified using the <tt>:status</tt> option:
38
28
  #
39
29
  # redirect_to post_url(@post), status: :found
40
30
  # redirect_to action: 'atom', status: :moved_permanently
41
31
  # redirect_to post_url(@post), status: 301
42
32
  # redirect_to action: 'atom', status: 302
43
33
  #
44
- # The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
34
+ # The status code can either be a standard {HTTP Status code}[https://www.iana.org/assignments/http-status-codes] as an
45
35
  # integer, or a symbol representing the downcased, underscored and symbolized description.
46
36
  # Note that the status code must be a 3xx HTTP code, or redirection will not occur.
47
37
  #
48
38
  # If you are using XHR requests other than GET or POST and redirecting after the
49
39
  # request then some browsers will follow the redirect using the original request
50
40
  # method. This may lead to undesirable behavior such as a double DELETE. To work
51
- # around this you can return a <tt>303 See Other</tt> status code which will be
41
+ # around this you can return a <tt>303 See Other</tt> status code which will be
52
42
  # followed using a GET request.
53
43
  #
54
44
  # redirect_to posts_url, status: :see_other
@@ -62,18 +52,45 @@ module ActionController
62
52
  # redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }
63
53
  # redirect_to({ action: 'atom' }, alert: "Something serious happened")
64
54
  #
65
- # When using <tt>redirect_to :back</tt>, if there is no referrer,
66
- # <tt>ActionController::RedirectBackError</tt> will be raised. You
67
- # may specify some fallback behavior for this case by rescuing
68
- # <tt>ActionController::RedirectBackError</tt>.
69
- def redirect_to(options = {}, response_status = {}) #:doc:
55
+ # Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function.
56
+ # To terminate the execution of the function immediately after the +redirect_to+, use return.
57
+ # redirect_to post_url(@post) and return
58
+ def redirect_to(options = {}, response_status = {})
70
59
  raise ActionControllerError.new("Cannot redirect to nil!") unless options
71
- raise ActionControllerError.new("Cannot redirect to a parameter hash!") if options.is_a?(ActionController::Parameters)
72
60
  raise AbstractController::DoubleRenderError if response_body
73
61
 
74
62
  self.status = _extract_redirect_to_status(options, response_status)
75
63
  self.location = _compute_redirect_to_location(request, options)
76
- self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
64
+ self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
65
+ end
66
+
67
+ # Redirects the browser to the page that issued the request (the referrer)
68
+ # if possible, otherwise redirects to the provided default fallback
69
+ # location.
70
+ #
71
+ # The referrer information is pulled from the HTTP +Referer+ (sic) header on
72
+ # the request. This is an optional header and its presence on the request is
73
+ # subject to browser security settings and user preferences. If the request
74
+ # is missing this header, the <tt>fallback_location</tt> will be used.
75
+ #
76
+ # redirect_back fallback_location: { action: "show", id: 5 }
77
+ # redirect_back fallback_location: @post
78
+ # redirect_back fallback_location: "http://www.rubyonrails.org"
79
+ # redirect_back fallback_location: "/images/screenshot.jpg"
80
+ # redirect_back fallback_location: posts_url
81
+ # redirect_back fallback_location: proc { edit_post_url(@post) }
82
+ # redirect_back fallback_location: '/', allow_other_host: false
83
+ #
84
+ # ==== Options
85
+ # * <tt>:fallback_location</tt> - The default fallback location that will be used on missing +Referer+ header.
86
+ # * <tt>:allow_other_host</tt> - Allow or disallow redirection to the host that is different to the current host, defaults to true.
87
+ #
88
+ # All other options that can be passed to <tt>redirect_to</tt> are accepted as
89
+ # options and the behavior is identical.
90
+ def redirect_back(fallback_location:, allow_other_host: true, **args)
91
+ referer = request.headers["Referer"]
92
+ redirect_to_referer = referer && (allow_other_host || _url_host_allowed?(referer))
93
+ redirect_to redirect_to_referer ? referer : fallback_location, **args
77
94
  end
78
95
 
79
96
  def _compute_redirect_to_location(request, options) #:nodoc:
@@ -81,16 +98,14 @@ module ActionController
81
98
  # The scheme name consist of a letter followed by any combination of
82
99
  # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
83
100
  # characters; and is terminated by a colon (":").
84
- # See http://tools.ietf.org/html/rfc3986#section-3.1
101
+ # See https://tools.ietf.org/html/rfc3986#section-3.1
85
102
  # The protocol relative scheme starts with a double slash "//".
86
103
  when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
87
104
  options
88
105
  when String
89
106
  request.protocol + request.host_with_port + options
90
- when :back
91
- request.headers["Referer"] or raise RedirectBackError
92
107
  when Proc
93
- _compute_redirect_to_location request, options.call
108
+ _compute_redirect_to_location request, instance_eval(&options)
94
109
  else
95
110
  url_for(options)
96
111
  end.delete("\0\r\n")
@@ -108,5 +123,11 @@ module ActionController
108
123
  302
109
124
  end
110
125
  end
126
+
127
+ def _url_host_allowed?(url)
128
+ URI(url.to_s).host == request.host
129
+ rescue ArgumentError, URI::Error
130
+ false
131
+ end
111
132
  end
112
133
  end