actionpack 4.2.11.3 → 5.0.7.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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +890 -384
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller/base.rb +28 -38
  6. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
  7. data/lib/abstract_controller/caching.rb +62 -0
  8. data/lib/abstract_controller/callbacks.rb +54 -19
  9. data/lib/abstract_controller/collector.rb +4 -9
  10. data/lib/abstract_controller/error.rb +4 -0
  11. data/lib/abstract_controller/helpers.rb +4 -3
  12. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  13. data/lib/abstract_controller/rendering.rb +28 -18
  14. data/lib/abstract_controller/translation.rb +8 -7
  15. data/lib/abstract_controller.rb +6 -2
  16. data/lib/action_controller/api/api_rendering.rb +14 -0
  17. data/lib/action_controller/api.rb +147 -0
  18. data/lib/action_controller/base.rb +14 -11
  19. data/lib/action_controller/caching.rb +13 -58
  20. data/lib/action_controller/form_builder.rb +48 -0
  21. data/lib/action_controller/log_subscriber.rb +3 -10
  22. data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
  23. data/lib/action_controller/metal/conditional_get.rb +106 -34
  24. data/lib/action_controller/metal/cookies.rb +1 -3
  25. data/lib/action_controller/metal/data_streaming.rb +14 -34
  26. data/lib/action_controller/metal/etag_with_template_digest.rb +8 -2
  27. data/lib/action_controller/metal/exceptions.rb +11 -6
  28. data/lib/action_controller/metal/force_ssl.rb +11 -11
  29. data/lib/action_controller/metal/head.rb +14 -8
  30. data/lib/action_controller/metal/helpers.rb +15 -6
  31. data/lib/action_controller/metal/http_authentication.rb +44 -35
  32. data/lib/action_controller/metal/implicit_render.rb +61 -6
  33. data/lib/action_controller/metal/instrumentation.rb +5 -5
  34. data/lib/action_controller/metal/live.rb +71 -88
  35. data/lib/action_controller/metal/mime_responds.rb +27 -42
  36. data/lib/action_controller/metal/params_wrapper.rb +9 -9
  37. data/lib/action_controller/metal/redirecting.rb +32 -9
  38. data/lib/action_controller/metal/renderers.rb +83 -40
  39. data/lib/action_controller/metal/rendering.rb +38 -6
  40. data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
  41. data/lib/action_controller/metal/rescue.rb +3 -12
  42. data/lib/action_controller/metal/streaming.rb +4 -4
  43. data/lib/action_controller/metal/strong_parameters.rb +527 -134
  44. data/lib/action_controller/metal/testing.rb +1 -12
  45. data/lib/action_controller/metal/url_for.rb +12 -5
  46. data/lib/action_controller/metal.rb +88 -63
  47. data/lib/action_controller/railtie.rb +11 -7
  48. data/lib/action_controller/renderer.rb +113 -0
  49. data/lib/action_controller/template_assertions.rb +9 -0
  50. data/lib/action_controller/test_case.rb +311 -374
  51. data/lib/action_controller.rb +12 -9
  52. data/lib/action_dispatch/http/cache.rb +73 -34
  53. data/lib/action_dispatch/http/filter_parameters.rb +16 -12
  54. data/lib/action_dispatch/http/filter_redirect.rb +7 -8
  55. data/lib/action_dispatch/http/headers.rb +45 -14
  56. data/lib/action_dispatch/http/mime_negotiation.rb +42 -23
  57. data/lib/action_dispatch/http/mime_type.rb +126 -90
  58. data/lib/action_dispatch/http/mime_types.rb +3 -4
  59. data/lib/action_dispatch/http/parameter_filter.rb +19 -9
  60. data/lib/action_dispatch/http/parameters.rb +70 -40
  61. data/lib/action_dispatch/http/request.rb +144 -89
  62. data/lib/action_dispatch/http/response.rb +215 -102
  63. data/lib/action_dispatch/http/upload.rb +6 -2
  64. data/lib/action_dispatch/http/url.rb +117 -8
  65. data/lib/action_dispatch/journey/formatter.rb +47 -30
  66. data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
  67. data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
  68. data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
  69. data/lib/action_dispatch/journey/nodes/node.rb +14 -4
  70. data/lib/action_dispatch/journey/parser.rb +2 -0
  71. data/lib/action_dispatch/journey/parser_extras.rb +8 -2
  72. data/lib/action_dispatch/journey/path/pattern.rb +38 -42
  73. data/lib/action_dispatch/journey/route.rb +88 -26
  74. data/lib/action_dispatch/journey/router/utils.rb +5 -5
  75. data/lib/action_dispatch/journey/router.rb +8 -10
  76. data/lib/action_dispatch/journey/routes.rb +14 -15
  77. data/lib/action_dispatch/journey/visitors.rb +89 -44
  78. data/lib/action_dispatch/middleware/callbacks.rb +10 -1
  79. data/lib/action_dispatch/middleware/cookies.rb +188 -134
  80. data/lib/action_dispatch/middleware/debug_exceptions.rb +128 -49
  81. data/lib/action_dispatch/middleware/debug_locks.rb +122 -0
  82. data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
  83. data/lib/action_dispatch/middleware/executor.rb +19 -0
  84. data/lib/action_dispatch/middleware/flash.rb +66 -45
  85. data/lib/action_dispatch/middleware/params_parser.rb +32 -46
  86. data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
  87. data/lib/action_dispatch/middleware/reloader.rb +14 -58
  88. data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
  89. data/lib/action_dispatch/middleware/request_id.rb +11 -6
  90. data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
  91. data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
  92. data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
  93. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
  94. data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
  95. data/lib/action_dispatch/middleware/ssl.rb +124 -36
  96. data/lib/action_dispatch/middleware/stack.rb +44 -40
  97. data/lib/action_dispatch/middleware/static.rb +51 -35
  98. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  99. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  101. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  102. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  103. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
  104. data/lib/action_dispatch/railtie.rb +2 -2
  105. data/lib/action_dispatch/request/session.rb +69 -33
  106. data/lib/action_dispatch/request/utils.rb +51 -19
  107. data/lib/action_dispatch/routing/inspector.rb +32 -43
  108. data/lib/action_dispatch/routing/mapper.rb +515 -348
  109. data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
  110. data/lib/action_dispatch/routing/redirection.rb +5 -4
  111. data/lib/action_dispatch/routing/route_set.rb +148 -240
  112. data/lib/action_dispatch/routing/url_for.rb +27 -10
  113. data/lib/action_dispatch/routing.rb +17 -13
  114. data/lib/action_dispatch/testing/assertion_response.rb +45 -0
  115. data/lib/action_dispatch/testing/assertions/response.rb +38 -20
  116. data/lib/action_dispatch/testing/assertions/routing.rb +16 -12
  117. data/lib/action_dispatch/testing/assertions.rb +1 -1
  118. data/lib/action_dispatch/testing/integration.rb +377 -149
  119. data/lib/action_dispatch/testing/request_encoder.rb +53 -0
  120. data/lib/action_dispatch/testing/test_process.rb +24 -20
  121. data/lib/action_dispatch/testing/test_request.rb +22 -31
  122. data/lib/action_dispatch/testing/test_response.rb +12 -4
  123. data/lib/action_dispatch.rb +4 -1
  124. data/lib/action_pack/gem_version.rb +4 -4
  125. data/lib/action_pack.rb +1 -1
  126. metadata +32 -34
  127. data/lib/action_controller/metal/hide_actions.rb +0 -40
  128. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  129. data/lib/action_controller/middleware.rb +0 -39
  130. data/lib/action_controller/model_naming.rb +0 -12
  131. data/lib/action_dispatch/journey/backwards.rb +0 -5
  132. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  133. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  134. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  135. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
  136. /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
