actionpack 2.2.3 → 2.3.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (264) hide show
  1. data/CHANGELOG +433 -375
  2. data/MIT-LICENSE +1 -1
  3. data/README +21 -75
  4. data/Rakefile +1 -1
  5. data/lib/action_controller.rb +80 -43
  6. data/lib/action_controller/assertions/model_assertions.rb +1 -0
  7. data/lib/action_controller/assertions/response_assertions.rb +43 -16
  8. data/lib/action_controller/assertions/routing_assertions.rb +1 -1
  9. data/lib/action_controller/assertions/selector_assertions.rb +17 -12
  10. data/lib/action_controller/assertions/tag_assertions.rb +1 -4
  11. data/lib/action_controller/base.rb +153 -82
  12. data/lib/action_controller/benchmarking.rb +9 -9
  13. data/lib/action_controller/caching.rb +9 -11
  14. data/lib/action_controller/caching/actions.rb +11 -18
  15. data/lib/action_controller/caching/fragments.rb +28 -20
  16. data/lib/action_controller/caching/pages.rb +13 -15
  17. data/lib/action_controller/caching/sweeping.rb +2 -2
  18. data/lib/action_controller/cgi_ext.rb +0 -1
  19. data/lib/action_controller/cgi_ext/cookie.rb +2 -0
  20. data/lib/action_controller/cgi_process.rb +54 -162
  21. data/lib/action_controller/cookies.rb +13 -25
  22. data/lib/action_controller/dispatcher.rb +43 -122
  23. data/lib/action_controller/failsafe.rb +52 -0
  24. data/lib/action_controller/flash.rb +38 -47
  25. data/lib/action_controller/helpers.rb +13 -9
  26. data/lib/action_controller/http_authentication.rb +203 -23
  27. data/lib/action_controller/integration.rb +126 -70
  28. data/lib/action_controller/layout.rb +36 -39
  29. data/lib/action_controller/middleware_stack.rb +119 -0
  30. data/lib/action_controller/middlewares.rb +13 -0
  31. data/lib/action_controller/mime_responds.rb +19 -4
  32. data/lib/action_controller/mime_type.rb +8 -0
  33. data/lib/action_controller/params_parser.rb +71 -0
  34. data/lib/action_controller/performance_test.rb +0 -1
  35. data/lib/action_controller/polymorphic_routes.rb +36 -30
  36. data/lib/action_controller/reloader.rb +14 -0
  37. data/lib/action_controller/request.rb +107 -499
  38. data/lib/action_controller/request_forgery_protection.rb +7 -39
  39. data/lib/action_controller/rescue.rb +55 -35
  40. data/lib/action_controller/resources.rb +34 -31
  41. data/lib/action_controller/response.rb +99 -57
  42. data/lib/action_controller/rewindable_input.rb +28 -0
  43. data/lib/action_controller/routing.rb +7 -7
  44. data/lib/action_controller/routing/builder.rb +4 -1
  45. data/lib/action_controller/routing/optimisations.rb +1 -1
  46. data/lib/action_controller/routing/recognition_optimisation.rb +1 -2
  47. data/lib/action_controller/routing/route.rb +15 -5
  48. data/lib/action_controller/routing/route_set.rb +82 -35
  49. data/lib/action_controller/routing/segments.rb +35 -0
  50. data/lib/action_controller/session/abstract_store.rb +181 -0
  51. data/lib/action_controller/session/cookie_store.rb +197 -175
  52. data/lib/action_controller/session/mem_cache_store.rb +36 -83
  53. data/lib/action_controller/session_management.rb +26 -134
  54. data/lib/action_controller/streaming.rb +24 -7
  55. data/lib/action_controller/templates/rescues/diagnostics.erb +2 -2
  56. data/lib/action_controller/templates/rescues/template_error.erb +2 -2
  57. data/lib/action_controller/test_case.rb +87 -30
  58. data/lib/action_controller/test_process.rb +145 -104
  59. data/lib/action_controller/uploaded_file.rb +44 -0
  60. data/lib/action_controller/url_rewriter.rb +3 -6
  61. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  62. data/lib/action_controller/vendor/html-scanner/html/selector.rb +1 -1
  63. data/lib/action_controller/vendor/rack-1.0/rack.rb +89 -0
  64. data/lib/action_controller/vendor/rack-1.0/rack/adapter/camping.rb +22 -0
  65. data/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb +37 -0
  66. data/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/request.rb +37 -0
  67. data/lib/action_controller/vendor/rack-1.0/rack/auth/basic.rb +58 -0
  68. data/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb +124 -0
  69. data/lib/action_controller/vendor/rack-1.0/rack/auth/digest/nonce.rb +51 -0
  70. data/lib/action_controller/vendor/rack-1.0/rack/auth/digest/params.rb +55 -0
  71. data/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb +40 -0
  72. data/lib/action_controller/vendor/rack-1.0/rack/auth/openid.rb +480 -0
  73. data/lib/action_controller/vendor/rack-1.0/rack/builder.rb +63 -0
  74. data/lib/action_controller/vendor/rack-1.0/rack/cascade.rb +36 -0
  75. data/lib/action_controller/vendor/rack-1.0/rack/chunked.rb +49 -0
  76. data/lib/action_controller/vendor/rack-1.0/rack/commonlogger.rb +61 -0
  77. data/lib/action_controller/vendor/rack-1.0/rack/conditionalget.rb +45 -0
  78. data/lib/action_controller/vendor/rack-1.0/rack/content_length.rb +29 -0
  79. data/lib/action_controller/vendor/rack-1.0/rack/content_type.rb +23 -0
  80. data/lib/action_controller/vendor/rack-1.0/rack/deflater.rb +85 -0
  81. data/lib/action_controller/vendor/rack-1.0/rack/directory.rb +153 -0
  82. data/lib/action_controller/vendor/rack-1.0/rack/file.rb +88 -0
  83. data/lib/action_controller/vendor/rack-1.0/rack/handler.rb +48 -0
  84. data/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb +61 -0
  85. data/lib/action_controller/vendor/rack-1.0/rack/handler/evented_mongrel.rb +8 -0
  86. data/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb +89 -0
  87. data/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb +55 -0
  88. data/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb +84 -0
  89. data/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb +59 -0
  90. data/lib/action_controller/vendor/rack-1.0/rack/handler/swiftiplied_mongrel.rb +8 -0
  91. data/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb +18 -0
  92. data/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb +67 -0
  93. data/lib/action_controller/vendor/rack-1.0/rack/head.rb +19 -0
  94. data/lib/action_controller/vendor/rack-1.0/rack/lint.rb +462 -0
  95. data/lib/action_controller/vendor/rack-1.0/rack/lobster.rb +65 -0
  96. data/lib/action_controller/vendor/rack-1.0/rack/lock.rb +16 -0
  97. data/lib/action_controller/vendor/rack-1.0/rack/methodoverride.rb +27 -0
  98. data/lib/action_controller/vendor/rack-1.0/rack/mime.rb +204 -0
  99. data/lib/action_controller/vendor/rack-1.0/rack/mock.rb +160 -0
  100. data/lib/action_controller/vendor/rack-1.0/rack/recursive.rb +57 -0
  101. data/lib/action_controller/vendor/rack-1.0/rack/reloader.rb +64 -0
  102. data/lib/action_controller/vendor/rack-1.0/rack/request.rb +241 -0
  103. data/lib/action_controller/vendor/rack-1.0/rack/response.rb +179 -0
  104. data/lib/action_controller/vendor/rack-1.0/rack/session/abstract/id.rb +142 -0
  105. data/lib/action_controller/vendor/rack-1.0/rack/session/cookie.rb +91 -0
  106. data/lib/action_controller/vendor/rack-1.0/rack/session/memcache.rb +109 -0
  107. data/lib/action_controller/vendor/rack-1.0/rack/session/pool.rb +100 -0
  108. data/lib/action_controller/vendor/rack-1.0/rack/showexceptions.rb +349 -0
  109. data/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb +106 -0
  110. data/lib/action_controller/vendor/rack-1.0/rack/static.rb +38 -0
  111. data/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb +55 -0
  112. data/lib/action_controller/vendor/rack-1.0/rack/utils.rb +392 -0
  113. data/lib/action_controller/verification.rb +1 -1
  114. data/lib/action_pack.rb +1 -1
  115. data/lib/action_pack/version.rb +2 -2
  116. data/lib/action_view.rb +22 -17
  117. data/lib/action_view/base.rb +53 -79
  118. data/lib/action_view/erb/util.rb +38 -0
  119. data/lib/action_view/helpers.rb +24 -5
  120. data/lib/action_view/helpers/active_record_helper.rb +2 -2
  121. data/lib/action_view/helpers/asset_tag_helper.rb +81 -50
  122. data/lib/action_view/helpers/atom_feed_helper.rb +1 -1
  123. data/lib/action_view/helpers/benchmark_helper.rb +26 -5
  124. data/lib/action_view/helpers/date_helper.rb +82 -7
  125. data/lib/action_view/helpers/form_helper.rb +295 -64
  126. data/lib/action_view/helpers/form_options_helper.rb +160 -18
  127. data/lib/action_view/helpers/form_tag_helper.rb +2 -2
  128. data/lib/action_view/helpers/number_helper.rb +31 -18
  129. data/lib/action_view/helpers/prototype_helper.rb +2 -12
  130. data/lib/action_view/helpers/sanitize_helper.rb +0 -10
  131. data/lib/action_view/helpers/scriptaculous_helper.rb +1 -0
  132. data/lib/action_view/helpers/tag_helper.rb +3 -4
  133. data/lib/action_view/helpers/text_helper.rb +99 -122
  134. data/lib/action_view/helpers/translation_helper.rb +19 -1
  135. data/lib/action_view/helpers/url_helper.rb +25 -2
  136. data/lib/action_view/inline_template.rb +1 -1
  137. data/lib/action_view/locale/en.yml +19 -1
  138. data/lib/action_view/partials.rb +46 -9
  139. data/lib/action_view/paths.rb +28 -84
  140. data/lib/action_view/reloadable_template.rb +117 -0
  141. data/lib/action_view/renderable.rb +28 -35
  142. data/lib/action_view/renderable_partial.rb +3 -4
  143. data/lib/action_view/template.rb +172 -31
  144. data/lib/action_view/template_error.rb +8 -9
  145. data/lib/action_view/template_handler.rb +1 -1
  146. data/lib/action_view/template_handlers.rb +9 -6
  147. data/lib/action_view/template_handlers/erb.rb +2 -39
  148. data/lib/action_view/template_handlers/rjs.rb +1 -0
  149. data/lib/action_view/test_case.rb +27 -1
  150. data/test/abstract_unit.rb +23 -17
  151. data/test/active_record_unit.rb +5 -4
  152. data/test/activerecord/active_record_store_test.rb +139 -106
  153. data/test/activerecord/render_partial_with_record_identification_test.rb +5 -21
  154. data/test/controller/action_pack_assertions_test.rb +25 -23
  155. data/test/controller/addresses_render_test.rb +3 -6
  156. data/test/controller/assert_select_test.rb +83 -70
  157. data/test/controller/base_test.rb +11 -13
  158. data/test/controller/benchmark_test.rb +3 -3
  159. data/test/controller/caching_test.rb +34 -24
  160. data/test/controller/capture_test.rb +3 -6
  161. data/test/controller/content_type_test.rb +3 -6
  162. data/test/controller/cookie_test.rb +31 -66
  163. data/test/controller/deprecation/deprecated_base_methods_test.rb +9 -11
  164. data/test/controller/dispatcher_test.rb +23 -28
  165. data/test/controller/fake_models.rb +8 -0
  166. data/test/controller/filters_test.rb +6 -2
  167. data/test/controller/flash_test.rb +2 -6
  168. data/test/controller/helper_test.rb +15 -1
  169. data/test/controller/html-scanner/document_test.rb +1 -1
  170. data/test/controller/html-scanner/sanitizer_test.rb +1 -1
  171. data/test/controller/http_basic_authentication_test.rb +88 -0
  172. data/test/controller/http_digest_authentication_test.rb +178 -0
  173. data/test/controller/integration_test.rb +56 -52
  174. data/test/controller/layout_test.rb +46 -44
  175. data/test/controller/middleware_stack_test.rb +90 -0
  176. data/test/controller/mime_responds_test.rb +7 -11
  177. data/test/controller/mime_type_test.rb +9 -0
  178. data/test/controller/polymorphic_routes_test.rb +235 -151
  179. data/test/controller/rack_test.rb +52 -81
  180. data/test/controller/redirect_test.rb +6 -14
  181. data/test/controller/render_test.rb +273 -60
  182. data/test/controller/request/json_params_parsing_test.rb +45 -0
  183. data/test/controller/request/multipart_params_parsing_test.rb +223 -0
  184. data/test/controller/request/query_string_parsing_test.rb +120 -0
  185. data/test/controller/request/url_encoded_params_parsing_test.rb +184 -0
  186. data/test/controller/request/xml_params_parsing_test.rb +88 -0
  187. data/test/controller/request_forgery_protection_test.rb +17 -98
  188. data/test/controller/request_test.rb +45 -530
  189. data/test/controller/rescue_test.rb +45 -22
  190. data/test/controller/resources_test.rb +112 -37
  191. data/test/controller/routing_test.rb +1442 -1384
  192. data/test/controller/selector_test.rb +3 -3
  193. data/test/controller/send_file_test.rb +30 -3
  194. data/test/controller/session/cookie_store_test.rb +169 -240
  195. data/test/controller/session/mem_cache_store_test.rb +94 -148
  196. data/test/controller/session/test_session_test.rb +58 -0
  197. data/test/controller/test_test.rb +32 -13
  198. data/test/controller/url_rewriter_test.rb +54 -4
  199. data/test/controller/verification_test.rb +1 -1
  200. data/test/controller/view_paths_test.rb +15 -15
  201. data/test/controller/webservice_test.rb +178 -147
  202. data/test/fixtures/alternate_helpers/foo_helper.rb +3 -0
  203. data/test/fixtures/layout_tests/alt/layouts/alt.rhtml +0 -0
  204. data/test/fixtures/layouts/default_html.html.erb +1 -0
  205. data/test/fixtures/layouts/xhr.html.erb +2 -0
  206. data/test/fixtures/multipart/empty +10 -0
  207. data/test/fixtures/multipart/hello.txt +1 -0
  208. data/test/fixtures/multipart/none +9 -0
  209. data/test/fixtures/public/500.da.html +1 -0
  210. data/test/fixtures/quiz/questions/_question.html.erb +1 -0
  211. data/test/fixtures/replies.yml +1 -1
  212. data/test/fixtures/test/_one.html.erb +1 -0
  213. data/test/fixtures/test/_two.html.erb +1 -0
  214. data/test/fixtures/test/dont_pick_me +1 -0
  215. data/test/fixtures/test/hello.builder +1 -1
  216. data/test/fixtures/test/hello_world.da.html.erb +1 -0
  217. data/test/fixtures/test/hello_world.erb~ +1 -0
  218. data/test/fixtures/test/hello_world.pt-BR.html.erb +1 -0
  219. data/test/fixtures/test/malformed/malformed.en.html.erb~ +1 -0
  220. data/test/fixtures/test/malformed/malformed.erb~ +1 -0
  221. data/test/fixtures/test/malformed/malformed.html.erb~ +1 -0
  222. data/test/fixtures/test/render_explicit_html_template.js.rjs +1 -0
  223. data/test/fixtures/test/render_implicit_html_template.js.rjs +1 -0
  224. data/test/fixtures/test/render_implicit_html_template_from_xhr_request.da.html.erb +1 -0
  225. data/test/fixtures/test/render_implicit_html_template_from_xhr_request.html.erb +1 -0
  226. data/test/fixtures/test/render_implicit_js_template_without_layout.js.erb +1 -0
  227. data/test/fixtures/test/utf8.html.erb +2 -0
  228. data/test/template/active_record_helper_i18n_test.rb +31 -33
  229. data/test/template/active_record_helper_test.rb +34 -0
  230. data/test/template/asset_tag_helper_test.rb +52 -14
  231. data/test/template/atom_feed_helper_test.rb +3 -5
  232. data/test/template/benchmark_helper_test.rb +50 -24
  233. data/test/template/compiled_templates_test.rb +177 -33
  234. data/test/template/date_helper_i18n_test.rb +88 -81
  235. data/test/template/date_helper_test.rb +427 -43
  236. data/test/template/form_helper_test.rb +243 -44
  237. data/test/template/form_options_helper_test.rb +631 -565
  238. data/test/template/form_tag_helper_test.rb +9 -2
  239. data/test/template/javascript_helper_test.rb +0 -5
  240. data/test/template/number_helper_i18n_test.rb +60 -48
  241. data/test/template/number_helper_test.rb +1 -0
  242. data/test/template/render_test.rb +117 -35
  243. data/test/template/test_test.rb +4 -6
  244. data/test/template/text_helper_test.rb +129 -50
  245. data/test/template/translation_helper_test.rb +23 -19
  246. data/test/template/url_helper_test.rb +35 -2
  247. data/test/view/test_case_test.rb +8 -0
  248. metadata +197 -23
  249. data/lib/action_controller/assertions.rb +0 -69
  250. data/lib/action_controller/caching/sql_cache.rb +0 -18
  251. data/lib/action_controller/cgi_ext/session.rb +0 -53
  252. data/lib/action_controller/components.rb +0 -169
  253. data/lib/action_controller/rack_process.rb +0 -297
  254. data/lib/action_controller/request_profiler.rb +0 -169
  255. data/lib/action_controller/session/active_record_store.rb +0 -340
  256. data/lib/action_controller/session/drb_server.rb +0 -32
  257. data/lib/action_controller/session/drb_store.rb +0 -35
  258. data/test/controller/cgi_test.rb +0 -269
  259. data/test/controller/components_test.rb +0 -156
  260. data/test/controller/http_authentication_test.rb +0 -54
  261. data/test/controller/integration_upload_test.rb +0 -43
  262. data/test/controller/session_fixation_test.rb +0 -89
  263. data/test/controller/session_management_test.rb +0 -178
  264. data/test/fixtures/test/hello_world.js +0 -1
