actionpack 2.0.5 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. data/CHANGELOG +149 -7
  2. data/MIT-LICENSE +1 -1
  3. data/README +1 -1
  4. data/Rakefile +5 -6
  5. data/lib/action_controller.rb +2 -2
  6. data/lib/action_controller/assertions/model_assertions.rb +2 -1
  7. data/lib/action_controller/assertions/response_assertions.rb +4 -2
  8. data/lib/action_controller/assertions/routing_assertions.rb +3 -3
  9. data/lib/action_controller/assertions/selector_assertions.rb +30 -27
  10. data/lib/action_controller/assertions/tag_assertions.rb +3 -3
  11. data/lib/action_controller/base.rb +103 -129
  12. data/lib/action_controller/benchmarking.rb +3 -3
  13. data/lib/action_controller/caching.rb +41 -652
  14. data/lib/action_controller/caching/actions.rb +144 -0
  15. data/lib/action_controller/caching/fragments.rb +138 -0
  16. data/lib/action_controller/caching/pages.rb +154 -0
  17. data/lib/action_controller/caching/sql_cache.rb +18 -0
  18. data/lib/action_controller/caching/sweeping.rb +97 -0
  19. data/lib/action_controller/cgi_ext/cookie.rb +27 -23
  20. data/lib/action_controller/cgi_ext/stdinput.rb +1 -0
  21. data/lib/action_controller/cgi_process.rb +6 -4
  22. data/lib/action_controller/components.rb +7 -6
  23. data/lib/action_controller/cookies.rb +31 -19
  24. data/lib/action_controller/dispatcher.rb +51 -84
  25. data/lib/action_controller/filters.rb +295 -421
  26. data/lib/action_controller/flash.rb +1 -6
  27. data/lib/action_controller/headers.rb +31 -0
  28. data/lib/action_controller/helpers.rb +26 -9
  29. data/lib/action_controller/http_authentication.rb +1 -1
  30. data/lib/action_controller/integration.rb +65 -13
  31. data/lib/action_controller/layout.rb +24 -39
  32. data/lib/action_controller/mime_responds.rb +7 -3
  33. data/lib/action_controller/mime_type.rb +25 -9
  34. data/lib/action_controller/mime_types.rb +1 -1
  35. data/lib/action_controller/polymorphic_routes.rb +32 -17
  36. data/lib/action_controller/record_identifier.rb +10 -4
  37. data/lib/action_controller/request.rb +46 -30
  38. data/lib/action_controller/request_forgery_protection.rb +10 -9
  39. data/lib/action_controller/request_profiler.rb +29 -8
  40. data/lib/action_controller/rescue.rb +24 -24
  41. data/lib/action_controller/resources.rb +66 -23
  42. data/lib/action_controller/response.rb +2 -2
  43. data/lib/action_controller/routing.rb +113 -1229
  44. data/lib/action_controller/routing/builder.rb +204 -0
  45. data/lib/action_controller/{routing_optimisation.rb → routing/optimisations.rb} +13 -12
  46. data/lib/action_controller/routing/recognition_optimisation.rb +158 -0
  47. data/lib/action_controller/routing/route.rb +240 -0
  48. data/lib/action_controller/routing/route_set.rb +435 -0
  49. data/lib/action_controller/routing/routing_ext.rb +46 -0
  50. data/lib/action_controller/routing/segments.rb +283 -0
  51. data/lib/action_controller/session/active_record_store.rb +13 -8
  52. data/lib/action_controller/session/cookie_store.rb +20 -17
  53. data/lib/action_controller/session_management.rb +10 -3
  54. data/lib/action_controller/streaming.rb +45 -31
  55. data/lib/action_controller/test_case.rb +33 -23
  56. data/lib/action_controller/test_process.rb +39 -35
  57. data/lib/action_controller/url_rewriter.rb +18 -12
  58. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +1 -1
  59. data/lib/action_pack.rb +1 -1
  60. data/lib/action_pack/version.rb +2 -2
  61. data/lib/action_view.rb +11 -3
  62. data/lib/action_view/base.rb +73 -390
  63. data/lib/action_view/helpers/active_record_helper.rb +83 -62
  64. data/lib/action_view/helpers/asset_tag_helper.rb +101 -44
  65. data/lib/action_view/helpers/atom_feed_helper.rb +35 -7
  66. data/lib/action_view/helpers/benchmark_helper.rb +5 -3
  67. data/lib/action_view/helpers/cache_helper.rb +3 -2
  68. data/lib/action_view/helpers/capture_helper.rb +1 -2
  69. data/lib/action_view/helpers/date_helper.rb +104 -82
  70. data/lib/action_view/helpers/form_helper.rb +148 -75
  71. data/lib/action_view/helpers/form_options_helper.rb +44 -23
  72. data/lib/action_view/helpers/form_tag_helper.rb +22 -13
  73. data/lib/action_view/helpers/javascripts/controls.js +1 -1
  74. data/lib/action_view/helpers/javascripts/dragdrop.js +1 -1
  75. data/lib/action_view/helpers/javascripts/effects.js +1 -1
  76. data/lib/action_view/helpers/number_helper.rb +10 -3
  77. data/lib/action_view/helpers/prototype_helper.rb +61 -29
  78. data/lib/action_view/helpers/record_tag_helper.rb +3 -3
  79. data/lib/action_view/helpers/sanitize_helper.rb +23 -17
  80. data/lib/action_view/helpers/scriptaculous_helper.rb +86 -60
  81. data/lib/action_view/helpers/text_helper.rb +153 -125
  82. data/lib/action_view/helpers/url_helper.rb +83 -28
  83. data/lib/action_view/inline_template.rb +20 -0
  84. data/lib/action_view/partial_template.rb +70 -0
  85. data/lib/action_view/partials.rb +31 -73
  86. data/lib/action_view/template.rb +127 -0
  87. data/lib/action_view/template_error.rb +8 -7
  88. data/lib/action_view/template_finder.rb +177 -0
  89. data/lib/action_view/template_handler.rb +18 -1
  90. data/lib/action_view/template_handlers/builder.rb +10 -2
  91. data/lib/action_view/template_handlers/compilable.rb +128 -0
  92. data/lib/action_view/template_handlers/erb.rb +37 -2
  93. data/lib/action_view/template_handlers/rjs.rb +14 -1
  94. data/lib/action_view/test_case.rb +58 -0
  95. data/test/abstract_unit.rb +1 -1
  96. data/test/active_record_unit.rb +3 -6
  97. data/test/activerecord/active_record_store_test.rb +1 -2
  98. data/test/activerecord/render_partial_with_record_identification_test.rb +158 -41
  99. data/test/adv_attr_test.rb +20 -0
  100. data/test/controller/action_pack_assertions_test.rb +16 -19
  101. data/test/controller/addresses_render_test.rb +1 -1
  102. data/test/controller/assert_select_test.rb +13 -6
  103. data/test/controller/base_test.rb +48 -2
  104. data/test/controller/benchmark_test.rb +1 -2
  105. data/test/controller/caching_test.rb +282 -21
  106. data/test/controller/capture_test.rb +1 -1
  107. data/test/controller/cgi_test.rb +1 -1
  108. data/test/controller/components_test.rb +1 -1
  109. data/test/controller/content_type_test.rb +2 -2
  110. data/test/controller/cookie_test.rb +13 -2
  111. data/test/controller/custom_handler_test.rb +14 -10
  112. data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -1
  113. data/test/controller/dispatcher_test.rb +31 -49
  114. data/test/controller/fake_controllers.rb +17 -0
  115. data/test/controller/fake_models.rb +6 -0
  116. data/test/controller/filter_params_test.rb +14 -8
  117. data/test/controller/filters_test.rb +44 -16
  118. data/test/controller/flash_test.rb +2 -2
  119. data/test/controller/header_test.rb +14 -0
  120. data/test/controller/helper_test.rb +19 -15
  121. data/test/controller/html-scanner/document_test.rb +1 -2
  122. data/test/controller/html-scanner/node_test.rb +1 -2
  123. data/test/controller/html-scanner/sanitizer_test.rb +8 -5
  124. data/test/controller/html-scanner/tag_node_test.rb +1 -2
  125. data/test/controller/html-scanner/text_node_test.rb +2 -3
  126. data/test/controller/html-scanner/tokenizer_test.rb +8 -2
  127. data/test/controller/http_authentication_test.rb +1 -1
  128. data/test/controller/integration_test.rb +14 -16
  129. data/test/controller/integration_upload_test.rb +43 -0
  130. data/test/controller/layout_test.rb +26 -6
  131. data/test/controller/mime_responds_test.rb +39 -7
  132. data/test/controller/mime_type_test.rb +29 -5
  133. data/test/controller/new_render_test.rb +105 -34
  134. data/test/controller/polymorphic_routes_test.rb +32 -20
  135. data/test/controller/record_identifier_test.rb +38 -2
  136. data/test/controller/redirect_test.rb +21 -1
  137. data/test/controller/render_test.rb +59 -15
  138. data/test/controller/request_forgery_protection_test.rb +92 -5
  139. data/test/controller/request_test.rb +64 -6
  140. data/test/controller/rescue_test.rb +22 -6
  141. data/test/controller/resources_test.rb +102 -14
  142. data/test/controller/routing_test.rb +231 -19
  143. data/test/controller/selector_test.rb +2 -2
  144. data/test/controller/send_file_test.rb +14 -3
  145. data/test/controller/session/cookie_store_test.rb +16 -4
  146. data/test/controller/session/mem_cache_store_test.rb +3 -4
  147. data/test/controller/session_fixation_test.rb +1 -1
  148. data/test/controller/session_management_test.rb +23 -1
  149. data/test/controller/test_test.rb +39 -18
  150. data/test/controller/url_rewriter_test.rb +35 -1
  151. data/test/controller/verification_test.rb +1 -1
  152. data/test/controller/view_paths_test.rb +15 -12
  153. data/test/controller/webservice_test.rb +48 -3
  154. data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
  155. data/test/fixtures/company.rb +1 -0
  156. data/test/fixtures/customers/_customer.html.erb +1 -0
  157. data/test/fixtures/db_definitions/sqlite.sql +6 -0
  158. data/test/fixtures/functional_caching/_partial.erb +3 -0
  159. data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
  160. data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
  161. data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
  162. data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
  163. data/test/fixtures/mascot.rb +3 -0
  164. data/test/fixtures/mascots.yml +4 -0
  165. data/test/fixtures/mascots/_mascot.html.erb +1 -0
  166. data/test/fixtures/multipart/boundary_problem_file +10 -0
  167. data/test/fixtures/public/javascripts/application.js +1 -0
  168. data/test/fixtures/public/javascripts/controls.js +1 -0
  169. data/test/fixtures/public/javascripts/dragdrop.js +1 -0
  170. data/test/fixtures/public/javascripts/effects.js +1 -0
  171. data/test/fixtures/public/javascripts/prototype.js +1 -0
  172. data/test/fixtures/public/javascripts/version.1.0.js +1 -0
  173. data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
  174. data/test/fixtures/reply.rb +1 -0
  175. data/test/fixtures/shared.html.erb +1 -0
  176. data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
  177. data/test/fixtures/test/_customer_counter.erb +1 -0
  178. data/test/fixtures/test/_form.erb +1 -0
  179. data/test/fixtures/test/_labelling_form.erb +1 -0
  180. data/test/fixtures/test/_raise.html.erb +1 -0
  181. data/test/fixtures/test/greeting.js.rjs +1 -0
  182. data/test/fixtures/topics/_topic.html.erb +1 -0
  183. data/test/template/active_record_helper_test.rb +25 -8
  184. data/test/template/asset_tag_helper_test.rb +100 -17
  185. data/test/template/atom_feed_helper_test.rb +29 -1
  186. data/test/template/benchmark_helper_test.rb +10 -22
  187. data/test/template/date_helper_test.rb +455 -153
  188. data/test/template/erb_util_test.rb +10 -42
  189. data/test/template/form_helper_test.rb +192 -66
  190. data/test/template/form_options_helper_test.rb +19 -8
  191. data/test/template/form_tag_helper_test.rb +11 -8
  192. data/test/template/javascript_helper_test.rb +3 -9
  193. data/test/template/number_helper_test.rb +6 -3
  194. data/test/template/prototype_helper_test.rb +27 -40
  195. data/test/template/record_tag_helper_test.rb +54 -0
  196. data/test/template/sanitize_helper_test.rb +5 -6
  197. data/test/template/scriptaculous_helper_test.rb +7 -13
  198. data/test/template/tag_helper_test.rb +3 -6
  199. data/test/template/template_finder_test.rb +73 -0
  200. data/test/template/template_object_test.rb +95 -0
  201. data/test/template/test_test.rb +56 -0
  202. data/test/template/text_helper_test.rb +46 -33
  203. data/test/template/url_helper_test.rb +8 -10
  204. metadata +65 -12
  205. data/lib/action_view/compiled_templates.rb +0 -69
  206. data/test/action_view_test.rb +0 -44
  207. data/test/activerecord/fixtures_test.rb +0 -24
  208. data/test/controller/fragment_store_setting_test.rb +0 -47
  209. data/test/template/compiled_templates_test.rb +0 -197
  210. data/test/template/deprecate_ivars_test.rb +0 -51
