actionpack 2.0.5 → 2.1.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 (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
@@ -92,7 +92,7 @@ module ActionController #:nodoc:
92
92
  # with the remaining data.
93
93
  #
94
94
  # Note that you can define your own XML parameter parser which would allow you to describe multiple entities
95
- # in a single request (i.e., by wrapping them all in a single root note), but if you just go with the flow
95
+ # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
96
96
  # and accept Rails' defaults, life will be much easier.
97
97
  #
98
98
  # If you need to use a MIME type which isn't supported by default, you can register your own handlers in
@@ -125,7 +125,7 @@ module ActionController #:nodoc:
125
125
 
126
126
  @order << mime_type
127
127
 
128
- @responses[mime_type] = Proc.new do
128
+ @responses[mime_type] ||= Proc.new do
129
129
  @response.template.template_format = mime_type.to_sym
130
130
  @response.content_type = mime_type.to_s
131
131
  block_given? ? block.call : @controller.send(:render, :action => @controller.action_name)
@@ -133,7 +133,11 @@ module ActionController #:nodoc:
133
133
  end
134
134
 
135
135
  def any(*args, &block)
136
- args.each { |type| send(type, &block) }
136
+ if args.any?
137
+ args.each { |type| send(type, &block) }
138
+ else
139
+ custom(@mime_type_priority.first, &block)
140
+ end
137
141
  end
138
142
 
139
143
  def method_missing(symbol, &block)
@@ -17,6 +17,10 @@ module Mime
17
17
  # end
18
18
  # end
19
19
  class Type
20
+ @@html_types = Set.new [:html, :all]
21
+ @@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml]
22
+ cattr_reader :html_types, :unverifiable_types
23
+
20
24
  # A simple helper class used in parsing the accept header
21
25
  class AcceptItem #:nodoc:
22
26
  attr_accessor :order, :name, :q
@@ -71,8 +75,11 @@ module Mime
71
75
  # keep track of creation order to keep the subsequent sort stable
72
76
  list = []
73
77
  accept_header.split(/,/).each_with_index do |header, index|
74
- params = header.split(/;\s*q=/)
75
- list << AcceptItem.new(index, *params) unless params.empty?
78
+ params, q = header.split(/;\s*q=/)
79
+ if params
80
+ params.strip!
81
+ list << AcceptItem.new(index, params, q) unless params.empty?
82
+ end
76
83
  end
77
84
  list.sort!
78
85
 
@@ -97,7 +104,7 @@ module Mime
97
104
  list[text_xml].name = Mime::XML.to_s
98
105
  end
99
106
 
100
- # Look for more specific xml-based types and sort them ahead of app/xml
107
+ # Look for more specific XML-based types and sort them ahead of app/xml
101
108
 
102
109
  if app_xml
103
110
  idx = app_xml
@@ -145,17 +152,26 @@ module Mime
145
152
  end
146
153
 
147
154
  def ==(mime_type)
148
- return false unless mime_type
149
- (@synonyms + [ self ]).any? do |synonym|
150
- synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
155
+ return false if mime_type.blank?
156
+ (@synonyms + [ self ]).any? do |synonym|
157
+ synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
151
158
  end
152
159
  end
153
-
160
+
161
+ # Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
162
+ # ActionController::RequestForgerProtection.
163
+ def verify_request?
164
+ !@@unverifiable_types.include?(to_sym)
165
+ end
166
+
167
+ def html?
168
+ @@html_types.include?(to_sym) || @string =~ /html/
169
+ end
170
+
154
171
  private
155
172
  def method_missing(method, *args)
156
173
  if method.to_s =~ /(\w+)\?$/
157
- mime_type = $1.downcase.to_sym
158
- mime_type == @symbol || (mime_type == :html && @symbol == :all)
174
+ $1.downcase.to_sym == to_sym
159
175
  else
160
176
  super
161
177
  end
@@ -17,4 +17,4 @@ Mime::Type.register "multipart/form-data", :multipart_form
17
17
  Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
18
18
 
19
19
  # http://www.ietf.org/rfc/rfc4627.txt
20
- Mime::Type.register "application/json", :json, %w( text/x-json )
20
+ Mime::Type.register "application/json", :json, %w( text/x-json )
@@ -1,6 +1,6 @@
1
1
  module ActionController