@@ -0,0 +1,28 @@
1
+ module ActionController
2
+ class RewindableInput
3
+ class RewindableIO < ActiveSupport::BasicObject
4
+ def initialize(io)
5
+ @io = io
6
+ @rewindable = io.is_a?(::StringIO)
7
+ end
8
+
9
+ def method_missing(method, *args, &block)
10
+ unless @rewindable
11
+ @io = ::StringIO.new(@io.read)
12
+ @rewindable = true
13
+ end
14
+
15
+ @io.__send__(method, *args, &block)
16
+ end
17
+ end
18
+
19
+ def initialize(app)
20
+ @app = app
21
+ end
22
+
23
+ def call(env)
24
+ env['rack.input'] = RewindableIO.new(env['rack.input'])
25
+ @app.call(env)
26
+ end
27
+ end
28
+ end
@@ -1,6 +1,5 @@
1
1
  require 'cgi'
2
2
  require 'uri'
3
- require 'action_controller/polymorphic_routes'
4
3
  require 'action_controller/routing/optimisations'
5
4
  require 'action_controller/routing/routing_ext'
6
5
  require 'action_controller/routing/route'
@@ -84,9 +83,11 @@ module ActionController
84
83
  # This sets up +blog+ as the default controller if no other is specified.
85
84
  # This means visiting '/' would invoke the blog controller.
