actionpack 1.12.5 → 1.13.0

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 (179) hide show
  1. data/CHANGELOG +517 -15
  2. data/MIT-LICENSE +1 -1
  3. data/README +18 -20
  4. data/Rakefile +7 -4
  5. data/examples/address_book_controller.rb +3 -3
  6. data/examples/blog_controller.cgi +3 -3
  7. data/examples/debate_controller.cgi +5 -5
  8. data/lib/action_controller.rb +2 -2
  9. data/lib/action_controller/assertions.rb +73 -311
  10. data/lib/action_controller/{deprecated_assertions.rb → assertions/deprecated_assertions.rb} +32 -8
  11. data/lib/action_controller/assertions/dom_assertions.rb +25 -0
  12. data/lib/action_controller/assertions/model_assertions.rb +12 -0
  13. data/lib/action_controller/assertions/response_assertions.rb +140 -0
  14. data/lib/action_controller/assertions/routing_assertions.rb +82 -0
  15. data/lib/action_controller/assertions/selector_assertions.rb +571 -0
  16. data/lib/action_controller/assertions/tag_assertions.rb +117 -0
  17. data/lib/action_controller/base.rb +334 -163
  18. data/lib/action_controller/benchmarking.rb +3 -6
  19. data/lib/action_controller/caching.rb +83 -22
  20. data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -7
  21. data/lib/action_controller/cgi_ext/cgi_methods.rb +167 -173
  22. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +43 -22
  23. data/lib/action_controller/cgi_process.rb +50 -27
  24. data/lib/action_controller/components.rb +21 -25
  25. data/lib/action_controller/cookies.rb +10 -9
  26. data/lib/action_controller/{dependencies.rb → deprecated_dependencies.rb} +9 -27
  27. data/lib/action_controller/filters.rb +448 -225
  28. data/lib/action_controller/flash.rb +24 -20
  29. data/lib/action_controller/helpers.rb +2 -5
  30. data/lib/action_controller/integration.rb +40 -16
  31. data/lib/action_controller/layout.rb +11 -8
  32. data/lib/action_controller/macros/auto_complete.rb +3 -2
  33. data/lib/action_controller/macros/in_place_editing.rb +3 -2
  34. data/lib/action_controller/mime_responds.rb +41 -29
  35. data/lib/action_controller/mime_type.rb +68 -10
  36. data/lib/action_controller/pagination.rb +4 -3
  37. data/lib/action_controller/request.rb +22 -14
  38. data/lib/action_controller/rescue.rb +25 -22
  39. data/lib/action_controller/resources.rb +302 -0
  40. data/lib/action_controller/response.rb +20 -2
  41. data/lib/action_controller/response.rb.rej +17 -0
  42. data/lib/action_controller/routing.rb +1165 -567
  43. data/lib/action_controller/scaffolding.rb +30 -31
  44. data/lib/action_controller/session/active_record_store.rb +2 -0
  45. data/lib/action_controller/session/drb_store.rb +4 -0
  46. data/lib/action_controller/session/mem_cache_store.rb +4 -0
  47. data/lib/action_controller/session_management.rb +6 -9
  48. data/lib/action_controller/status_codes.rb +89 -0
  49. data/lib/action_controller/streaming.rb +6 -15
  50. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +5 -5
  51. data/lib/action_controller/templates/rescues/diagnostics.rhtml +2 -2
  52. data/lib/action_controller/templates/rescues/routing_error.rhtml +4 -4
  53. data/lib/action_controller/templates/rescues/template_error.rhtml +1 -1
  54. data/lib/action_controller/templates/scaffolds/list.rhtml +1 -1
  55. data/lib/action_controller/test_process.rb +52 -30
  56. data/lib/action_controller/url_rewriter.rb +63 -29
  57. data/lib/action_controller/vendor/html-scanner/html/document.rb +1 -0
  58. data/lib/action_controller/vendor/html-scanner/html/node.rb +3 -4
  59. data/lib/action_controller/vendor/html-scanner/html/selector.rb +822 -0
  60. data/lib/action_controller/verification.rb +22 -11
  61. data/lib/action_pack.rb +1 -1
  62. data/lib/action_pack/version.rb +2 -2
  63. data/lib/action_view.rb +1 -1
  64. data/lib/action_view/base.rb +46 -43
  65. data/lib/action_view/compiled_templates.rb +1 -1
  66. data/lib/action_view/helpers/active_record_helper.rb +54 -17
  67. data/lib/action_view/helpers/asset_tag_helper.rb +97 -46
  68. data/lib/action_view/helpers/capture_helper.rb +1 -1
  69. data/lib/action_view/helpers/date_helper.rb +258 -136
  70. data/lib/action_view/helpers/debug_helper.rb +1 -1
  71. data/lib/action_view/helpers/deprecated_helper.rb +34 -0
  72. data/lib/action_view/helpers/form_helper.rb +75 -35
  73. data/lib/action_view/helpers/form_options_helper.rb +7 -5
  74. data/lib/action_view/helpers/form_tag_helper.rb +44 -6
  75. data/lib/action_view/helpers/java_script_macros_helper.rb +59 -46
  76. data/lib/action_view/helpers/javascript_helper.rb +71 -10
  77. data/lib/action_view/helpers/javascripts/controls.js +41 -23
  78. data/lib/action_view/helpers/javascripts/dragdrop.js +105 -76
  79. data/lib/action_view/helpers/javascripts/effects.js +293 -163
  80. data/lib/action_view/helpers/javascripts/prototype.js +897 -389
  81. data/lib/action_view/helpers/javascripts/prototype.js.rej +561 -0
  82. data/lib/action_view/helpers/number_helper.rb +111 -65
  83. data/lib/action_view/helpers/prototype_helper.rb +84 -109
  84. data/lib/action_view/helpers/scriptaculous_helper.rb +5 -0
  85. data/lib/action_view/helpers/tag_helper.rb +69 -16
  86. data/lib/action_view/helpers/text_helper.rb +149 -112
  87. data/lib/action_view/helpers/url_helper.rb +200 -107
  88. data/lib/action_view/template_error.rb +66 -42
  89. data/test/abstract_unit.rb +4 -2
  90. data/test/active_record_unit.rb +84 -56
  91. data/test/activerecord/active_record_assertions_test.rb +26 -18
  92. data/test/activerecord/active_record_store_test.rb +4 -36
  93. data/test/activerecord/pagination_test.rb +1 -6
  94. data/test/controller/action_pack_assertions_test.rb +230 -113
  95. data/test/controller/addresses_render_test.rb +2 -6
  96. data/test/controller/assert_select_test.rb +576 -0
  97. data/test/controller/base_test.rb +73 -3
  98. data/test/controller/caching_test.rb +228 -0
  99. data/test/controller/capture_test.rb +12 -10
  100. data/test/controller/cgi_test.rb +89 -12
  101. data/test/controller/components_test.rb +24 -2
  102. data/test/controller/content_type_test.rb +139 -0
  103. data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
  104. data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
  105. data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
  106. data/test/controller/cookie_test.rb +33 -25
  107. data/test/controller/deprecated_instance_variables_test.rb +48 -0
  108. data/test/controller/deprecation/deprecated_base_methods_test.rb +60 -0
  109. data/test/controller/fake_controllers.rb +0 -1
  110. data/test/controller/filters_test.rb +301 -16
  111. data/test/controller/flash_test.rb +19 -2
  112. data/test/controller/helper_test.rb +2 -2
  113. data/test/controller/integration_test.rb +154 -0
  114. data/test/controller/layout_test.rb +115 -1
  115. data/test/controller/mime_responds_test.rb +94 -0
  116. data/test/controller/mime_type_test.rb +9 -0
  117. data/test/controller/new_render_test.rb +161 -11
  118. data/test/controller/raw_post_test.rb +52 -15
  119. data/test/controller/redirect_test.rb +27 -14
  120. data/test/controller/render_test.rb +76 -29
  121. data/test/controller/request_test.rb +55 -4
  122. data/test/controller/resources_test.rb +274 -0
  123. data/test/controller/routing_test.rb +1533 -824
  124. data/test/controller/selector_test.rb +628 -0
  125. data/test/controller/send_file_test.rb +9 -1
  126. data/test/controller/session_management_test.rb +51 -0
  127. data/test/controller/test_test.rb +113 -29
  128. data/test/controller/url_rewriter_test.rb +86 -17
  129. data/test/controller/verification_test.rb +19 -17
  130. data/test/controller/webservice_test.rb +0 -7
  131. data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
  132. data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
  133. data/test/fixtures/content_type/render_default_for_rjs.rjs +1 -0
  134. data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
  135. data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +1 -0
  136. data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +1 -0
  137. data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +1 -0
  138. data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +1 -0
  139. data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +1 -0
  140. data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +1 -0
  141. data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +1 -0
  142. data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +1 -0
  143. data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +1 -0
  144. data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +1 -0
  145. data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +1 -0
  146. data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +1 -0
  147. data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +1 -0
  148. data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +1 -0
  149. data/test/fixtures/multipart/binary_file +0 -0
  150. data/test/fixtures/public/javascripts/application.js +1 -0
  151. data/test/fixtures/test/_hello.rxml +1 -0
  152. data/test/fixtures/test/hello_world_container.rxml +3 -0
  153. data/test/fixtures/topic.rb +2 -2
  154. data/test/template/active_record_helper_test.rb +83 -12
  155. data/test/template/asset_tag_helper_test.rb +75 -95
  156. data/test/template/compiled_templates_test.rb +1 -0
  157. data/test/template/date_helper_test.rb +873 -181
  158. data/test/template/deprecated_helper_test.rb +36 -0
  159. data/test/template/deprecated_instance_variables_test.rb +43 -0
  160. data/test/template/form_helper_test.rb +77 -1
  161. data/test/template/form_options_helper_test.rb +4 -0
  162. data/test/template/form_tag_helper_test.rb +66 -2
  163. data/test/template/java_script_macros_helper_test.rb +4 -1
  164. data/test/template/javascript_helper_test.rb +29 -0
  165. data/test/template/number_helper_test.rb +63 -27
  166. data/test/template/prototype_helper_test.rb +77 -34
  167. data/test/template/tag_helper_test.rb +34 -6
  168. data/test/template/text_helper_test.rb +69 -34
  169. data/test/template/url_helper_test.rb +168 -16
  170. data/test/testing_sandbox.rb +7 -22
  171. metadata +66 -20
  172. data/filler.txt +0 -50
  173. data/lib/action_controller/code_generation.rb +0 -235
  174. data/lib/action_controller/vendor/xml_simple.rb +0 -1019
  175. data/test/controller/caching_filestore.rb +0 -74
  176. data/test/fixtures/application_root/app/controllers/a_class_that_contains_a_controller/poorly_placed_controller.rb +0 -7
  177. data/test/fixtures/application_root/app/controllers/module_that_holds_controllers/nested_controller.rb +0 -3
  178. data/test/fixtures/application_root/app/models/a_class_that_contains_a_controller.rb +0 -7
  179. data/test/fixtures/dont_load.rb +0 -3