2
2
  # Polymorphic URL helpers are methods for smart resolution to a named route call when
3
- # given an ActiveRecord model instance. They are to be used in combination with
3
+ # given an Active Record model instance. They are to be used in combination with
4
4
  # ActionController::Resources.
5
5
  #
6
6
  # These methods are useful when you want to generate correct URL or path to a RESTful
@@ -9,7 +9,9 @@ module ActionController
9
9
  # Nested resources and/or namespaces are also supported, as illustrated in the example:
10
10
  #
11
11
  # polymorphic_url([:admin, @article, @comment])
12
- # #-> results in:
12
+ #
13
+ # results in:
14
+ #
13
15
  # admin_article_comment_url(@article, @comment)
14
16
  #
15
17
  # == Usage within the framework
@@ -19,7 +21,7 @@ module ActionController
19
21
  # * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
20
22
  # <tt>url_for(@article)</tt>;
21
23
  # * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
22
- # <tt>form_for(@article)</tt> without having to specify :url parameter for the form
24
+ # <tt>form_for(@article)</tt> without having to specify <tt>:url</tt> parameter for the form
23
25
  # action;
24
26
  # * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
25
27
  # <tt>redirect_to(post)</tt> in your controllers;
@@ -38,38 +40,41 @@ module ActionController
38
40
  #
39
41
  # Example usage:
40
42
  #
41
- # edit_polymorphic_path(@post)
42
- # #=> /posts/1/edit
43
- #
44
- # formatted_polymorphic_path([@post, :pdf])
45
- # #=> /posts/1.pdf
43
+ # edit_polymorphic_path(@post) # => "/posts/1/edit"
44
+ # formatted_polymorphic_path([@post, :pdf]) # => "/posts/1.pdf"
46
45
  module PolymorphicRoutes
47
46
  # Constructs a call to a named RESTful route for the given record and returns the
48
47
  # resulting URL string. For example:
49
48
  #
50
- # polymorphic_url(post)
51
- # # calls post_url(post) #=> "http://example.com/posts/1"
49
+ # # calls post_url(post)
50
+ # polymorphic_url(post) # => "http://example.com/posts/1"
52
51
  #
53
52
  # ==== Options
54
- # * <tt>:action</tt> -- specifies the action prefix for the named route:
55
- # <tt>:new</tt>, <tt>:edit</tt> or <tt>:formatted</tt>. Default is no prefix.
56
- # * <tt>:routing_type</tt> -- <tt>:path</tt> or <tt>:url</tt> (default <tt>:url</tt>).
53
+ #
54
+ # * <tt>:action</tt> - Specifies the action prefix for the named route:
55
+ # <tt>:new</tt>, <tt>:edit</tt>, or <tt>:formatted</tt>. Default is no prefix.
56
+ # * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
57
+ # Default is <tt>:url</tt>.
57
58
  #
58
59
  # ==== Examples
59
60
  #
60
61
  # # an Article record
61
- # polymorphic_url(record) #-> article_url(record)
62
+ # polymorphic_url(record) # same as article_url(record)
62
63
  #
63
64
  # # a Comment record
64
- # polymorphic_url(record) #-> comment_url(record)
65
+ # polymorphic_url(record) # same as comment_url(record)
65
66
  #
66
67
  # # it recognizes new records and maps to the collection
67
68
  # record = Comment.new
68
- # polymorphic_url(record) #-> comments_url()
69
+ # polymorphic_url(record) # same as comments_url()
69
70
  #
70
71
  def polymorphic_url(record_or_hash_or_array, options = {})
72
+ if record_or_hash_or_array.kind_of?(Array)
73
+ record_or_hash_or_array = record_or_hash_or_array.dup
74
+ end
75
+
71
76
  record = extract_record(record_or_hash_or_array)
72
- format = (options[:action].to_s == "formatted" and record_or_hash_or_array.pop)
77
+ format = extract_format(record_or_hash_or_array, options)
73
78
  namespace = extract_namespace(record_or_hash_or_array)
74
79
 
75
80
  args = case record_or_hash_or_array
@@ -148,6 +153,16 @@ module ActionController
148
153
  end
149
154
  end
150
155
 