86
85
  #
87
- # More formally, you can define defaults in a route with the <tt>:defaults</tt> key.
86
+ # More formally, you can include arbitrary parameters in the route, thus:
88
87
  #
89
- # map.connect ':controller/:action/:id', :action => 'show', :defaults => { :page => 'Dashboard' }
88
+ # map.connect ':controller/:action/:id', :action => 'show', :page => 'Dashboard'
89
+ #
90
+ # This will pass the :page parameter to all incoming requests that match this route.
90
91
  #
91
92
  # Note: The default routes, as provided by the Rails generator, make all actions in every
92
93
  # controller accessible via GET requests. You should consider removing them or commenting
@@ -192,9 +193,8 @@ module ActionController
192
193
  #
193
194
  # map.connect '*path' , :controller => 'blog' , :action => 'unrecognized?'
194
195
  #
195
- # will glob all remaining parts of the route that were not recognized earlier. This idiom
196
- # must appear at the end of the path. The globbed values are in <tt>params[:path]</tt> in
197
- # this case.
196
+ # will glob all remaining parts of the route that were not recognized earlier.
197
+ # The globbed values are in <tt>params[:path]</tt> as an array of path segments.
198
198
  #
199
199
  # == Route conditions
200
200
  #
@@ -267,7 +267,7 @@ module ActionController
267
267
  module Routing