@@ -0,0 +1,117 @@
1
+ require 'rexml/document'
2
+ require File.dirname(__FILE__) + "/../vendor/html-scanner/html/document"
3
+
4
+ module ActionController
5
+ module Assertions
6
+ module TagAssertions
7
+ # Asserts that there is a tag/node/element in the body of the response
8
+ # that meets all of the given conditions. The +conditions+ parameter must
9
+ # be a hash of any of the following keys (all are optional):
10
+ #
11
+ # * <tt>:tag</tt>: the node type must match the corresponding value
12
+ # * <tt>:attributes</tt>: a hash. The node's attributes must match the
13
+ # corresponding values in the hash.
14
+ # * <tt>:parent</tt>: a hash. The node's parent must match the
15
+ # corresponding hash.
16
+ # * <tt>:child</tt>: a hash. At least one of the node's immediate children
17
+ # must meet the criteria described by the hash.
18
+ # * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
19
+ # meet the criteria described by the hash.
20
+ # * <tt>:descendant</tt>: a hash. At least one of the node's descendants
21
+ # must meet the criteria described by the hash.
22
+ # * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
23
+ # meet the criteria described by the hash.
24
+ # * <tt>:after</tt>: a hash. The node must be after any sibling meeting
25
+ # the criteria described by the hash, and at least one sibling must match.
26
+ # * <tt>:before</tt>: a hash. The node must be before any sibling meeting
27
+ # the criteria described by the hash, and at least one sibling must match.
28
+ # * <tt>:children</tt>: a hash, for counting children of a node. Accepts
29
+ # the keys:
30
+ # * <tt>:count</tt>: either a number or a range which must equal (or
31
+ # include) the number of children that match.
32
+ # * <tt>:less_than</tt>: the number of matching children must be less
33
+ # than this number.
34
+ # * <tt>:greater_than</tt>: the number of matching children must be
35
+ # greater than this number.
36
+ # * <tt>:only</tt>: another hash consisting of the keys to use
37
+ # to match on the children, and only matching children will be
38
+ # counted.
39
+ # * <tt>:content</tt>: the textual content of the node must match the
40
+ # given value. This will not match HTML tags in the body of a
41
+ # tag--only text.
42
+ #
43
+ # Conditions are matched using the following algorithm:
44
+ #
45
+ # * if the condition is a string, it must be a substring of the value.
46
+ # * if the condition is a regexp, it must match the value.
47
+ # * if the condition is a number, the value must match number.to_s.
48
+ # * if the condition is +true+, the value must not be +nil+.
49
+ # * if the condition is +false+ or +nil+, the value must be +nil+.
50
+ #
51
+ # Usage:
52
+ #
53
+ # # assert that there is a "span" tag
54
+ # assert_tag :tag => "span"
55
+ #
56
+ # # assert that there is a "span" tag with id="x"
57
+ # assert_tag :tag => "span", :attributes => { :id => "x" }
58
+ #
59
+ # # assert that there is a "span" tag using the short-hand
60
+ # assert_tag :span
61
+ #
62
+ # # assert that there is a "span" tag with id="x" using the short-hand
63
+ # assert_tag :span, :attributes => { :id => "x" }
64
+ #
65
+ # # assert that there is a "span" inside of a "div"
66
+ # assert_tag :tag => "span", :parent => { :tag => "div" }
67
+ #
68
+ # # assert that there is a "span" somewhere inside a table
69
+ # assert_tag :tag => "span", :ancestor => { :tag => "table" }
70
+ #
71
+ # # assert that there is a "span" with at least one "em" child
72
+ # assert_tag :tag => "span", :child => { :tag => "em" }
73
+ #
74
+ # # assert that there is a "span" containing a (possibly nested)
75
+ # # "strong" tag.
76
+ # assert_tag :tag => "span", :descendant => { :tag => "strong" }
77
+ #
78
+ # # assert that there is a "span" containing between 2 and 4 "em" tags
79
+ # # as immediate children
80
+ # assert_tag :tag => "span",
81
+ # :children => { :count => 2..4, :only => { :tag => "em" } }
82
+ #
83
+ # # get funky: assert that there is a "div", with an "ul" ancestor
84
+ # # and an "li" parent (with "class" = "enum"), and containing a
85
+ # # "span" descendant that contains text matching /hello world/
86
+ # assert_tag :tag => "div",
87
+ # :ancestor => { :tag => "ul" },
88
+ # :parent => { :tag => "li",
89
+ # :attributes => { :class => "enum" } },
90
+ # :descendant => { :tag => "span",
91
+ # :child => /hello world/ }
92
+ #
93
+ # <strong>Please note</strong: #assert_tag and #assert_no_tag only work
94
+ # with well-formed XHTML. They recognize a few tags as implicitly self-closing
95
+ # (like br and hr and such) but will not work correctly with tags
96
+ # that allow optional closing tags (p, li, td). <em>You must explicitly
97
+ # close all of your tags to use these assertions.</em>
98
+ def assert_tag(*opts)
99
+ clean_backtrace do
100
+ opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
101
+ tag = find_tag(opts)
102
+ assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
103
+ end
104
+ end
105
+
106
+ # Identical to #assert_tag, but asserts that a matching tag does _not_
107
+ # exist. (See #assert_tag for a full discussion of the syntax.)
108
+ def assert_no_tag(*opts)
109
+ clean_backtrace do
110
+ opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
111
+ tag = find_tag(opts)
112
+ assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -2,8 +2,9 @@ require 'action_controller/mime_type'
2
2
  require 'action_controller/request'
