halorgium-actionpack 3.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. data/CHANGELOG +5179 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +409 -0
  4. data/lib/abstract_controller.rb +16 -0
  5. data/lib/abstract_controller/base.rb +158 -0
  6. data/lib/abstract_controller/callbacks.rb +113 -0
  7. data/lib/abstract_controller/exceptions.rb +12 -0
  8. data/lib/abstract_controller/helpers.rb +151 -0
  9. data/lib/abstract_controller/layouts.rb +250 -0
  10. data/lib/abstract_controller/localized_cache.rb +49 -0
  11. data/lib/abstract_controller/logger.rb +61 -0
  12. data/lib/abstract_controller/rendering_controller.rb +188 -0
  13. data/lib/action_controller.rb +72 -0
  14. data/lib/action_controller/base.rb +168 -0
  15. data/lib/action_controller/caching.rb +80 -0
  16. data/lib/action_controller/caching/actions.rb +163 -0
  17. data/lib/action_controller/caching/fragments.rb +116 -0
  18. data/lib/action_controller/caching/pages.rb +154 -0
  19. data/lib/action_controller/caching/sweeping.rb +97 -0
  20. data/lib/action_controller/deprecated.rb +4 -0
  21. data/lib/action_controller/deprecated/integration_test.rb +2 -0
  22. data/lib/action_controller/deprecated/performance_test.rb +1 -0
  23. data/lib/action_controller/dispatch/dispatcher.rb +57 -0
  24. data/lib/action_controller/metal.rb +129 -0
  25. data/lib/action_controller/metal/benchmarking.rb +73 -0
  26. data/lib/action_controller/metal/compatibility.rb +145 -0
  27. data/lib/action_controller/metal/conditional_get.rb +86 -0
  28. data/lib/action_controller/metal/configuration.rb +28 -0
  29. data/lib/action_controller/metal/cookies.rb +105 -0
  30. data/lib/action_controller/metal/exceptions.rb +55 -0
  31. data/lib/action_controller/metal/filter_parameter_logging.rb +77 -0
  32. data/lib/action_controller/metal/flash.rb +162 -0
  33. data/lib/action_controller/metal/head.rb +27 -0
  34. data/lib/action_controller/metal/helpers.rb +115 -0
  35. data/lib/action_controller/metal/hide_actions.rb +47 -0
  36. data/lib/action_controller/metal/http_authentication.rb +312 -0
  37. data/lib/action_controller/metal/layouts.rb +171 -0
  38. data/lib/action_controller/metal/mime_responds.rb +317 -0
  39. data/lib/action_controller/metal/rack_convenience.rb +27 -0
  40. data/lib/action_controller/metal/redirector.rb +22 -0
  41. data/lib/action_controller/metal/render_options.rb +103 -0
  42. data/lib/action_controller/metal/rendering_controller.rb +57 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +108 -0
  44. data/lib/action_controller/metal/rescuable.rb +13 -0
  45. data/lib/action_controller/metal/responder.rb +200 -0
  46. data/lib/action_controller/metal/session.rb +15 -0
  47. data/lib/action_controller/metal/session_management.rb +45 -0
  48. data/lib/action_controller/metal/streaming.rb +188 -0
  49. data/lib/action_controller/metal/testing.rb +39 -0
  50. data/lib/action_controller/metal/url_for.rb +41 -0
  51. data/lib/action_controller/metal/verification.rb +130 -0
  52. data/lib/action_controller/middleware.rb +38 -0
  53. data/lib/action_controller/notifications.rb +10 -0
  54. data/lib/action_controller/polymorphic_routes.rb +183 -0
  55. data/lib/action_controller/record_identifier.rb +91 -0
  56. data/lib/action_controller/testing/process.rb +111 -0
  57. data/lib/action_controller/testing/test_case.rb +345 -0
  58. data/lib/action_controller/translation.rb +13 -0
  59. data/lib/action_controller/url_rewriter.rb +204 -0
  60. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  61. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  62. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +176 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  65. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  66. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  67. data/lib/action_dispatch.rb +70 -0
  68. data/lib/action_dispatch/http/headers.rb +33 -0
  69. data/lib/action_dispatch/http/mime_type.rb +231 -0
  70. data/lib/action_dispatch/http/mime_types.rb +23 -0
  71. data/lib/action_dispatch/http/request.rb +539 -0
  72. data/lib/action_dispatch/http/response.rb +290 -0
  73. data/lib/action_dispatch/http/status_codes.rb +42 -0
  74. data/lib/action_dispatch/http/utils.rb +20 -0
  75. data/lib/action_dispatch/middleware/callbacks.rb +50 -0
  76. data/lib/action_dispatch/middleware/params_parser.rb +79 -0
  77. data/lib/action_dispatch/middleware/rescue.rb +26 -0
  78. data/lib/action_dispatch/middleware/session/abstract_store.rb +208 -0
  79. data/lib/action_dispatch/middleware/session/cookie_store.rb +235 -0
  80. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +47 -0
  81. data/lib/action_dispatch/middleware/show_exceptions.rb +143 -0
  82. data/lib/action_dispatch/middleware/stack.rb +116 -0
  83. data/lib/action_dispatch/middleware/static.rb +44 -0
  84. data/lib/action_dispatch/middleware/string_coercion.rb +29 -0
  85. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +24 -0
  86. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +26 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +10 -0
  88. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +29 -0
  89. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +2 -0
  90. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +10 -0
  91. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +21 -0
  92. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +2 -0
  93. data/lib/action_dispatch/routing.rb +381 -0
  94. data/lib/action_dispatch/routing/deprecated_mapper.rb +878 -0
  95. data/lib/action_dispatch/routing/mapper.rb +327 -0
  96. data/lib/action_dispatch/routing/route.rb +49 -0
  97. data/lib/action_dispatch/routing/route_set.rb +497 -0
  98. data/lib/action_dispatch/testing/assertions.rb +8 -0
  99. data/lib/action_dispatch/testing/assertions/dom.rb +35 -0
  100. data/lib/action_dispatch/testing/assertions/model.rb +19 -0
  101. data/lib/action_dispatch/testing/assertions/response.rb +145 -0
  102. data/lib/action_dispatch/testing/assertions/routing.rb +144 -0
  103. data/lib/action_dispatch/testing/assertions/selector.rb +639 -0
  104. data/lib/action_dispatch/testing/assertions/tag.rb +123 -0
  105. data/lib/action_dispatch/testing/integration.rb +504 -0
  106. data/lib/action_dispatch/testing/performance_test.rb +15 -0
  107. data/lib/action_dispatch/testing/test_request.rb +83 -0
  108. data/lib/action_dispatch/testing/test_response.rb +131 -0
  109. data/lib/action_pack.rb +24 -0
  110. data/lib/action_pack/version.rb +9 -0
  111. data/lib/action_view.rb +58 -0
  112. data/lib/action_view/base.rb +308 -0
  113. data/lib/action_view/context.rb +44 -0
  114. data/lib/action_view/erb/util.rb +48 -0
  115. data/lib/action_view/helpers.rb +62 -0
  116. data/lib/action_view/helpers/active_model_helper.rb +306 -0
  117. data/lib/action_view/helpers/ajax_helper.rb +68 -0
  118. data/lib/action_view/helpers/asset_tag_helper.rb +830 -0
  119. data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
  120. data/lib/action_view/helpers/cache_helper.rb +39 -0
  121. data/lib/action_view/helpers/capture_helper.rb +168 -0
  122. data/lib/action_view/helpers/date_helper.rb +988 -0
  123. data/lib/action_view/helpers/debug_helper.rb +38 -0
  124. data/lib/action_view/helpers/form_helper.rb +1102 -0
  125. data/lib/action_view/helpers/form_options_helper.rb +600 -0
  126. data/lib/action_view/helpers/form_tag_helper.rb +495 -0
  127. data/lib/action_view/helpers/javascript_helper.rb +208 -0
  128. data/lib/action_view/helpers/number_helper.rb +311 -0
  129. data/lib/action_view/helpers/prototype_helper.rb +1309 -0
  130. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  131. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  132. data/lib/action_view/helpers/record_tag_helper.rb +58 -0
  133. data/lib/action_view/helpers/sanitize_helper.rb +259 -0
  134. data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
  135. data/lib/action_view/helpers/tag_helper.rb +151 -0
  136. data/lib/action_view/helpers/text_helper.rb +594 -0
  137. data/lib/action_view/helpers/translation_helper.rb +39 -0
  138. data/lib/action_view/helpers/url_helper.rb +639 -0
  139. data/lib/action_view/locale/en.yml +117 -0
  140. data/lib/action_view/paths.rb +80 -0
  141. data/lib/action_view/render/partials.rb +342 -0
  142. data/lib/action_view/render/rendering.rb +134 -0
  143. data/lib/action_view/safe_buffer.rb +28 -0
  144. data/lib/action_view/template/error.rb +101 -0
  145. data/lib/action_view/template/handler.rb +36 -0
  146. data/lib/action_view/template/handlers.rb +52 -0
  147. data/lib/action_view/template/handlers/builder.rb +17 -0
  148. data/lib/action_view/template/handlers/erb.rb +53 -0
  149. data/lib/action_view/template/handlers/rjs.rb +18 -0
  150. data/lib/action_view/template/resolver.rb +165 -0
  151. data/lib/action_view/template/template.rb +131 -0
  152. data/lib/action_view/template/text.rb +38 -0
  153. data/lib/action_view/test_case.rb +163 -0
  154. metadata +236 -0