268
268
  SEPARATORS = %w( / . ? )
269
269
 
270
- HTTP_METHODS = [:get, :head, :post, :put, :delete]
270
+ HTTP_METHODS = [:get, :head, :post, :put, :delete, :options]
271
271
 
272
272
  ALLOWED_REQUIREMENTS_FOR_OPTIMISATION = [:controller, :action].to_set
273
273
 
@@ -34,6 +34,8 @@ module ActionController
34
34
  def segment_for(string)
35
35
  segment =
36
36
  case string
37
+ when /\A\.(:format)?\//
38
+ OptionalFormatSegment.new
37
39
  when /\A:(\w+)/
38
40
  key = $1.to_sym
39
41
  key == :controller ? ControllerSegment.new(key) : DynamicSegment.new(key)
@@ -157,7 +159,8 @@ module ActionController
157
159
  path = "/#{path}" unless path[0] == ?/
158
160
  path = "#{path}/" unless path[-1] == ?/
159
161
 
160
- path = "/#{options[:path_prefix].to_s.gsub(/^\//,'')}#{path}" if options[:path_prefix]
162
+ prefix = options[:path_prefix].to_s.gsub(/^\//,'')
163
+ path = "/#{prefix}#{path}" unless prefix.blank?
161
164
 
162
165
  segments = segments_for_route_path(path)
163
166
  defaults, requirements, conditions = divide_route_options(segments, options)
@@ -65,7 +65,7 @@ module ActionController
65
65
  # rather than triggering the expensive logic in +url_for+.
66
66
  class PositionalArguments < Optimiser
67
67
  def guard_conditions
68
- number_of_arguments = route.segment_keys.size
68
+ number_of_arguments = route.required_segment_keys.size
69
69
  # if they're using foo_url(:id=>2) it's one
70
70
  # argument, but we don't want to generate /foos/id2
71
71
  if number_of_arguments == 1
@@ -56,7 +56,7 @@ module ActionController
56
56
  result = recognize_optimized(path, environment) and return result
57
57
 
58
58
  # Route was not recognized. Try to find out why (maybe wrong verb).
59
- allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, :method => verb) } }
59
+ allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, environment.merge(:method => verb)) } }
60
60
 