3
3
  require 'action_controller/response'
4
4
  require 'action_controller/routing'
5
- require 'action_controller/code_generation'
5
+ require 'action_controller/resources'
6
6
  require 'action_controller/url_rewriter'
7
+ require 'action_controller/status_codes'
7
8
  require 'drb'
8
9
  require 'set'
9
10
 
@@ -27,6 +28,8 @@ module ActionController #:nodoc:
27
28
  end
28
29
  class MissingFile < ActionControllerError #:nodoc:
29
30
  end
31
+ class RenderError < ActionControllerError #:nodoc:
32
+ end
30
33
  class SessionOverflowError < ActionControllerError #:nodoc:
31
34
  DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
32
35
 
@@ -35,23 +38,23 @@ module ActionController #:nodoc:
35
38
  end
36
39
  end
37
40
  class DoubleRenderError < ActionControllerError #:nodoc:
38
- DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and only once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\". Finally, note that to cause a before filter to halt execution of the rest of the filter chain, the filter must return false, explicitly, so \"render(...) and return false\"."
41
+ DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and only once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\". Finally, note that to cause a before filter to halt execution of the rest of the filter chain, the filter must return false, explicitly, so \"render(...) and return false\"."
39
42
 
40
43
  def initialize(message = nil)
41
44
  super(message || DEFAULT_MESSAGE)
42
45
  end
43
46
  end
44
47
  class RedirectBackError < ActionControllerError #:nodoc:
45
- DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify @request.env["HTTP_REFERER"].'
46
-
48
+ DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
49
+
47
50
  def initialize(message = nil)
48
51
  super(message || DEFAULT_MESSAGE)
49
52
  end
50
53
  end
51
54
 
52
- # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed
55
+ # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed
53
56
  # on request and then either render a template or redirect to another action. An action is defined as a public method
54
- # on the controller, which will automatically be made accessible to the web-server through Rails Routes.
57
+ # on the controller, which will automatically be made accessible to the web-server through Rails Routes.
55
58
  #
56
59
  # A sample controller could look like this:
57
60
  #
@@ -59,7 +62,7 @@ module ActionController #:nodoc:
59
62
  # def index
60
63
  # @entries = Entry.find(:all)
61
64
  # end
62
- #
65
+ #
63
66
  # def sign
64
67
  # Entry.create(params[:entry])
65
68
  # redirect_to :action => "index"
@@ -67,11 +70,11 @@ module ActionController #:nodoc:
67
70
  # end
68
71
  #
69
72
  # Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
70
- # after executing code in the action. For example, the +index+ action of the +GuestBookController+ would render the
73
+ # after executing code in the action. For example, the +index+ action of the +GuestBookController+ would render the
71
74
  # template <tt>app/views/guestbook/index.rhtml</tt> by default after populating the <tt>@entries</tt> instance variable.
72
75
  #
73
- # Unlike index, the sign action will not render a template. After performing its main purpose (creating a
74
- # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external
76
+ # Unlike index, the sign action will not render a template. After performing its main purpose (creating a
77
+ # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external
75
78
  # "302 Moved" HTTP response that takes the user to the index action.
76
79
  #
77
80
  # The index and sign represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
@@ -95,7 +98,7 @@ module ActionController #:nodoc:
95
98
  # == Parameters
96
99
  #
97
100
  # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
98
- # which returns a hash. For example, an action that was performed through <tt>/weblog/list?category=All&limit=5</tt> will include
101
+ # which returns a hash. For example, an action that was performed through <tt>/weblog/list?category=All&limit=5</tt> will include
99
102
  # <tt>{ "category" => "All", "limit" => 5 }</tt> in params.
100
103
  #
101
104
  # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
@@ -104,14 +107,14 @@ module ActionController #:nodoc:
104
107
  # <input type="text" name="post[address]" value="hyacintvej">
105
108
  #
106
109
  # A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
107
- # If the address input had been named "post[address][street]", the params would have included
110
+ # If the address input had been named "post[address][street]", the params would have included
108
111
  # <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
109
112
  #
110
113
  # == Sessions
111
114
  #
112
115
  # Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
113
116
  # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
114
- # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
117
+ # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
115
118
  # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
116
119
  #
117
120
  # You can place objects in the session by using the <tt>session</tt> method, which accesses a hash:
@@ -125,13 +128,13 @@ module ActionController #:nodoc:
125
128
  # For removing objects from the session, you can either assign a single key to nil, like <tt>session[:person] = nil</tt>, or you can
126
129
  # remove the entire session with reset_session.
127
130
  #
128
- # By default, sessions are stored on the file system in <tt>RAILS_ROOT/tmp/sessions</tt>. Any object can be placed in the session
131
+ # By default, sessions are stored on the file system in <tt>RAILS_ROOT/tmp/sessions</tt>. Any object can be placed in the session
129
132
  # (as long as it can be Marshalled). But remember that 1000 active sessions each storing a 50kb object could lead to a 50MB store on the filesystem.
130
133
  # In other words, think carefully about size and caching before resorting to the use of the session on the filesystem.
131
134
  #
132
135
  # An alternative to storing sessions on disk is to use ActiveRecordStore to store sessions in your database, which can solve problems
133
136
  # caused by storing sessions in the file system and may speed up your application. To use ActiveRecordStore, uncomment the line:
134
- #
137
+ #
135
138
  # config.action_controller.session_store = :active_record_store
136
139
  #
137
140
  # in your <tt>environment.rb</tt> and run <tt>rake db:sessions:create</tt>.
@@ -205,9 +208,10 @@ module ActionController #:nodoc:
205
208
  #
206
209
  class Base
207
210
  DEFAULT_RENDER_STATUS_CODE = "200 OK"
208
-
209
- include Reloadable::Subclasses
210
-
211
+
212
+ include Reloadable::Deprecated
213
+ include StatusCodes
214
+
211
215
  # Determines whether the view has access to controller internals @request, @response, @session, and @template.
212
216
  # By default, it does.
213
217
  @@view_controller_internals = true
@@ -217,8 +221,8 @@ module ActionController #:nodoc:
217
221
  @@protected_variables_cache = nil
218
222
  cattr_accessor :protected_variables_cache
219
223
 
220
- # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets,
221
- # and images to a dedicated asset server away from the main web server. Example:
224
+ # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets,
225
+ # and images to a dedicated asset server away from the main web server. Example:
222
226
  # ActionController::Base.asset_host = "http://assets.example.com"
223
227
  @@asset_host = ""
224
228
  cattr_accessor :asset_host
@@ -228,7 +232,7 @@ module ActionController #:nodoc:
228
232
  # should instead be implemented in the controller to determine when debugging screens should be shown.
229
233
  @@consider_all_requests_local = true
230
234
  cattr_accessor :consider_all_requests_local
