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,210 +1,213 @@
1
- require 'abstract_controller/collector'
1
+ # frozen_string_literal: true
2
2
 
3
- module ActionController #:nodoc:
4
- module MimeResponds
5
- extend ActiveSupport::Concern
3
+ # :markup: markdown
6
4
 
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
5
+ require "abstract_controller/collector"
17
6
 
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
- # Without web-service support, an action which collects the data for displaying a list of people
28
- # might look something like this:
7
+ module ActionController # :nodoc:
8
+ module MimeResponds
9
+ # Without web-service support, an action which collects the data for displaying
10
+ # a list of people might look something like this:
29
11
  #
30
- # def index
31
- # @people = Person.all
32
- # end
12
+ # def index
13
+ # @people = Person.all
14
+ # end
15
+ #
16
+ # That action implicitly responds to all formats, but formats can also be
17
+ # explicitly enumerated:
18
+ #
19
+ # def index
20
+ # @people = Person.all
21
+ # respond_to :html, :js
22
+ # end
33
23
  #
34
24
  # Here's the same action, with web-service support baked in:
35
25
  #
36
- # def index
37
- # @people = Person.all
26
+ # def index
27
+ # @people = Person.all
38
28
  #
39
- # respond_to do |format|
40
- # format.html
41
- # format.xml { render xml: @people }
29
+ # respond_to do |format|
30
+ # format.html
31
+ # format.js
32
+ # format.xml { render xml: @people }
33
+ # end
42
34
  # end
43
- # end
44
35
  #
45
- # What that says is, "if the client wants HTML in response to this action, just respond as we
46
- # would have before, but if the client wants XML, return them the list of people in XML format."
47
- # (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
36
+ # What that says is, "if the client wants HTML or JS in response to this action,
37
+ # just respond as we would have before, but if the client wants XML, return them
38
+ # the list of people in XML format." (Rails determines the desired response
39
+ # format from the HTTP Accept header submitted by the client.)
48
40
  #
49
- # Supposing you have an action that adds a new person, optionally creating their company
50
- # (by name) if it does not already exist, without web-services, it might look like this:
41
+ # Supposing you have an action that adds a new person, optionally creating their
42
+ # company (by name) if it does not already exist, without web-services, it might
43
+ # look like this:
51
44
  #
52
- # def create
53
- # @company = Company.find_or_create_by(name: params[:company][:name])
54
- # @person = @company.people.create(params[:person])
45
+ # def create
46
+ # @company = Company.find_or_create_by(name: params[:company][:name])
47
+ # @person = @company.people.create(params[:person])
55
48
  #
56
- # redirect_to(person_list_url)
57
- # end
49
+ # redirect_to(person_list_url)
50
+ # end
58
51
  #
59
52
  # Here's the same action, with web-service support baked in:
60
53
  #
61
- # def create
62
- # company = params[:person].delete(:company)
63
- # @company = Company.find_or_create_by(name: company[:name])
64
- # @person = @company.people.create(params[:person])
54
+ # def create
55
+ # company = params[:person].delete(:company)
56
+ # @company = Company.find_or_create_by(name: company[:name])
57
+ # @person = @company.people.create(params[:person])
65
58
  #
66
- # respond_to do |format|
67
- # format.html { redirect_to(person_list_url) }
68
- # format.js
69
- # format.xml { render xml: @person.to_xml(include: @company) }
59
+ # respond_to do |format|
60
+ # format.html { redirect_to(person_list_url) }
61
+ # format.js
62
+ # format.xml { render xml: @person.to_xml(include: @company) }
63
+ # end
70
64
  # end
71
- # end
72
65
  #
73
- # If the client wants HTML, we just redirect them back to the person list. If they want JavaScript,
74
- # then it is an Ajax request and we render the JavaScript template associated with this action.
75
- # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
76
- # include the person's company in the rendered XML, so you get something like this:
66
+ # If the client wants HTML, we just redirect them back to the person list. If
67
+ # they want JavaScript, then it is an Ajax request and we render the JavaScript
68
+ # template associated with this action. Lastly, if the client wants XML, we
69
+ # render the created person as XML, but with a twist: we also include the
70
+ # person's company in the rendered XML, so you get something like this:
77
71
  #