61
61
  if environment[:method] && !HTTP_METHODS.include?(environment[:method])
62
62
  raise NotImplemented.new(*allows)
@@ -98,7 +98,6 @@ module ActionController
98
98
  if Array === item
99
99
  i += 1
100
100
  start = (i == 1)
101
- final = (i == list.size)
102
101
  tag, sub = item
103
102
  if tag == :dynamic
104
103
  body += padding + "#{start ? 'if' : 'elsif'} true\n"
@@ -35,6 +35,11 @@ module ActionController
35
35
  segment.key if segment.respond_to? :key
36
36
  end.compact
37
37
  end
38
+
39
+ def required_segment_keys
40
+ required_segments = segments.select {|seg| (!seg.optional? && !seg.is_a?(DividerSegment)) || seg.is_a?(PathSegment) }
41
+ required_segments.collect { |seg| seg.key if seg.respond_to?(:key)}.compact
42
+ end
38
43
 
39
44
  # Build a query string from the keys of the given hash. If +only_keys+
40
45
  # is given (as an array), only the keys indicated will be used to build
@@ -122,6 +127,16 @@ module ActionController
122
127
  super
123
128
  end
124
129
 
130
+ def generate(options, hash, expire_on = {})
131
+ path, hash = generate_raw(options, hash, expire_on)
132
+ append_query_string(path, hash, extra_keys(options))
133
+ end
134
+
135
+ def generate_extras(options, hash, expire_on = {})
136
+ path, hash = generate_raw(options, hash, expire_on)
137
+ [path, extra_keys(options)]
138
+ end
139
+
125
140
  private