@@ -0,0 +1,97 @@
1
+ module ActionController #:nodoc:
2
+ module Caching
3
+ # Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change.
4
+ # They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:
5
+ #
6
+ # class ListSweeper < ActionController::Caching::Sweeper
7
+ # observe List, Item
8
+ #
9
+ # def after_save(record)
10
+ # list = record.is_a?(List) ? record : record.list
11
+ # expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
12
+ # expire_action(:controller => "lists", :action => "all")
13
+ # list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
14
+ # end
15
+ # end
16
+ #
17
+ # The sweeper is assigned in the controllers that wish to have its job performed using the <tt>cache_sweeper</tt> class method:
18
+ #
19
+ # class ListsController < ApplicationController
20
+ # caches_action :index, :show, :public, :feed
21
+ # cache_sweeper :list_sweeper, :only => [ :edit, :destroy, :share ]
22
+ # end
23
+ #
24
+ # In the example above, four actions are cached and three actions are responsible for expiring those caches.
25
+ #
26
+ # You can also name an explicit class in the declaration of a sweeper, which is needed if the sweeper is in a module:
27
+ #
28
+ # class ListsController < ApplicationController
29
+ # caches_action :index, :show, :public, :feed
30
+ # cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
31
+ # end
32
+ module Sweeping
33
+ def self.included(base) #:nodoc:
34
+ base.extend(ClassMethods)
35
+ end
36
+
37
+ module ClassMethods #:nodoc:
38
+ def cache_sweeper(*sweepers)
39
+ configuration = sweepers.extract_options!
40
+
41
+ sweepers.each do |sweeper|
42
+ ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
43
+ sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(sweeper.to_s.classify) : sweeper).instance
44
+
45
+ if sweeper_instance.is_a?(Sweeper)
46
+ around_filter(sweeper_instance, :only => configuration[:only])
47
+ else
48
+ after_filter(sweeper_instance, :only => configuration[:only])
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
56
+ class Sweeper < ActiveRecord::Observer #:nodoc:
57
+ attr_accessor :controller
58
+
59
+ def before(controller)
60
+ self.controller = controller
61
+ callback(:before) if controller.perform_caching
62
+ end
63
+
64
+ def after(controller)
65
+ callback(:after) if controller.perform_caching
66
+ # Clean up, so that the controller can be collected after this request
67
+ self.controller = nil
68
+ end
69
+
70
+ protected
71
+ # gets the action cache path for the given options.
72
+ def action_path_for(options)
73
+ ActionController::Caching::Actions::ActionCachePath.path_for(controller, options)
74
+ end
75
+
76
+ # Retrieve instance variables set in the controller.
77
+ def assigns(key)
78
+ controller.instance_variable_get("@#{key}")
79
+ end
80
+
81
+ private
82
+ def callback(timing)
83
+ controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
84
+ action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
85
+
86
+ send!(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
87
+ send!(action_callback_method_name) if respond_to?(action_callback_method_name, true)
88
+ end
89
+
90
+ def method_missing(method, *arguments)
91
+ return if @controller.nil?
92
+ @controller.send!(method, *arguments)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -6,25 +6,24 @@ class CGI #:nodoc:
6
6
  attr_accessor :name, :value, :path, :domain, :expires
7
7
  attr_reader :secure, :http_only
8
8
 
9
- # Create a new CGI::Cookie object.
9
+ # Creates a new CGI::Cookie object.
10
10
  #
11
11
  # The contents of the cookie can be specified as a +name+ and one
12
12
  # or more +value+ arguments. Alternatively, the contents can
13
13
  # be specified as a single hash argument. The possible keywords of
14
14
  # this hash are as follows:
15
15
  #
16
- # name:: the name of the cookie. Required.
17
- # value:: the cookie's value or list of values.
18
- # path:: the path for which this cookie applies. Defaults to the
19
- # base directory of the CGI script.
20
- # domain:: the domain for which this cookie applies.
21
- # expires:: the time at which this cookie expires, as a +Time+ object.
22
- # secure:: whether this cookie is a secure cookie or not (default to
23
- # false). Secure cookies are only transmitted to HTTPS
24
- # servers.
25
- # http_only:: whether this cookie can be accessed by client side scripts (e.g. document.cookie) or only over HTTP
26
- # More details: http://msdn2.microsoft.com/en-us/library/system.web.httpcookie.httponly.aspx
27
- # Defaults to false.
16
+ # * <tt>:name</tt> - The name of the cookie. Required.
17
+ # * <tt>:value</tt> - The cookie's value or list of values.
18
+ # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the
19
+ # base directory of the CGI script.
20
+ # * <tt>:domain</tt> - The domain for which this cookie applies.
21
+ # * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
22
+ # * <tt>:secure</tt> - Whether this cookie is a secure cookie or not (defaults to
23
+ # +false+). Secure cookies are only transmitted to HTTPS servers.
24
+ # * <tt>:http_only</tt> - Whether this cookie can be accessed by client side scripts (e.g. document.cookie) or only over HTTP.
25
+ # More details in http://msdn2.microsoft.com/en-us/library/system.web.httpcookie.httponly.aspx. Defaults to +false+.
26
+ #
28
27
  # These keywords correspond to attributes of the cookie object.
29
28
  def initialize(name = '', *value)
30
29
  if name.kind_of?(String)
@@ -37,7 +36,7 @@ class CGI #:nodoc:
37
36
  @path = nil
38
37
  else
39
38
  @name = name['name']
40
- @value = Array(name['value'])
39
+ @value = (name['value'].kind_of?(String) ? [name['value']] : Array(name['value'])).delete_if(&:blank?)
41
40
  @domain = name['domain']
42
41
  @expires = name['expires']
43
42
  @secure = name['secure'] || false
@@ -56,17 +55,17 @@ class CGI #:nodoc:
56
55
  super(@value)
57
56
  end
58
57
 
59
- # Set whether the Cookie is a secure cookie or not.
58
+ # Sets whether the Cookie is a secure cookie or not.
60
59
  def secure=(val)
61
60
  @secure = val == true
62
61
  end
63
62
 
64
- # Set whether the Cookie is an HTTP only cookie or not.
63
+ # Sets whether the Cookie is an HTTP only cookie or not.
65
64
  def http_only=(val)
66
65
  @http_only = val == true
67
66
  end
68
67
 
69
- # Convert the Cookie to its string representation.
68
+ # Converts the Cookie to its string representation.
70
69
  def to_s
71
70
  buf = ''
72
71
  buf << @name << '='
@@ -79,23 +78,28 @@ class CGI #:nodoc:
79
78
  buf
80
79
  end
81
80
 
82
- # Parse a raw cookie string into a hash of cookie-name=>Cookie
81
+ # FIXME: work around broken 1.8.7 DelegateClass#respond_to?
82
+ def respond_to?(method, include_private = false)
83
+ return true if super(method)
84
+ return __getobj__.respond_to?(method, include_private)
85
+ end
86
+
87
+ # Parses a raw cookie string into a hash of <tt>cookie-name => cookie-object</tt>
83
88
  # pairs.
84
89
  #
85
90
  # cookies = CGI::Cookie::parse("raw_cookie_string")
86
- # # { "name1" => cookie1, "name2" => cookie2, ... }
91
+ # # => { "name1" => cookie1, "name2" => cookie2, ... }
87
92
  #
88
93
  def self.parse(raw_cookie)
89
94
  cookies = Hash.new([])
90
95
 
91
96
  if raw_cookie
92
97
  raw_cookie.split(/;\s?/).each do |pairs|
93
- name, values = pairs.split('=',2)
94
- next unless name and values
98
+ name, value = pairs.split('=',2)
99
+ next unless name and value
95
100
  name = CGI::unescape(name)
96
- values = values.split('&').collect!{|v| CGI::unescape(v) }
97
101
  unless cookies.has_key?(name)
98
- cookies[name] = new(name, *values)
102
+ cookies[name] = new(name, CGI::unescape(value))
99
103
  end
100
104
  end
101
105
  end
@@ -16,6 +16,7 @@ module ActionController
16
16
 
17
17
  def initialize_with_stdinput(type = nil, stdinput = $stdin)
18
18
  @stdinput = stdinput
19
+ @stdinput.set_encoding(Encoding::BINARY) if @stdinput.respond_to?(:set_encoding)
19
20
  initialize_without_stdinput(type || 'query')
20
21
  end
21
22
  end
@@ -3,7 +3,7 @@ require 'action_controller/session/cookie_store'
3
3
 
4
4
  module ActionController #:nodoc:
5
5
  class Base
6
- # Process a request extracted from an CGI object and return a response. Pass false as <tt>session_options</tt> to disable
6
+ # Process a request extracted from a CGI object and return a response. Pass false as <tt>session_options</tt> to disable
7
7
  # sessions (large performance increase if sessions are not needed). The <tt>session_options</tt> are the same as for CGI::Session:
8
8
  #
9
9
  # * <tt>:database_manager</tt> - standard options are CGI::Session::FileStore, CGI::Session::MemoryStore, and CGI::Session::PStore
@@ -15,9 +15,9 @@ module ActionController #:nodoc:
15
15
  # * <tt>:new_session</tt> - if true, force creation of a new session. If not set, a new session is only created if none currently
16
16
  # exists. If false, a new session is never created, and if none currently exists and the +session_id+ option is not set,
17
17
  # an ArgumentError is raised.
18
- # * <tt>:session_expires</tt> - the time the current session expires, as a +Time+ object. If not set, the session will continue
18
+ # * <tt>:session_expires</tt> - the time the current session expires, as a Time object. If not set, the session will continue
19
19
  # indefinitely.
20
- # * <tt>:session_domain</tt> - the hostname domain for which this session is valid. If not set, defaults to the hostname of the
20
+ # * <tt>:session_domain</tt> - the hostname domain for which this session is valid. If not set, defaults to the hostname of the
21
21
  # server.
22
22
  # * <tt>:session_secure</tt> - if +true+, this session will only work over HTTPS.
23
23
  # * <tt>:session_path</tt> - the path for which this session applies. Defaults to the directory of the CGI script.
@@ -34,7 +34,8 @@ module ActionController #:nodoc:
34
34
 
35
35
  class CgiRequest < AbstractRequest #:nodoc:
36
36
  attr_accessor :cgi, :session_options
37
- class SessionFixationAttempt < StandardError; end #:nodoc:
37
+ class SessionFixationAttempt < StandardError #:nodoc:
38
+ end
38
39
 
39
40
  DEFAULT_SESSION_OPTIONS = {
40
41
  :database_manager => CGI::Session::CookieStore, # store data in cookie
@@ -64,6 +65,7 @@ module ActionController #:nodoc:
64
65
  # variable is already set, wrap it in a StringIO.
65
66
  def body
66
67
  if raw_post = env['RAW_POST_DATA']
68
+ raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
67
69
  StringIO.new(raw_post)
68
70
  else
69
71
  @cgi.stdinput
@@ -39,12 +39,7 @@ module ActionController #:nodoc:
39
39
  base.class_eval do
40
40
  include InstanceMethods
41
41
  extend ClassMethods
42
-
43
- helper do
44
- def render_component(options)
45
- @controller.send!(:render_component_as_string, options)
46
- end
47
- end
42
+ helper HelperMethods
48
43
 
49
44
  # If this controller was instantiated to process a component request,
50
45
  # +parent_controller+ points to the instantiator of this controller.
@@ -67,6 +62,12 @@ module ActionController #:nodoc:
67
62
  end
68
63
  end
69
64
 
65
+ module HelperMethods
66
+ def render_component(options)
67
+ @controller.send!(:render_component_as_string, options)
68
+ end
69
+ end
70
+
70
71
  module InstanceMethods
71
72
  # Extracts the action_name from the request parameters and performs that action.
72
73
  def process_with_components(request, response, method = :perform_action, *arguments) #:nodoc:
@@ -1,31 +1,38 @@
1
1
  module ActionController #:nodoc:
2
- # Cookies are read and written through ActionController#cookies. The cookies being read are what were received along with the request,
3
- # the cookies being written are what will be sent out with the response. Cookies are read by value (so you won't get the cookie object
4
- # itself back -- just the value it holds). Examples for writing:
2
+ # Cookies are read and written through ActionController#cookies.
5
3
  #
6
- # cookies[:user_name] = "david" # => Will set a simple session cookie
4
+ # The cookies being read are the ones received along with the request, the cookies
5
+ # being written will be sent out with the response. Reading a cookie does not get
6
+ # the cookie object itself back, just the value it holds.
7
+ #
8
+ # Examples for writing:
9
+ #
10
+ # # Sets a simple session cookie.
11
+ # cookies[:user_name] = "david"
12
+ #
13
+ # # Sets a cookie that expires in 1 hour.
7
14
  # cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now }
8
- # # => Will set a cookie that expires in 1 hour
9
15
  #
10
16
  # Examples for reading:
11
17
  #
12
18
  # cookies[:user_name] # => "david"
13
- # cookies.size # => 2
19
+ # cookies.size # => 2
14
20
  #
15
21
  # Example for deleting:
16
22
  #
17
23
  # cookies.delete :user_name
18
24
  #
19
- # All the option symbols for setting cookies are:
25
+ # The option symbols for setting cookies are:
20
26
  #
21
- # * <tt>value</tt> - the cookie's value or list of values (as an array).
22
- # * <tt>path</tt> - the path for which this cookie applies. Defaults to the root of the application.
23
- # * <tt>domain</tt> - the domain for which this cookie applies.
24
- # * <tt>expires</tt> - the time at which this cookie expires, as a +Time+ object.
25
- # * <tt>secure</tt> - whether this cookie is a secure cookie or not (default to false).
26
- # Secure cookies are only transmitted to HTTPS servers.
27
- # * <tt>http_only</tt> - whether this cookie is accessible via scripting or only HTTP (defaults to false).
28
-
27
+ # * <tt>:value</tt> - The cookie's value or list of values (as an array).
28
+ # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
29
+ # of the application.
30
+ # * <tt>:domain</tt> - The domain for which this cookie applies.
31
+ # * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
32
+ # * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers.
33
+ # Default is +false+.
34
+ # * <tt>:http_only</tt> - Whether this cookie is accessible via scripting or
35
+ # only HTTP. Defaults to +false+.
29
36
  module Cookies
30
37
  def self.included(base)
31
38
  base.helper_method :cookies
@@ -45,8 +52,7 @@ module ActionController #:nodoc:
45
52
  update(@cookies)
46
53
  end
47
54
 
48
- # Returns the value of the cookie by +name+ -- or nil if no such cookie exists. You set new cookies using cookies[]=
49
- # (for simple name/value cookies without options).
55
+ # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
50
56
  def [](name)
51
57
  cookie = @cookies[name.to_s]
52
58
  if cookie && cookie.respond_to?(:value)
@@ -54,6 +60,8 @@ module ActionController #:nodoc:
54
60
  end
55
61
  end
56
62
 
63
+ # Sets the cookie named +name+. The second argument may be the very cookie
64
+ # value, or a hash of options as documented above.
57
65
  def []=(name, options)
58
66
  if options.is_a?(Hash)
59
67
  options = options.inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
@@ -66,14 +74,18 @@ module ActionController #:nodoc:
66
74
  end
67
75
 
68
76
  # Removes the cookie on the client machine by setting the value to an empty string
69
- # and setting its expiration date into the past. Like []=, you can pass in an options
70
- # hash to delete cookies with extra data such as a +path+.
77
+ # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
78
+ # an options hash to delete cookies with extra data such as a <tt>:path</tt>.
71
79
  def delete(name, options = {})
72
80
  options.stringify_keys!
73
81
  set_cookie(options.merge("name" => name.to_s, "value" => "", "expires" => Time.at(0)))
74
82
  end
75
83
 
76
84
  private
85
+ # Builds a CGI::Cookie object and adds the cookie to the response headers.
86
+ #
87
+ # The path of the cookie defaults to "/" if there's none in +options+, and
88
+ # everything is passed to the CGI::Cookie constructor.
77
89
  def set_cookie(options) #:doc:
78
90
  options["path"] = "/" unless options["path"]
79
91
  cookie = CGI::Cookie.new(options)
@@ -2,27 +2,39 @@ module ActionController
2
2
  # Dispatches requests to the appropriate controller and takes care of
3
3
  # reloading the app after each request when Dependencies.load? is true.
4
4
  class Dispatcher
5
+ @@guard = Mutex.new
6
+
5
7
  class << self
8
+ def define_dispatcher_callbacks(cache_classes)
9
+ unless cache_classes
10
+ # Development mode callbacks
11
+ before_dispatch :reload_application
12
+ after_dispatch :cleanup_application
13
+ end
14
+
15
+ # Common callbacks
16
+ to_prepare :load_application_controller do
17
+ begin
18
+ require_dependency 'application' unless defined?(::ApplicationController)
19
+ rescue LoadError => error
20
+ raise unless error.message =~ /application\.rb/
21
+ end
22
+ end
23
+
24
+ if defined?(ActiveRecord)
25
+ before_dispatch { ActiveRecord::Base.verify_active_connections! }
26
+ to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers }
27
+ end
28
+
29
+ after_dispatch :flush_logger if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:flush)
30
+ end
31
+
6
32
  # Backward-compatible class method takes CGI-specific args. Deprecated
