actionpack 2.2.3 → 2.3.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 (264) hide show
  1. data/CHANGELOG +433 -375
  2. data/MIT-LICENSE +1 -1
  3. data/README +21 -75
  4. data/Rakefile +1 -1
  5. data/lib/action_controller.rb +80 -43
  6. data/lib/action_controller/assertions/model_assertions.rb +1 -0
  7. data/lib/action_controller/assertions/response_assertions.rb +43 -16
  8. data/lib/action_controller/assertions/routing_assertions.rb +1 -1
  9. data/lib/action_controller/assertions/selector_assertions.rb +17 -12
  10. data/lib/action_controller/assertions/tag_assertions.rb +1 -4
  11. data/lib/action_controller/base.rb +153 -82
  12. data/lib/action_controller/benchmarking.rb +9 -9
  13. data/lib/action_controller/caching.rb +9 -11
  14. data/lib/action_controller/caching/actions.rb +11 -18
  15. data/lib/action_controller/caching/fragments.rb +28 -20
  16. data/lib/action_controller/caching/pages.rb +13 -15
  17. data/lib/action_controller/caching/sweeping.rb +2 -2
  18. data/lib/action_controller/cgi_ext.rb +0 -1
  19. data/lib/action_controller/cgi_ext/cookie.rb +2 -0
  20. data/lib/action_controller/cgi_process.rb +54 -162
  21. data/lib/action_controller/cookies.rb +13 -25
  22. data/lib/action_controller/dispatcher.rb +43 -122
  23. data/lib/action_controller/failsafe.rb +52 -0
  24. data/lib/action_controller/flash.rb +38 -47
  25. data/lib/action_controller/helpers.rb +13 -9
  26. data/lib/action_controller/http_authentication.rb +203 -23
  27. data/lib/action_controller/integration.rb +126 -70
  28. data/lib/action_controller/layout.rb +36 -39
  29. data/lib/action_controller/middleware_stack.rb +119 -0
  30. data/lib/action_controller/middlewares.rb +13 -0
  31. data/lib/action_controller/mime_responds.rb +19 -4
  32. data/lib/action_controller/mime_type.rb +8 -0
  33. data/lib/action_controller/params_parser.rb +71 -0
  34. data/lib/action_controller/performance_test.rb +0 -1
  35. data/lib/action_controller/polymorphic_routes.rb +36 -30
  36. data/lib/action_controller/reloader.rb +14 -0
  37. data/lib/action_controller/request.rb +107 -499
  38. data/lib/action_controller/request_forgery_protection.rb +7 -39
  39. data/lib/action_controller/rescue.rb +55 -35
  40. data/lib/action_controller/resources.rb +34 -31
  41. data/lib/action_controller/response.rb +99 -57
  42. data/lib/action_controller/rewindable_input.rb +28 -0
  43. data/lib/action_controller/routing.rb +7 -7
  44. data/lib/action_controller/routing/builder.rb +4 -1
  45. data/lib/action_controller/routing/optimisations.rb +1 -1
  46. data/lib/action_controller/routing/recognition_optimisation.rb +1 -2
  47. data/lib/action_controller/routing/route.rb +15 -5
  48. data/lib/action_controller/routing/route_set.rb +82 -35
  49. data/lib/action_controller/routing/segments.rb +35 -0
  50. data/lib/action_controller/session/abstract_store.rb +181 -0
  51. data/lib/action_controller/session/cookie_store.rb +197 -175
  52. data/lib/action_controller/session/mem_cache_store.rb +36 -83
  53. data/lib/action_controller/session_management.rb +26 -134
  54. data/lib/action_controller/streaming.rb +24 -7
  55. data/lib/action_controller/templates/rescues/diagnostics.erb +2 -2
  56. data/lib/action_controller/templates/rescues/template_error.erb +2 -2
  57. data/lib/action_controller/test_case.rb +87 -30
  58. data/lib/action_controller/test_process.rb +145 -104
  59. data/lib/action_controller/uploaded_file.rb +44 -0
  60. data/lib/action_controller/url_rewriter.rb +3 -6
  61. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  62. data/lib/action_controller/vendor/html-scanner/html/selector.rb +1 -1
  63. data/lib/action_controller/vendor/rack-1.0/rack.rb +89 -0
  64. data/lib/action_controller/vendor/rack-1.0/rack/adapter/camping.rb +22 -0
  65. data/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb +37 -0
  66. data/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/request.rb +37 -0
  67. data/lib/action_controller/vendor/rack-1.0/rack/auth/basic.rb +58 -0
  68. data/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb +124 -0
  69. data/lib/action_controller/vendor/rack-1.0/rack/auth/digest/nonce.rb +51 -0
  70. data/lib/action_controller/vendor/rack-1.0/rack/auth/digest/params.rb +55 -0
  71. data/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb +40 -0
  72. data/lib/action_controller/vendor/rack-1.0/rack/auth/openid.rb +480 -0
  73. data/lib/action_controller/vendor/rack-1.0/rack/builder.rb +63 -0
  74. data/lib/action_controller/vendor/rack-1.0/rack/cascade.rb +36 -0
  75. data/lib/action_controller/vendor/rack-1.0/rack/chunked.rb +49 -0
  76. data/lib/action_controller/vendor/rack-1.0/rack/commonlogger.rb +61 -0
  77. data/lib/action_controller/vendor/rack-1.0/rack/conditionalget.rb +45 -0
  78. data/lib/action_controller/vendor/rack-1.0/rack/content_length.rb +29 -0
  79. data/lib/action_controller/vendor/rack-1.0/rack/content_type.rb +23 -0
  80. data/lib/action_controller/vendor/rack-1.0/rack/deflater.rb +85 -0
  81. data/lib/action_controller/vendor/rack-1.0/rack/directory.rb +153 -0
  82. data/lib/action_controller/vendor/rack-1.0/rack/file.rb +88 -0
  83. data/lib/action_controller/vendor/rack-1.0/rack/handler.rb +48 -0
  84. data/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb +61 -0
  85. data/lib/action_controller/vendor/rack-1.0/rack/handler/evented_mongrel.rb +8 -0
  86. data/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb +89 -0
  87. data/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb +55 -0
  88. data/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb +84 -0
  89. data/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb +59 -0
  90. data/lib/action_controller/vendor/rack-1.0/rack/handler/swiftiplied_mongrel.rb +8 -0
  91. data/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb +18 -0
  92. data/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb +67 -0
  93. data/lib/action_controller/vendor/rack-1.0/rack/head.rb +19 -0
  94. data/lib/action_controller/vendor/rack-1.0/rack/lint.rb +462 -0
  95. data/lib/action_controller/vendor/rack-1.0/rack/lobster.rb +65 -0
  96. data/lib/action_controller/vendor/rack-1.0/rack/lock.rb +16 -0
  97. data/lib/action_controller/vendor/rack-1.0/rack/methodoverride.rb +27 -0
  98. data/lib/action_controller/vendor/rack-1.0/rack/mime.rb +204 -0
  99. data/lib/action_controller/vendor/rack-1.0/rack/mock.rb +160 -0
  100. data/lib/action_controller/vendor/rack-1.0/rack/recursive.rb +57 -0
  101. data/lib/action_controller/vendor/rack-1.0/rack/reloader.rb +64 -0
  102. data/lib/action_controller/vendor/rack-1.0/rack/request.rb +241 -0
  103. data/lib/action_controller/vendor/rack-1.0/rack/response.rb +179 -0
  104. data/lib/action_controller/vendor/rack-1.0/rack/session/abstract/id.rb +142 -0
  105. data/lib/action_controller/vendor/rack-1.0/rack/session/cookie.rb +91 -0
  106. data/lib/action_controller/vendor/rack-1.0/rack/session/memcache.rb +109 -0
  107. data/lib/action_controller/vendor/rack-1.0/rack/session/pool.rb +100 -0
  108. data/lib/action_controller/vendor/rack-1.0/rack/showexceptions.rb +349 -0
  109. data/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb +106 -0
  110. data/lib/action_controller/vendor/rack-1.0/rack/static.rb +38 -0
  111. data/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb +55 -0
  112. data/lib/action_controller/vendor/rack-1.0/rack/utils.rb +392 -0
  113. data/lib/action_controller/verification.rb +1 -1
  114. data/lib/action_pack.rb +1 -1
  115. data/lib/action_pack/version.rb +2 -2
  116. data/lib/action_view.rb +22 -17
  117. data/lib/action_view/base.rb +53 -79
  118. data/lib/action_view/erb/util.rb +38 -0
  119. data/lib/action_view/helpers.rb +24 -5
  120. data/lib/action_view/helpers/active_record_helper.rb +2 -2
  121. data/lib/action_view/helpers/asset_tag_helper.rb +81 -50
  122. data/lib/action_view/helpers/atom_feed_helper.rb +1 -1
  123. data/lib/action_view/helpers/benchmark_helper.rb +26 -5
  124. data/lib/action_view/helpers/date_helper.rb +82 -7
  125. data/lib/action_view/helpers/form_helper.rb +295 -64
  126. data/lib/action_view/helpers/form_options_helper.rb +160 -18
  127. data/lib/action_view/helpers/form_tag_helper.rb +2 -2
  128. data/lib/action_view/helpers/number_helper.rb +31 -18
  129. data/lib/action_view/helpers/prototype_helper.rb +2 -12
  130. data/lib/action_view/helpers/sanitize_helper.rb +0 -10
  131. data/lib/action_view/helpers/scriptaculous_helper.rb +1 -0
  132. data/lib/action_view/helpers/tag_helper.rb +3 -4
  133. data/lib/action_view/helpers/text_helper.rb +99 -122
  134. data/lib/action_view/helpers/translation_helper.rb +19 -1
  135. data/lib/action_view/helpers/url_helper.rb +25 -2
  136. data/lib/action_view/inline_template.rb +1 -1
  137. data/lib/action_view/locale/en.yml +19 -1
  138. data/lib/action_view/partials.rb +46 -9
  139. data/lib/action_view/paths.rb +28 -84
  140. data/lib/action_view/reloadable_template.rb +117 -0
  141. data/lib/action_view/renderable.rb +28 -35
  142. data/lib/action_view/renderable_partial.rb +3 -4
  143. data/lib/action_view/template.rb +172 -31
  144. data/lib/action_view/template_error.rb +8 -9
  145. data/lib/action_view/template_handler.rb +1 -1
  146. data/lib/action_view/template_handlers.rb +9 -6
  147. data/lib/action_view/template_handlers/erb.rb +2 -39
  148. data/lib/action_view/template_handlers/rjs.rb +1 -0
  149. data/lib/action_view/test_case.rb +27 -1
  150. data/test/abstract_unit.rb +23 -17
  151. data/test/active_record_unit.rb +5 -4
  152. data/test/activerecord/active_record_store_test.rb +139 -106
  153. data/test/activerecord/render_partial_with_record_identification_test.rb +5 -21
  154. data/test/controller/action_pack_assertions_test.rb +25 -23
  155. data/test/controller/addresses_render_test.rb +3 -6
  156. data/test/controller/assert_select_test.rb +83 -70
  157. data/test/controller/base_test.rb +11 -13
  158. data/test/controller/benchmark_test.rb +3 -3
  159. data/test/controller/caching_test.rb +34 -24
  160. data/test/controller/capture_test.rb +3 -6
  161. data/test/controller/content_type_test.rb +3 -6
  162. data/test/controller/cookie_test.rb +31 -66
  163. data/test/controller/deprecation/deprecated_base_methods_test.rb +9 -11
  164. data/test/controller/dispatcher_test.rb +23 -28
  165. data/test/controller/fake_models.rb +8 -0
  166. data/test/controller/filters_test.rb +6 -2
  167. data/test/controller/flash_test.rb +2 -6
  168. data/test/controller/helper_test.rb +15 -1
  169. data/test/controller/html-scanner/document_test.rb +1 -1
  170. data/test/controller/html-scanner/sanitizer_test.rb +1 -1
  171. data/test/controller/http_basic_authentication_test.rb +88 -0
  172. data/test/controller/http_digest_authentication_test.rb +178 -0
  173. data/test/controller/integration_test.rb +56 -52
  174. data/test/controller/layout_test.rb +46 -44
  175. data/test/controller/middleware_stack_test.rb +90 -0
  176. data/test/controller/mime_responds_test.rb +7 -11
  177. data/test/controller/mime_type_test.rb +9 -0
  178. data/test/controller/polymorphic_routes_test.rb +235 -151
  179. data/test/controller/rack_test.rb +52 -81
  180. data/test/controller/redirect_test.rb +6 -14
  181. data/test/controller/render_test.rb +273 -60
  182. data/test/controller/request/json_params_parsing_test.rb +45 -0
  183. data/test/controller/request/multipart_params_parsing_test.rb +223 -0
  184. data/test/controller/request/query_string_parsing_test.rb +120 -0
  185. data/test/controller/request/url_encoded_params_parsing_test.rb +184 -0
  186. data/test/controller/request/xml_params_parsing_test.rb +88 -0
  187. data/test/controller/request_forgery_protection_test.rb +17 -98
  188. data/test/controller/request_test.rb +45 -530
  189. data/test/controller/rescue_test.rb +45 -22
  190. data/test/controller/resources_test.rb +112 -37
  191. data/test/controller/routing_test.rb +1442 -1384
  192. data/test/controller/selector_test.rb +3 -3
  193. data/test/controller/send_file_test.rb +30 -3
  194. data/test/controller/session/cookie_store_test.rb +169 -240
  195. data/test/controller/session/mem_cache_store_test.rb +94 -148
  196. data/test/controller/session/test_session_test.rb +58 -0
  197. data/test/controller/test_test.rb +32 -13
  198. data/test/controller/url_rewriter_test.rb +54 -4
  199. data/test/controller/verification_test.rb +1 -1
  200. data/test/controller/view_paths_test.rb +15 -15
  201. data/test/controller/webservice_test.rb +178 -147
  202. data/test/fixtures/alternate_helpers/foo_helper.rb +3 -0
  203. data/test/fixtures/layout_tests/alt/layouts/alt.rhtml +0 -0
  204. data/test/fixtures/layouts/default_html.html.erb +1 -0
  205. data/test/fixtures/layouts/xhr.html.erb +2 -0
  206. data/test/fixtures/multipart/empty +10 -0
  207. data/test/fixtures/multipart/hello.txt +1 -0
  208. data/test/fixtures/multipart/none +9 -0
  209. data/test/fixtures/public/500.da.html +1 -0
  210. data/test/fixtures/quiz/questions/_question.html.erb +1 -0
  211. data/test/fixtures/replies.yml +1 -1
  212. data/test/fixtures/test/_one.html.erb +1 -0
  213. data/test/fixtures/test/_two.html.erb +1 -0
  214. data/test/fixtures/test/dont_pick_me +1 -0
  215. data/test/fixtures/test/hello.builder +1 -1
  216. data/test/fixtures/test/hello_world.da.html.erb +1 -0
  217. data/test/fixtures/test/hello_world.erb~ +1 -0
  218. data/test/fixtures/test/hello_world.pt-BR.html.erb +1 -0
  219. data/test/fixtures/test/malformed/malformed.en.html.erb~ +1 -0
  220. data/test/fixtures/test/malformed/malformed.erb~ +1 -0
  221. data/test/fixtures/test/malformed/malformed.html.erb~ +1 -0
  222. data/test/fixtures/test/render_explicit_html_template.js.rjs +1 -0
  223. data/test/fixtures/test/render_implicit_html_template.js.rjs +1 -0
  224. data/test/fixtures/test/render_implicit_html_template_from_xhr_request.da.html.erb +1 -0
  225. data/test/fixtures/test/render_implicit_html_template_from_xhr_request.html.erb +1 -0
  226. data/test/fixtures/test/render_implicit_js_template_without_layout.js.erb +1 -0
  227. data/test/fixtures/test/utf8.html.erb +2 -0
  228. data/test/template/active_record_helper_i18n_test.rb +31 -33
  229. data/test/template/active_record_helper_test.rb +34 -0
  230. data/test/template/asset_tag_helper_test.rb +52 -14
  231. data/test/template/atom_feed_helper_test.rb +3 -5
  232. data/test/template/benchmark_helper_test.rb +50 -24
  233. data/test/template/compiled_templates_test.rb +177 -33
  234. data/test/template/date_helper_i18n_test.rb +88 -81
  235. data/test/template/date_helper_test.rb +427 -43
  236. data/test/template/form_helper_test.rb +243 -44
  237. data/test/template/form_options_helper_test.rb +631 -565
  238. data/test/template/form_tag_helper_test.rb +9 -2
  239. data/test/template/javascript_helper_test.rb +0 -5
  240. data/test/template/number_helper_i18n_test.rb +60 -48
  241. data/test/template/number_helper_test.rb +1 -0
  242. data/test/template/render_test.rb +117 -35
  243. data/test/template/test_test.rb +4 -6
  244. data/test/template/text_helper_test.rb +129 -50
  245. data/test/template/translation_helper_test.rb +23 -19
  246. data/test/template/url_helper_test.rb +35 -2
  247. data/test/view/test_case_test.rb +8 -0
  248. metadata +197 -23
  249. data/lib/action_controller/assertions.rb +0 -69
  250. data/lib/action_controller/caching/sql_cache.rb +0 -18
  251. data/lib/action_controller/cgi_ext/session.rb +0 -53
  252. data/lib/action_controller/components.rb +0 -169
  253. data/lib/action_controller/rack_process.rb +0 -297
  254. data/lib/action_controller/request_profiler.rb +0 -169
  255. data/lib/action_controller/session/active_record_store.rb +0 -340
  256. data/lib/action_controller/session/drb_server.rb +0 -32
  257. data/lib/action_controller/session/drb_store.rb +0 -35
  258. data/test/controller/cgi_test.rb +0 -269
  259. data/test/controller/components_test.rb +0 -156
  260. data/test/controller/http_authentication_test.rb +0 -54
  261. data/test/controller/integration_upload_test.rb +0 -43
  262. data/test/controller/session_fixation_test.rb +0 -89
  263. data/test/controller/session_management_test.rb +0 -178
  264. data/test/fixtures/test/hello_world.js +0 -1