231
-
235
+
232
236
  # Enable or disable the collection of failure information for RoutingErrors.
233
237
  # This information can be extremely useful when tweaking custom routes, but is
234
238
  # pointless once routes have been tested and verified.
@@ -241,43 +245,47 @@ module ActionController #:nodoc:
241
245
  @@allow_concurrency = false
242
246
  cattr_accessor :allow_concurrency
243
247
 
244
- # Modern REST web services often need to submit complex data to the web application.
245
- # The param_parsers hash lets you register handlers wich will process the http body and add parameters to the
248
+ # Modern REST web services often need to submit complex data to the web application.
249
+ # The param_parsers hash lets you register handlers which will process the http body and add parameters to the
246
250
  # <tt>params</tt> hash. These handlers are invoked for post and put requests.
247
251
  #
248
- # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instanciated
252
+ # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instanciated
249
253
  # in the <tt>params</tt>. This allows XML requests to mask themselves as regular form submissions, so you can have one
250
254
  # action serve both regular forms and web service requests.
251
- #
255
+ #
252
256
  # Example of doing your own parser for a custom content type:
253
257
  #
254
- # ActionController::Base.param_parsers[Mime::Type.lookup('application/atom+xml')] = Proc.new do |data|
255
- # node = REXML::Document.new(post)
258
+ # ActionController::Base.param_parsers[Mime::Type.lookup('application/atom+xml')] = Proc.new do |data|
259
+ # node = REXML::Document.new(post)
256
260
  # { node.root.name => node.root }
257
261
  # end
258
262
  #
259
- # Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the
263
+ # Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the
260
264
  # root node for such requests. The new default is to keep the root, such that "<r><name>David</name></r>" results
261
- # in params[:r][:name] for "David" instead of params[:name]. To get the old behavior, you can
265
+ # in params[:r][:name] for "David" instead of params[:name]. To get the old behavior, you can
262
266
  # re-register XmlSimple as application/xml handler ike this:
263
267
  #
264
- # ActionController::Base.param_parsers[Mime::XML] =
268
+ # ActionController::Base.param_parsers[Mime::XML] =
265
269
  # Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' => false) }
266
270
  #
267
271
  # A YAML parser is also available and can be turned on with:
268
272
  #
269
273
  # ActionController::Base.param_parsers[Mime::YAML] = :yaml
270
274
  @@param_parsers = { Mime::XML => :xml_simple }
271
- cattr_accessor :param_parsers
275
+ cattr_accessor :param_parsers
276
+
277
+ # Controls the default charset for all renders.
278
+ @@default_charset = "utf-8"
279
+ cattr_accessor :default_charset
272
280
 
273
281
  # Template root determines the base from which template references will be made. So a call to render("test/template")
274
282
  # will be converted to "#{template_root}/test/template.rhtml".
275
283
  class_inheritable_accessor :template_root
276
-
284
+
277
285
  # The logger is used for generating information on the action run-time (including benchmarking) if available.
278
286
  # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
279
287
  cattr_accessor :logger
280
-
288
+
281
289
  # Determines which template class should be used by ActionController.
282
290
  cattr_accessor :template_class
283
291
 
@@ -286,39 +294,42 @@ module ActionController #:nodoc:
286
294
 
287
295
  # Holds the request object that's primarily used to get environment variables through access like
288
296
  # <tt>request.env["REQUEST_URI"]</tt>.
289
- attr_accessor :request
290
-
297
+ attr_internal :request
298
+
291
299
  # Holds a hash of all the GET, POST, and Url parameters passed to the action. Accessed like <tt>params["post_id"]</tt>
292
300
  # to get the post_id. No type casts are made, so all values are returned as strings.
293
- attr_accessor :params
294
-
295
- # Holds the response object that's primarily used to set additional HTTP headers through access like
301
+ attr_internal :params
302
+
303
+ # Holds the response object that's primarily used to set additional HTTP headers through access like
296
304
  # <tt>response.headers["Cache-Control"] = "no-cache"</tt>. Can also be used to access the final body HTML after a template
297
305
  # has been rendered through response.body -- useful for <tt>after_filter</tt>s that wants to manipulate the output,
298
306
  # such as a OutputCompressionFilter.
299
- attr_accessor :response
300
-
307
+ attr_internal :response
308
+
301
309
  # Holds a hash of objects in the session. Accessed like <tt>session[:person]</tt> to get the object tied to the "person"
302
310
  # key. The session will hold any type of object as values, but the key should be a string or symbol.
303
- attr_accessor :session
304
-
311
+ attr_internal :session
312
+
305
313
  # Holds a hash of header names and values. Accessed like <tt>headers["Cache-Control"]</tt> to get the value of the Cache-Control
306
314
  # directive. Values should always be specified as strings.
307
- attr_accessor :headers
308
-
315
+ attr_internal :headers
316
+
309
317
  # Holds the hash of variables that are passed on to the template class to be made available to the view. This hash
310
318
  # is generated by taking a snapshot of all the instance variables in the current scope just before a template is rendered.
311
319
  attr_accessor :assigns
312
320
 
313
321
  # Returns the name of the action this controller is processing.
314
322
  attr_accessor :action_name
315
-
323
+
324
+ # Templates that are exempt from layouts
325
+ @@exempt_from_layout = Set.new([/\.rjs$/])
326
+
316
327
  class << self
317
328
  # Factory for the standard create, process loop where the controller is discarded after processing.
318
329
  def process(request, response) #:nodoc:
319
330
  new.process(request, response)
320
331
  end
321
-
332
+
322
333
  # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
323
334
  def controller_class_name
324
335
  @controller_class_name ||= name.demodulize
@@ -328,7 +339,7 @@ module ActionController #:nodoc:
328
339
  def controller_name
329
340
  @controller_name ||= controller_class_name.sub(/Controller$/, '').underscore
330
341
  end
331
-
342
+
332
343
  # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
333
344
  def controller_path
334
345
  @controller_path ||= name.gsub(/Controller$/, '').underscore
@@ -346,7 +357,7 @@ module ActionController #:nodoc:
346
357
  def hide_action(*names)
347
358
  write_inheritable_attribute(:hidden_actions, hidden_actions | names.collect { |n| n.to_s })
348
359
  end
349
-
360
+
350
361
  # Replace sensitive paramater data from the request log.
351
362
  # Filters paramaters that have any of the arguments as a substring.
352
363
  # Looks in all subhashes of the param hash for keys to filter.
@@ -368,7 +379,7 @@ module ActionController #:nodoc:
368
379
  # => reverses the value to all keys matching /secret/i
369
380
  #
370
381
  # filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i }
371
- # => reverses the value to all keys matching /secret/i, and
382
+ # => reverses the value to all keys matching /secret/i, and
372
383
  # replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
373
384
  def filter_parameter_logging(*filter_words, &block)
374
385
  parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0
@@ -380,12 +391,12 @@ module ActionController #:nodoc:
380
391
  if key =~ parameter_filter
381
392
  filtered_parameters[key] = '[FILTERED]'
382
393
  elsif value.is_a?(Hash)
383
- filtered_parameters[key] = filter_parameters(value)
394
+ filtered_parameters[key] = filter_parameters(value)
384
395
  elsif block_given?
385
396
  key, value = key.dup, value.dup
386
397
  yield key, value
387
398
  filtered_parameters[key] = value
388
- else
399
+ else
389
400
  filtered_parameters[key] = value
390
401
  end
391
402
  end
@@ -393,9 +404,20 @@ module ActionController #:nodoc:
393
404
  filtered_parameters
394
405
  end
395
406
  end
407
+
408
+ # Don't render layouts for templates with the given extensions.
409
+ def exempt_from_layout(*extensions)
410
+ @@exempt_from_layout.merge extensions.collect { |extension|
411
+ if extension.is_a?(Regexp)
412
+ extension
413
+ else
414
+ /\.#{Regexp.escape(extension.to_s)}$/
415
+ end
416
+ }
417
+ end
396
418
  end