@@ -1,23 +1,33 @@
1
- require 'set'
1
+ # -*- frozen-string-literal: true -*-
2
+
2
3
  require 'singleton'
3
4
  require 'active_support/core_ext/module/attribute_accessors'
4
5
  require 'active_support/core_ext/string/starts_ends_with'
5
6
 
6
7
  module Mime
7
- class Mimes < Array
8
- def symbols
9
- @symbols ||= map { |m| m.to_sym }
8
+ class Mimes
9
+ include Enumerable
10
+
11
+ def initialize
12
+ @mimes = []
13
+ @symbols = nil
10
14
  end
11
15
 
12
- %w(<< concat shift unshift push pop []= clear compact! collect!
13
- delete delete_at delete_if flatten! map! insert reject! reverse!
14
- replace slice! sort! uniq!).each do |method|
15
- module_eval <<-CODE, __FILE__, __LINE__ + 1
16
- def #{method}(*)
17
- @symbols = nil
18
- super
19
- end
20
- CODE
16
+ def each
17
+ @mimes.each { |x| yield x }
18
+ end
19
+
20
+ def <<(type)
21
+ @mimes << type
22
+ @symbols = nil
23
+ end
24
+
25
+ def delete_if
26
+ @mimes.delete_if { |x| yield x }.tap { @symbols = nil }
27
+ end
28
+
29
+ def symbols
30
+ @symbols ||= map(&:to_sym)
21
31
  end