@@ -0,0 +1,57 @@
1
+ require 'uri'
2
+
3
+ module Rack
4
+ # Rack::ForwardRequest gets caught by Rack::Recursive and redirects
5
+ # the current request to the app at +url+.
6
+ #
7
+ # raise ForwardRequest.new("/not-found")
8
+ #
9
+
10
+ class ForwardRequest < Exception
11
+ attr_reader :url, :env
12
+
13
+ def initialize(url, env={})
14
+ @url = URI(url)
15
+ @env = env
16
+
17
+ @env["PATH_INFO"] = @url.path
18
+ @env["QUERY_STRING"] = @url.query if @url.query
19
+ @env["HTTP_HOST"] = @url.host if @url.host
20
+ @env["HTTP_PORT"] = @url.port if @url.port
21
+ @env["rack.url_scheme"] = @url.scheme if @url.scheme
22
+
23
+ super "forwarding to #{url}"
24
+ end
25
+ end
26
+
27
+ # Rack::Recursive allows applications called down the chain to
28
+ # include data from other applications (by using
29
+ # <tt>rack['rack.recursive.include'][...]</tt> or raise a
30
+ # ForwardRequest to redirect internally.
31
+
32
+ class Recursive
33
+ def initialize(app)
34
+ @app = app
35
+ end
36
+
37
+ def call(env)
38
+ @script_name = env["SCRIPT_NAME"]
39
+ @app.call(env.merge('rack.recursive.include' => method(:include)))
40
+ rescue ForwardRequest => req
41
+ call(env.merge(req.env))
42
+ end
43
+
44
+ def include(env, path)
45
+ unless path.index(@script_name) == 0 && (path[@script_name.size] == ?/ ||
46
+ path[@script_name.size].nil?)
47
+ raise ArgumentError, "can only include below #{@script_name}, not #{path}"
48
+ end
49
+
50
+ env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name,
51
+ "REQUEST_METHOD" => "GET",
52
+ "CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "",
53
+ "rack.input" => StringIO.new(""))
54
+ @app.call(env)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,64 @@
1
+ require 'thread'
2
+
3
+ module Rack
4
+ # Rack::Reloader checks on every request, but at most every +secs+
5
+ # seconds, if a file loaded changed, and reloads it, logging to
6
+ # rack.errors.
7
+ #
8
+ # It is recommended you use ShowExceptions to catch SyntaxErrors etc.
9
+
10
+ class Reloader
11
+ def initialize(app, secs=10)
12
+ @app = app
13
+ @secs = secs # reload every @secs seconds max
14
+ @last = Time.now
15
+ end
16
+
17
+ def call(env)
18
+ if Time.now > @last + @secs
19
+ Thread.exclusive {
20
+ reload!(env['rack.errors'])
21
+ @last = Time.now
22
+ }
23
+ end
24
+
25
+ @app.call(env)
26
+ end
27
+
28
+ def reload!(stderr=$stderr)
29
+ need_reload = $LOADED_FEATURES.find_all { |loaded|
30
+ begin
31
+ if loaded =~ /\A[.\/]/ # absolute filename or 1.9
32
+ abs = loaded
33
+ else
34
+ abs = $LOAD_PATH.map { |path| ::File.join(path, loaded) }.
35
+ find { |file| ::File.exist? file }
36
+ end
37
+
38
+ if abs
39
+ ::File.mtime(abs) > @last - @secs rescue false
40
+ else
41
+ false
42
+ end
43
+ end
44
+ }
45
+
46
+ need_reload.each { |l|
47
+ $LOADED_FEATURES.delete l
48
+ }
49
+
50
+ need_reload.each { |to_load|
51
+ begin
52
+ if require to_load
53
+ stderr.puts "#{self.class}: reloaded `#{to_load}'"
54
+ end
55
+ rescue LoadError, SyntaxError => e
56
+ raise e # Possibly ShowExceptions
57
+ end
58
+ }
59
+
60
+ stderr.flush
61
+ need_reload
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,241 @@
1
+ require 'rack/utils'
2
+
3
+ module Rack
4
+ # Rack::Request provides a convenient interface to a Rack
5
+ # environment. It is stateless, the environment +env+ passed to the
6
+ # constructor will be directly modified.
7
+ #
8
+ # req = Rack::Request.new(env)
9
+ # req.post?
10
+ # req.params["data"]
11
+ #
12
+ # The environment hash passed will store a reference to the Request object
13
+ # instantiated so that it will only instantiate if an instance of the Request
14
+ # object doesn't already exist.
15
+
16
+ class Request
17
+ # The environment of the request.
18
+ attr_reader :env
19
+
20
+ def self.new(env)
21
+ if self == Rack::Request
22
+ env["rack.request"] ||= super
23
+ else
24
+ super
25
+ end
26
+ end
27
+
28
+ def initialize(env)
29
+ @env = env
30
+ end
31
+
32
+ def body; @env["rack.input"] end
33
+ def scheme; @env["rack.url_scheme"] end
34
+ def script_name; @env["SCRIPT_NAME"].to_s end
35
+ def path_info; @env["PATH_INFO"].to_s end
36
+ def port; @env["SERVER_PORT"].to_i end
37
+ def request_method; @env["REQUEST_METHOD"] end
38
+ def query_string; @env["QUERY_STRING"].to_s end
39
+ def content_length; @env['CONTENT_LENGTH'] end
40
+ def content_type; @env['CONTENT_TYPE'] end
41
+
42
+ # The media type (type/subtype) portion of the CONTENT_TYPE header
43
+ # without any media type parameters. e.g., when CONTENT_TYPE is
44
+ # "text/plain;charset=utf-8", the media-type is "text/plain".
45
+ #
46
+ # For more information on the use of media types in HTTP, see:
47
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
48
+ def media_type
49
+ content_type && content_type.split(/\s*[;,]\s*/, 2)[0].downcase
50
+ end
51
+
52
+ # The media type parameters provided in CONTENT_TYPE as a Hash, or
53
+ # an empty Hash if no CONTENT_TYPE or media-type parameters were
54
+ # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
55
+ # this method responds with the following Hash:
56
+ # { 'charset' => 'utf-8' }
57
+ def media_type_params
58
+ return {} if content_type.nil?
59
+ content_type.split(/\s*[;,]\s*/)[1..-1].
60
+ collect { |s| s.split('=', 2) }.
61
+ inject({}) { |hash,(k,v)| hash[k.downcase] = v ; hash }
62
+ end
63
+
64
+ # The character set of the request body if a "charset" media type
65
+ # parameter was given, or nil if no "charset" was specified. Note
66
+ # that, per RFC2616, text/* media types that specify no explicit
67
+ # charset are to be considered ISO-8859-1.
68
+ def content_charset
69
+ media_type_params['charset']
70
+ end
71
+
72
+ def host
73
+ # Remove port number.
74
+ (@env["HTTP_HOST"] || @env["SERVER_NAME"]).gsub(/:\d+\z/, '')
75
+ end
76
+
77
+ def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
78
+ def path_info=(s); @env["PATH_INFO"] = s.to_s end
79
+
80
+ def get?; request_method == "GET" end
81
+ def post?; request_method == "POST" end
82
+ def put?; request_method == "PUT" end
83
+ def delete?; request_method == "DELETE" end
84
+ def head?; request_method == "HEAD" end
85
+
86
+ # The set of form-data media-types. Requests that do not indicate
87
+ # one of the media types presents in this list will not be eligible
88
+ # for form-data / param parsing.
89
+ FORM_DATA_MEDIA_TYPES = [
90
+ nil,
91
+ 'application/x-www-form-urlencoded',
92
+ 'multipart/form-data'
93
+ ]
94
+
95
+ # Determine whether the request body contains form-data by checking
96
+ # the request media_type against registered form-data media-types:
97
+ # "application/x-www-form-urlencoded" and "multipart/form-data". The
98
+ # list of form-data media types can be modified through the
99
+ # +FORM_DATA_MEDIA_TYPES+ array.
100
+ def form_data?
101
+ FORM_DATA_MEDIA_TYPES.include?(media_type)
102
+ end
103
+
104
+ # Returns the data recieved in the query string.
105
+ def GET
106
+ if @env["rack.request.query_string"] == query_string
107
+ @env["rack.request.query_hash"]
108
+ else
109
+ @env["rack.request.query_string"] = query_string
110
+ @env["rack.request.query_hash"] =
111
+ Utils.parse_nested_query(query_string)
112
+ end
113
+ end
114
+
115
+ # Returns the data recieved in the request body.
116
+ #
117
+ # This method support both application/x-www-form-urlencoded and
118
+ # multipart/form-data.
119
+ def POST
120
+ if @env["rack.request.form_input"].eql? @env["rack.input"]
121
+ @env["rack.request.form_hash"]
122
+ elsif form_data?
123
+ @env["rack.request.form_input"] = @env["rack.input"]
124
+ unless @env["rack.request.form_hash"] =
125
+ Utils::Multipart.parse_multipart(env)
126
+ form_vars = @env["rack.input"].read
127
+
128
+ # Fix for Safari Ajax postings that always append \0
129
+ form_vars.sub!(/\0\z/, '')
130
+
131
+ @env["rack.request.form_vars"] = form_vars
132
+ @env["rack.request.form_hash"] = Utils.parse_nested_query(form_vars)
133
+
134
+ begin
135
+ @env["rack.input"].rewind if @env["rack.input"].respond_to?(:rewind)
136
+ rescue Errno::ESPIPE
137
+ # Handles exceptions raised by input streams that cannot be rewound
138
+ # such as when using plain CGI under Apache
139
+ end
140
+ end
141
+ @env["rack.request.form_hash"]
142
+ else
143
+ {}
144
+ end
145
+ end
146
+
147
+ # The union of GET and POST data.
148
+ def params
149
+ self.put? ? self.GET : self.GET.update(self.POST)
150
+ rescue EOFError => e
151
+ self.GET
152
+ end
153
+
154
+ # shortcut for request.params[key]
155
+ def [](key)
156
+ params[key.to_s]
157
+ end
158
+
159
+ # shortcut for request.params[key] = value
160
+ def []=(key, value)
161
+ params[key.to_s] = value
162
+ end
163
+
164
+ # like Hash#values_at
165
+ def values_at(*keys)
166
+ keys.map{|key| params[key] }
167
+ end
168
+
169
+ # the referer of the client or '/'
170
+ def referer
171
+ @env['HTTP_REFERER'] || '/'
172
+ end
173
+ alias referrer referer
174
+
175
+
176
+ def cookies
177
+ return {} unless @env["HTTP_COOKIE"]
178
+
179
+ if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"]
180
+ @env["rack.request.cookie_hash"]
181
+ else
182
+ @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"]
183
+ # According to RFC 2109:
184
+ # If multiple cookies satisfy the criteria above, they are ordered in
185
+ # the Cookie header such that those with more specific Path attributes
186
+ # precede those with less specific. Ordering with respect to other
187
+ # attributes (e.g., Domain) is unspecified.
188
+ @env["rack.request.cookie_hash"] =
189
+ Utils.parse_query(@env["rack.request.cookie_string"], ';,').inject({}) {|h,(k,v)|
190
+ h[k] = Array === v ? v.first : v
191
+ h
192
+ }
193
+ end
194
+ end
195
+
196
+ def xhr?
197
+ @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
198
+ end
199
+
200
+ # Tries to return a remake of the original request URL as a string.
201
+ def url
202
+ url = scheme + "://"
203
+ url << host
204
+
205
+ if scheme == "https" && port != 443 ||
206
+ scheme == "http" && port != 80
207
+ url << ":#{port}"
208
+ end
209
+
210
+ url << fullpath
211
+
212
+ url
213
+ end
214
+
215
+ def fullpath
216
+ path = script_name + path_info
217
+ path << "?" << query_string unless query_string.empty?
218
+ path
219
+ end
220
+
221
+ def accept_encoding
222
+ @env["HTTP_ACCEPT_ENCODING"].to_s.split(/,\s*/).map do |part|
223
+ m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick
224
+
225
+ if m
226
+ [m[1], (m[2] || 1.0).to_f]
227
+ else
228
+ raise "Invalid value for Accept-Encoding: #{part.inspect}"
229
+ end
230
+ end
231
+ end
232
+
233
+ def ip
234
+ if addr = @env['HTTP_X_FORWARDED_FOR']
235
+ addr.split(',').last.strip
236
+ else
237
+ @env['REMOTE_ADDR']
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,179 @@
1
+ require 'rack/request'
2
+ require 'rack/utils'
3
+
4
+ module Rack
5
+ # Rack::Response provides a convenient interface to create a Rack
6
+ # response.
7
+ #
8
+ # It allows setting of headers and cookies, and provides useful
9
+ # defaults (a OK response containing HTML).
10
+ #
11
+ # You can use Response#write to iteratively generate your response,
12
+ # but note that this is buffered by Rack::Response until you call
13
+ # +finish+. +finish+ however can take a block inside which calls to
14
+ # +write+ are syncronous with the Rack response.
15
+ #
16
+ # Your application's +call+ should end returning Response#finish.
17
+
18
+ class Response
19
+ attr_accessor :length
20
+
21
+ def initialize(body=[], status=200, header={}, &block)
22
+ @status = status
23
+ @header = Utils::HeaderHash.new({"Content-Type" => "text/html"}.
24
+ merge(header))
25
+
26
+ @writer = lambda { |x| @body << x }
27
+ @block = nil
28
+ @length = 0
29
+
30
+ @body = []
31
+
32
+ if body.respond_to? :to_str
33
+ write body.to_str
34
+ elsif body.respond_to?(:each)
35
+ body.each { |part|
36
+ write part.to_s
37
+ }
38
+ else
39
+ raise TypeError, "stringable or iterable required"
40
+ end
41
+
42
+ yield self if block_given?
43
+ end
44
+
45
+ attr_reader :header
46
+ attr_accessor :status, :body
47
+
48
+ def [](key)
49
+ header[key]
50
+ end
51
+
52
+ def []=(key, value)
53
+ header[key] = value
54
+ end
55
+
56
+ def set_cookie(key, value)
57
+ case value
58
+ when Hash
59
+ domain = "; domain=" + value[:domain] if value[:domain]
60
+ path = "; path=" + value[:path] if value[:path]
61
+ # According to RFC 2109, we need dashes here.
62
+ # N.B.: cgi.rb uses spaces...
63
+ expires = "; expires=" + value[:expires].clone.gmtime.
64
+ strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
65
+ secure = "; secure" if value[:secure]
66
+ httponly = "; HttpOnly" if value[:httponly]
67
+ value = value[:value]
68
+ end
69
+ value = [value] unless Array === value
70
+ cookie = Utils.escape(key) + "=" +
71
+ value.map { |v| Utils.escape v }.join("&") +
72
+ "#{domain}#{path}#{expires}#{secure}#{httponly}"
73
+
74
+ case self["Set-Cookie"]
75
+ when Array
76
+ self["Set-Cookie"] << cookie
77
+ when String
78
+ self["Set-Cookie"] = [self["Set-Cookie"], cookie]
79
+ when nil
80
+ self["Set-Cookie"] = cookie
81
+ end
82
+ end
83
+
84
+ def delete_cookie(key, value={})
85
+ unless Array === self["Set-Cookie"]
86
+ self["Set-Cookie"] = [self["Set-Cookie"]].compact
87
+ end
88
+
89
+ self["Set-Cookie"].reject! { |cookie|
90
+ cookie =~ /\A#{Utils.escape(key)}=/
91
+ }
92
+
93
+ set_cookie(key,
94
+ {:value => '', :path => nil, :domain => nil,
95
+ :expires => Time.at(0) }.merge(value))
96
+ end
97
+
98
+
99
+ def finish(&block)
100
+ @block = block
101
+
102
+ if [204, 304].include?(status.to_i)
103
+ header.delete "Content-Type"
104
+ [status.to_i, header.to_hash, []]
105
+ else
106
+ [status.to_i, header.to_hash, self]
107
+ end
108
+ end
109
+ alias to_a finish # For *response
110
+
111
+ def each(&callback)
112
+ @body.each(&callback)
113
+ @writer = callback
114
+ @block.call(self) if @block
115
+ end
116
+
117
+ # Append to body and update Content-Length.
118
+ #
119
+ # NOTE: Do not mix #write and direct #body access!
120
+ #
121
+ def write(str)
122
+ s = str.to_s
123
+ @length += s.size
124
+ @writer.call s
125
+
126
+ header["Content-Length"] = @length.to_s
127
+ str
128
+ end
129
+
130
+ def close
131
+ body.close if body.respond_to?(:close)
132
+ end
133
+
134
+ def empty?
135
+ @block == nil && @body.empty?
136
+ end
137
+
138
+ alias headers header
139
+
140
+ module Helpers
141
+ def invalid?; @status < 100 || @status >= 600; end
142
+
143
+ def informational?; @status >= 100 && @status < 200; end
144
+ def successful?; @status >= 200 && @status < 300; end
145
+ def redirection?; @status >= 300 && @status < 400; end
146
+ def client_error?; @status >= 400 && @status < 500; end
147
+ def server_error?; @status >= 500 && @status < 600; end
148
+
149
+ def ok?; @status == 200; end
150
+ def forbidden?; @status == 403; end
151
+ def not_found?; @status == 404; end
152
+
153
+ def redirect?; [301, 302, 303, 307].include? @status; end
154
+ def empty?; [201, 204, 304].include? @status; end
155
+
156
+ # Headers
157
+ attr_reader :headers, :original_headers
158
+
159
+ def include?(header)
160
+ !!headers[header]
161
+ end
162
+
163
+ def content_type
164
+ headers["Content-Type"]
165
+ end
166
+
167
+ def content_length
168
+ cl = headers["Content-Length"]
169
+ cl ? cl.to_i : cl
170
+ end
171
+
172
+ def location
173
+ headers["Location"]
174
+ end
175
+ end
176
+
177
+ include Helpers
178
+ end
179
+ end