78
- # <person>
79
- # <id>...</id>
80
- # ...
81
- # <company>
72
+ # <person>
82
73
  # <id>...</id>
83
- # <name>...</name>
84
74
  # ...
85
- # </company>
86
- # </person>
75
+ # <company>
76
+ # <id>...</id>
77
+ # <name>...</name>
78
+ # ...
79
+ # </company>
80
+ # </person>
87
81
  #
88
82
  # Note, however, the extra bit at the top of that action:
89
83
  #
90
- # company = params[:person].delete(:company)
91
- # @company = Company.find_or_create_by(name: company[:name])
84
+ # company = params[:person].delete(:company)
85
+ # @company = Company.find_or_create_by(name: company[:name])
92
86
  #
93
- # This is because the incoming XML document (if a web-service request is in process) can only contain a
94
- # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
87
+ # This is because the incoming XML document (if a web-service request is in
88
+ # process) can only contain a single root-node. So, we have to rearrange things
89
+ # so that the request looks like this (url-encoded):
95
90
  #
96
- # person[name]=...&person[company][name]=...&...
91
+ # person[name]=...&person[company][name]=...&...
97
92
  #
98
93
  # And, like this (xml-encoded):
99
94
  #
100
- # <person>
101
- # <name>...</name>
102
- # <company>
95
+ # <person>
103
96
  # <name>...</name>
104
- # </company>
105
- # </person>
97
+ # <company>
98
+ # <name>...</name>
99
+ # </company>
100
+ # </person>
106
101
  #
107
- # In other words, we make the request so that it operates on a single entity's person. Then, in the action,
108
- # we extract the company data from the request, find or create the company, and then create the new person
109
- # with the remaining data.
102
+ # In other words, we make the request so that it operates on a single entity's
103
+ # person. Then, in the action, we extract the company data from the request,
104
+ # find or create the company, and then create the new person with the remaining
105
+ # data.
110
106
  #
111
- # Note that you can define your own XML parameter parser which would allow you to describe multiple entities
112
- # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
113
- # and accept Rails' defaults, life will be much easier.
107
+ # Note that you can define your own XML parameter parser which would allow you
108
+ # to describe multiple entities in a single request (i.e., by wrapping them all
109
+ # in a single root node), but if you just go with the flow and accept Rails'
110
+ # defaults, life will be much easier.
114
111
  #
115
- # 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.
112
+ # If you need to use a MIME type which isn't supported by default, you can
113
+ # register your own handlers in `config/initializers/mime_types.rb` as follows.
117
114
  #
118
- # Mime::Type.register "image/jpg", :jpg
115
+ # Mime::Type.register "image/jpeg", :jpg
119
116
  #
120
- # Respond to also allows you to specify a common block for different formats by using any:
117
+ # `respond_to` also allows you to specify a common block for different formats
118
+ # by using `any`:
121
119
  #
122
- # def index
123
- # @people = Person.all
120
+ # def index
121
+ # @people = Person.all
124
122
  #
125
- # respond_to do |format|
126
- # format.html
127
- # format.any(:xml, :json) { render request.format.to_sym => @people }
123
+ # respond_to do |format|
124
+ # format.html
125
+ # format.any(:xml, :json) { render request.format.to_sym => @people }
126
+ # end
128
127
  # end
129
- # end
130
128
  #
131
129
  # In the example above, if the format is xml, it will render:
132
130
  #
133
- # render xml: @people
131
+ # render xml: @people
134
132
  #
135
133
  # Or if the format is json:
136
134
  #
137
- # render json: @people
135
+ # render json: @people
136
+ #
137
+ # `any` can also be used with no arguments, in which case it will be used for
138
+ # any format requested by the user:
139
+ #
140
+ # respond_to do |format|
141
+ # format.html
142
+ # format.any { redirect_to support_path }
143
+ # end
138
144
  #
139
145
  # Formats can have different variants.
140
146
  #