397
419
 
398
- public
420
+ public
399
421
  # Extracts the action_name from the request parameters and performs that action.
400
422
  def process(request, response, method = :perform_action, *arguments) #:nodoc:
401
423
  initialize_template_class(response)
@@ -403,25 +425,26 @@ module ActionController #:nodoc:
403
425
  initialize_current_url
404
426
  assign_names
405
427
  forget_variables_added_to_assigns
406
-
428
+
407
429
  log_processing
408
430
  send(method, *arguments)
409
-
431
+
432
+ assign_default_content_type_and_charset
410
433
  response
411
434
  ensure
412
435
  process_cleanup
413
436
  end
414
437
 
415
- # Returns a URL that has been rewritten according to the options hash and the defined Routes.
438
+ # Returns a URL that has been rewritten according to the options hash and the defined Routes.
416
439
  # (For doing a complete redirect, use redirect_to).
417
440
  #  
418
441
  # <tt>url_for</tt> is used to:
419
442
  #  
420
443
  # All keys given to url_for are forwarded to the Route module, save for the following:
421
- # * <tt>:anchor</tt> -- specifies the anchor name to be appended to the path. For example,
422
- # <tt>url_for :controller => 'posts', :action => 'show', :id => 10, :anchor => 'comments'</tt>
444
+ # * <tt>:anchor</tt> -- specifies the anchor name to be appended to the path. For example,
445
+ # <tt>url_for :controller => 'posts', :action => 'show', :id => 10, :anchor => 'comments'</tt>
423
446
  # will produce "/posts/show/10#comments".
424
- # * <tt>:only_path</tt> -- if true, returns the absolute URL (omitting the protocol, host name, and port)
447
+ # * <tt>:only_path</tt> -- if true, returns the relative URL (omitting the protocol, host name, and port) (<tt>false</tt> by default)
425
448
  # * <tt>:trailing_slash</tt> -- if true, adds a trailing slash, as in "/archive/2005/". Note that this
426
449
  # is currently not recommended since it breaks caching.
427
450
  # * <tt>:host</tt> -- overrides the default (current) host if provided
@@ -472,7 +495,7 @@ module ActionController #:nodoc:
472
495
  # displayed on:
473
496
  #
474
497
  # url_for :controller => 'posts', :action => nil
475
- #
498
+ #
476
499
  # If you explicitly want to create a URL that's almost the same as the current URL, you can do so using the
477
500
  # :overwrite_params options. Say for your posts you have different views for showing and printing them.
478
501
  # Then, in the show view, you get the URL for the print view like this
@@ -483,9 +506,20 @@ module ActionController #:nodoc:
483
506
  # would have slashed-off the path components after the changed action.
484
507
  def url_for(options = {}, *parameters_for_method_reference) #:doc:
485
508
  case options
486
- when String then options
487
- when Symbol then send(options, *parameters_for_method_reference)
488
- when Hash then @url.rewrite(rewrite_options(options))
509
+ when String
510
+ options
511
+
512
+ when Symbol
513
+ ActiveSupport::Deprecation.warn(
514
+ "You called url_for(:#{options}), which is a deprecated API call. Instead you should use the named " +
515
+ "route directly, like #{options}(). Using symbols and parameters with url_for will be removed from Rails 2.0.",
516
+ caller
517
+ )
518
+
519
+ send(options, *parameters_for_method_reference)
520
+
521
+ when Hash
522
+ @url.rewrite(rewrite_options(options))
489
523
  end
490
524
  end
491
525
 
@@ -499,6 +533,11 @@ module ActionController #:nodoc:
499
533
  self.class.controller_name
500
534
  end
501
535
 
536
+ # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
537
+ def controller_path
538
+ self.class.controller_path
539
+ end
540
+
502
541
  def session_enabled?
503
542
  request.session_options[:disabled] != false
504
543
  end
@@ -507,7 +546,7 @@ module ActionController #:nodoc:
507
546
  # Renders the content that will be returned to the browser as the response body.
508
547
  #
509
548
  # === Rendering an action
510
- #
549
+ #
511
550
  # Action rendering is the most common form and the type used automatically by Action Controller when nothing else is
512
551
  # specified. By default, actions are rendered within the current layout (if one exists).
513
552
  #
@@ -523,47 +562,54 @@ module ActionController #:nodoc:
523
562
  # render :action => "long_goal", :layout => "spectacular"
524
563
  #
525
564
  # _Deprecation_ _notice_: This used to have the signatures <tt>render_action("action", status = 200)</tt>,
526
- # <tt>render_without_layout("controller/action", status = 200)</tt>, and
565
+ # <tt>render_without_layout("controller/action", status = 200)</tt>, and
527
566
  # <tt>render_with_layout("controller/action", status = 200, layout)</tt>.
528
567
  #
529
568
  # === Rendering partials
530
- #
531
- # Partial rendering is most commonly used together with Ajax calls that only update one or a few elements on a page
569
+ #
570
+ # Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page
532
571
  # without reloading. Rendering of partials from the controller makes it possible to use the same partial template in
533
572
  # both the full-page rendering (by calling it from within the template) and when sub-page updates happen (from the
534
573
  # controller action responding to Ajax calls). By default, the current layout is not used.
535
574
  #
536
- # # Renders the partial located at app/views/controller/_win.r(html|xml)
537
- # render :partial => "win"
575
+ # # Renders the same partial with a local variable.
576
+ # render :partial => "person", :locals => { :name => "david" }
538
577
  #
539
- # # Renders the partial with a status code of 500 (internal error)
540
- # render :partial => "broken", :status => 500
578
+ # # Renders a collection of the same partial by making each element
579
+ # # of @winners available through the local variable "person" as it
580
+ # # builds the complete response.
581
+ # render :partial => "person", :collection => @winners
582
+ #
583
+ # # Renders the same collection of partials, but also renders the
584
+ # # person_divider partial between each person partial.
585
+ # render :partial => "person", :collection => @winners, :spacer_template => "person_divider"
541
586
  #
542
- # # Renders the same partial but also makes a local variable available to it
543
- # render :partial => "win", :locals => { :name => "david" }
587
+ # # Renders a collection of partials located in a view subfolder
588
+ # # outside of our current controller. In this example we will be
589
+ # # rendering app/views/shared/_note.r(html|xml) Inside the partial
590
+ # # each element of @new_notes is available as the local var "note".
591
+ # render :partial => "shared/note", :collection => @new_notes
544
592
  #
545
- # # Renders a collection of the same partial by making each element of @wins available through
546
- # # the local variable "win" as it builds the complete response
547
- # render :partial => "win", :collection => @wins
593
+ # # Renders the partial with a status code of 500 (internal error).
594
+ # render :partial => "broken", :status => 500
548
595
  #
549
- # # Renders the same collection of partials, but also renders the win_divider partial in between
550
- # # each win partial.
551
- # render :partial => "win", :collection => @wins, :spacer_template => "win_divider"
596
+ # Note that the partial filename must also be a valid Ruby variable name,
597
+ # so e.g. 2005 and register-user are invalid.
552
598
  #
553
- # _Deprecation_ _notice_: This used to have the signatures
599
+ # _Deprecation_ _notice_: This used to have the signatures
554
600
  # <tt>render_partial(partial_path = default_template_name, object = nil, local_assigns = {})</tt> and
555
601
  # <tt>render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = {})</tt>.
556
602
  #
557
603
  # === Rendering a template
558
- #
559
- # Template rendering works just like action rendering except that it takes a path relative to the template root.
604
+ #
605
+ # Template rendering works just like action rendering except that it takes a path relative to the template root.
560
606
  # The current layout is automatically applied.
