omg-actionpack 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +129 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller/asset_paths.rb +14 -0
  6. data/lib/abstract_controller/base.rb +299 -0
  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 +265 -0
  10. data/lib/abstract_controller/collector.rb +44 -0
  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 +243 -0
  14. data/lib/abstract_controller/logger.rb +16 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
  16. data/lib/abstract_controller/rendering.rb +126 -0
  17. data/lib/abstract_controller/translation.rb +42 -0
  18. data/lib/abstract_controller/url_for.rb +37 -0
  19. data/lib/abstract_controller.rb +36 -0
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +155 -0
  22. data/lib/action_controller/base.rb +332 -0
  23. data/lib/action_controller/caching.rb +49 -0
  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 +96 -0
  27. data/lib/action_controller/metal/allow_browser.rb +123 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +341 -0
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +20 -0
  32. data/lib/action_controller/metal/data_streaming.rb +154 -0
  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 +59 -0
  36. data/lib/action_controller/metal/exceptions.rb +106 -0
  37. data/lib/action_controller/metal/flash.rb +67 -0
  38. data/lib/action_controller/metal/head.rb +67 -0
  39. data/lib/action_controller/metal/helpers.rb +129 -0
  40. data/lib/action_controller/metal/http_authentication.rb +565 -0
  41. data/lib/action_controller/metal/implicit_render.rb +67 -0
  42. data/lib/action_controller/metal/instrumentation.rb +120 -0
  43. data/lib/action_controller/metal/live.rb +398 -0
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +337 -0
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +312 -0
  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 +251 -0
  51. data/lib/action_controller/metal/renderers.rb +181 -0
  52. data/lib/action_controller/metal/rendering.rb +260 -0
  53. data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
  54. data/lib/action_controller/metal/rescue.rb +33 -0
  55. data/lib/action_controller/metal/streaming.rb +183 -0
  56. data/lib/action_controller/metal/strong_parameters.rb +1546 -0
  57. data/lib/action_controller/metal/testing.rb +25 -0
  58. data/lib/action_controller/metal/url_for.rb +65 -0
  59. data/lib/action_controller/metal.rb +339 -0
  60. data/lib/action_controller/railtie.rb +149 -0
  61. data/lib/action_controller/railties/helpers.rb +26 -0
  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 +691 -0
  65. data/lib/action_controller.rb +80 -0
  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 +249 -0
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +365 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +80 -0
  72. data/lib/action_dispatch/http/filter_redirect.rb +50 -0
  73. data/lib/action_dispatch/http/headers.rb +134 -0
  74. data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
  75. data/lib/action_dispatch/http/mime_type.rb +389 -0
  76. data/lib/action_dispatch/http/mime_types.rb +54 -0
  77. data/lib/action_dispatch/http/parameters.rb +119 -0
  78. data/lib/action_dispatch/http/permissions_policy.rb +189 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +67 -0
  80. data/lib/action_dispatch/http/request.rb +498 -0
  81. data/lib/action_dispatch/http/response.rb +556 -0
  82. data/lib/action_dispatch/http/upload.rb +107 -0
  83. data/lib/action_dispatch/http/url.rb +344 -0
  84. data/lib/action_dispatch/journey/formatter.rb +226 -0
  85. data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
  88. data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
  89. data/lib/action_dispatch/journey/nodes/node.rb +208 -0
  90. data/lib/action_dispatch/journey/parser.rb +103 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +209 -0
  92. data/lib/action_dispatch/journey/route.rb +189 -0
  93. data/lib/action_dispatch/journey/router/utils.rb +105 -0
  94. data/lib/action_dispatch/journey/router.rb +151 -0
  95. data/lib/action_dispatch/journey/routes.rb +82 -0
  96. data/lib/action_dispatch/journey/scanner.rb +70 -0
  97. data/lib/action_dispatch/journey/visitors.rb +267 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/journey.rb +7 -0
  102. data/lib/action_dispatch/log_subscriber.rb +25 -0
  103. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  104. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  105. data/lib/action_dispatch/middleware/callbacks.rb +38 -0
  106. data/lib/action_dispatch/middleware/cookies.rb +719 -0
  107. data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
  108. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  109. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  110. data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
  111. data/lib/action_dispatch/middleware/executor.rb +32 -0
  112. data/lib/action_dispatch/middleware/flash.rb +318 -0
  113. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  114. data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
  115. data/lib/action_dispatch/middleware/reloader.rb +16 -0
  116. data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
  117. data/lib/action_dispatch/middleware/request_id.rb +50 -0
  118. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  119. data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
  120. data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
  121. data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
  122. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
  123. data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
  124. data/lib/action_dispatch/middleware/ssl.rb +180 -0
  125. data/lib/action_dispatch/middleware/stack.rb +194 -0
  126. data/lib/action_dispatch/middleware/static.rb +192 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +17 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  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 +35 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  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 +284 -0
  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 +11 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  148. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  149. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  150. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  151. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  152. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  153. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
  154. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
  155. data/lib/action_dispatch/railtie.rb +77 -0
  156. data/lib/action_dispatch/request/session.rb +283 -0
  157. data/lib/action_dispatch/request/utils.rb +109 -0
  158. data/lib/action_dispatch/routing/endpoint.rb +19 -0
  159. data/lib/action_dispatch/routing/inspector.rb +323 -0
  160. data/lib/action_dispatch/routing/mapper.rb +2372 -0
  161. data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
  162. data/lib/action_dispatch/routing/redirection.rb +218 -0
  163. data/lib/action_dispatch/routing/route_set.rb +958 -0
  164. data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
  165. data/lib/action_dispatch/routing/url_for.rb +244 -0
  166. data/lib/action_dispatch/routing.rb +262 -0
  167. data/lib/action_dispatch/system_test_case.rb +206 -0
  168. data/lib/action_dispatch/system_testing/browser.rb +75 -0
  169. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  170. data/lib/action_dispatch/system_testing/server.rb +33 -0
  171. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  172. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  173. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  174. data/lib/action_dispatch/testing/assertions/response.rb +114 -0
  175. data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
  176. data/lib/action_dispatch/testing/assertions.rb +25 -0
  177. data/lib/action_dispatch/testing/integration.rb +694 -0
  178. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  179. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  180. data/lib/action_dispatch/testing/test_process.rb +57 -0
  181. data/lib/action_dispatch/testing/test_request.rb +73 -0
  182. data/lib/action_dispatch/testing/test_response.rb +58 -0
  183. data/lib/action_dispatch.rb +147 -0
  184. data/lib/action_pack/gem_version.rb +19 -0
  185. data/lib/action_pack/version.rb +12 -0
  186. data/lib/action_pack.rb +27 -0
  187. metadata +375 -0