22
32
  end
23
33
 
@@ -35,6 +45,32 @@ module Mime
35
45
  return type if type.is_a?(Type)
36
46
  EXTENSION_LOOKUP.fetch(type.to_s) { |k| yield k }
37
47
  end
48
+
49
+ def const_missing(sym)
50
+ ext = sym.downcase
51
+ if Mime[ext]
52
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
53
+ Accessing mime types via constants is deprecated.
54
+ Please change `Mime::#{sym}` to `Mime[:#{ext}]`.
55
+ MSG
56
+ Mime[ext]
57
+ else
58
+ super
59
+ end
60
+ end
61
+
62
+ def const_defined?(sym, inherit = true)
63
+ ext = sym.downcase
64
+ if Mime[ext]
65
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
66
+ Accessing mime types via constants is deprecated.
67
+ Please change `Mime.const_defined?(#{sym})` to `Mime[:#{ext}]`.
68
+ MSG
69
+ true
70
+ else
71
+ super
72
+ end
73
+ end
38
74
  end
39
75
 
40
76
  # Encapsulates the notion of a mime type. Can be used at render time, for example, with:
@@ -45,15 +81,12 @@ module Mime
45
81
  #
46
82
  # respond_to do |format|
47
83
  # format.html
48
- # format.ics { render text: @post.to_ics, mime_type: Mime::Type["text/calendar"] }
84
+ # format.ics { render body: @post.to_ics, mime_type: Mime::Type.lookup("text/calendar") }
49
85
  # format.xml { render xml: @post }
50
86
  # end
51
87
  # end
52
88
  # end
53
89
  class Type
54
- @@html_types = Set.new [:html, :all]
55
- cattr_reader :html_types
56
-
57
90
  attr_reader :symbol
58
91
 
59
92
  @register_callbacks = []
@@ -66,7 +99,7 @@ module Mime
66
99
  def initialize(index, name, q = nil)
67
100
  @index = index
68
101
  @name = name
69
- q ||= 0.0 if @name == Mime::ALL.to_s # default wildcard match to end of list
102
+ q ||= 0.0 if @name == '*/*'.freeze # default wildcard match to end of list
70
103
  @q = ((q || 1.0).to_f * 100).to_i
71
104
  end
72
105
 
@@ -75,70 +108,58 @@ module Mime
75
108
  result = @index <=> item.index if result == 0
76
109
  result
77
110
  end
78
-
79
- def ==(item)
80
- @name == item.to_s
81
- end
82
111
  end
83
112
 
84
- class AcceptList < Array #:nodoc:
85
- def assort!
86
- sort!
113
+ class AcceptList #:nodoc:
114
+ def self.sort!(list)
115
+ list.sort!
116
+
117
+ text_xml_idx = find_item_by_name list, 'text/xml'
118
+ app_xml_idx = find_item_by_name list, Mime[:xml].to_s
87
119
 
88
120
  # Take care of the broken text/xml entry by renaming or deleting it
89
121
  if text_xml_idx && app_xml_idx
122
+ app_xml = list[app_xml_idx]
123
+ text_xml = list[text_xml_idx]
124
+
90
125
  app_xml.q = [text_xml.q, app_xml.q].max # set the q value to the max of the two
91
- exchange_xml_items if app_xml_idx > text_xml_idx # make sure app_xml is ahead of text_xml in the list
92
- delete_at(text_xml_idx) # delete text_xml from the list
126
+ if app_xml_idx > text_xml_idx # make sure app_xml is ahead of text_xml in the list
127
+ list[app_xml_idx], list[text_xml_idx] = text_xml, app_xml
128
+ app_xml_idx, text_xml_idx = text_xml_idx, app_xml_idx
129
+ end
130
+ list.delete_at(text_xml_idx) # delete text_xml from the list
93
131
  elsif text_xml_idx