561
607
  #
562
608
  # # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.rhtml)
563
609
  # render :template => "weblog/show"
564
610
  #
565
611
  # === Rendering a file
566
- #
612
+ #
567
613
  # File rendering works just like action rendering except that it takes a filesystem path. By default, the path
568
614
  # is assumed to be absolute, and the current layout is not applied.
569
615
  #
@@ -581,7 +627,7 @@ module ActionController #:nodoc:
581
627
  # _Deprecation_ _notice_: This used to have the signature <tt>render_file(path, status = 200)</tt>
582
628
  #
583
629
  # === Rendering text
584
- #
630
+ #
585
631
  # Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text
586
632
  # rendering is not done within the active layout.
587
633
  #
@@ -589,17 +635,38 @@ module ActionController #:nodoc:
589
635
  # render :text => "hello world!"
590
636
  #
591
637
  # # Renders the clear text "Explosion!" with status code 500
592
- # render :text => "Explosion!", :status => 500
638
+ # render :text => "Explosion!", :status => 500
593
639
  #
594
640
  # # Renders the clear text "Hi there!" within the current active layout (if one exists)
595
641
  # render :text => "Explosion!", :layout => true
596
642
  #
597
- # # Renders the clear text "Hi there!" within the layout
643
+ # # Renders the clear text "Hi there!" within the layout
598
644
  # # placed in "app/views/layouts/special.r(html|xml)"
599
645
  # render :text => "Explosion!", :layout => "special"
600
646
  #
647
+ # The :text option can also accept a Proc object, which can be used to manually control the page generation. This should
648
+ # generally be avoided, as it violates the separation between code and content, and because almost everything that can be
649
+ # done with this method can also be done more cleanly using one of the other rendering methods, most notably templates.
650
+ #
651
+ # # Renders "Hello from code!"
652
+ # render :text => proc { |response, output| output.write("Hello from code!") }
653
+ #
601
654
  # _Deprecation_ _notice_: This used to have the signature <tt>render_text("text", status = 200)</tt>
602
655
  #
656
+ # === Rendering JSON
657
+ #
658
+ # Rendering JSON sets the content type to text/x-json and optionally wraps the JSON in a callback. It is expected
659
+ # that the response will be eval'd for use as a data structure.
660
+ #
661
+ # # Renders '{name: "David"}'
662
+ # render :json => {:name => "David"}.to_json
663
+ #
664
+ # Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag),
665
+ # so the callback option is provided for these cases.
666
+ #
667
+ # # Renders 'show({name: "David"})'
668
+ # render :json => {:name => "David"}.to_json, :callback => 'show'
669
+ #
603
670
  # === Rendering an inline template
604
671
  #
605
672
  # Rendering of an inline template works as a cross between text and action rendering where the source for the template
@@ -607,7 +674,7 @@ module ActionController #:nodoc:
607
674
  # and the current layout is not used.
608
675
  #
609
676
  # # Renders "hello, hello, hello, again"
610
- # render :inline => "<%= 'hello, ' * 3 + 'again' %>"
677
+ # render :inline => "<%= 'hello, ' * 3 + 'again' %>"
611
678
  #
612
679
  # # Renders "<p>Good seeing you!</p>" using Builder
613
680
  # render :inline => "xml.p { 'Good seeing you!' }", :type => :rxml
@@ -640,17 +707,27 @@ module ActionController #:nodoc:
640
707
  def render(options = nil, deprecated_status = nil, &block) #:doc:
641
708
  raise DoubleRenderError, "Can only render or redirect once per action" if performed?
642
709
 
643
- # Backwards compatibility
644
- unless options.is_a?(Hash)
645
- if options == :update
646
- options = {:update => true}
647
- else
648
- return render_file(options || default_template_name, deprecated_status, true)
710
+ if options.nil?
711
+ return render_file(default_template_name, deprecated_status, true)
712
+ else
713
+ # Backwards compatibility
714
+ unless options.is_a?(Hash)
715
+ if options == :update
716
+ options = { :update => true }
717
+ else
718
+ ActiveSupport::Deprecation.warn(
719
+ "You called render('#{options}'), which is a deprecated API call. Instead you use " +
720
+ "render :file => #{options}. Calling render with just a string will be removed from Rails 2.0.",
721
+ caller
722
+ )
723
+
724
+ return render_file(options, deprecated_status, true)
725
+ end
649
726
  end
650
727
  end
651
728
 
652
729
  if content_type = options[:content_type]
653
- headers["Content-Type"] = content_type
730
+ response.content_type = content_type.to_s
654
731
  end
655
732
 
656
733
  if text = options[:text]
@@ -662,16 +739,21 @@ module ActionController #:nodoc:
662
739
 
663
740
  elsif template = options[:template]
664
741
  render_file(template, options[:status], true)
665
-
742
+
666
743
  elsif inline = options[:inline]
667
744
  render_template(inline, options[:status], options[:type], options[:locals] || {})
668
-
745
+
669
746
  elsif action_name = options[:action]
670
- render_action(action_name, options[:status], options[:layout])
747
+ ActiveSupport::Deprecation.silence do
748
+ render_action(action_name, options[:status], options[:layout])
749
+ end
671
750
 
672
751
  elsif xml = options[:xml]
673
752
  render_xml(xml, options[:status])
674
753
 
754
+ elsif json = options[:json]
755
+ render_json(json, options[:callback], options[:status])
756
+
675
757
  elsif partial = options[:partial]
676
758
  partial = default_template_name if partial == true
677
759
  if collection = options[:collection]
@@ -683,17 +765,17 @@ module ActionController #:nodoc:
683
765
  elsif options[:update]
684
766
  add_variables_to_assigns
685
767
  @template.send :evaluate_assigns
686
-
768
+
687
769
  generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
688
770
  render_javascript(generator.to_s)
689
771
 
690
772
  elsif options[:nothing]
691
773
  # Safari doesn't pass the headers of the return if the response is zero length
692
774
  render_text(" ", options[:status])
693
-
775
+
694
776
  else
695
777
  render_file(default_template_name, options[:status], true)
696
-
778
+
697
779
  end
698
780
  end
699
781
  end
@@ -701,21 +783,19 @@ module ActionController #:nodoc:
701
783
  # Renders according to the same rules as <tt>render</tt>, but returns the result in a string instead
702
784
  # of sending it as the response body to the browser.
703
785
  def render_to_string(options = nil, &block) #:doc:
704
- result = render(options, &block)
705
-
786
+ ActiveSupport::Deprecation.silence { render(options, &block) }
787
+ ensure
706
788
  erase_render_results
707
789
  forget_variables_added_to_assigns
708
790
  reset_variables_added_to_assigns
709
-
710
- result
711
- end
791
+ end
712
792
 
713
793
  def render_action(action_name, status = nil, with_layout = true) #:nodoc:
714
794
  template = default_template_name(action_name.to_s)
715
- if with_layout && !template_exempt_from_layout?(template)
716
- render_with_layout(template, status)
795
+ if with_layout && !template_exempt_from_layout?(template)
796
+ render_with_layout(:file => template, :status => status, :use_full_path => true, :layout => true)
717
797
  else
718
- render_without_layout(template, status)
798
+ render_without_layout(:file => template, :status => status, :use_full_path => true)
719
799
  end
720
800
  end
721
801
 
@@ -731,22 +811,36 @@ module ActionController #:nodoc:
731
811
  render_text(@template.render_template(type, template, nil, local_assigns), status)
732
812
  end
733
813
 
734
- def render_text(text = nil, status = nil) #:nodoc:
814
+ def render_text(text = nil, status = nil, append_response = false) #:nodoc:
735
815
  @performed_render = true