126
141
  def requirement_for(key)
127
142
  return requirements[key] if requirements.key? key
@@ -150,11 +165,6 @@ module ActionController
150
165
  # the query string. (Never use keys from the recalled request when building the
151
166
  # query string.)
152
167
 
153
- method_decl = "def generate(#{args})\npath, hash = generate_raw(options, hash, expire_on)\nappend_query_string(path, hash, extra_keys(options))\nend"
154
- instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
155
-
156
- method_decl = "def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(options)]\nend"
157
- instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
158
168
  raw_method
159
169
  end
160
170
 
@@ -7,6 +7,8 @@ module ActionController
7
7
  # Mapper instances have relatively few instance methods, in order to avoid
8
8
  # clashes with named routes.
9
9
  class Mapper #:doc:
10
+ include ActionController::Resources
11
+
10
12
  def initialize(set) #:nodoc:
11
13
  @set = set
12
14
  end
@@ -143,10 +145,10 @@ module ActionController
143
145
  def define_hash_access(route, name, kind, options)
144
146
  selector = hash_access_name(name, kind)
145
147
  named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks
146
- def #{selector}(options = nil)
147
- options ? #{options.inspect}.merge(options) : #{options.inspect}
148
- end
149
- protected :#{selector}
148
+ def #{selector}(options = nil) # def hash_for_users_url(options = nil)
149
+ options ? #{options.inspect}.merge(options) : #{options.inspect} # options ? {:only_path=>false}.merge(options) : {:only_path=>false}
150
+ end # end
151
+ protected :#{selector} # protected :hash_for_users_url
150
152
  end_eval
151
153
  helpers << selector
152
154
  end
@@ -171,32 +173,43 @@ module ActionController
171
173
  # foo_url(bar, baz, bang, :sort_by => 'baz')
172
174
  #
173
175
  named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks
