actionpack 1.13.6 → 2.0.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 (317) hide show
  1. data/CHANGELOG +1400 -20
  2. data/MIT-LICENSE +1 -1
  3. data/README +5 -5
  4. data/RUNNING_UNIT_TESTS +4 -5
  5. data/Rakefile +5 -6
  6. data/install.rb +2 -2
  7. data/lib/action_controller.rb +11 -15
  8. data/lib/action_controller/assertions.rb +12 -25
  9. data/lib/action_controller/assertions/dom_assertions.rb +18 -4
  10. data/lib/action_controller/assertions/model_assertions.rb +8 -1
  11. data/lib/action_controller/assertions/response_assertions.rb +35 -12
  12. data/lib/action_controller/assertions/routing_assertions.rb +56 -12
  13. data/lib/action_controller/assertions/selector_assertions.rb +105 -38
  14. data/lib/action_controller/assertions/tag_assertions.rb +28 -15
  15. data/lib/action_controller/base.rb +318 -250
  16. data/lib/action_controller/benchmarking.rb +33 -29
  17. data/lib/action_controller/caching.rb +130 -64
  18. data/lib/action_controller/cgi_ext.rb +16 -0
  19. data/lib/action_controller/cgi_ext/{cookie_performance_fix.rb → cookie.rb} +25 -40
  20. data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
  21. data/lib/action_controller/cgi_ext/session.rb +73 -0
  22. data/lib/action_controller/cgi_ext/stdinput.rb +23 -0
  23. data/lib/action_controller/cgi_process.rb +34 -57
  24. data/lib/action_controller/components.rb +19 -36
  25. data/lib/action_controller/cookies.rb +10 -9
  26. data/lib/action_controller/dispatcher.rb +195 -0
  27. data/lib/action_controller/filters.rb +35 -34
  28. data/lib/action_controller/flash.rb +30 -35
  29. data/lib/action_controller/helpers.rb +121 -47
  30. data/lib/action_controller/http_authentication.rb +126 -0
  31. data/lib/action_controller/integration.rb +105 -101
  32. data/lib/action_controller/layout.rb +59 -47
  33. data/lib/action_controller/mime_responds.rb +57 -68
  34. data/lib/action_controller/mime_type.rb +43 -80
  35. data/lib/action_controller/mime_types.rb +20 -0
  36. data/lib/action_controller/polymorphic_routes.rb +88 -0
  37. data/lib/action_controller/record_identifier.rb +91 -0
  38. data/lib/action_controller/request.rb +553 -88
  39. data/lib/action_controller/request_forgery_protection.rb +126 -0
  40. data/lib/action_controller/request_profiler.rb +138 -0
  41. data/lib/action_controller/rescue.rb +185 -69
  42. data/lib/action_controller/resources.rb +211 -172
  43. data/lib/action_controller/response.rb +49 -8
  44. data/lib/action_controller/routing.rb +359 -236
  45. data/lib/action_controller/routing_optimisation.rb +119 -0
  46. data/lib/action_controller/session/active_record_store.rb +3 -2
  47. data/lib/action_controller/session/cookie_store.rb +161 -0
  48. data/lib/action_controller/session/mem_cache_store.rb +9 -16
  49. data/lib/action_controller/session_management.rb +17 -8
  50. data/lib/action_controller/streaming.rb +6 -3
  51. data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
  52. data/lib/action_controller/templates/rescues/{_trace.rhtml → _trace.erb} +0 -0
  53. data/lib/action_controller/templates/rescues/{diagnostics.rhtml → diagnostics.erb} +2 -2
  54. data/lib/action_controller/templates/rescues/{layout.rhtml → layout.erb} +0 -0
  55. data/lib/action_controller/templates/rescues/{missing_template.rhtml → missing_template.erb} +0 -0
  56. data/lib/action_controller/templates/rescues/{routing_error.rhtml → routing_error.erb} +0 -0
  57. data/lib/action_controller/templates/rescues/{template_error.rhtml → template_error.erb} +2 -2
  58. data/lib/action_controller/templates/rescues/{unknown_action.rhtml → unknown_action.erb} +0 -0
  59. data/lib/action_controller/test_case.rb +53 -0
  60. data/lib/action_controller/test_process.rb +59 -46
  61. data/lib/action_controller/url_rewriter.rb +48 -24
  62. data/lib/action_controller/vendor/html-scanner/html/document.rb +7 -4
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +11 -6
  65. data/lib/action_controller/verification.rb +27 -21
  66. data/lib/action_pack.rb +1 -1
  67. data/lib/action_pack/version.rb +4 -4
  68. data/lib/action_view.rb +2 -3
  69. data/lib/action_view/base.rb +218 -63
  70. data/lib/action_view/compiled_templates.rb +1 -2
  71. data/lib/action_view/helpers/active_record_helper.rb +35 -17
  72. data/lib/action_view/helpers/asset_tag_helper.rb +395 -87
  73. data/lib/action_view/helpers/atom_feed_helper.rb +111 -0
  74. data/lib/action_view/helpers/benchmark_helper.rb +12 -5
  75. data/lib/action_view/helpers/cache_helper.rb +29 -0
  76. data/lib/action_view/helpers/capture_helper.rb +97 -63
  77. data/lib/action_view/helpers/date_helper.rb +295 -35
  78. data/lib/action_view/helpers/debug_helper.rb +6 -2
  79. data/lib/action_view/helpers/form_helper.rb +354 -111
  80. data/lib/action_view/helpers/form_options_helper.rb +171 -109
  81. data/lib/action_view/helpers/form_tag_helper.rb +332 -76
  82. data/lib/action_view/helpers/javascript_helper.rb +35 -11
  83. data/lib/action_view/helpers/javascripts/controls.js +484 -354
  84. data/lib/action_view/helpers/javascripts/dragdrop.js +88 -58
  85. data/lib/action_view/helpers/javascripts/effects.js +396 -364
  86. data/lib/action_view/helpers/javascripts/prototype.js +2817 -1107
  87. data/lib/action_view/helpers/number_helper.rb +84 -60
  88. data/lib/action_view/helpers/prototype_helper.rb +419 -43
  89. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  90. data/lib/action_view/helpers/record_tag_helper.rb +59 -0
  91. data/lib/action_view/helpers/sanitize_helper.rb +223 -0
  92. data/lib/action_view/helpers/scriptaculous_helper.rb +63 -4
  93. data/lib/action_view/helpers/tag_helper.rb +69 -39
  94. data/lib/action_view/helpers/text_helper.rb +221 -148
  95. data/lib/action_view/helpers/url_helper.rb +283 -165
  96. data/lib/action_view/partials.rb +134 -62
  97. data/lib/action_view/template_error.rb +4 -12
  98. data/lib/actionpack.rb +1 -0
  99. data/test/abstract_unit.rb +21 -1
  100. data/test/action_view_test.rb +26 -0
  101. data/test/active_record_unit.rb +12 -20
  102. data/test/activerecord/active_record_store_test.rb +2 -2
  103. data/test/activerecord/render_partial_with_record_identification_test.rb +74 -0
  104. data/test/controller/action_pack_assertions_test.rb +21 -152
  105. data/test/controller/addresses_render_test.rb +2 -7
  106. data/test/controller/assert_select_test.rb +120 -14
  107. data/test/controller/base_test.rb +11 -13
  108. data/test/controller/caching_test.rb +125 -5
  109. data/test/controller/capture_test.rb +23 -16
  110. data/test/controller/cgi_test.rb +66 -391
  111. data/test/controller/components_test.rb +31 -42
  112. data/test/controller/content_type_test.rb +1 -1
  113. data/test/controller/cookie_test.rb +42 -14
  114. data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -42
  115. data/test/controller/dispatcher_test.rb +123 -0
  116. data/test/controller/fake_models.rb +5 -0
  117. data/test/controller/filters_test.rb +44 -7
  118. data/test/controller/flash_test.rb +46 -2
  119. data/test/controller/fragment_store_setting_test.rb +10 -8
  120. data/test/controller/helper_test.rb +19 -2
  121. data/test/controller/html-scanner/document_test.rb +124 -0
  122. data/test/controller/html-scanner/node_test.rb +69 -0
  123. data/test/controller/html-scanner/sanitizer_test.rb +250 -0
  124. data/test/controller/html-scanner/tag_node_test.rb +239 -0
  125. data/test/controller/html-scanner/text_node_test.rb +51 -0
  126. data/test/controller/html-scanner/tokenizer_test.rb +125 -0
  127. data/test/controller/http_authentication_test.rb +54 -0
  128. data/test/controller/integration_test.rb +12 -26
  129. data/test/controller/layout_test.rb +64 -12
  130. data/test/controller/mime_responds_test.rb +193 -38
  131. data/test/controller/mime_type_test.rb +30 -8
  132. data/test/controller/new_render_test.rb +104 -22
  133. data/test/controller/polymorphic_routes_test.rb +98 -0
  134. data/test/controller/record_identifier_test.rb +103 -0
  135. data/test/controller/redirect_test.rb +120 -18
  136. data/test/controller/render_test.rb +195 -45
  137. data/test/controller/request_forgery_protection_test.rb +217 -0
  138. data/test/controller/request_test.rb +545 -27
  139. data/test/controller/rescue_test.rb +501 -0
  140. data/test/controller/resources_test.rb +258 -132
  141. data/test/controller/routing_test.rb +502 -106
  142. data/test/controller/selector_test.rb +5 -5
  143. data/test/controller/send_file_test.rb +17 -7
  144. data/test/controller/session/cookie_store_test.rb +246 -0
  145. data/test/controller/session/mem_cache_store_test.rb +182 -0
  146. data/test/controller/session_fixation_test.rb +8 -11
  147. data/test/controller/session_management_test.rb +7 -7
  148. data/test/controller/test_test.rb +150 -38
  149. data/test/controller/url_rewriter_test.rb +87 -12
  150. data/test/controller/verification_test.rb +11 -0
  151. data/test/controller/view_paths_test.rb +137 -0
  152. data/test/controller/webservice_test.rb +11 -75
  153. data/test/fixtures/addresses/{list.rhtml → list.erb} +0 -0
  154. data/test/fixtures/db_definitions/sqlite.sql +2 -1
  155. data/test/fixtures/developer.rb +2 -0
  156. data/test/fixtures/fun/games/{hello_world.rhtml → hello_world.erb} +0 -0
  157. data/test/fixtures/helpers/fun/pdf_helper.rb +1 -1
  158. data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
  159. data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
  160. data/test/fixtures/layouts/{builder.rxml → builder.builder} +0 -0
  161. data/test/fixtures/layouts/{standard.rhtml → standard.erb} +0 -0
  162. data/test/fixtures/layouts/{talk_from_action.rhtml → talk_from_action.erb} +0 -0
  163. data/test/fixtures/layouts/{yield.rhtml → yield.erb} +0 -0
  164. data/test/fixtures/multipart/binary_file +0 -0
  165. data/test/fixtures/multipart/bracketed_param +5 -0
  166. data/test/fixtures/override/test/hello_world.erb +1 -0
  167. data/test/fixtures/override2/layouts/test/sub.erb +1 -0
  168. data/test/fixtures/post_test/layouts/post.html.erb +1 -0
  169. data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
  170. data/test/fixtures/post_test/post/index.html.erb +1 -0
  171. data/test/fixtures/post_test/post/index.iphone.erb +1 -0
  172. data/test/fixtures/post_test/super_post/index.html.erb +1 -0
  173. data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
  174. data/test/fixtures/public/404.html +1 -0
  175. data/test/fixtures/public/500.html +1 -0
  176. data/test/fixtures/public/javascripts/application.js +0 -1
  177. data/test/fixtures/public/javascripts/bank.js +1 -0
  178. data/test/fixtures/public/javascripts/robber.js +1 -0
  179. data/test/fixtures/public/stylesheets/bank.css +1 -0
  180. data/test/fixtures/public/stylesheets/robber.css +1 -0
  181. data/test/fixtures/replies.yml +2 -0
  182. data/test/fixtures/reply.rb +2 -1
  183. data/test/fixtures/respond_to/{all_types_with_layout.rhtml → all_types_with_layout.html.erb} +0 -0
  184. data/test/fixtures/respond_to/{all_types_with_layout.rjs → all_types_with_layout.js.rjs} +0 -0
  185. data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
  186. data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
  187. data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
  188. data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
  189. data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
  190. data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
  191. data/test/fixtures/respond_to/{using_defaults.rhtml → using_defaults.html.erb} +0 -0
  192. data/test/fixtures/respond_to/{using_defaults.rjs → using_defaults.js.rjs} +0 -0
  193. data/test/fixtures/respond_to/{using_defaults.rxml → using_defaults.xml.builder} +0 -0
  194. data/test/fixtures/respond_to/{using_defaults_with_type_list.rhtml → using_defaults_with_type_list.html.erb} +0 -0
  195. data/test/fixtures/respond_to/{using_defaults_with_type_list.rjs → using_defaults_with_type_list.js.rjs} +0 -0
  196. data/test/fixtures/respond_to/{using_defaults_with_type_list.rxml → using_defaults_with_type_list.xml.builder} +0 -0
  197. data/test/fixtures/scope/test/{modgreet.rhtml → modgreet.erb} +0 -0
  198. data/test/fixtures/test/{_customer.rhtml → _customer.erb} +0 -0
  199. data/test/fixtures/test/{_customer_greeting.rhtml → _customer_greeting.erb} +0 -0
  200. data/test/fixtures/test/_hash_greeting.erb +1 -0
  201. data/test/fixtures/test/_hash_object.erb +2 -0
  202. data/test/fixtures/test/{_hello.rxml → _hello.builder} +0 -0
  203. data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
  204. data/test/fixtures/test/_partial.erb +1 -0
  205. data/test/fixtures/test/_partial.html.erb +1 -0
  206. data/test/fixtures/test/_partial.js.erb +1 -0
  207. data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
  208. data/test/fixtures/test/{_partial_only.rhtml → _partial_only.erb} +0 -0
  209. data/test/fixtures/test/{_person.rhtml → _person.erb} +0 -0
  210. data/test/fixtures/test/{action_talk_to_layout.rhtml → action_talk_to_layout.erb} +0 -0
  211. data/test/fixtures/test/{block_content_for.rhtml → block_content_for.erb} +0 -0
  212. data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
  213. data/test/fixtures/test/{capturing.rhtml → capturing.erb} +0 -0
  214. data/test/fixtures/test/{content_for.rhtml → content_for.erb} +0 -0
  215. data/test/fixtures/test/content_for_concatenated.erb +3 -0
  216. data/test/fixtures/test/content_for_with_parameter.erb +2 -0
  217. data/test/fixtures/test/dot.directory/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
  218. data/test/fixtures/test/{erb_content_for.rhtml → erb_content_for.erb} +0 -0
  219. data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
  220. data/test/fixtures/test/formatted_xml_erb.builder +1 -0
  221. data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
  222. data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
  223. data/test/fixtures/test/{greeting.rhtml → greeting.erb} +0 -0
  224. data/test/fixtures/test/{hello.rxml → hello.builder} +0 -0
  225. data/test/fixtures/test/{hello_world.rxml → hello_world.builder} +0 -0
  226. data/test/fixtures/test/{hello_world.rhtml → hello_world.erb} +0 -0
  227. data/test/fixtures/test/{hello_world_container.rxml → hello_world_container.builder} +0 -0
  228. data/test/fixtures/test/{hello_world_with_layout_false.rhtml → hello_world_with_layout_false.erb} +0 -0
  229. data/test/fixtures/test/{hello_xml_world.rxml → hello_xml_world.builder} +0 -0
  230. data/test/fixtures/test/list.erb +1 -0
  231. data/test/fixtures/test/{non_erb_block_content_for.rxml → non_erb_block_content_for.builder} +0 -0
  232. data/test/fixtures/test/{potential_conflicts.rhtml → potential_conflicts.erb} +0 -0
  233. data/test/fixtures/test/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
  234. data/test/fixtures/test/{render_file_with_locals.rhtml → render_file_with_locals.erb} +0 -0
  235. data/test/fixtures/test/{render_to_string_test.rhtml → render_to_string_test.erb} +0 -0
  236. data/test/fixtures/test/{update_element_with_capture.rhtml → update_element_with_capture.erb} +0 -0
  237. data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
  238. data/test/fixtures/topic.rb +1 -1
  239. data/test/template/active_record_helper_test.rb +67 -20
  240. data/test/template/asset_tag_helper_test.rb +222 -54
  241. data/test/template/atom_feed_helper_test.rb +101 -0
  242. data/test/template/benchmark_helper_test.rb +2 -2
  243. data/test/template/compiled_templates_test.rb +76 -32
  244. data/test/template/date_helper_test.rb +125 -9
  245. data/test/template/form_helper_test.rb +326 -33
  246. data/test/template/form_options_helper_test.rb +822 -15
  247. data/test/template/form_tag_helper_test.rb +96 -30
  248. data/test/template/javascript_helper_test.rb +61 -13
  249. data/test/template/number_helper_test.rb +12 -11
  250. data/test/template/prototype_helper_test.rb +185 -24
  251. data/test/template/sanitize_helper_test.rb +49 -0
  252. data/test/template/scriptaculous_helper_test.rb +9 -3
  253. data/test/template/tag_helper_test.rb +13 -2
  254. data/test/template/text_helper_test.rb +38 -52
  255. data/test/template/url_helper_test.rb +216 -46
  256. metadata +144 -116
  257. data/examples/.htaccess +0 -24
  258. data/examples/address_book/index.rhtml +0 -33
  259. data/examples/address_book/layout.rhtml +0 -8
  260. data/examples/address_book_controller.cgi +0 -9
  261. data/examples/address_book_controller.fcgi +0 -6
  262. data/examples/address_book_controller.rb +0 -52
  263. data/examples/address_book_controller.rbx +0 -4
  264. data/examples/benchmark.rb +0 -52
  265. data/examples/benchmark_with_ar.fcgi +0 -89
  266. data/examples/blog_controller.cgi +0 -53
  267. data/examples/debate/index.rhtml +0 -14
  268. data/examples/debate/new_topic.rhtml +0 -22
  269. data/examples/debate/topic.rhtml +0 -32
  270. data/examples/debate_controller.cgi +0 -57
  271. data/lib/action_controller/assertions/deprecated_assertions.rb +0 -228
  272. data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -36
  273. data/lib/action_controller/cgi_ext/cgi_methods.rb +0 -211
  274. data/lib/action_controller/cgi_ext/pstore_performance_fix.rb +0 -30
  275. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +0 -95
  276. data/lib/action_controller/cgi_ext/session_performance_fix.rb +0 -30
  277. data/lib/action_controller/deprecated_dependencies.rb +0 -65
  278. data/lib/action_controller/deprecated_redirects.rb +0 -17
  279. data/lib/action_controller/deprecated_request_methods.rb +0 -34
  280. data/lib/action_controller/macros/auto_complete.rb +0 -53
  281. data/lib/action_controller/macros/in_place_editing.rb +0 -33
  282. data/lib/action_controller/pagination.rb +0 -408
  283. data/lib/action_controller/scaffolding.rb +0 -189
  284. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -44
  285. data/lib/action_controller/templates/scaffolds/edit.rhtml +0 -7
  286. data/lib/action_controller/templates/scaffolds/layout.rhtml +0 -69
  287. data/lib/action_controller/templates/scaffolds/list.rhtml +0 -27
  288. data/lib/action_controller/templates/scaffolds/new.rhtml +0 -6
  289. data/lib/action_controller/templates/scaffolds/show.rhtml +0 -9
  290. data/lib/action_controller/vendor/xml_node.rb +0 -97
  291. data/lib/action_view/helpers/deprecated_helper.rb +0 -37
  292. data/lib/action_view/helpers/java_script_macros_helper.rb +0 -233
  293. data/lib/action_view/helpers/pagination_helper.rb +0 -86
  294. data/test/activerecord/active_record_assertions_test.rb +0 -92
  295. data/test/activerecord/pagination_test.rb +0 -165
  296. data/test/controller/deprecated_instance_variables_test.rb +0 -48
  297. data/test/controller/raw_post_test.rb +0 -68
  298. data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +0 -1
  299. data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +0 -1
  300. data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +0 -1
  301. data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +0 -1
  302. data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +0 -1
  303. data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +0 -1
  304. data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +0 -1
  305. data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +0 -1
  306. data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +0 -1
  307. data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +0 -1
  308. data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +0 -1
  309. data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +0 -1
  310. data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +0 -1
  311. data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +0 -1
  312. data/test/fixtures/respond_to/layouts/standard.rhtml +0 -1
  313. data/test/fixtures/test/_hash_object.rhtml +0 -1
  314. data/test/fixtures/test/list.rhtml +0 -1
  315. data/test/template/deprecated_helper_test.rb +0 -36
  316. data/test/template/deprecated_instance_variables_test.rb +0 -43
  317. data/test/template/java_script_macros_helper_test.rb +0 -109