@@ -0,0 +1,290 @@
1
+ require 'digest/md5'
2
+ require 'active_support/core_ext/module/delegation'
3
+
4
+ module ActionDispatch # :nodoc:
5
+ # Represents an HTTP response generated by a controller action. One can use
6
+ # an ActionDispatch::Response object to retrieve the current state
7
+ # of the response, or customize the response. An Response object can
8
+ # either represent a "real" HTTP response (i.e. one that is meant to be sent
9
+ # back to the web browser) or a test response (i.e. one that is generated
10
+ # from integration tests). See CgiResponse and TestResponse, respectively.
11
+ #
12
+ # Response is mostly a Ruby on Rails framework implement detail, and
13
+ # should never be used directly in controllers. Controllers should use the
14
+ # methods defined in ActionController::Base instead. For example, if you want
15
+ # to set the HTTP response's content MIME type, then use
16
+ # ActionControllerBase#headers instead of Response#headers.
17
+ #
18
+ # Nevertheless, integration tests may want to inspect controller responses in
19
+ # more detail, and that's when Response can be useful for application
20
+ # developers. Integration test methods such as
21
+ # ActionDispatch::Integration::Session#get and
22
+ # ActionDispatch::Integration::Session#post return objects of type
23
+ # TestResponse (which are of course also of type Response).
24
+ #
25
+ # For example, the following demo integration "test" prints the body of the
26
+ # controller response to the console:
27
+ #
28
+ # class DemoControllerTest < ActionDispatch::IntegrationTest
29
+ # def test_print_root_path_to_console
30
+ # get('/')
31
+ # puts @response.body
32
+ # end
33
+ # end
34
+ class Response < Rack::Response
35
+ attr_accessor :request, :blank
36
+ attr_reader :cache_control
37
+
38
+ attr_writer :header, :sending_file
39
+ alias_method :headers=, :header=
40
+
41
+ def initialize
42
+ @status = 200
43
+ @header = {}
44
+ @cache_control = {}
45
+
46
+ @writer = lambda { |x| @body << x }
47
+ @block = nil
48
+ @length = 0
49
+
50
+ @body, @cookie = [], []
51
+ @sending_file = false
52
+
53
+ yield self if block_given?
54
+ end
55
+
56
+ def cache_control
57
+ @cache_control ||= {}
58
+ end
59
+
60
+ def write(str)
61
+ s = str.to_s
62
+ @writer.call s
63
+ str
64
+ end
65
+
66
+ def status=(status)
67
+ @status = status.to_i
68
+ end
69
+
70
+ # The response code of the request
71
+ def response_code
72
+ @status
73
+ end
74
+
75
+ # Returns a String to ensure compatibility with Net::HTTPResponse
76
+ def code
77
+ @status.to_s
78
+ end
79
+
80
+ def message
81
+ StatusCodes::STATUS_CODES[@status]
82
+ end
83
+ alias_method :status_message, :message
84
+
85
+ def body
86
+ str = ''
87
+ each { |part| str << part.to_s }
88
+ str
89
+ end
90
+
91
+ EMPTY = " "
92
+
93
+ def body=(body)
94
+ @blank = true if body == EMPTY
95
+ @body = body.respond_to?(:to_str) ? [body] : body
96
+ end
97
+
98
+ def body_parts
99
+ @body
100
+ end
101
+
102
+ def location
103
+ headers['Location']
104
+ end
105
+ alias_method :redirect_url, :location
106
+
107
+ def location=(url)
108
+ headers['Location'] = url
109
+ end
110
+
111
+ # Sets the HTTP response's content MIME type. For example, in the controller
112
+ # you could write this:
113
+ #
114
+ # response.content_type = "text/plain"
115
+ #
116
+ # If a character set has been defined for this response (see charset=) then
117
+ # the character set information will also be included in the content type
118
+ # information.
119
+ attr_accessor :charset, :content_type
120
+
121
+ def last_modified
122
+ if last = headers['Last-Modified']
123
+ Time.httpdate(last)
124
+ end
125
+ end
126
+
127
+ def last_modified?
128
+ headers.include?('Last-Modified')
129
+ end
130
+
131
+ def last_modified=(utc_time)
132
+ headers['Last-Modified'] = utc_time.httpdate
133
+ end
134
+
135
+ def etag
136
+ @etag
137
+ end
138
+
139
+ def etag?
140
+ @etag
141
+ end
142
+
143
+ def etag=(etag)
144
+ key = ActiveSupport::Cache.expand_cache_key(etag)
145
+ @etag = %("#{Digest::MD5.hexdigest(key)}")
146
+ end
147
+
148
+ CONTENT_TYPE = "Content-Type"
149
+
150
+ cattr_accessor(:default_charset) { "utf-8" }
151
+
152
+ def assign_default_content_type_and_charset!
153
+ return if headers[CONTENT_TYPE].present?
154
+
155
+ @content_type ||= Mime::HTML
156
+ @charset ||= self.class.default_charset
157
+
158
+ type = @content_type.to_s.dup
159
+ type << "; charset=#{@charset}" unless @sending_file
160
+
161
+ headers[CONTENT_TYPE] = type
162
+ end
163
+
164
+ def to_a
165
+ assign_default_content_type_and_charset!
166
+ handle_conditional_get!
167
+ self["Set-Cookie"] = @cookie.join("\n")
168
+ self["ETag"] = @etag if @etag
169
+ super
170
+ end
171
+
172
+ alias prepare! to_a
173
+
174
+ def each(&callback)
175
+ if @body.respond_to?(:call)
176
+ @writer = lambda { |x| callback.call(x) }
177
+ @body.call(self, self)
178
+ else
179
+ @body.each { |part| callback.call(part.to_s) }
180
+ end
181
+
182
+ @writer = callback
183
+ @block.call(self) if @block
184
+ end
185
+
186
+ def write(str)
187
+ str = str.to_s
188
+ @writer.call str
189
+ str
190
+ end
191
+
192
+ # Returns the response cookies, converted to a Hash of (name => value) pairs
193
+ #
194
+ # assert_equal 'AuthorOfNewPage', r.cookies['author']
195
+ def cookies
196
+ cookies = {}
197
+ if header = @cookie
198
+ header = header.split("\n") if header.respond_to?(:to_str)
199
+ header.each do |cookie|
200
+ if pair = cookie.split(';').first
201
+ key, value = pair.split("=").map { |v| Rack::Utils.unescape(v) }
202
+ cookies[key] = value
203
+ end
204
+ end
205
+ end
206
+ cookies
207
+ end
208
+
209
+ def set_cookie(key, value)
210
+ case value
211
+ when Hash
212
+ domain = "; domain=" + value[:domain] if value[:domain]
213
+ path = "; path=" + value[:path] if value[:path]
214
+ # According to RFC 2109, we need dashes here.
215
+ # N.B.: cgi.rb uses spaces...
216
+ expires = "; expires=" + value[:expires].clone.gmtime.
217
+ strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
218
+ secure = "; secure" if value[:secure]
219
+ httponly = "; HttpOnly" if value[:httponly]
220
+ value = value[:value]
221
+ end
222
+ value = [value] unless Array === value
223
+ cookie = Rack::Utils.escape(key) + "=" +
224
+ value.map { |v| Rack::Utils.escape v }.join("&") +
225
+ "#{domain}#{path}#{expires}#{secure}#{httponly}"
226
+
227
+ @cookie << cookie
228
+ end
229
+
230
+ def delete_cookie(key, value={})
231
+ @cookie.reject! { |cookie|
232
+ cookie =~ /\A#{Rack::Utils.escape(key)}=/
233
+ }
234
+
235
+ set_cookie(key,
236
+ {:value => '', :path => nil, :domain => nil,
237
+ :expires => Time.at(0) }.merge(value))
238
+ end
239
+
240
+ private
241
+ def handle_conditional_get!
242
+ if etag? || last_modified? || !@cache_control.empty?
243
+ set_conditional_cache_control!
244
+ elsif nonempty_ok_response?
245
+ self.etag = @body
246
+
247
+ if request && request.etag_matches?(etag)
248
+ self.status = 304
249
+ self.body = []
250
+ end
251
+
252
+ set_conditional_cache_control!
253
+ else
254
+ headers["Cache-Control"] = "no-cache"
255
+ end
256
+ end
257
+
258
+ def nonempty_ok_response?
259
+ @status == 200 && string_body?
260
+ end
261
+
262
+ def string_body?
263
+ !@blank && @body.respond_to?(:all?) && @body.all? { |part| part.is_a?(String) }
264
+ end
265
+
266
+ DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
267
+
268
+ def set_conditional_cache_control!
269
+ control = @cache_control
270
+
271
+ if control.empty?
272
+ headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
273
+ elsif @cache_control[:no_cache]
274
+ headers["Cache-Control"] = "no-cache"
275
+ else
276
+ extras = control[:extras]
277
+ max_age = control[:max_age]
278
+
279
+ options = []
280
+ options << "max-age=#{max_age}" if max_age
281
+ options << (control[:public] ? "public" : "private")
282
+ options << "must-revalidate" if control[:must_revalidate]
283
+ options.concat(extras) if extras
284
+
285
+ headers["Cache-Control"] = options.join(", ")
286
+ end
287
+
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,42 @@
1
+ require 'active_support/inflector'
2
+
3
+ module ActionDispatch
4
+ module StatusCodes #:nodoc:
5
+ STATUS_CODES = Rack::Utils::HTTP_STATUS_CODES.merge({
6
+ 102 => "Processing",
7
+ 207 => "Multi-Status",
8
+ 226 => "IM Used",
9
+ 422 => "Unprocessable Entity",
10
+ 423 => "Locked",
11
+ 424 => "Failed Dependency",
12
+ 426 => "Upgrade Required",
13
+ 507 => "Insufficient Storage",
14
+ 510 => "Not Extended"
15
+ }).freeze
16
+
17
+ # Provides a symbol-to-fixnum lookup for converting a symbol (like
18
+ # :created or :not_implemented) into its corresponding HTTP status
19
+ # code (like 200 or 501).
20
+ SYMBOL_TO_STATUS_CODE = STATUS_CODES.inject({}) { |hash, (code, message)|
21
+ hash[ActiveSupport::Inflector.underscore(message.gsub(/ /, "")).to_sym] = code
22
+ hash
23
+ }.freeze
24
+
25
+ private
26
+ # Given a status parameter, determine whether it needs to be converted
27
+ # to a string. If it is a fixnum, use the STATUS_CODES hash to lookup
28
+ # the default message. If it is a symbol, use the SYMBOL_TO_STATUS_CODE
29
+ # hash to convert it.
30
+ def interpret_status(status)
31
+ case status
32
+ when Fixnum then
33
+ "#{status} #{STATUS_CODES[status]}".strip
34
+ when Symbol then
35
+ interpret_status(SYMBOL_TO_STATUS_CODE[status] ||
36
+ "500 Unknown Status #{status.inspect}")
37
+ else
38
+ status.to_s
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,20 @@
1
+ module ActionDispatch
2
+ module Utils
3
+ # TODO: Pull this into rack core
4
+ # http://github.com/halorgium/rack/commit/feaf071c1de743fbd10bc316830180a9af607278
5
+ def parse_config(config)
6
+ if config =~ /\.ru$/
7
+ cfgfile = ::File.read(config)
8
+ if cfgfile[/^#\\(.*)/]
9
+ opts.parse! $1.split(/\s+/)
10
+ end
11
+ inner_app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app",
12
+ nil, config
13
+ else
14
+ require config
15
+ inner_app = Object.const_get(::File.basename(config, '.rb').capitalize)
16
+ end
17
+ end
18
+ module_function :parse_config
19
+ end
20
+ end
@@ -0,0 +1,50 @@
1
+ module ActionDispatch
2
+ class Callbacks
3
+ include ActiveSupport::Callbacks
4
+
5
+ define_callbacks :call, :terminator => "result == false", :rescuable => true
6
+ define_callbacks :prepare, :scope => :name
7
+
8
+ # Add a preparation callback. Preparation callbacks are run before every
9
+ # request in development mode, and before the first request in production mode.
10
+ #
11
+ # If a symbol with a block is given, the symbol is used as an identifier.
12
+ # That allows to_prepare to be called again with the same identifier to
13
+ # replace the existing callback. Passing an identifier is a suggested
14
+ # practice if the code adding a preparation block may be reloaded.
15
+ def self.to_prepare(*args, &block)
16
+ if args.first.is_a?(Symbol) && block_given?
17
+ define_method :"__#{args.first}", &block
18
+ set_callback(:prepare, :"__#{args.first}")
19
+ else
20
+ set_callback(:prepare, *args, &block)
21
+ end
22
+ end
23
+
24
+ def self.before(*args, &block)
25
+ set_callback(:call, :before, *args, &block)
26
+ end
27
+
28
+ def self.after(*args, &block)
29
+ set_callback(:call, :after, *args, &block)
30
+ end
31
+
32
+ class << self
33
+ # DEPRECATED
34
+ alias_method :before_dispatch, :before
35
+ alias_method :after_dispatch, :after
36
+ end
37
+
38
+ def initialize(app, prepare_each_request = false)
39
+ @app, @prepare_each_request = app, prepare_each_request
40
+ run_callbacks(:prepare)
41
+ end
42
+
43
+ def call(env)
44
+ run_callbacks(:call) do
45
+ run_callbacks(:prepare) if @prepare_each_request
46
+ @app.call(env)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,79 @@
1
+ require 'active_support/json'
2
+
3
+ module ActionDispatch
4
+ class ParamsParser
5
+ DEFAULT_PARSERS = {
6
+ Mime::XML => :xml_simple,
7
+ Mime::JSON => :json
8
+ }
9
+
10
+ def initialize(app, parsers = {})
11
+ @app, @parsers = app, DEFAULT_PARSERS.merge(parsers)
12
+ end
13
+
14
+ def call(env)
15
+ if params = parse_formatted_parameters(env)
16
+ env["action_dispatch.request.request_parameters"] = params
17
+ end
18
+
19
+ @app.call(env)
20
+ end
21
+
22
+ private
23
+ def parse_formatted_parameters(env)
24
+ request = Request.new(env)
25
+
26
+ return false if request.content_length.zero?
27
+
28
+ mime_type = content_type_from_legacy_post_data_format_header(env) || request.content_type
29
+ strategy = @parsers[mime_type]
30
+
31
+ return false unless strategy
32
+
33
+ case strategy
34
+ when Proc
35
+ strategy.call(request.raw_post)
36
+ when :xml_simple, :xml_node
37
+ request.body.size == 0 ? {} : Hash.from_xml(request.body).with_indifferent_access
38
+ when :yaml
39
+ YAML.load(request.body)
40
+ when :json
41
+ if request.body.size == 0
42
+ {}
43
+ else
44
+ data = ActiveSupport::JSON.decode(request.body)
45
+ data = {:_json => data} unless data.is_a?(Hash)
46
+ data.with_indifferent_access
47
+ end
48
+ else
49
+ false
50
+ end
51
+ rescue Exception => e # YAML, XML or Ruby code block errors
52
+ logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
53
+
54
+ raise
55
+ { "body" => request.raw_post,
56
+ "content_type" => request.content_type,
57
+ "content_length" => request.content_length,
58
+ "exception" => "#{e.message} (#{e.class})",
59
+ "backtrace" => e.backtrace }
60
+ end
61
+
62
+ def content_type_from_legacy_post_data_format_header(env)
63
+ if x_post_format = env['HTTP_X_POST_DATA_FORMAT']
64
+ case x_post_format.to_s.downcase
65
+ when 'yaml'
66
+ return Mime::YAML
67
+ when 'xml'
68
+ return Mime::XML
69
+ end
70
+ end
71
+
72
+ nil
73
+ end
74
+
75
+ def logger
76
+ defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
77
+ end
78
+ end
79
+ end