174
- def #{selector}(*args)
175
-
176
- #{generate_optimisation_block(route, kind)}
177
-
178
- opts = if args.empty? || Hash === args.first
179
- args.first || {}
180
- else
181
- options = args.extract_options!
182
- args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)|
183
- h[k] = v
184
- h
185
- end
186
- options.merge(args)
187
- end
188
-
189
- url_for(#{hash_access_method}(opts))
190
- end
191
- protected :#{selector}
176
+ def #{selector}(*args) # def users_url(*args)
177
+ #
178
+ #{generate_optimisation_block(route, kind)} # #{generate_optimisation_block(route, kind)}
179
+ #
180
+ opts = if args.empty? || Hash === args.first # opts = if args.empty? || Hash === args.first
181
+ args.first || {} # args.first || {}
182
+ else # else
183
+ options = args.extract_options! # options = args.extract_options!
184
+ args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)| # args = args.zip([]).inject({}) do |h, (v, k)|
185
+ h[k] = v # h[k] = v
186
+ h # h
187
+ end # end
188
+ options.merge(args) # options.merge(args)
189
+ end # end
190
+ #
191
+ url_for(#{hash_access_method}(opts)) # url_for(hash_for_users_url(opts))
192
+ #
193
+ end # end
194
+ #Add an alias to support the now deprecated formatted_* URL. # #Add an alias to support the now deprecated formatted_* URL.
195
+ def formatted_#{selector}(*args) # def formatted_users_url(*args)
196
+ ActiveSupport::Deprecation.warn( # ActiveSupport::Deprecation.warn(
197
+ "formatted_#{selector}() has been deprecated. " + # "formatted_users_url() has been deprecated. " +
198
+ "Please pass format to the standard " + # "Please pass format to the standard " +
199
+ "#{selector} method instead.", caller) # "users_url method instead.", caller)
200
+ #{selector}(*args) # users_url(*args)
201
+ end # end
202
+ protected :#{selector} # protected :users_url
192
203
  end_eval
193
204
  helpers << selector
194
205
  end
195
206
  end
196
207
 
197
- attr_accessor :routes, :named_routes, :configuration_file
208
+ attr_accessor :routes, :named_routes, :configuration_files
198
209
 
199
210
  def initialize
211
+ self.configuration_files = []
212
+
200
213
  self.routes = []
201
214
  self.named_routes = NamedRouteCollection.new
202
215
 
@@ -210,7 +223,6 @@ module ActionController
210
223
  end
211
224
 
212
225
  def draw
213
- clear!
214
226
  yield Mapper.new(self)
215
227
  install_helpers
216
228
  end
@@ -234,8 +246,22 @@ module ActionController
234
246
  routes.empty?
235
247
  end
236
248
 
249
+ def add_configuration_file(path)
250
+ self.configuration_files << path
251
+ end
252
+
253
+ # Deprecated accessor
254
+ def configuration_file=(path)
255
+ add_configuration_file(path)
256
+ end
257
+
258
+ # Deprecated accessor
259
+ def configuration_file
260
+ configuration_files
261
+ end
262
+
237
263
  def load!
238
- Routing.use_controllers! nil # Clear the controller cache so we may discover new ones
264
+ Routing.use_controllers!(nil) # Clear the controller cache so we may discover new ones
239
265
  clear!
240
266
  load_routes!
241
267
  end
@@ -244,24 +270,39 @@ module ActionController
244
270
  alias reload! load!
245
271
 
246
272
  def reload
247
- if @routes_last_modified && configuration_file
248
- mtime = File.stat(configuration_file).mtime
249
- # if it hasn't been changed, then just return
250
- return if mtime == @routes_last_modified
251
- # if it has changed then record the new time and fall to the load! below
252
- @routes_last_modified = mtime
273
+ if configuration_files.any? && @routes_last_modified
274
+ if routes_changed_at == @routes_last_modified
275
+ return # routes didn't change, don't reload
276
+ else
277
+ @routes_last_modified = routes_changed_at
278
+ end
253
279
  end
280
+
254
281
  load!
255
282
  end
256
283
 
257
284
  def load_routes!
258
- if configuration_file
259
- load configuration_file
260
- @routes_last_modified = File.stat(configuration_file).mtime
285
+ if configuration_files.any?
286
+ configuration_files.each { |config| load(config) }
287
+ @routes_last_modified = routes_changed_at
261
288
  else
262
289
  add_route ":controller/:action/:id"
263
290
  end
264
291
  end
292
+
293
+ def routes_changed_at
294
+ routes_changed_at = nil
295
+
296
+ configuration_files.each do |config|
297
+ config_changed_at = File.stat(config).mtime
298
+
299
+ if routes_changed_at.nil? || config_changed_at > routes_changed_at
300
+ routes_changed_at = config_changed_at
301
+ end
302
+ end
303
+
304
+ routes_changed_at
305
+ end
265
306
 
266
307
  def add_route(path, options = {})
267
308
  route = builder.build(path, options)
@@ -363,7 +404,7 @@ module ActionController
363
404
  end
364
405
 
365
406
  # don't use the recalled keys when determining which routes to check
366
- routes = routes_by_controller[controller][action][options.keys.sort_by { |x| x.object_id }]
407
+ routes = routes_by_controller[controller][action][options.reject {|k,v| !v}.keys.sort_by { |x| x.object_id }]
367
408
 
368
409
  routes.each do |route|
369
410
  results = route.__send__(method, options, merged, expire_on)
@@ -386,6 +427,12 @@ module ActionController
386
427
  end
387
428
  end
388
429
 
430
+ def call(env)
431
+ request = Request.new(env)
432
+ app = Routing::Routes.recognize(request)
433
+ app.call(env).to_a
434
+ end
435
+
389
436
  def recognize(request)
390
437
  params = recognize_path(request.path, extract_request_environment(request))
391
438
  request.path_parameters = params.with_indifferent_access
@@ -304,5 +304,40 @@ module ActionController
304
304
  end
305
305
  end
306
306
  end
307
+
308
+ # The OptionalFormatSegment allows for any resource route to have an optional
309
+ # :format, which decreases the amount of routes created by 50%.
310
+ class OptionalFormatSegment < DynamicSegment
311
+
312
+ def initialize(key = nil, options = {})
313
+ super(:format, {:optional => true}.merge(options))
314
+ end
315
+
316
+ def interpolation_chunk
317
+ "." + super
318
+ end
319
+
320
+ def regexp_chunk
321
+ '/|(\.[^/?\.]+)?'
322
+ end
323
+
324
+ def to_s
325
+ '(.:format)?'
326
+ end
327
+
328
+ def extract_value
329
+ "#{local_name} = options[:#{key}] && options[:#{key}].to_s.downcase"
330
+ end
331
+
332
+ #the value should not include the period (.)
333
+ def match_extraction(next_capture)
334
+ %[
335
+ if (m = match[#{next_capture}])
336
+ params[:#{key}] = URI.unescape(m.from(1))
337
+ end
338
+ ]
339
+ end
340
+ end
341
+
307
342
  end
308
343
  end
@@ -0,0 +1,181 @@
1
+ require 'rack/utils'
2
+
3
+ module ActionController
4
+ module Session
5
+ class AbstractStore
6
+ ENV_SESSION_KEY = 'rack.session'.freeze
7
+ ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
8
+
9
+ HTTP_COOKIE = 'HTTP_COOKIE'.freeze
10
+ SET_COOKIE = 'Set-Cookie'.freeze
11
+
12
+ class SessionHash < Hash
13
+ def initialize(by, env)
14
+ super()
15
+ @by = by
16
+ @env = env
17
+ @loaded = false
18
+ end
19
+
20
+ def session_id
21
+ ActiveSupport::Deprecation.warn(
22
+ "ActionController::Session::AbstractStore::SessionHash#session_id " +
23
+ "has been deprecated. Please use request.session_options[:id] instead.", caller)
24
+ @env[ENV_SESSION_OPTIONS_KEY][:id]
25
+ end
26
+
27
+ def [](key)
28
+ load! unless @loaded
29
+ super
30
+ end
31
+
32
+ def []=(key, value)
33
+ load! unless @loaded
34
+ super
35
+ end
36
+
37
+ def to_hash
38
+ h = {}.replace(self)
39
+ h.delete_if { |k,v| v.nil? }
40
+ h
41
+ end
42
+
43
+ def data
44
+ ActiveSupport::Deprecation.warn(
45
+ "ActionController::Session::AbstractStore::SessionHash#data " +
46
+ "has been deprecated. Please use #to_hash instead.", caller)
47
+ to_hash
48
+ end
49
+
50
+ def inspect
51
+ load! unless @loaded
52
+ super
53
+ end
54
+
55
+ private
56
+ def loaded?
57
+ @loaded
58
+ end
59
+
60
+ def load!
61
+ stale_session_check! do
62
+ id, session = @by.send(:load_session, @env)
63
+ (@env[ENV_SESSION_OPTIONS_KEY] ||= {})[:id] = id
64
+ replace(session)
65
+ @loaded = true
66
+ end
67
+ end
68
+
69
+ def stale_session_check!
70
+ yield
71
+ rescue ArgumentError => argument_error
72
+ if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
73
+ begin
74
+ # Note that the regexp does not allow $1 to end with a ':'
75
+ $1.constantize
76
+ rescue LoadError, NameError => const_error
77
+ raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: \#{const_error.message} [\#{const_error.class}])\n"
78
+ end
79
+
80
+ retry
81
+ else
82
+ raise
83
+ end
84
+ end
85
+ end
86
+
87
+ DEFAULT_OPTIONS = {
88
+ :key => '_session_id',
89
+ :path => '/',
90
+ :domain => nil,
91
+ :expire_after => nil,
92
+ :secure => false,
93
+ :httponly => true,
94
+ :cookie_only => true
95
+ }
96
+
97
+ def initialize(app, options = {})
98
+ # Process legacy CGI options
99
+ options = options.symbolize_keys
100
+ if options.has_key?(:session_path)
101
+ options[:path] = options.delete(:session_path)
102
+ end
103
+ if options.has_key?(:session_key)
104
+ options[:key] = options.delete(:session_key)
105
+ end
106
+ if options.has_key?(:session_http_only)
107
+ options[:httponly] = options.delete(:session_http_only)
108
+ end
109
+
110
+ @app = app
111
+ @default_options = DEFAULT_OPTIONS.merge(options)
112
+ @key = @default_options[:key]
113
+ @cookie_only = @default_options[:cookie_only]
114
+ end
115
+
116
+ def call(env)
117
+ session = SessionHash.new(self, env)
118
+
119
+ env[ENV_SESSION_KEY] = session
120
+ env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
121
+
122
+ response = @app.call(env)
123
+
124
+ session_data = env[ENV_SESSION_KEY]
125
+ options = env[ENV_SESSION_OPTIONS_KEY]
126
+
127
+ if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after]
128
+ session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?)
129
+
130
+ sid = options[:id] || generate_sid
131
+
132
+ unless set_session(env, sid, session_data.to_hash)
133
+ return response
134
+ end
135
+
136
+ cookie = Rack::Utils.escape(@key) + '=' + Rack::Utils.escape(sid)
137
+ cookie << "; domain=#{options[:domain]}" if options[:domain]
138
+ cookie << "; path=#{options[:path]}" if options[:path]
139
+ if options[:expire_after]
140
+ expiry = Time.now + options[:expire_after]
141
+ cookie << "; expires=#{expiry.httpdate}"
142
+ end
143
+ cookie << "; Secure" if options[:secure]
144
+ cookie << "; HttpOnly" if options[:httponly]
145
+
146
+ headers = response[1]
147
+ unless headers[SET_COOKIE].blank?
148
+ headers[SET_COOKIE] << "\n#{cookie}"
149
+ else
150
+ headers[SET_COOKIE] = cookie
151
+ end
152
+ end
153
+
154
+ response
155
+ end
156
+
157
+ private
158
+ def generate_sid
159
+ ActiveSupport::SecureRandom.hex(16)
160
+ end
161
+
162
+ def load_session(env)
163
+ request = Rack::Request.new(env)
164
+ sid = request.cookies[@key]
165
+ unless @cookie_only
166
+ sid ||= request.params[@key]
167
+ end
168
+ sid, session = get_session(env, sid)
169
+ [sid, session]
170
+ end
171
+
172
+ def get_session(env, sid)
173
+ raise '#get_session needs to be implemented.'
174
+ end
175
+
176
+ def set_session(env, sid, session_data)
177
+ raise '#set_session needs to be implemented.'
178
+ end
179
+ end
180
+ end
181
+ end