736
- @response.headers['Status'] = (status || DEFAULT_RENDER_STATUS_CODE).to_s
737
- @response.body = text
816
+
817
+ response.headers['Status'] = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
818
+
819
+ if append_response
820
+ response.body ||= ''
821
+ response.body << text
822
+ else
823
+ response.body = text
824
+ end
738
825
  end
739
826
 
740
- def render_javascript(javascript, status = nil) #:nodoc:
741
- @response.headers['Content-Type'] = 'text/javascript; charset=UTF-8'
742
- render_text(javascript, status)
827
+ def render_javascript(javascript, status = nil, append_response = true) #:nodoc:
828
+ response.content_type = Mime::JS
829
+ render_text(javascript, status, append_response)
743
830
  end
744
831
 
745
832
  def render_xml(xml, status = nil) #:nodoc:
746
- @response.headers['Content-Type'] = 'application/xml'
833
+ response.content_type = Mime::XML
747
834
  render_text(xml, status)
748
835
  end
749
836
 
837
+ def render_json(json, callback = nil, status = nil) #:nodoc:
838
+ json = "#{callback}(#{json})" unless callback.blank?
839
+
840
+ response.content_type = Mime::JSON
841
+ render_text(json, status)
842
+ end
843
+
750
844
  def render_nothing(status = nil) #:nodoc:
751
845
  render_text(' ', status)
752
846
  end
@@ -770,13 +864,52 @@ module ActionController #:nodoc:
770
864
  end
771
865
 
772
866
 
867
+ # Return a response that has no content (merely headers). The options
868
+ # argument is interpreted to be a hash of header names and values.
869
+ # This allows you to easily return a response that consists only of
870
+ # significant headers:
871
+ #
872
+ # head :created, :location => person_path(@person)
873
+ #
874
+ # It can also be used to return exceptional conditions:
875
+ #
876
+ # return head(:method_not_allowed) unless request.post?
877
+ # return head(:bad_request) unless valid_request?
878
+ # render
879
+ def head(*args)
880
+ if args.length > 2
881
+ raise ArgumentError, "too many arguments to head"
882
+ elsif args.empty?
883
+ raise ArgumentError, "too few arguments to head"
884
+ elsif args.length == 2
885
+ status = args.shift
886
+ options = args.shift
887
+ elsif args.first.is_a?(Hash)
888
+ options = args.first
889
+ else
890
+ status = args.first
891
+ options = {}
892
+ end
893
+
894
+ raise ArgumentError, "head requires an options hash" if !options.is_a?(Hash)
895
+
896
+ status = interpret_status(status || options.delete(:status) || :ok)
897
+
898
+ options.each do |key, value|
899
+ headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
900
+ end
901
+
902
+ render :nothing => true, :status => status
903
+ end
904
+
905
+
773
906
  # Clears the rendered results, allowing for another render to be performed.
774
907
  def erase_render_results #:nodoc:
775
- @response.body = nil
908
+ response.body = nil
776
909
  @performed_render = false
777
910
  end
778
-
779
- # Clears the redirected results from the headers, resets the status to 200 and returns
911
+
912
+ # Clears the redirected results from the headers, resets the status to 200 and returns
780
913
  # the URL that was used to redirect or nil if there was no redirected URL
781
914
  # Note that +redirect_to+ will change the body of the response to indicate a redirection.
782
915
  # The response body is not reset here, see +erase_render_results+
@@ -785,7 +918,7 @@ module ActionController #:nodoc:
785
918
  response.redirected_to = nil
786
919
  response.redirected_to_method_params = nil
787
920
  response.headers['Status'] = DEFAULT_RENDER_STATUS_CODE
788
- response.headers.delete('location')
921
+ response.headers.delete('Location')
789
922
  end
790
923
 
791
924
  # Erase both render and redirect results
@@ -801,7 +934,7 @@ module ActionController #:nodoc:
801
934
  options
802
935
  end
803
936
  end
804
-
937
+
805
938
  # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
806
939
  # the form of a hash, just like the one you would use for url_for directly. Example:
807
940
  #
@@ -814,7 +947,7 @@ module ActionController #:nodoc:
814
947
  # by this method.
815
948
  def default_url_options(options) #:doc:
816
949
  end
817
-
950
+
818
951
  # Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
819
952
  #
820
953
  # * <tt>Hash</tt>: The URL will be generated by calling url_for with the +options+.
@@ -822,7 +955,7 @@ module ActionController #:nodoc:
822
955
  # * <tt>String not containing a protocol</tt>: The current protocol and host is prepended to the string.
823
956
  # * <tt>:back</tt>: Back to the page that issued the request. Useful for forms that are triggered from multiple places.
824
957
  # Short-hand for redirect_to(request.env["HTTP_REFERER"])
825
- #
958
+ #
826
959
  # Examples:
827
960
  # redirect_to :action => "show", :id => 5
828
961
  # redirect_to "http://www.rubyonrails.org"
@@ -845,7 +978,7 @@ module ActionController #:nodoc:
845
978
 
846
979
  when String
847
980
  redirect_to(request.protocol + request.host_with_port + options)
848
-
981
+
849
982
  when :back
850
983
  request.env["HTTP_REFERER"] ? redirect_to(request.env["HTTP_REFERER"]) : raise(RedirectBackError)
851
984
 
@@ -854,12 +987,13 @@ module ActionController #:nodoc:
854
987
  redirect_to(url_for(options))
855
988
  response.redirected_to = options
856
989
  else
990
+ # TOOD: Deprecate me!
857
991
  redirect_to(url_for(options, *parameters_for_method_reference))
858
992
  response.redirected_to, response.redirected_to_method_params = options, parameters_for_method_reference
859
993
  end
860
994
  end
861
995
  end
862
-
996
+
863
997
  # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that
864
998
  # intermediate caches shouldn't cache the response.
865
999
  #
@@ -867,29 +1001,29 @@ module ActionController #:nodoc:
867
1001
  # expires_in 20.minutes
868
1002
  # expires_in 3.hours, :private => false
869
1003
  # expires in 3.hours, 'max-stale' => 5.hours, :private => nil, :public => true
870
- #
1004
+ #
871
1005
  # This method will overwrite an existing Cache-Control header.
872
1006
  # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
873
1007
  def expires_in(seconds, options = {}) #:doc:
874
1008
  cache_options = { 'max-age' => seconds, 'private' => true }.symbolize_keys.merge!(options.symbolize_keys)
875
1009
  cache_options.delete_if { |k,v| v.nil? or v == false }
876
1010
  cache_control = cache_options.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"}
877
- @response.headers["Cache-Control"] = cache_control.join(', ')
1011
+ response.headers["Cache-Control"] = cache_control.join(', ')
878
1012
  end
879
-
1013
+
880
1014
  # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or
881
1015
  # intermediate caches (like caching proxy servers).
882
1016
  def expires_now #:doc:
883
- @response.headers["Cache-Control"] = "no-cache"
1017
+ response.headers["Cache-Control"] = "no-cache"
884
1018
  end
885
1019
 
886
1020
  # Resets the session by clearing out all the objects stored within and initializing a new session object.
887
1021
  def reset_session #:doc:
888
- @request.reset_session
889
- @session = @request.session
890
- @response.session = @session
1022
+ request.reset_session
1023
+ @_session = request.session
1024
+ response.session = @_session
891
1025
  end
892
-
1026
+
893
1027
  private
894
1028
  def self.view_class
895
1029
  @view_class ||=
@@ -905,48 +1039,71 @@ module ActionController #:nodoc:
905
1039
 
906
1040
  def initialize_template_class(response)
907
1041
  raise "You must assign a template class through ActionController.template_class= before processing a request" unless @@template_class
908
-
1042
+
909
1043
  response.template = self.class.view_class.new(self.class.view_root, {}, self)
910
1044
  response.redirected_to = nil
911
1045
  @performed_render = @performed_redirect = false
912
1046
  end