94
- text_xml.name = Mime::XML.to_s
132
+ list[text_xml_idx].name = Mime[:xml].to_s
95
133
  end
96
134
 
97
135
  # Look for more specific XML-based types and sort them ahead of app/xml
98
136
  if app_xml_idx
137
+ app_xml = list[app_xml_idx]
99
138
  idx = app_xml_idx
100
139
 
101
- while idx < length
102
- type = self[idx]
140
+ while idx < list.length
141
+ type = list[idx]
103
142
  break if type.q < app_xml.q
104
143
 
105
144
  if type.name.ends_with? '+xml'
106
- self[app_xml_idx], self[idx] = self[idx], app_xml
107
- @app_xml_idx = idx
145
+ list[app_xml_idx], list[idx] = list[idx], app_xml
146
+ app_xml_idx = idx
108
147
  end
109
148
  idx += 1
110
149
  end
111
150
  end
112
151
 
113
- map! { |i| Mime::Type.lookup(i.name) }.uniq!
114
- to_a
152
+ list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
153
+ list
115
154
  end
116
155
 
117
- private
118
- def text_xml_idx
119
- @text_xml_idx ||= index('text/xml')
120
- end
121
-
122
- def app_xml_idx
123
- @app_xml_idx ||= index(Mime::XML.to_s)
124
- end
125
-
126
- def text_xml
127
- self[text_xml_idx]
128
- end
129
-
130
- def app_xml
131
- self[app_xml_idx]
132
- end
133
-
134
- def exchange_xml_items
135
- self[app_xml_idx], self[text_xml_idx] = text_xml, app_xml
136
- @app_xml_idx, @text_xml_idx = text_xml_idx, app_xml_idx
137
- end
156
+ def self.find_item_by_name(array, name)
157
+ array.index { |item| item.name == name }
158
+ end
138
159
  end
139
160
 
140
161
  class << self
141
- TRAILING_STAR_REGEXP = /(text|application)\/\*/
162
+ TRAILING_STAR_REGEXP = /^(text|application)\/\*/
142
163
  PARAMETER_SEPARATOR_REGEXP = /;\s*\w+="?\w+"?/
143
164
 
144
165
  def register_callback(&block)
@@ -160,17 +181,17 @@ module Mime
160
181
  end
161
182
 
162
183
  def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
163
- Mime.const_set(symbol.upcase, Type.new(string, symbol, mime_type_synonyms))
184
+ new_mime = Type.new(string, symbol, mime_type_synonyms)
164
185
 
165
- new_mime = Mime.const_get(symbol.upcase)
166
186
  SET << new_mime
167
187
 
168
- ([string] + mime_type_synonyms).each { |str| LOOKUP[str] = SET.last } unless skip_lookup
169
- ([symbol] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext.to_s] = SET.last }
188
+ ([string] + mime_type_synonyms).each { |str| LOOKUP[str] = new_mime } unless skip_lookup
189
+ ([symbol] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext.to_s] = new_mime }
170
190
 
171
191
  @register_callbacks.each do |callback|
172
192
  callback.call(new_mime)
173
193
  end
194
+ new_mime
174
195
  end
175
196
 
176
197
  def parse(accept_header)
@@ -178,21 +199,22 @@ module Mime
178
199
  accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first
179
200
  parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)].compact
180
201
  else
181
- list, index = AcceptList.new, 0
202
+ list, index = [], 0
182
203
  accept_header.split(',').each do |header|
183
204
  params, q = header.split(PARAMETER_SEPARATOR_REGEXP)
184
- if params.present?
185
- params.strip!
186
205
 
187
- params = parse_trailing_star(params) || [params]
206
+ next unless params
207
+ params.strip!
208
+ next if params.empty?
188
209
 
189
- params.each do |m|
190
- list << AcceptItem.new(index, m.to_s, q)
191
- index += 1
192
- end
210
+ params = parse_trailing_star(params) || [params]
211
+
212
+ params.each do |m|
213
+ list << AcceptItem.new(index, m.to_s, q)
214
+ index += 1
193
215
  end
194
216
  end
195
- list.assort!
217
+ AcceptList.sort! list
196
218
  end
197
219
  end
198
220
 
@@ -200,28 +222,27 @@ module Mime
200
222
  parse_data_with_trailing_star($1) if accept_header =~ TRAILING_STAR_REGEXP