@@ -1,9 +1,9 @@
1
- require File.dirname(__FILE__) + '/tokenizer'
2
- require File.dirname(__FILE__) + '/node'
3
- require File.dirname(__FILE__) + '/selector'
1
+ require 'html/tokenizer'
2
+ require 'html/node'
3
+ require 'html/selector'
4
+ require 'html/sanitizer'
4
5
 
5
6
  module HTML #:nodoc:
6
-
7
7
  # A top-level HTMl document. You give it a body of text, and it will parse that
8
8
  # text into a tree of nodes.
9
9
  class Document #:nodoc:
@@ -23,6 +23,9 @@ module HTML #:nodoc:
23
23
  if node.tag?
24
24
  if node_stack.length > 1 && node.closing == :close
25
25
  if node_stack.last.name == node.name
26
+ if node_stack.last.children.empty?
27
+ node_stack.last.children << Text.new(node_stack.last, node.line, node.position, "")
28
+ end
26
29
  node_stack.pop
27
30
  else
28
31
  open_start = node_stack.last.position - 20
@@ -0,0 +1,173 @@
1
+ module HTML
2
+ class Sanitizer
3
+ def sanitize(text, options = {})
4
+ return text unless sanitizeable?(text)
5
+ tokenize(text, options).join
6
+ end
7
+
8
+ def sanitizeable?(text)
9
+ !(text.nil? || text.empty? || !text.index("<"))
10
+ end
11
+
12
+ protected
13
+ def tokenize(text, options)
14
+ tokenizer = HTML::Tokenizer.new(text)
15
+ result = []
16
+ while token = tokenizer.next
17
+ node = Node.parse(nil, 0, 0, token, false)
18
+ process_node node, result, options
19
+ end
20
+ result
21
+ end
22
+
23
+ def process_node(node, result, options)
24
+ result << node.to_s
25
+ end
26
+ end
27
+
28
+ class FullSanitizer < Sanitizer
29
+ def sanitize(text, options = {})
30
+ result = super
31
+ # strip any comments, and if they have a newline at the end (ie. line with
32
+ # only a comment) strip that too
33
+ result.gsub!(/<!--(.*?)-->[\n]?/m, "") if result
34
+ # Recurse - handle all dirty nested tags
35
+ result == text ? result : sanitize(result, options)
36
+ end
37
+
38
+ def process_node(node, result, options)
39
+ result << node.to_s if node.class == HTML::Text
40
+ end
41
+ end
42
+
43
+ class LinkSanitizer < FullSanitizer
44
+ cattr_accessor :included_tags, :instance_writer => false
45
+ self.included_tags = Set.new(%w(a href))
46
+
47
+ def sanitizeable?(text)
48
+ !(text.nil? || text.empty? || !((text.index("<a") || text.index("<href")) && text.index(">")))
49
+ end
50
+
51
+ protected
52
+ def process_node(node, result, options)
53
+ result << node.to_s unless node.is_a?(HTML::Tag) && included_tags.include?(node.name)
54
+ end
55
+ end
56
+
57
+ class WhiteListSanitizer < Sanitizer
58
+ [:protocol_separator, :uri_attributes, :allowed_attributes, :allowed_tags, :allowed_protocols, :bad_tags,
59
+ :allowed_css_properties, :allowed_css_keywords, :shorthand_css_properties].each do |attr|
60
+ class_inheritable_accessor attr, :instance_writer => false
61
+ end
62
+
63
+ # A regular expression of the valid characters used to separate protocols like
64
+ # the ':' in 'http://foo.com'
65
+ self.protocol_separator = /:|(&#0*58)|(&#x70)|(%|&#37;)3A/
66
+
67
+ # Specifies a Set of HTML attributes that can have URIs.
68
+ self.uri_attributes = Set.new(%w(href src cite action longdesc xlink:href lowsrc))
69
+
70
+ # Specifies a Set of 'bad' tags that the #sanitize helper will remove completely, as opposed
71
+ # to just escaping harmless tags like &lt;font&gt;
72
+ self.bad_tags = Set.new(%w(script))
73
+
74
+ # Specifies the default Set of tags that the #sanitize helper will allow unscathed.
75
+ self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
76
+ sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dt dd abbr
77
+ acronym a img blockquote del ins))
78
+
79
+ # Specifies the default Set of html attributes that the #sanitize helper will leave
80
+ # in the allowed tag.
81
+ self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
82
+
83
+ # Specifies the default Set of acceptable css properties that #sanitize and #sanitize_css will accept.
84
+ self.allowed_protocols = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto
85
+ feed svn urn aim rsync tag ssh sftp rtsp afs))
86
+
87
+ # Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
88
+ self.allowed_css_properties = Set.new(%w(azimuth background-color border-bottom-color border-collapse
89
+ border-color border-left-color border-right-color border-top-color clear color cursor direction display
90
+ elevation float font font-family font-size font-style font-variant font-weight height letter-spacing line-height
91
+ overflow pause pause-after pause-before pitch pitch-range richness speak speak-header speak-numeral speak-punctuation
92
+ speech-rate stress text-align text-decoration text-indent unicode-bidi vertical-align voice-family volume white-space
93
+ width))
94
+
95
+ # Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
96
+ self.allowed_css_keywords = Set.new(%w(auto aqua black block blue bold both bottom brown center
97
+ collapse dashed dotted fuchsia gray green !important italic left lime maroon medium none navy normal
98
+ nowrap olive pointer purple red right solid silver teal top transparent underline white yellow))
99
+
100
+ # Specifies the default Set of allowed shorthand css properties for the #sanitize and #sanitize_css helpers.
101
+ self.shorthand_css_properties = Set.new(%w(background border margin padding))
102
+
103
+ # Sanitizes a block of css code. Used by #sanitize when it comes across a style attribute
104
+ def sanitize_css(style)
105
+ # disallow urls
106
+ style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ')
107
+
108
+ # gauntlet
109
+ if style !~ /^([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*$/ ||
110
+ style !~ /^(\s*[-\w]+\s*:\s*[^:;]*(;|$))*$/
111
+ return ''
112
+ end
113
+
114
+ clean = []
115
+ style.scan(/([-\w]+)\s*:\s*([^:;]*)/) do |prop,val|
116
+ if allowed_css_properties.include?(prop.downcase)
117
+ clean << prop + ': ' + val + ';'
118
+ elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
119
+ unless val.split().any? do |keyword|
120
+ !allowed_css_keywords.include?(keyword) &&
121
+ keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/
122
+ end
123
+ clean << prop + ': ' + val + ';'
124
+ end
125
+ end
126
+ end
127
+ clean.join(' ')
128
+ end
129
+
130
+ protected
131
+ def tokenize(text, options)
132
+ options[:parent] = []
133
+ options[:attributes] ||= allowed_attributes
134
+ options[:tags] ||= allowed_tags
135
+ super
136
+ end
137
+
138
+ def process_node(node, result, options)
139
+ result << case node
140
+ when HTML::Tag
141
+ if node.closing == :close
142
+ options[:parent].shift
143
+ else
144
+ options[:parent].unshift node.name
145
+ end
146
+
147
+ process_attributes_for node, options
148
+
149
+ options[:tags].include?(node.name) ? node : nil
150
+ else
151
+ bad_tags.include?(options[:parent].first) ? nil : node.to_s.gsub(/</, "&lt;")
152
+ end
153
+ end
154
+
155
+ def process_attributes_for(node, options)
156
+ return unless node.attributes
157
+ node.attributes.keys.each do |attr_name|
158
+ value = node.attributes[attr_name].to_s
159
+
160
+ if !options[:attributes].include?(attr_name) || contains_bad_protocols?(attr_name, value)
161
+ node.attributes.delete(attr_name)
162
+ else
163
+ node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(value)
164
+ end
165
+ end
166
+ end
167
+
168
+ def contains_bad_protocols?(attr_name, value)
169
+ uri_attributes.include?(attr_name) &&
170
+ (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(%|&#37;)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first))
171
+ end
172
+ end
173
+ end
@@ -240,19 +240,24 @@ module HTML
240
240
  raise ArgumentError, "CSS expression cannot be empty" if selector.empty?
241
241
  @source = ""
242
242
  values = values[0] if values.size == 1 && values[0].is_a?(Array)
243
+
243
244
  # We need a copy to determine if we failed to parse, and also
244
245
  # preserve the original pass by-ref statement.
245
246
  statement = selector.strip.dup
247
+
246
248
  # Create a simple selector, along with negation.
247
249
  simple_selector(statement, values).each { |name, value| instance_variable_set("@#{name}", value) }
248
250
 
251
+ @alternates = []
252
+ @depends = nil
253
+
249
254
  # Alternative selector.
250
255
  if statement.sub!(/^\s*,\s*/, "")
251
256
  second = Selector.new(statement, values)
252
- (@alternates ||= []) << second
257
+ @alternates << second
253
258
  # If there are alternate selectors, we group them in the top selector.
254
259
  if alternates = second.instance_variable_get(:@alternates)
255
- second.instance_variable_set(:@alternates, nil)
260
+ second.instance_variable_set(:@alternates, [])
256
261
  @alternates.concat alternates
257
262
  end
258
263
  @source << " , " << second.to_s
@@ -412,7 +417,7 @@ module HTML
412
417
 
413
418
  # If this selector is part of the group, try all the alternative
414
419
  # selectors (unless first_only).
415
- if @alternates && (!first_only || !matches)
420
+ if !first_only || !matches
416
421
  @alternates.each do |alternate|
417
422
  break if matches && first_only
418
423
  if subset = alternate.match(element, first_only)
@@ -789,15 +794,15 @@ module HTML
789
794
  # eventually, and array of substitution values.
790
795
  #
791
796
  # This method is called from four places, so it helps to put it here
792
- # for resue. The only logic deals with the need to detect comma
797
+ # for reuse. The only logic deals with the need to detect comma
793
798
  # separators (alternate) and apply them to the selector group of the
794
799
  # top selector.
795
800
  def next_selector(statement, values)
796
801
  second = Selector.new(statement, values)
797
802
  # If there are alternate selectors, we group them in the top selector.
798
803
  if alternates = second.instance_variable_get(:@alternates)
799
- second.instance_variable_set(:@alternates, nil)
800
- (@alternates ||= []).concat alternates
804
+ second.instance_variable_set(:@alternates, [])
805
+ @alternates.concat alternates
801
806
  end
802
807
  second
803
808
  end
@@ -12,7 +12,8 @@ module ActionController #:nodoc:
12
12
  # parameters being set, or without certain session values existing.
13
13
  #
14
14
  # When a verification is violated, values may be inserted into the flash, and
15
- # a specified redirection is triggered.
15
+ # a specified redirection is triggered. If no specific action is configured,
16
+ # verification failures will by default result in a 400 Bad Request response.
16
17
  #
17
18
  # Usage:
18
19
  #
@@ -42,37 +43,37 @@ module ActionController #:nodoc:
42
43
  # the user is redirected to a different action. The +options+ parameter
43
44
  # is a hash consisting of the following key/value pairs:
44
45
  #
45
- # * <tt>:params</tt>: a single key or an array of keys that must
46
+ # * <tt>:params</tt> - a single key or an array of keys that must
46
47
  # be in the <tt>params</tt> hash in order for the action(s) to be safely
47
48
  # called.
48
- # * <tt>:session</tt>: a single key or an array of keys that must
49
+ # * <tt>:session</tt> - a single key or an array of keys that must
49
50
  # be in the <tt>session</tt> in order for the action(s) to be safely called.
50
- # * <tt>:flash</tt>: a single key or an array of keys that must
51
+ # * <tt>:flash</tt> - a single key or an array of keys that must
51
52
  # be in the flash in order for the action(s) to be safely called.
52
- # * <tt>:method</tt>: a single key or an array of keys--any one of which
53
+ # * <tt>:method</tt> - a single key or an array of keys--any one of which
53
54
  # must match the current request method in order for the action(s) to
54
55
  # be safely called. (The key should be a symbol: <tt>:get</tt> or
55
56
  # <tt>:post</tt>, for example.)
56
- # * <tt>:xhr</tt>: true/false option to ensure that the request is coming
57
+ # * <tt>:xhr</tt> - true/false option to ensure that the request is coming
57
58
  # from an Ajax call or not.
58
- # * <tt>:add_flash</tt>: a hash of name/value pairs that should be merged
59
+ # * <tt>:add_flash</tt> - a hash of name/value pairs that should be merged
59
60
  # into the session's flash if the prerequisites cannot be satisfied.
60
- # * <tt>:add_headers</tt>: a hash of name/value pairs that should be
61
+ # * <tt>:add_headers</tt> - a hash of name/value pairs that should be
61
62
  # merged into the response's headers hash if the prerequisites cannot
62
63
  # be satisfied.
63
- # * <tt>:redirect_to</tt>: the redirection parameters to be used when
64
+ # * <tt>:redirect_to</tt> - the redirection parameters to be used when
64
65
  # redirecting if the prerequisites cannot be satisfied. You can
65
66
  # redirect either to named route or to the action in some controller.
66
- # * <tt>:render</tt>: the render parameters to be used when
67
+ # * <tt>:render</tt> - the render parameters to be used when
67
68
  # the prerequisites cannot be satisfied.
68
- # * <tt>:only</tt>: only apply this verification to the actions specified
69
+ # * <tt>:only</tt> - only apply this verification to the actions specified
69
70
  # in the associated array (may also be a single value).
70
- # * <tt>:except</tt>: do not apply this verification to the actions
71
+ # * <tt>:except</tt> - do not apply this verification to the actions
71
72
  # specified in the associated array (may also be a single value).
72
73
  def verify(options={})
73
74
  filter_opts = { :only => options[:only], :except => options[:except] }
74
75
  before_filter(filter_opts) do |c|
75
- c.send :verify_action, options
76
+ c.send! :verify_action, options
76
77
  end
77
78
  end
78
79
  end
@@ -81,7 +82,7 @@ module ActionController #:nodoc:
81
82
  prereqs_invalid =
82
83
  [*options[:params] ].find { |v| params[v].nil? } ||
83
84
  [*options[:session]].find { |v| session[v].nil? } ||
84
- [*options[:flash] ].find { |v| flash[v].nil? }
85
+ [*options[:flash] ].find { |v| flash[v].nil? }
85
86
 
86
87
  if !prereqs_invalid && options[:method]
87
88
  prereqs_invalid ||=
@@ -93,16 +94,21 @@ module ActionController #:nodoc:
93
94
  if prereqs_invalid
94
95
  flash.update(options[:add_flash]) if options[:add_flash]
95
96
  response.headers.update(options[:add_headers]) if options[:add_headers]
97
+
96
98
  unless performed?
97
- render(options[:render]) if options[:render]
98
- options[:redirect_to] = self.send(options[:redirect_to]) if options[:redirect_to].is_a? Symbol
99
- redirect_to(options[:redirect_to]) if options[:redirect_to]
99
+ case
100
+ when options[:render]
101
+ render(options[:render])
102
+ when options[:redirect_to]
103
+ options[:redirect_to] = self.send!(options[:redirect_to]) if options[:redirect_to].is_a?(Symbol)
104
+ redirect_to(options[:redirect_to])
105
+ else
106
+ head(:bad_request)
107
+ end
100
108
  end
101
- return false
102
109
  end
103
-
104
- true
105
110
  end
111
+
106
112
  private :verify_action
107
113
  end
108
- end
114
+ end
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2004-2006 David Heinemeier Hansson
2
+ # Copyright (c) 2004-2007 David Heinemeier Hansson
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -1,9 +1,9 @@
1
1
  module ActionPack #:nodoc:
2
2
  module VERSION #:nodoc:
3
- MAJOR = 1
4
- MINOR = 13
5
- TINY = 6
6
-
3
+ MAJOR = 2
4
+ MINOR = 0
5
+ TINY = 0
6
+
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
9
9
  end
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2004-2006 David Heinemeier Hansson
2
+ # Copyright (c) 2004-2007 David Heinemeier Hansson
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -21,7 +21,6 @@
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  #++
23
23
 
24
- $:.unshift(File.dirname(__FILE__) + "/action_view/vendor")
25
24
  require 'action_view/base'
26
25
  require 'action_view/partials'
27
26
 
@@ -29,4 +28,4 @@ ActionView::Base.class_eval do
29
28
  include ActionView::Partials
30
29
  end
31
30
 
32
- ActionView::Base.load_helpers(File.dirname(__FILE__) + "/action_view/helpers/")
31
+ ActionView::Base.load_helpers
@@ -1,11 +1,22 @@
1
1
  require 'erb'
2
+ require 'builder'
3
+
4
+ class ERB
5
+ module Util
6
+ HTML_ESCAPE = { '&' => '&amp;', '"' => '&quot;', '>' => '&gt;', '<' => '&lt;' }
7
+
8
+ def html_escape(s)
9
+ s.to_s.gsub(/[&\"><]/) { |special| HTML_ESCAPE[special] }
10
+ end
11
+ end
12
+ end
2
13
 
3
14
  module ActionView #:nodoc:
4
15
  class ActionViewError < StandardError #:nodoc:
5
16
  end
6
17
 
7
- # Action View templates can be written in three ways. If the template file has a +.rhtml+ extension then it uses a mixture of ERb
8
- # (included in Ruby) and HTML. If the template file has a +.rxml+ extension then Jim Weirich's Builder::XmlMarkup library is used.
18
+ # Action View templates can be written in three ways. If the template file has a +.erb+ (or +.rhtml+) extension then it uses a mixture of ERb
19
+ # (included in Ruby) and HTML. If the template file has a +.builder+ (or +.rxml+) extension then Jim Weirich's Builder::XmlMarkup library is used.
9
20
  # If the template file has a +.rjs+ extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.
10
21
  #
11
22
  # = ERb
@@ -77,12 +88,12 @@ module ActionView #:nodoc:
77
88
  # == Builder
78
89
  #
79
90
  # Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An +XmlMarkup+ object
80
- # named +xml+ is automatically made available to templates with a +.rxml+ extension.
91
+ # named +xml+ is automatically made available to templates with a +.builder+ extension.
81
92
  #
82
93
  # Here are some basic examples:
83
94
  #
84
95
  # xml.em("emphasized") # => <em>emphasized</em>
85
- # xml.em { xml.b("emp & bold") } # => <em><b>emph &amp; bold</b></em>
96
+ # xml.em { xml.b("emph & bold") } # => <em><b>emph &amp; bold</b></em>
86
97
  # xml.a("A Link", "href"=>"http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
87
98
  # xml.target("name"=>"compile", "option"=>"fast") # => <target option="fast" name="compile"\>
88
99
  # # NOTE: order of attributes is not specified.
@@ -154,10 +165,12 @@ module ActionView #:nodoc:
154
165
 
155
166
  attr_reader :first_render
156
167
  attr_accessor :base_path, :assigns, :template_extension
157
- attr_accessor :controller
168
+ attr_accessor :controller, :view_paths
158
169
 
159
170
  attr_reader :logger, :response, :headers
160
- attr_internal *ActionController::Base::DEPRECATED_INSTANCE_VARIABLES
171
+ attr_internal :cookies, :flash, :headers, :params, :request, :response, :session
172
+
173
+ attr_writer :template_format
161
174
 
162
175
  # Specify trim mode for the ERB compiler. Defaults to '-'.
163
176
  # See ERb documentation for suitable values.
@@ -168,7 +181,7 @@ module ActionView #:nodoc:
168
181
  @@cache_template_loading = false
169
182
  cattr_accessor :cache_template_loading
170
183
 
171
- # Specify whether file extension lookup should be cached.
184
+ # Specify whether file extension lookup should be cached, and whether template base path lookup should be cached.
172
185
  # Should be +false+ for development environments. Defaults to +true+.
173
186
  @@cache_template_extensions = true
174
187
  cattr_accessor :cache_template_extensions
@@ -183,6 +196,11 @@ module ActionView #:nodoc:
183
196
  # that alert()s the caught exception (and then re-raises it).
184
197
  @@debug_rjs = false
185
198
  cattr_accessor :debug_rjs
199
+
200
+ @@erb_variable = '_erbout'
201
+ cattr_accessor :erb_variable
202
+
203
+ delegate :request_forgery_protection_token, :to => :controller
186
204
 
187
205
  @@template_handlers = HashWithIndifferentAccess.new
188
206
 
@@ -203,16 +221,34 @@ module ActionView #:nodoc:
203
221
  # If for a given path, path.ext1 and path.ext2 exist on the file system, the order of extensions
204
222
  # used by pick_template_extension determines whether ext1 or ext2 will be stored.
205
223
  @@cached_template_extension = {}
224
+ # Maps template paths / extensions to
225
+ @@cached_base_paths = {}
226
+
227
+ # Cache public asset paths
228
+ cattr_reader :computed_public_paths
229
+ @@computed_public_paths = {}
230
+
231
+ @@templates_requiring_setup = Set.new(%w(builder rxml rjs))
232
+
233
+ # Order of template handers checked by #file_exists? depending on the current #template_format
234
+ DEFAULT_TEMPLATE_HANDLER_PREFERENCE = [:erb, :rhtml, :builder, :rxml, :javascript, :delegate]
235
+ TEMPLATE_HANDLER_PREFERENCES = {
236
+ :js => [:javascript, :erb, :rhtml, :builder, :rxml, :delegate],
237
+ :xml => [:builder, :rxml, :erb, :rhtml, :javascript, :delegate],
238
+ :delegate => [:delegate]
239
+ }
206
240
 
207
241
  class ObjectWrapper < Struct.new(:value) #:nodoc:
208
242
  end
209
243
 
210
- def self.load_helpers(helper_dir)#:nodoc:
211
- Dir.entries(helper_dir).sort.each do |helper_file|
212
- next unless helper_file =~ /^([a-z][a-z_]*_helper).rb$/
213
- require File.join(helper_dir, $1)
244
+ def self.load_helpers #:nodoc:
245
+ Dir.entries("#{File.dirname(__FILE__)}/helpers").sort.each do |file|
246
+ next unless file =~ /^([a-z][a-z_]*_helper).rb$/
247
+ require "action_view/helpers/#{$1}"
214
248
  helper_module_name = $1.camelize
215
- class_eval("include ActionView::Helpers::#{helper_module_name}") if Helpers.const_defined?(helper_module_name)
249
+ if Helpers.const_defined?(helper_module_name)
250
+ include Helpers.const_get(helper_module_name)
251
+ end
216
252
  end
217
253
  end
218
254
 
@@ -224,38 +260,58 @@ module ActionView #:nodoc:
224
260
  # local assigns available to the template. The #render method ought to
225
261
  # return the rendered template as a string.
226
262
  def self.register_template_handler(extension, klass)
263
+ TEMPLATE_HANDLER_PREFERENCES[extension.to_sym] = TEMPLATE_HANDLER_PREFERENCES[:delegate]
227
264
  @@template_handlers[extension] = klass
228
265
  end
229
266
 
230
- def initialize(base_path = nil, assigns_for_first_render = {}, controller = nil)#:nodoc:
231
- @base_path, @assigns = base_path, assigns_for_first_render
267
+ def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:
268
+ @view_paths = view_paths.respond_to?(:find) ? view_paths.dup : [*view_paths].compact
269
+ @assigns = assigns_for_first_render
232
270
  @assigns_added = nil
233
271
  @controller = controller
234
272
  @logger = controller && controller.logger
235
273
  end
236
274
 
237
275
  # Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true,
238
- # it's relative to the template_root, otherwise it's absolute. The hash in <tt>local_assigns</tt>
276
+ # it's relative to the view_paths array, otherwise it's absolute. The hash in <tt>local_assigns</tt>
239
277
  # is made available as local variables.
240
278
  def render_file(template_path, use_full_path = true, local_assigns = {}) #:nodoc:
241
- @first_render ||= template_path
279
+ if defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) && !template_path.include?("/")
280
+ raise ActionViewError, <<-END_ERROR
281
+ Due to changes in ActionMailer, you need to provide the mailer_name along with the template name.
242
282
 
243
- if use_full_path
244
- template_path_without_extension, template_extension = path_and_extension(template_path)
283
+ render "user_mailer/signup"
284
+ render :file => "user_mailer/signup"
285
+
286
+ If you are rendering a subtemplate, you must now use controller-like partial syntax:
245
287
 
288
+ render :partial => 'signup' # no mailer_name necessary
289
+ END_ERROR
290
+ end
291
+
292
+ @first_render ||= template_path
293
+ template_path_without_extension, template_extension = path_and_extension(template_path)
294
+ if use_full_path
246
295
  if template_extension
247
296
  template_file_name = full_template_path(template_path_without_extension, template_extension)
248
297
  else
249
298
  template_extension = pick_template_extension(template_path).to_s
299
+ unless template_extension
300
+ raise ActionViewError, "No #{template_handler_preferences.to_sentence} template found for #{template_path} in #{view_paths.inspect}"
301
+ end
250
302
  template_file_name = full_template_path(template_path, template_extension)
303
+ template_extension = template_extension.gsub(/^.+\./, '') # strip off any formats
251
304
  end
252
305
  else
253
306
  template_file_name = template_path
254
- template_extension = template_path.split('.').last
255
307
  end
256
308
 
257
309
  template_source = nil # Don't read the source until we know that it is required
258
310
 
311
+ if template_file_name.blank?
312
+ raise ActionViewError, "Couldn't find template file for #{template_path} in #{view_paths.inspect}"
313
+ end
314
+
259
315
  begin
260
316
  render_template(template_extension, template_source, template_file_name, local_assigns)
261
317
  rescue Exception => e
@@ -263,12 +319,12 @@ module ActionView #:nodoc:
263
319
  e.sub_template_of(template_file_name)
264
320
  raise e
265
321
  else
266
- raise TemplateError.new(@base_path, template_file_name, @assigns, template_source, e)
322
+ raise TemplateError.new(find_base_path_for("#{template_path_without_extension}.#{template_extension}") || view_paths.first, template_file_name, @assigns, template_source, e)
267
323
  end
268
324
  end
269
325
  end
270
-
271
- # Renders the template present at <tt>template_path</tt> (relative to the template_root).
326
+
327
+ # Renders the template present at <tt>template_path</tt> (relative to the view_paths array).
272
328
  # The hash in <tt>local_assigns</tt> is made available as local variables.
273
329
  def render(options = {}, old_local_assigns = {}, &block) #:nodoc:
274
330
  if options.is_a?(String)
@@ -276,22 +332,31 @@ module ActionView #:nodoc:
276
332
  elsif options == :update
277
333
  update_page(&block)
278
334
  elsif options.is_a?(Hash)
279
- options[:locals] ||= {}
280
- options[:use_full_path] = options[:use_full_path].nil? ? true : options[:use_full_path]
335
+ options = options.reverse_merge(:type => :erb, :locals => {}, :use_full_path => true)
281
336
 
282
- if options[:file]
337
+ if options[:layout]
338
+ path, partial_name = partial_pieces(options.delete(:layout))
339
+
340
+ if block_given?
341
+ @content_for_layout = capture(&block)
342
+ concat(render(options.merge(:partial => "#{path}/#{partial_name}")), block.binding)
343
+ else
344
+ @content_for_layout = render(options)
345
+ render(options.merge(:partial => "#{path}/#{partial_name}"))
346
+ end
347
+ elsif options[:file]
283
348
  render_file(options[:file], options[:use_full_path], options[:locals])
284
349
  elsif options[:partial] && options[:collection]
285
350
  render_partial_collection(options[:partial], options[:collection], options[:spacer_template], options[:locals])
286
351
  elsif options[:partial]
287
352
  render_partial(options[:partial], ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals])
288
353
  elsif options[:inline]
289
- render_template(options[:type] || :rhtml, options[:inline], nil, options[:locals] || {})
354
+ render_template(options[:type], options[:inline], nil, options[:locals])
290
355
  end
291
356
  end
292
357
  end
293
358
 
294
- # Renders the +template+ which is given as a string as either rhtml or rxml depending on <tt>template_extension</tt>.
359
+ # Renders the +template+ which is given as a string as either erb or builder depending on <tt>template_extension</tt>.
295
360
  # The hash in <tt>local_assigns</tt> is made available as local variables.
296
361
  def render_template(template_extension, template, file_path = nil, local_assigns = {}) #:nodoc:
297
362
  if handler = @@template_handlers[template_extension]
@@ -327,23 +392,54 @@ module ActionView #:nodoc:
327
392
  end
328
393
  end
329
394
 
395
+ # Gets the full template path with base path for the given template_path and extension.
396
+ #
397
+ # full_template_path('users/show', 'html.erb')
398
+ # # => '~/rails/app/views/users/show.html.erb
399
+ #
400
+ def full_template_path(template_path, extension)
401
+ if @@cache_template_extensions
402
+ (@@cached_base_paths[template_path] ||= {})[extension.to_s] ||= find_full_template_path(template_path, extension)
403
+ else
404
+ find_full_template_path(template_path, extension)
405
+ end
406
+ end
407
+
408
+ # Gets the extension for an existing template with the given template_path.
409
+ # Returns the format with the extension if that template exists.
410
+ #
411
+ # pick_template_extension('users/show')
412
+ # # => 'html.erb'
413
+ #
414
+ # pick_template_extension('users/legacy')
415
+ # # => "rhtml"
416
+ #
330
417
  def pick_template_extension(template_path)#:nodoc:
331
418
  if @@cache_template_extensions
332
- @@cached_template_extension[template_path] ||= find_template_extension_for(template_path)
419
+ (@@cached_template_extension[template_path] ||= {})[template_format] ||= find_template_extension_for(template_path)
333
420
  else
334
421
  find_template_extension_for(template_path)
335
422
  end
336
423
  end
337
424
 
338
425
  def delegate_template_exists?(template_path)#:nodoc:
339
- @@template_handlers.find { |k,| template_exists?(template_path, k) }
426
+ delegate = @@template_handlers.find { |k,| template_exists?(template_path, k) }
427
+ delegate && delegate.first.to_sym
340
428
  end
341
429
 
342
430
  def erb_template_exists?(template_path)#:nodoc:
343
- template_exists?(template_path, :rhtml)
431
+ template_exists?(template_path, :erb)
344
432
  end
345
433
 
346
434
  def builder_template_exists?(template_path)#:nodoc:
435
+ template_exists?(template_path, :builder)
436
+ end
437
+
438
+ def rhtml_template_exists?(template_path)#:nodoc:
439
+ template_exists?(template_path, :rhtml)
440
+ end
441
+
442
+ def rxml_template_exists?(template_path)#:nodoc:
347
443
  template_exists?(template_path, :rxml)
348
444
  end
349
445
 
@@ -353,14 +449,10 @@ module ActionView #:nodoc:
353
449
 
354
450
  def file_exists?(template_path)#:nodoc:
355
451
  template_file_name, template_file_extension = path_and_extension(template_path)
356
-
357
452
  if template_file_extension
358
453
  template_exists?(template_file_name, template_file_extension)
359
454
  else
360
- cached_template_extension(template_path) ||
361
- %w(erb builder javascript delegate).any? do |template_type|
362
- send("#{template_type}_template_exists?", template_path)
363
- end
455
+ pick_template_extension(template_path)
364
456
  end
365
457
  end
366
458
 
@@ -369,37 +461,95 @@ module ActionView #:nodoc:
369
461
  template_path.split('/').last[0,1] != '_'
370
462
  end
371
463
 
464
+ # symbolized version of the :format parameter of the request, or :html by default.
465
+ def template_format
466
+ return @template_format if @template_format
467
+ format = controller && controller.respond_to?(:request) && controller.request.parameters[:format]
468
+ @template_format = format.blank? ? :html : format.to_sym
469
+ end
470
+
471
+ def template_handler_preferences
472
+ TEMPLATE_HANDLER_PREFERENCES[template_format] || DEFAULT_TEMPLATE_HANDLER_PREFERENCE
473
+ end
474
+
475
+ # Adds a view_path to the front of the view_paths array.
476
+ # This change affects the current request only.
477
+ #
478
+ # @template.prepend_view_path("views/default")
479
+ # @template.prepend_view_path(["views/default", "views/custom"])
480
+ #
481
+ def prepend_view_path(path)
482
+ @view_paths.unshift(*path)
483
+ end
484
+
485
+ # Adds a view_path to the end of the view_paths array.
486
+ # This change affects the current request only.
487
+ #
488
+ # @template.append_view_path("views/default")
489
+ # @template.append_view_path(["views/default", "views/custom"])
490
+ #
491
+ def append_view_path(path)
492
+ @view_paths.push(*path)
493
+ end
494
+
372
495
  private
373
- # Builds a string holding the full path of the template including extension
374
- def full_template_path(template_path, extension)
375
- "#{@base_path}/#{template_path}.#{extension}"
496
+ def find_full_template_path(template_path, extension)
497
+ file_name = "#{template_path}.#{extension}"
498
+ base_path = find_base_path_for(file_name)
499
+ base_path.blank? ? "" : "#{base_path}/#{file_name}"
376
500
  end
377
501
 
378
502
  # Asserts the existence of a template.
379
503
  def template_exists?(template_path, extension)
380
504
  file_path = full_template_path(template_path, extension)
381
- @@method_names.has_key?(file_path) || FileTest.exists?(file_path)
505
+ !file_path.blank? && @@method_names.has_key?(file_path) || FileTest.exists?(file_path)
382
506
  end
383
507
 
508
+ # Splits the path and extension from the given template_path and returns as an array.
384
509
  def path_and_extension(template_path)
385
510
  template_path_without_extension = template_path.sub(/\.(\w+)$/, '')
386
511
  [ template_path_without_extension, $1 ]
387
512
  end
513
+
514
+ # Returns the view path that contains the given relative template path.
515
+ def find_base_path_for(template_file_name)
516
+ view_paths.find { |p| File.file?(File.join(p, template_file_name)) }
517
+ end
388
518
 
389
- def cached_template_extension(template_path)
390
- @@cache_template_extensions && @@cached_template_extension[template_path]
519
+ # Returns the view path that the full path resides in.
520
+ def extract_base_path_from(full_path)
521
+ view_paths.find { |p| full_path[0..p.size - 1] == p }
391
522
  end
392
523
 
393
524
  # Determines the template's file extension, such as rhtml, rxml, or rjs.
394
525
  def find_template_extension_for(template_path)
395
- if match = delegate_template_exists?(template_path)
396
- match.first.to_sym
397
- elsif erb_template_exists?(template_path): :rhtml
398
- elsif builder_template_exists?(template_path): :rxml
399
- elsif javascript_template_exists?(template_path): :rjs
400
- else
401
- raise ActionViewError, "No rhtml, rxml, rjs or delegate template found for #{template_path} in #{@base_path}"
526
+ find_template_extension_from_handler(template_path, true) ||
527
+ find_template_extension_from_handler(template_path) ||
528
+ find_template_extension_from_first_render()
529
+ end
530
+
531
+ def find_template_extension_from_handler(template_path, formatted = nil)
532
+ checked_template_path = formatted ? "#{template_path}.#{template_format}" : template_path
533
+ template_handler_preferences.each do |template_type|
534
+ extension =
535
+ case template_type
536
+ when :javascript
537
+ template_exists?(checked_template_path, :rjs) && :rjs
538
+ when :delegate
539
+ delegate_template_exists?(checked_template_path)
540
+ else
541
+ template_exists?(checked_template_path, template_type) && template_type
542
+ end
543
+ if extension
544
+ return formatted ? "#{template_format}.#{extension}" : extension.to_s
545
+ end
402
546
  end
547
+ nil
548
+ end
549
+
550
+ # Determine the template extension from the <tt>@first_render</tt> filename
551
+ def find_template_extension_from_first_render
552
+ File.basename(@first_render.to_s)[/^[^.]+\.(.+)$/, 1]
403
553
  end
404
554
 
405
555
  # This method reads a template file.
@@ -439,27 +589,36 @@ module ActionView #:nodoc:
439
589
  method_key = file_name || template
440
590
  render_symbol = @@method_names[method_key]
441
591
 
442
- if @@compile_time[render_symbol] && supports_local_assigns?(render_symbol, local_assigns)
443
- if file_name && !@@cache_template_loading
444
- @@compile_time[render_symbol] < File.mtime(file_name) ||
445
- (File.symlink?(file_name) && (@@compile_time[render_symbol] < File.lstat(file_name).mtime))
592
+ compile_time = @@compile_time[render_symbol]
593
+ if compile_time && supports_local_assigns?(render_symbol, local_assigns)
594
+ if file_name && !@@cache_template_loading
595
+ template_changed_since?(file_name, compile_time)
446
596
  end
447
597
  else
448
598
  true
449
599
  end
450
600
  end
451
601
 
602
+ # Method to handle checking a whether a template has changed since last compile; isolated so that templates
603
+ # not stored on the file system can hook and extend appropriately.
604
+ def template_changed_since?(file_name, compile_time)
605
+ lstat = File.lstat(file_name)
606
+ compile_time < lstat.mtime ||
607
+ (lstat.symlink? && compile_time < File.stat(file_name).mtime)
608
+ end
609
+
452
610
  # Method to create the source code for a given template.
453
611
  def create_template_source(extension, template, render_symbol, locals)
454
612
  if template_requires_setup?(extension)
455
613
  body = case extension.to_sym
456
- when :rxml
457
- "controller.response.content_type ||= 'application/xml'\n" +
458
- "xml ||= Builder::XmlMarkup.new(:indent => 2)\n" +
614
+ when :rxml, :builder
615
+ content_type_handler = (controller.respond_to?(:response) ? "controller.response" : "controller")
616
+ "#{content_type_handler}.content_type ||= Mime::XML\n" +
617
+ "xml = Builder::XmlMarkup.new(:indent => 2)\n" +
459
618
  template +
460
619
  "\nxml.target!\n"
461
620
  when :rjs
462
- "controller.response.content_type ||= 'text/javascript'\n" +
621
+ "controller.response.content_type ||= Mime::JS\n" +
463
622
  "update_page do |page|\n#{template}\nend"
464
623
  end
465
624
  else
@@ -479,11 +638,7 @@ module ActionView #:nodoc:
479
638
  end
480
639
 
481
640
  def template_requires_setup?(extension) #:nodoc:
482
- templates_requiring_setup.include? extension.to_s
483
- end
484
-
485
- def templates_requiring_setup #:nodoc:
486
- %w(rxml rjs)
641
+ @@templates_requiring_setup.include? extension.to_s
487
642
  end
488
643
 
489
644
  def assign_method_name(extension, template, file_name)
@@ -514,7 +669,7 @@ module ActionView #:nodoc:
514
669
  line_offset = @@template_args[render_symbol].size
515
670
  if extension
516
671
  case extension.to_sym
517
- when :rxml, :rjs
672
+ when :builder, :rxml, :rjs
518
673
  line_offset += 2
519
674
  end
520
675
  end
@@ -532,7 +687,7 @@ module ActionView #:nodoc:
532
687
  logger.debug "Backtrace: #{e.backtrace.join("\n")}"
533
688
  end
534
689
 
535
- raise TemplateError.new(@base_path, file_name || template, @assigns, template, e)
690
+ raise TemplateError.new(extract_base_path_from(file_name) || view_paths.first, file_name || template, @assigns, template, e)
536
691
  end
537
692
 
538
693
  @@compile_time[render_symbol] = Time.now