156
+ def extract_format(record_or_hash_or_array, options)
157
+ if options[:action].to_s == "formatted" && record_or_hash_or_array.is_a?(Array)
158
+ record_or_hash_or_array.pop
159
+ elsif options[:format]
160
+ options[:format]
161
+ else
162
+ nil
163
+ end
164
+ end
165
+
151
166
  def extract_namespace(record_or_hash_or_array)
152
167
  returning "" do |namespace|
153
168
  if record_or_hash_or_array.is_a?(Array)
@@ -33,11 +33,17 @@ module ActionController
33
33
 
34
34
  # Returns plural/singular for a record or class. Example:
35
35
  #
36
- # partial_path(post) # => "posts/post"
37
- # partial_path(Person) # => "people/person"
38
- def partial_path(record_or_class)
36
+ # partial_path(post) # => "posts/post"
37
+ # partial_path(Person) # => "people/person"
38
+ # partial_path(Person, "admin/games") # => "admin/people/person"
39
+ def partial_path(record_or_class, controller_path = nil)
39
40
  klass = class_from_record_or_class(record_or_class)
40
- "#{klass.name.tableize}/#{klass.name.demodulize.underscore}"
41
+
42
+ if controller_path && controller_path.include?("/")
43
+ "#{File.dirname(controller_path)}/#{klass.name.tableize}/#{klass.name.demodulize.underscore}"
44
+ else
45
+ "#{klass.name.tableize}/#{klass.name.demodulize.underscore}"
46
+ end
41
47
  end
42
48
 
43
49
  # The DOM class convention is to use the singular form of an object or class. Examples:
@@ -15,7 +15,7 @@ module ActionController
15
15
  # such as { 'RAILS_ENV' => 'production' }.
16
16
  attr_reader :env
17
17
 
18
- # The true HTTP request method as a lowercase symbol, such as :get.
18
+ # The true HTTP request method as a lowercase symbol, such as <tt>:get</tt>.
19
19
  # UnknownHttpMethod is raised for invalid methods not listed in ACCEPTED_HTTP_METHODS.
20
20
  def request_method
21
21
  @request_method ||= begin
@@ -28,41 +28,43 @@ module ActionController
28
28
  end
29
29
  end
30
30
 
31
- # The HTTP request method as a lowercase symbol, such as :get.
32
- # Note, HEAD is returned as :get since the two are functionally
31
+ # The HTTP request method as a lowercase symbol, such as <tt>:get</tt>.
32
+ # Note, HEAD is returned as <tt>:get</tt> since the two are functionally
33
33
  # equivalent from the application's perspective.
34
34
  def method
35
35
  request_method == :head ? :get : request_method
36
36
  end
37
37
 
38
- # Is this a GET (or HEAD) request? Equivalent to request.method == :get
38
+ # Is this a GET (or HEAD) request? Equivalent to <tt>request.method == :get</tt>.
39
39
  def get?
40
40
  method == :get
41
41
  end
42
42
 
43
- # Is this a POST request? Equivalent to request.method == :post
43
+ # Is this a POST request? Equivalent to <tt>request.method == :post</tt>.
44
44
  def post?
45
45
  request_method == :post
46
46
  end
47
47
 
48
- # Is this a PUT request? Equivalent to request.method == :put
48
+ # Is this a PUT request? Equivalent to <tt>request.method == :put</tt>.
49
49
  def put?
50
50
  request_method == :put
51
51
  end
52
52
 
53
- # Is this a DELETE request? Equivalent to request.method == :delete
53
+ # Is this a DELETE request? Equivalent to <tt>request.method == :delete</tt>.
54
54
  def delete?
55
55
  request_method == :delete
56
56
  end
57
57
 
58
- # Is this a HEAD request? request.method sees HEAD as :get, so check the
59
- # HTTP method directly.
58
+ # Is this a HEAD request? <tt>request.method</tt> sees HEAD as <tt>:get</tt>,
59
+ # so check the HTTP method directly.
60
60
  def head?
61
61
  request_method == :head
62
62
  end
63
63
 
64
+ # Provides acccess to the request's HTTP headers, for example:
65
+ # request.headers["Content-Type"] # => "text/plain"
64
66
  def headers