7
33
  # in favor of Dispatcher.new(output, request, response).dispatch.
8
34
  def dispatch(cgi = nil, session_options = CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
9
35
  new(output).dispatch_cgi(cgi, session_options)
10
36
  end
11
37
 
12
- # Declare a block to be called before each dispatch.
13
- # Run in the order declared.
14
- def before_dispatch(*method_names, &block)
15
- callbacks[:before].concat method_names
16
- callbacks[:before] << block if block_given?
17
- end
18
-
19
- # Declare a block to be called after each dispatch.
20
- # Run in reverse of the order declared.
21
- def after_dispatch(*method_names, &block)
22
- callbacks[:after].concat method_names
23
- callbacks[:after] << block if block_given?
24
- end
25
-
26
38
  # Add a preparation callback. Preparation callbacks are run before every
27
39
  # request in development mode, and before the first request in production
28
40
  # mode.
@@ -32,16 +44,9 @@ module ActionController
32
44
  # existing callback. Passing an identifier is a suggested practice if the
33
45
  # code adding a preparation block may be reloaded.
34
46
  def to_prepare(identifier = nil, &block)
35
- # Already registered: update the existing callback
36
- if identifier
37
- if callback = callbacks[:prepare].assoc(identifier)
38
- callback[1] = block
39
- else
40
- callbacks[:prepare] << [identifier, block]
41
- end
42
- else
43
- callbacks[:prepare] << block
44
- end
47
+ @prepare_dispatch_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new
48
+ callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier)
49
+ @prepare_dispatch_callbacks | callback
45
50
  end