141
- # The request variant is a specialization of the request format, like <tt>:tablet</tt>,
142
- # <tt>:phone</tt>, or <tt>:desktop</tt>.
147
+ # The request variant is a specialization of the request format, like `:tablet`,
148
+ # `:phone`, or `:desktop`.
143
149
  #
144
- # We often want to render different html/json/xml templates for phones,
145
- # tablets, and desktop browsers. Variants make it easy.
150
+ # We often want to render different html/json/xml templates for phones, tablets,
151
+ # and desktop browsers. Variants make it easy.
146
152
  #
147
- # You can set the variant in a +before_action+:
153
+ # You can set the variant in a `before_action`:
148
154
  #
149
- # request.variant = :tablet if request.user_agent =~ /iPad/
155
+ # request.variant = :tablet if /iPad/.match?(request.user_agent)
150
156
  #
151
157
  # Respond to variants in the action just like you respond to formats:
152
158
  #
153
- # respond_to do |format|
154
- # format.html do |variant|
155
- # variant.tablet # renders app/views/projects/show.html+tablet.erb
156
- # variant.phone { extra_setup; render ... }
157
- # variant.none { special_setup } # executed only if there is no variant set
159
+ # respond_to do |format|
160
+ # format.html do |variant|
161
+ # variant.tablet # renders app/views/projects/show.html+tablet.erb
162
+ # variant.phone { extra_setup; render ... }
163
+ # variant.none { special_setup } # executed only if there is no variant set
164
+ # end
158
165
  # end
159
- # end
160
166
  #
161
167
  # Provide separate templates for each format and variant:
162
168
  #
163
- # app/views/projects/show.html.erb
164
- # app/views/projects/show.html+tablet.erb
165
- # app/views/projects/show.html+phone.erb
169
+ # app/views/projects/show.html.erb
170
+ # app/views/projects/show.html+tablet.erb
171
+ # app/views/projects/show.html+phone.erb
166
172
  #
167
- # When you're not sharing any code within the format, you can simplify defining variants
168
- # using the inline syntax:
173
+ # When you're not sharing any code within the format, you can simplify defining
174
+ # variants using the inline syntax:
169
175
  #
170
- # respond_to do |format|
171
- # format.js { render "trash" }
172
- # format.html.phone { redirect_to progress_path }
173
- # format.html.none { render "trash" }
174
- # end
176
+ # respond_to do |format|
177
+ # format.js { render "trash" }
178
+ # format.html.phone { redirect_to progress_path }
179
+ # format.html.none { render "trash" }
180
+ # end
175
181
  #
176
182
  # Variants also support common `any`/`all` block that formats have.
177
183
  #
178
184
  # It works for both inline:
179
185
  #
180
- # respond_to do |format|
181
- # format.html.any { render text: "any" }
182
- # format.html.phone { render text: "phone" }
183
- # end
186
+ # respond_to do |format|
187
+ # format.html.any { render html: "any" }
188
+ # format.html.phone { render html: "phone" }
189
+ # end
184
190
  #
185
191
  # and block syntax:
186
192
  #
187
- # respond_to do |format|
188
- # format.html do |variant|
189
- # variant.any(:tablet, :phablet){ render text: "any" }
190
- # variant.phone { render text: "phone" }
193
+ # respond_to do |format|
194
+ # format.html do |variant|
195
+ # variant.any(:tablet, :phablet){ render html: "any" }
196
+ # variant.phone { render html: "phone" }
197
+ # end
191
198
  # end
192
- # end
193
199
  #
194
200
  # You can also set an array of variants:
195
201
  #
196
- # request.variant = [:tablet, :phone]
197
- #
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:
202
+ # request.variant = [:tablet, :phone]
200
203
  #
201
- # respond_to do |format|
202
- # format.html.none
203
- # format.html.phone # this gets rendered
204
- # end
204
+ # This will work similarly to formats and MIME types negotiation. If there is no
205
+ # `:tablet` variant declared, the `:phone` variant will be used:
205
206
  #
206
- # Be sure to check the documentation of <tt>ActionController::MimeResponds.respond_to</tt>
207
- # for more examples.
207
+ # respond_to do |format|
208
+ # format.html.none
209
+ # format.html.phone # this gets rendered
210
+ # end
208
211
  def respond_to(*mimes)