65
- @env
67
+ @headers ||= ActionController::Http::Headers.new(@env)
66
68
  end
67
69
 
68
70
  def content_length
@@ -400,6 +402,14 @@ EOM
400
402
  body.blank? ? {} : Hash.from_xml(body).with_indifferent_access
401
403
  when :yaml
402
404
  YAML.load(body)
405
+ when :json
406
+ if body.blank?
407
+ {}
408
+ else
409
+ data = ActiveSupport::JSON.decode(body)
410
+ data = {:_json => data} unless data.is_a?(Hash)
411
+ data.with_indifferent_access
412
+ end
403
413
  else
404
414
  {}
405
415
  end
@@ -456,8 +466,8 @@ EOM
456
466
  parser.result
457
467
  end
458
468
 
459
- def parse_multipart_form_parameters(body, boundary, content_length, env)
460
- parse_request_parameters(read_multipart(body, boundary, content_length, env))
469
+ def parse_multipart_form_parameters(body, boundary, body_size, env)
470
+ parse_request_parameters(read_multipart(body, boundary, body_size, env))
461
471
  end
462
472
 
463
473
  def extract_multipart_boundary(content_type_with_parameters)
@@ -488,7 +498,7 @@ EOM
488
498
  when Array
489
499
  value.map { |v| get_typed_value(v) }
490
500
  else
491
- if value.is_a?(UploadedFile)
501
+ if value.respond_to? :original_filename
492
502
  # Uploaded file
493
503
  if value.original_filename
494
504
  value
@@ -505,23 +515,28 @@ EOM
505
515
  end
506
516
  end
507
517
 
508
-
509
518
  MULTIPART_BOUNDARY = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
510
519
 
511
520
  EOL = "\015\012"
512
521
 
513
- def read_multipart(body, boundary, content_length, env)
522
+ def read_multipart(body, boundary, body_size, env)
514
523
  params = Hash.new([])
515
524
  boundary = "--" + boundary
516
- quoted_boundary = Regexp.quote(boundary, "n")
525
+ quoted_boundary = Regexp.quote(boundary)
517
526
  buf = ""
518
527
  bufsize = 10 * 1024
519
528
  boundary_end=""
520
529
 
521
530
  # start multipart/form-data
522
531
  body.binmode if defined? body.binmode
532
+ case body
533
+ when File
534
+ body.set_encoding(Encoding::BINARY) if body.respond_to?(:set_encoding)
535
+ when StringIO
536
+ body.string.force_encoding(Encoding::BINARY) if body.string.respond_to?(:force_encoding)
537
+ end
523
538
  boundary_size = boundary.size + EOL.size
524
- content_length -= boundary_size
539
+ body_size -= boundary_size
525
540
  status = body.read(boundary_size)
526
541
  if nil == status
527
542
  raise EOFError, "no content body"
@@ -532,7 +547,7 @@ EOM
532
547
  loop do
533
548
  head = nil
534
549
  content =
535
- if 10240 < content_length
550
+ if 10240 < body_size
536
551
  UploadedTempfile.new("CGI")
537
552
  else
538
553
  UploadedStringIO.new
@@ -554,24 +569,24 @@ EOM
554
569
  buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
555
570
  end
556
571
 
557
- c = if bufsize < content_length
572
+ c = if bufsize < body_size
558
573
  body.read(bufsize)
559
574
  else
560
- body.read(content_length)
575
+ body.read(body_size)
561
576
  end
562
577
  if c.nil? || c.empty?
563
578
  raise EOFError, "bad content body"
564
579
  end
565
580
  buf.concat(c)
566
- content_length -= c.size
581
+ body_size -= c.size
567
582
  end
568
583
 
569
584
  buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
570
585
  content.print $1
571
586
  if "--" == $2
572
- content_length = -1
587
+ body_size = -1
573
588
  end
574
- boundary_end = $2.dup
589
+ boundary_end = $2.dup
575
590
  ""
576
591
  end
577
592
 
@@ -598,17 +613,16 @@ EOM
598
613
  else
599
614
  params[name] = [content]
600
615
  end
601
- break if buf.size == 0
602
- break if content_length == -1
616
+ break if body_size == -1
603
617
  end
604
618
  raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
605
619
 