46
51
 
47
52
  # If the block raises, send status code as a last-ditch response.
@@ -86,37 +91,26 @@ module ActionController
86
91
  end
87
92
 
88
93
  cattr_accessor :error_file_path
89
- self.error_file_path = "#{::RAILS_ROOT}/public" if defined? ::RAILS_ROOT
90
-
91
- cattr_accessor :callbacks
92
- self.callbacks = Hash.new { |h, k| h[k] = [] }
93
-
94
- cattr_accessor :unprepared
95
- self.unprepared = true
96
-
94
+ self.error_file_path = Rails.public_path if defined?(Rails.public_path)
97
95
 
98
- before_dispatch :reload_application
99
- before_dispatch :prepare_application
100
- after_dispatch :flush_logger
101
- after_dispatch :cleanup_application
102
-
103
- if defined? ActiveRecord
104
- to_prepare :activerecord_instantiate_observers do
105
- ActiveRecord::Base.instantiate_observers
106
- end
107
- end
96
+ include ActiveSupport::Callbacks
97
+ define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
108
98
 
109
99
  def initialize(output, request = nil, response = nil)
110
100
  @output, @request, @response = output, request, response
111
101
  end
112
102
 
113
103
  def dispatch
114
- run_callbacks :before
115
- handle_request
116
- rescue Exception => exception
117
- failsafe_rescue exception
118
- ensure
119
- run_callbacks :after, :reverse_each
104
+ @@guard.synchronize do
105
+ begin
106
+ run_callbacks :before_dispatch
107
+ handle_request
108
+ rescue Exception => exception
109
+ failsafe_rescue exception
110
+ ensure
111
+ run_callbacks :after_dispatch, :enumerator => :reverse_each
112
+ end
113
+ end
120
114
  end