209
212
  raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
210
213
 
@@ -212,35 +215,38 @@ module ActionController #:nodoc:
212
215
  yield collector if block_given?
213
216
 
214
217
  if format = collector.negotiate_format(request)
218
+ if media_type && media_type != format
219
+ raise ActionController::RespondToMismatchError
220
+ end
215
221
  _process_format(format)
222
+ _set_rendered_content_type(format) unless collector.any_response?
216
223
  response = collector.response
217
- response ? response.call : render({})
224
+ response.call if response
218
225
  else
219
226
  raise ActionController::UnknownFormat
220
227
  end
221
228
  end
222
229
 
223
- # A container for responses available from the current controller for
224
- # requests for different mime-types sent to a particular action.
225
- #
226
- # The public controller methods +respond_to+ may be called with a block
227
- # that is used to define responses to different mime-types, e.g.
228
- # for +respond_to+ :
229
- #
230
- # respond_to do |format|
231
- # format.html
232
- # format.xml { render xml: @people }
233
- # end
234
- #
235
- # In this usage, the argument passed to the block (+format+ above) is an
236
- # instance of the ActionController::MimeResponds::Collector class. This
237
- # object serves as a container in which available responses can be stored by
238
- # calling any of the dynamically generated, mime-type-specific methods such
239
- # as +html+, +xml+ etc on the Collector. Each response is represented by a
240
- # corresponding block if present.
241
- #
242
- # A subsequent call to #negotiate_format(request) will enable the Collector
243
- # to determine which specific mime-type it should respond with for the current
230
+ # A container for responses available from the current controller for requests
231
+ # for different mime-types sent to a particular action.
232
+ #
233
+ # The public controller methods `respond_to` may be called with a block that is
234
+ # used to define responses to different mime-types, e.g. for `respond_to` :
235
+ #
236
+ # respond_to do |format|
237
+ # format.html
238
+ # format.xml { render xml: @people }
239
+ # end
240
+ #
241
+ # In this usage, the argument passed to the block (`format` above) is an
242
+ # instance of the ActionController::MimeResponds::Collector class. This object
243
+ # serves as a container in which available responses can be stored by calling
244
+ # any of the dynamically generated, mime-type-specific methods such as `html`,
245
+ # `xml` etc on the Collector. Each response is represented by a corresponding
246
+ # block if present.
247
+ #
248
+ # A subsequent call to #negotiate_format(request) will enable the Collector to
249
+ # determine which specific mime-type it should respond with for the current
244
250
  # request, with this response then being accessible by calling #response.
245
251
  class Collector
246
252
  include AbstractController::Collector
@@ -250,7 +256,7 @@ module ActionController #:nodoc:
250
256
  @responses = {}
251
257
  @variant = variant
252
258
 
253
- mimes.each { |mime| @responses["Mime::#{mime.upcase}".constantize] = nil }
259
+ mimes.each { |mime| @responses[Mime[mime]] = nil }
254
260
  end
255
261
 
256
262
  def any(*args, &block)
@@ -271,6 +277,10 @@ module ActionController #:nodoc:
271
277
  end
272
278
  end
273
279
 
280
+ def any_response?
281
+ !@responses.fetch(format, false) && @responses[Mime::ALL]
282
+ end
283
+
274
284
  def response
275
285
  response = @responses.fetch(format, @responses[Mime::ALL])
276
286
  if response.is_a?(VariantCollector) # `format.html.phone` - variant inline syntax
@@ -288,7 +298,7 @@ module ActionController #:nodoc:
288
298
  @format = request.negotiate_mime(@responses.keys)
289
299
  end
290
300
 
291
- class VariantCollector #:nodoc:
301
+ class VariantCollector # :nodoc:
292
302
  def initialize(variant = nil)
293
303
  @variant = variant
294
304
  @variants = {}
@@ -296,8 +306,8 @@ module ActionController #:nodoc:
296
306
 
297
307
  def any(*args, &block)
298
308
  if block_given?