913
-
1047
+
914
1048
  def assign_shortcuts(request, response)
915
- @request, @params, @cookies = request, request.parameters, request.cookies
1049
+ @_request, @_params, @_cookies = request, request.parameters, request.cookies
1050
+
1051
+ @_response = response
1052
+ @_response.session = request.session
1053
+
1054
+ @_session = @_response.session
1055
+ @template = @_response.template
1056
+ @assigns = @_response.template.assigns
916
1057
 
917
- @response = response
918
- @response.session = request.session
1058
+ @_headers = @_response.headers
919
1059
 
920
- @session = @response.session
921
- @template = @response.template
922
- @assigns = @response.template.assigns
923
-
924
- @headers = @response.headers
1060
+ assign_deprecated_shortcuts(request, response)
1061
+ end
1062
+
1063
+
1064
+ # TODO: assigns cookies headers params request response template
1065
+ DEPRECATED_INSTANCE_VARIABLES = %w(cookies flash headers params request response session)
1066
+
1067
+ # Gone after 1.2.
1068
+ def assign_deprecated_shortcuts(request, response)
1069
+ DEPRECATED_INSTANCE_VARIABLES.each do |method|
1070
+ var = "@#{method}"
1071
+ if instance_variables.include?(var)
1072
+ value = instance_variable_get(var)
1073
+ unless ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy === value
1074
+ raise "Deprecating #{var}, but it's already set to #{value.inspect}! Use the #{method}= writer method instead of setting #{var} directly."
1075
+ end
1076
+ end
1077
+ instance_variable_set var, ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, method)
1078
+ end
925
1079
  end
926
-
1080
+
927
1081
  def initialize_current_url
928
- @url = UrlRewriter.new(@request, @params.clone())
1082
+ @url = UrlRewriter.new(request, params.clone)
929
1083
  end
930
1084
 
931
1085
  def log_processing
932
1086
  if logger
933
1087
  logger.info "\n\nProcessing #{controller_class_name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
934
- logger.info " Session ID: #{@session.session_id}" if @session and @session.respond_to?(:session_id)
935
- logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(@params).inspect : @params.inspect}"
1088
+ logger.info " Session ID: #{@_session.session_id}" if @_session and @_session.respond_to?(:session_id)
1089
+ logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(params).inspect : params.inspect}"
936
1090
  end
937
1091
  end
938
-
1092
+
939
1093
  def perform_action
940
- if self.class.action_methods.include?(action_name) || self.class.action_methods.include?('method_missing')
1094
+ if self.class.action_methods.include?(action_name)
941
1095
  send(action_name)
942
1096
  render unless performed?
1097
+ elsif respond_to? :method_missing
1098
+ send(:method_missing, action_name)
1099
+ render unless performed?
943
1100
  elsif template_exists? && template_public?
944
1101
  render
945
1102
  else
946
1103
  raise UnknownAction, "No action responded to #{action_name}", caller
947
1104
  end
948
1105
  end
949
-
1106
+
950
1107
  def performed?
951
1108
  @performed_render || @performed_redirect
952
1109
  end
@@ -954,7 +1111,16 @@ module ActionController #:nodoc:
954
1111
  def assign_names
955
1112
  @action_name = (params['action'] || 'index')
956
1113
  end
957
-
1114
+
1115
+ def assign_default_content_type_and_charset
1116
+ response.content_type ||= Mime::HTML
1117
+ response.charset ||= self.class.default_charset unless sending_file?
1118
+ end
1119
+
1120
+ def sending_file?
1121
+ response.headers["Content-Transfer-Encoding"] == "binary"
1122
+ end
1123
+
958
1124
  def action_methods
959
1125
  self.class.action_methods
960
1126
  end
@@ -970,17 +1136,17 @@ module ActionController #:nodoc:
970
1136
  @variables_added = true
971
1137
  end
972
1138
  end
973
-
1139
+
974
1140
  def forget_variables_added_to_assigns
975
1141
  @variables_added = nil
976
1142
  end
977
-
1143
+
978
1144
  def reset_variables_added_to_assigns
979
1145
  @template.instance_variable_set("@assigns_added", nil)
980
1146
  end
981
1147
 
982
1148
  def add_instance_variables_to_assigns
983
- @@protected_variables_cache ||= protected_instance_variables.inject({}) { |h, k| h[k] = true; h }
1149
+ @@protected_variables_cache ||= Set.new(protected_instance_variables)
984
1150
  instance_variables.each do |var|
985
1151
  next if @@protected_variables_cache.include?(var)
986
1152
  @assigns[var[1..-1]] = instance_variable_get(var)
@@ -988,33 +1154,36 @@ module ActionController #:nodoc:
988
1154
  end
989
1155
 
990
1156
  def add_class_variables_to_assigns
991
- %w( template_root logger template_class ignore_missing_templates ).each do |cvar|
1157
+ %w(template_root logger template_class ignore_missing_templates).each do |cvar|
992
1158
  @assigns[cvar] = self.send(cvar)
993
1159
  end
994
1160
  end
995
1161
 
996
1162
  def protected_instance_variables
997
1163
  if view_controller_internals
998
- [ "@assigns", "@performed_redirect", "@performed_render" ]
1164
+ %w(@assigns @performed_redirect @performed_render)
999
1165
  else
1000
- [ "@assigns", "@performed_redirect", "@performed_render", "@request", "@response", "@session", "@cookies", "@template", "@request_origin", "@parent_controller" ]
1166
+ %w(@assigns @performed_redirect @performed_render
1167
+ @_request @request @_response @response @_params @params
1168
+ @_session @session @_cookies @cookies
1169
+ @template @request_origin @parent_controller)
1001
1170
  end
1002
1171
  end
1003
1172
 
1004
1173
  def request_origin
1005
1174
  # this *needs* to be cached!
1006
1175
  # otherwise you'd get different results if calling it more than once
1007
- @request_origin ||= "#{@request.remote_ip} at #{Time.now.to_s(:db)}"
1176
+ @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}"
1008
1177
  end
1009
-
1178
+
1010
1179
  def complete_request_uri
1011
- "#{@request.protocol}#{@request.host}#{@request.request_uri}"
1180
+ "#{request.protocol}#{request.host}#{request.request_uri}"
1012
1181
  end
1013
1182
 
1014
1183
  def close_session
1015
- @session.close unless @session.nil? || Hash === @session
1184
+ @_session.close if @_session && @_session.respond_to?(:close)
1016
1185
  end
1017
-
1186
+
1018
1187
  def template_exists?(template_name = default_template_name)
1019
1188
  @template.file_exists?(template_name)
1020
1189
  end
@@ -1024,7 +1193,9 @@ module ActionController #:nodoc:
1024
1193
  end
1025
1194
 
1026
1195
  def template_exempt_from_layout?(template_name = default_template_name)
1027
- template_name =~ /\.rjs$/ || (@template.pick_template_extension(template_name) == :rjs rescue false)
1196
+ extension = @template.pick_template_extension(template_name) rescue nil
1197
+ name_with_extension = !template_name.include?('.') && extension ? "#{template_name}.#{extension}" : template_name
1198
+ extension == :rjs || @@exempt_from_layout.any? { |ext| name_with_extension =~ ext }
1028
1199
  end
1029
1200
 
1030
1201
  def assert_existence_of_template_file(template_name)
@@ -1044,11 +1215,11 @@ module ActionController #:nodoc:
1044
1215
  end
1045
1216
  "#{self.class.controller_path}/#{action_name}"
1046
1217
  end
1047
-
1218
+
1048
1219
  def strip_out_controller(path)
1049
1220
  path.split('/', 2).last
1050
1221
  end
1051
-
1222
+
1052
1223
  def template_path_includes_controller?(path)
1053
1224
  self.class.controller_path.split('/')[-1] == path.split('/')[0]
1054
1225
  end