201
223
  end
202
224
 
203
- # For an input of <tt>'text'</tt>, returns <tt>[Mime::JSON, Mime::XML, Mime::ICS,
204
- # Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT]</tt>.
225
+ # For an input of <tt>'text'</tt>, returns <tt>[Mime[:json], Mime[:xml], Mime[:ics],
226
+ # Mime[:html], Mime[:css], Mime[:csv], Mime[:js], Mime[:yaml], Mime[:text]</tt>.
205
227
  #
206
- # For an input of <tt>'application'</tt>, returns <tt>[Mime::HTML, Mime::JS,
207
- # Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM]</tt>.
208
- def parse_data_with_trailing_star(input)
209
- Mime::SET.select { |m| m =~ input }
228
+ # For an input of <tt>'application'</tt>, returns <tt>[Mime[:html], Mime[:js],
229
+ # Mime[:xml], Mime[:yaml], Mime[:atom], Mime[:json], Mime[:rss], Mime[:url_encoded_form]</tt>.
230
+ def parse_data_with_trailing_star(type)
231
+ Mime::SET.select { |m| m =~ type }
210
232
  end
211
233
 
212
234
  # This method is opposite of register method.
213
235
  #
214
- # Usage:
236
+ # To unregister a MIME type:
215
237
  #
216
238
  # Mime::Type.unregister(:mobile)
217
239
  def unregister(symbol)
218
- symbol = symbol.upcase
219
- mime = Mime.const_get(symbol)
220
- Mime.instance_eval { remove_const(symbol) }
221
-
222
- SET.delete_if { |v| v.eql?(mime) }
223
- LOOKUP.delete_if { |_,v| v.eql?(mime) }
224
- EXTENSION_LOOKUP.delete_if { |_,v| v.eql?(mime) }
240
+ symbol = symbol.downcase
241
+ if mime = Mime[symbol]
242
+ SET.delete_if { |v| v.eql?(mime) }
243
+ LOOKUP.delete_if { |_, v| v.eql?(mime) }
244
+ EXTENSION_LOOKUP.delete_if { |_, v| v.eql?(mime) }
245
+ end
225
246
  end
226
247
  end
227
248
 
@@ -246,7 +267,7 @@ module Mime
246
267
  end
247
268
 
248
269
  def ref
249
- to_sym || to_s
270
+ symbol || to_s
250
271
  end
251
272
 
252
273
  def ===(list)
@@ -258,7 +279,7 @@ module Mime
258
279
  end
259
280
 
260
281
  def ==(mime_type)
261
- return false if mime_type.blank?
282
+ return false unless mime_type
262
283
  (@synonyms + [ self ]).any? do |synonym|
263
284
  synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
264
285
  end
@@ -272,17 +293,16 @@ module Mime
272
293
  end
273
294
 
274
295
  def =~(mime_type)
275
- return false if mime_type.blank?
296
+ return false unless mime_type
276
297
  regexp = Regexp.new(Regexp.quote(mime_type.to_s))
277
- (@synonyms + [ self ]).any? do |synonym|
278
- synonym.to_s =~ regexp
279
- end
298
+ @synonyms.any? { |synonym| synonym.to_s =~ regexp } || @string =~ regexp
280
299
  end
281
300
 
282
301
  def html?
283
- @@html_types.include?(to_sym) || @string =~ /html/
302
+ symbol == :html || @string =~ /html/
284
303
  end
285
304
 
305
+ def all?; false; end
286
306
 
287
307
  protected
288
308
 
@@ -306,6 +326,22 @@ module Mime
306
326
  end
307
327
  end
308
328
 
329
+ class AllType < Type
330
+ include Singleton
331
+
332
+ def initialize
333
+ super '*/*', :all
334
+ end
335
+
336
+ def all?; true; end
337
+ def html?; true; end
338
+ end
339
+
340
+ # ALL isn't a real MIME type, so we don't register it for lookup with the
341
+ # other concrete types. It's a wildcard match that we use for `respond_to`
342
+ # negotiation internals.
343
+ ALL = AllType.instance
344
+
309
345
  class NullType
310
346
  include Singleton
311
347
 
@@ -14,13 +14,14 @@ Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe pjpeg)
14
14
  Mime::Type.register "image/gif", :gif, [], %w(gif)
15
15
  Mime::Type.register "image/bmp", :bmp, [], %w(bmp)