299
- if args.any? && args.none?{ |a| a == @variant }
300
- args.each{ |v| @variants[v] = block }
309
+ if args.any? && args.none? { |a| a == @variant }
310
+ args.each { |v| @variants[v] = block }
301
311
  else
302
312
  @variants[:any] = block
303
313
  end
@@ -305,21 +315,22 @@ module ActionController #:nodoc:
305
315
  end
306
316
  alias :all :any
307
317
 
308
- def method_missing(name, *args, &block)
318
+ def method_missing(name, *, &block)
309
319
  @variants[name] = block if block_given?
310
320
  end
311
321
 
312
322
  def variant
313
- if @variant.nil?
323
+ if @variant.empty?
314
324
  @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
325
  else
320
- @variants[:any]
326
+ @variants[variant_key]
321
327
  end
322
328
  end
329
+
330
+ private
331
+ def variant_key
332
+ @variant.find { |variant| @variants.key?(variant) } || :any
333
+ end
323
334
  end
324
335
  end
325
336
  end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionController
6
+ # Specify binary encoding for parameters for a given action.
7
+ module ParameterEncoding
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods
11
+ def inherited(klass) # :nodoc:
12
+ super
13
+ klass.setup_param_encode
14
+ end
15
+
16
+ def setup_param_encode # :nodoc:
17
+ @_parameter_encodings = Hash.new { |h, k| h[k] = {} }
18
+ end
19
+
20
+ def action_encoding_template(action) # :nodoc:
21
+ if @_parameter_encodings.has_key?(action.to_s)
22
+ @_parameter_encodings[action.to_s]
23
+ end
24
+ end
25
+
26
+ # Specify that a given action's parameters should all be encoded as ASCII-8BIT
27
+ # (it "skips" the encoding default of UTF-8).
28
+ #
29
+ # For example, a controller would use it like this:
30
+ #
31
+ # class RepositoryController < ActionController::Base
32
+ # skip_parameter_encoding :show
33
+ #
34
+ # def show
35
+ # @repo = Repository.find_by_filesystem_path params[:file_path]
36
+ #
37
+ # # `repo_name` is guaranteed to be UTF-8, but was ASCII-8BIT, so
38
+ # # tag it as such
39
+ # @repo_name = params[:repo_name].force_encoding 'UTF-8'
40
+ # end
41
+ #
42
+ # def index
43
+ # @repositories = Repository.all
44
+ # end
45
+ # end
46
+ #
47
+ # The show action in the above controller would have all parameter values
48
+ # encoded as ASCII-8BIT. This is useful in the case where an application must
49
+ # handle data but encoding of the data is unknown, like file system data.
50
+ def skip_parameter_encoding(action)
51
+ @_parameter_encodings[action.to_s] = Hash.new { Encoding::ASCII_8BIT }
52
+ end
53
+
54
+ # Specify the encoding for a parameter on an action. If not specified the
55
+ # default is UTF-8.
56
+ #
57
+ # You can specify a binary (ASCII_8BIT) parameter with:
58
+ #
59
+ # class RepositoryController < ActionController::Base
60
+ # # This specifies that file_path is not UTF-8 and is instead ASCII_8BIT
61
+ # param_encoding :show, :file_path, Encoding::ASCII_8BIT
62
+ #
63
+ # def show
64
+ # @repo = Repository.find_by_filesystem_path params[:file_path]
65
+ #
66
+ # # params[:repo_name] remains UTF-8 encoded
67
+ # @repo_name = params[:repo_name]
68
+ # end
69
+ #
70
+ # def index
71
+ # @repositories = Repository.all
72
+ # end
73
+ # end
74
+ #
75
+ # The file_path parameter on the show action would be encoded as ASCII-8BIT, but
76
+ # all other arguments will remain UTF-8 encoded. This is useful in the case
77
+ # where an application must handle data but encoding of the data is unknown,
78
+ # like file system data.
79
+ def param_encoding(action, param, encoding)
80
+ @_parameter_encodings[action.to_s][param.to_s] = encoding
81
+ end
82
+ end
83
+ end
84
+ end