121
115
 
122
116
  def dispatch_cgi(cgi, session_options)
@@ -130,39 +124,23 @@ module ActionController
130
124
  end
131
125
 
132
126
  def reload_application
133
- if Dependencies.load?
134
- Routing::Routes.reload
135
- self.unprepared = true
136
- end
137
- end
127
+ # Run prepare callbacks before every request in development mode
128
+ run_callbacks :prepare_dispatch
138
129
 
139
- def prepare_application(force = false)
140
- begin
141
- require_dependency 'application' unless defined?(::ApplicationController)
142
- rescue LoadError => error
143
- raise unless error.message =~ /application\.rb/
144
- end
145
-
146
- ActiveRecord::Base.verify_active_connections! if defined?(ActiveRecord)
147
-
148
- if unprepared || force
149
- run_callbacks :prepare
150
- self.unprepared = false
151
- end
130
+ Routing::Routes.reload
131
+ ActionView::TemplateFinder.reload! unless ActionView::Base.cache_template_loading
152
132
  end
153
133
 
154
134
  # Cleanup the application by clearing out loaded classes so they can
155
135
  # be reloaded on the next request without restarting the server.
156
- def cleanup_application(force = false)
157
- if Dependencies.load? || force
158
- ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
159
- Dependencies.clear
160
- ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
161
- end
136
+ def cleanup_application
137
+ ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
138
+ Dependencies.clear
139
+ ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
162
140
  end
163
141
 
164
142
  def flush_logger
165
- RAILS_DEFAULT_LOGGER.flush if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:flush)
143
+ RAILS_DEFAULT_LOGGER.flush
166
144
  end
167
145
 
168
146
  protected
@@ -171,17 +149,6 @@ module ActionController
171
149
  @controller.process(@request, @response).out(@output)
172
150
  end
173
151
 
174
- def run_callbacks(kind, enumerator = :each)
175
- callbacks[kind].send!(enumerator) do |callback|
176
- case callback
177
- when Proc; callback.call(self)
178
- when String, Symbol; send!(callback)
179
- when Array; callback[1].call(self)
180
- else raise ArgumentError, "Unrecognized callback #{callback.inspect}"
181
- end
182
- end
183
- end
184
-
185
152
  def failsafe_rescue(exception)
186
153
  self.class.failsafe_response(@output, '500 Internal Server Error', exception) do
187
154
  if @controller ||= defined?(::ApplicationController) ? ::ApplicationController : Base