16
16
  Mime::Type.register "image/tiff", :tiff, [], %w(tif tiff)
17
+ Mime::Type.register "image/svg+xml", :svg
17
18
 
18
19
  Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe)
19
20
 
20
21
  Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml )
21
22
  Mime::Type.register "application/rss+xml", :rss
22
23
  Mime::Type.register "application/atom+xml", :atom
23
- Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml )
24
+ Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml ), %w(yml yaml)
24
25
 
25
26
  Mime::Type.register "multipart/form-data", :multipart_form
26
27
  Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
@@ -31,6 +32,4 @@ Mime::Type.register "application/json", :json, %w( text/x-json application/jsonr
31
32
 
32
33
  Mime::Type.register "application/pdf", :pdf, [], %w(pdf)
33
34
  Mime::Type.register "application/zip", :zip, [], %w(zip)
34
-
35
- # Create Mime::ALL but do not add it to the SET.
36
- Mime::ALL = Mime::Type.new("*/*", :all, [])
35
+ Mime::Type.register "application/gzip", :gzip, %w(application/x-gzip), %w(gz)
@@ -30,36 +30,46 @@ module ActionDispatch
30
30
  when Regexp
31
31
  regexps << item
32
32
  else
33
- strings << item.to_s
33
+ strings << Regexp.escape(item.to_s)
34
34
  end
35
35
  end
36
36
 
37
- regexps << Regexp.new(strings.join('|'), true) unless strings.empty?
38
- new regexps, blocks
37
+ deep_regexps, regexps = regexps.partition { |r| r.to_s.include?("\\.".freeze) }
38
+ deep_strings, strings = strings.partition { |s| s.include?("\\.".freeze) }
39
+
40
+ regexps << Regexp.new(strings.join('|'.freeze), true) unless strings.empty?
41
+ deep_regexps << Regexp.new(deep_strings.join('|'.freeze), true) unless deep_strings.empty?
42
+
43
+ new regexps, deep_regexps, blocks
39
44
  end
40
45
 
41
- attr_reader :regexps, :blocks
46
+ attr_reader :regexps, :deep_regexps, :blocks
42
47
 
43
- def initialize(regexps, blocks)
48
+ def initialize(regexps, deep_regexps, blocks)
44
49
  @regexps = regexps
50
+ @deep_regexps = deep_regexps.any? ? deep_regexps : nil
45
51
  @blocks = blocks
46
52
  end
47
53
 
48
- def call(original_params)
49
- filtered_params = {}
54
+ def call(original_params, parents = [])
55
+ filtered_params = original_params.class.new
50
56
 
51
57
  original_params.each do |key, value|
58
+ parents.push(key) if deep_regexps
52
59
  if regexps.any? { |r| key =~ r }
53
60
  value = FILTERED
61
+ elsif deep_regexps && (joined = parents.join('.')) && deep_regexps.any? { |r| joined =~ r }
62
+ value = FILTERED
54
63
  elsif value.is_a?(Hash)
55
- value = call(value)
64
+ value = call(value, parents)
56
65
  elsif value.is_a?(Array)
57
- value = value.map { |v| v.is_a?(Hash) ? call(v) : v }
66
+ value = value.map { |v| v.is_a?(Hash) ? call(v, parents) : v }
58
67
  elsif blocks.any?
59
68
  key = key.dup if key.duplicable?
60
69
  value = value.dup if value.duplicable?
61
70
  blocks.each { |b| b.call(key, value) }
62
71
  end
72
+ parents.pop if deep_regexps
63
73
 
64
74
  filtered_params[key] = value
65
75
  end
@@ -1,35 +1,67 @@
1
- require 'active_support/core_ext/hash/keys'
2
- require 'active_support/core_ext/hash/indifferent_access'
3
- require 'active_support/deprecation'
4
-
5
1
  module ActionDispatch
6
2
  module Http
7
3
  module Parameters
4
+ extend ActiveSupport::Concern
5
+
8
6
  PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
9
7
 
8
+ DEFAULT_PARSERS = {
9
+ Mime[:json].symbol => -> (raw_post) {
10
+ data = ActiveSupport::JSON.decode(raw_post)
11
+ data.is_a?(Hash) ? data : {:_json => data}
12
+ }
13
+ }
14
+
15
+ included do
16
+ class << self
17
+ # Returns the parameter parsers.
18
+ attr_reader :parameter_parsers
19
+ end
20
+
21
+ self.parameter_parsers = DEFAULT_PARSERS
22
+ end
23
+
24
+ module ClassMethods
25
+ # Configure the parameter parser for a give mime type.
26
+ #
27
+ # It accepts a hash where the key is the symbol of the mime type
28
+ # and the value is a proc.
29
+ #
30
+ # original_parsers = ActionDispatch::Request.parameter_parsers
31
+ # xml_parser = -> (raw_post) { Hash.from_xml(raw_post) || {} }
32
+ # new_parsers = original_parsers.merge(xml: xml_parser)
33
+ # ActionDispatch::Request.parameter_parsers = new_parsers
34
+ def parameter_parsers=(parsers)
35
+ @parameter_parsers = parsers.transform_keys { |key| key.respond_to?(:symbol) ? key.symbol : key }
36
+ end
37
+ end
38
+
10
39
  # Returns both GET and POST \parameters in a single hash.
11
40
  def parameters
12
- @env["action_dispatch.request.parameters"] ||= begin
13
- params = begin
14
- request_parameters.merge(query_parameters)
15
- rescue EOFError
16
- query_parameters.dup
17
- end
18
- params.merge!(path_parameters)
19
- end
41
+ params = get_header("action_dispatch.request.parameters")
42
+ return params if params
43
+
44
+ params = begin
45
+ request_parameters.merge(query_parameters)
46
+ rescue EOFError
47
+ query_parameters.dup
48
+ end
49
+ params.merge!(path_parameters)
50
+ set_header("action_dispatch.request.parameters", params)
51
+ params
20
52
  end
21
53
  alias :params :parameters
22
54
 
23
55
  def path_parameters=(parameters) #:nodoc:
24
- @env.delete('action_dispatch.request.parameters')
25
- @env[PARAMETERS_KEY] = parameters
26
- end
56
+ delete_header('action_dispatch.request.parameters')
27
57
 
28
- def symbolized_path_parameters
29
- ActiveSupport::Deprecation.warn(
30
- '`symbolized_path_parameters` is deprecated. Please use `path_parameters`.'
31
- )
32
- path_parameters
58
+ # If any of the path parameters has an invalid encoding then
59
+ # raise since it's likely to trigger errors further on.
60
+ Request::Utils.check_param_encoding(parameters)
61
+
62
+ set_header PARAMETERS_KEY, parameters
63
+ rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
64
+ raise ActionController::BadRequest.new("Invalid path parameters: #{e.message}")
33
65
  end
34
66
 
35
67
  # Returns a hash with the \parameters used to form the \path of the request.
@@ -37,31 +69,29 @@ module ActionDispatch
37
69
  #
38
70
  # {'action' => 'my_action', 'controller' => 'my_controller'}
39
71
  def path_parameters
40
- @env[PARAMETERS_KEY] ||= {}
72
+ get_header(PARAMETERS_KEY) || set_header(PARAMETERS_KEY, {})
41
73
  end
42
74
 
43
- private
75
+ private
44
76
 
45
- # Convert nested Hash to HashWithIndifferentAccess.
46
- #
47
- def normalize_encode_params(params)
48
- case params
49
- when Hash
50
- if params.has_key?(:tempfile)
51
- UploadedFile.new(params)
52
- else
53
- params.each_with_object({}) do |(key, val), new_hash|
54
- new_hash[key] = if val.is_a?(Array)
55
- val.map! { |el| normalize_encode_params(el) }
56
- else
57
- normalize_encode_params(val)
58
- end
59
- end.with_indifferent_access
60
- end
61
- else
62
- params
77
+ def parse_formatted_parameters(parsers)
78
+ return yield if content_length.zero? || content_mime_type.nil?
79
+
80
+ strategy = parsers.fetch(content_mime_type.symbol) { return yield }
81
+
82
+ begin
83
+ strategy.call(raw_post)
84
+ rescue # JSON or Ruby code block errors
85
+ my_logger = logger || ActiveSupport::Logger.new($stderr)
86
+ my_logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{raw_post}"
87
+
88
+ raise ParamsParser::ParseError
63
89
  end
64
90
  end
91
+
92
+ def params_parsers
93
+ ActionDispatch::Request.parameter_parsers
94
+ end
65
95
  end
66
96
  end
67
97
  end