@@ -0,0 +1,337 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "abstract_controller/collector"
6
+
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:
11
+ #
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
23
+ #
24
+ # Here's the same action, with web-service support baked in:
25
+ #
26
+ # def index
27
+ # @people = Person.all
28
+ #
29
+ # respond_to do |format|
30
+ # format.html
31
+ # format.js
32
+ # format.xml { render xml: @people }
33
+ # end
34
+ # end
35
+ #
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.)
40
+ #
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:
44
+ #
45
+ # def create
46
+ # @company = Company.find_or_create_by(name: params[:company][:name])
47
+ # @person = @company.people.create(params[:person])
48
+ #
49
+ # redirect_to(person_list_url)
50
+ # end
51
+ #
52
+ # Here's the same action, with web-service support baked in:
53
+ #
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])
58
+ #
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
64
+ # end
65
+ #
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:
71
+ #
72
+ # <person>
73
+ # <id>...</id>
74
+ # ...
75
+ # <company>
76
+ # <id>...</id>
77
+ # <name>...</name>
78
+ # ...
79
+ # </company>
80
+ # </person>
81
+ #
82
+ # Note, however, the extra bit at the top of that action:
83
+ #
84
+ # company = params[:person].delete(:company)
85
+ # @company = Company.find_or_create_by(name: company[:name])
86
+ #
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):
90
+ #
91
+ # person[name]=...&person[company][name]=...&...
92
+ #
93
+ # And, like this (xml-encoded):
94
+ #
95
+ # <person>
96
+ # <name>...</name>
97
+ # <company>
98
+ # <name>...</name>
99
+ # </company>
100
+ # </person>
101
+ #
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.
106
+ #
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.
111
+ #
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.
114
+ #
115
+ # Mime::Type.register "image/jpeg", :jpg
116
+ #
117
+ # `respond_to` also allows you to specify a common block for different formats
118
+ # by using `any`:
119
+ #
120
+ # def index
121
+ # @people = Person.all
122
+ #
123
+ # respond_to do |format|
124
+ # format.html
125
+ # format.any(:xml, :json) { render request.format.to_sym => @people }
126
+ # end
127
+ # end
128
+ #
129
+ # In the example above, if the format is xml, it will render:
130
+ #
131
+ # render xml: @people
132
+ #
133
+ # Or if the format is json:
134
+ #
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
144
+ #
145
+ # Formats can have different variants.
146
+ #
147
+ # The request variant is a specialization of the request format, like `:tablet`,
148
+ # `:phone`, or `:desktop`.
149
+ #
150
+ # We often want to render different html/json/xml templates for phones, tablets,
151
+ # and desktop browsers. Variants make it easy.
152
+ #
153
+ # You can set the variant in a `before_action`:
154
+ #
155
+ # request.variant = :tablet if /iPad/.match?(request.user_agent)
156
+ #
157
+ # Respond to variants in the action just like you respond to formats:
158
+ #
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
165
+ # end
166
+ #
167
+ # Provide separate templates for each format and variant:
168
+ #
169
+ # app/views/projects/show.html.erb
170
+ # app/views/projects/show.html+tablet.erb
171
+ # app/views/projects/show.html+phone.erb
172
+ #
173
+ # When you're not sharing any code within the format, you can simplify defining
174
+ # variants using the inline syntax:
175
+ #
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
181
+ #
182
+ # Variants also support common `any`/`all` block that formats have.
183
+ #
184
+ # It works for both inline:
185
+ #
186
+ # respond_to do |format|
187
+ # format.html.any { render html: "any" }
188
+ # format.html.phone { render html: "phone" }
189
+ # end
190
+ #
191
+ # and block syntax:
192
+ #
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
198
+ # end
199
+ #
200
+ # You can also set an array of variants:
201
+ #
202
+ # request.variant = [:tablet, :phone]
203
+ #
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:
206
+ #
207
+ # respond_to do |format|
208
+ # format.html.none
209
+ # format.html.phone # this gets rendered
210
+ # end
211
+ def respond_to(*mimes)
212
+ raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
213
+
214
+ collector = Collector.new(mimes, request.variant)
215
+ yield collector if block_given?
216
+
217
+ if format = collector.negotiate_format(request)
218
+ if media_type && media_type != format
219
+ raise ActionController::RespondToMismatchError
220
+ end
221
+ _process_format(format)
222
+ _set_rendered_content_type(format) unless collector.any_response?
223
+ response = collector.response
224
+ response.call if response
225
+ else
226
+ raise ActionController::UnknownFormat
227
+ end
228
+ end
229
+
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
250
+ # request, with this response then being accessible by calling #response.
251
+ class Collector
252
+ include AbstractController::Collector
253
+ attr_accessor :format
254
+
255
+ def initialize(mimes, variant = nil)
256
+ @responses = {}
257
+ @variant = variant
258
+
259
+ mimes.each { |mime| @responses[Mime[mime]] = nil }
260
+ end
261
+
262
+ def any(*args, &block)
263
+ if args.any?
264
+ args.each { |type| send(type, &block) }
265
+ else
266
+ custom(Mime::ALL, &block)
267
+ end
268
+ end
269
+ alias :all :any
270
+
271
+ def custom(mime_type, &block)
272
+ mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
273
+ @responses[mime_type] ||= if block_given?
274
+ block
275
+ else
276
+ VariantCollector.new(@variant)
277
+ end
278
+ end
279
+
280
+ def any_response?
281
+ !@responses.fetch(format, false) && @responses[Mime::ALL]
282
+ end
283
+
284
+ def response
285
+ response = @responses.fetch(format, @responses[Mime::ALL])
286
+ if response.is_a?(VariantCollector) # `format.html.phone` - variant inline syntax
287
+ response.variant
288
+ elsif response.nil? || response.arity == 0 # `format.html` - just a format, call its block
289
+ response
290
+ else # `format.html{ |variant| variant.phone }` - variant block syntax
291
+ variant_collector = VariantCollector.new(@variant)
292
+ response.call(variant_collector) # call format block with variants collector
293
+ variant_collector.variant
294
+ end
295
+ end
296
+
297
+ def negotiate_format(request)
298
+ @format = request.negotiate_mime(@responses.keys)
299
+ end
300
+
301
+ class VariantCollector # :nodoc:
302
+ def initialize(variant = nil)
303
+ @variant = variant
304
+ @variants = {}
305
+ end
306
+
307
+ def any(*args, &block)
308
+ if block_given?
309
+ if args.any? && args.none? { |a| a == @variant }
310
+ args.each { |v| @variants[v] = block }
311
+ else
312
+ @variants[:any] = block
313
+ end
314
+ end
315
+ end
316
+ alias :all :any
317
+
318
+ def method_missing(name, *, &block)
319
+ @variants[name] = block if block_given?
320
+ end
321
+
322
+ def variant
323
+ if @variant.empty?
324
+ @variants[:none] || @variants[:any]
325
+ else
326
+ @variants[variant_key]
327
+ end
328
+ end
329
+
330
+ private
331
+ def variant_key
332
+ @variant.find { |variant| @variants.key?(variant) } || :any
333
+ end
334
+ end
335
+ end
336
+ end
337
+ 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