606
- begin
620
+ begin
607
621
  body.rewind if body.respond_to?(:rewind)
608
- rescue Errno::ESPIPE
622
+ rescue Errno::ESPIPE
609
623
  # Handles exceptions raised by input streams that cannot be rewound
610
624
  # such as when using plain CGI under Apache
611
- end
625
+ end
612
626
 
613
627
  params
614
628
  end
@@ -687,6 +701,7 @@ EOM
687
701
  else
688
702
  top << {key => value}.with_indifferent_access
689
703
  push top.last
704
+ value = top[key]
690
705
  end
691
706
  else
692
707
  top << value
@@ -694,7 +709,8 @@ EOM
694
709
  elsif top.is_a? Hash
695
710
  key = CGI.unescape(key)
696
711
  parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
697
- return top[key] ||= value
712
+ top[key] ||= value
713
+ return top[key]
698
714
  else
699
715
  raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
700
716
  end
@@ -703,7 +719,7 @@ EOM
703
719
  end
704
720
 
705
721
  def type_conflict!(klass, value)
706
- raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value."
722
+ raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)"
707
723
  end
708
724
  end
709
725
 
@@ -36,7 +36,7 @@ module ActionController #:nodoc:
36
36
  #
37
37
  # # Disable request forgery protection in test environment
38
38
  # config.action_controller.allow_forgery_protection = false
39
- #
39
+ #
40
40
  # == Learn more about CSRF (Cross-Site Request Forgery) attacks
41
41
  #
42
42
  # Here are some resources:
@@ -45,7 +45,7 @@ module ActionController #:nodoc:
45
45
  #
46
46
  # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
47
47
  # There are a few guidelines you should follow:
48
- #
48
+ #
49
49
  # * Keep your GET requests safe and idempotent. More reading material:
50
50
  # * http://www.xml.com/pub/a/2002/04/24/deviant.html
51
51
  # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
@@ -69,10 +69,10 @@ module ActionController #:nodoc:
69
69
  #
70
70
  # Valid Options:
71
71
  #
72
- # * <tt>:only/:except</tt> - passed to the <tt>before_filter</tt> call. Set which actions are verified.
72
+ # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
73
73
  # * <tt>:secret</tt> - Custom salt used to generate the <tt>form_authenticity_token</tt>.
74
74
  # Leave this off if you are using the cookie session store.
75
- # * <tt>:digest</tt> - Message digest used for hashing. Defaults to 'SHA1'
75
+ # * <tt>:digest</tt> - Message digest used for hashing. Defaults to 'SHA1'.
76
76
  def protect_from_forgery(options = {})
77
77
  self.request_forgery_protection_token ||= :authenticity_token
78
78
  before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except)
@@ -99,17 +99,18 @@ module ActionController #:nodoc:
99
99
  end
100
100
 
101
101
  def verifiable_request_format?
102
- request.format.html? || request.format.js?
102
+ request.content_type.nil? || request.content_type.verify_request?
103
103
  end
104
104
 
105
- # Sets the token value for the current session. Pass a :secret option in #protect_from_forgery to add a custom salt to the hash.
105
+ # Sets the token value for the current session. Pass a <tt>:secret</tt> option
106
+ # in +protect_from_forgery+ to add a custom salt to the hash.
106
107
  def form_authenticity_token
107
- @form_authenticity_token ||= if request_forgery_protection_options[:secret]
108
+ @form_authenticity_token ||= if !session.respond_to?(:session_id)
109
+ raise InvalidAuthenticityToken, "Request Forgery Protection requires a valid session. Use #allow_forgery_protection to disable it, or use a valid session."
110
+ elsif request_forgery_protection_options[:secret]
108
111
  authenticity_token_from_session_id
109
112
  elsif session.respond_to?(:dbman) && session.dbman.respond_to?(:generate_digest)
110
113
  authenticity_token_from_cookie_session
111
- elsif session.nil?
112
- raise InvalidAuthenticityToken, "Request Forgery Protection requires a valid session. Use #allow_forgery_protection to disable it, or use a valid session."
113
114
  else
114
115
  raise InvalidAuthenticityToken, "No :secret given to the #protect_from_forgery call. Set that or use a session store capable of generating its own keys (Cookie Session Store)."
115
116
  end