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,64 +1,103 @@
1
- require File.dirname(__FILE__) + '/tag_helper'
1
+ require 'action_view/helpers/tag_helper'
2
+ require 'html/document'
2
3
 
3
4
  module ActionView
4
5
  module Helpers #:nodoc:
5
- # The TextHelper Module provides a set of methods for filtering, formatting
6
- # and transforming strings that can reduce the amount of inline Ruby code in
6
+ # The TextHelper module provides a set of methods for filtering, formatting
7
+ # and transforming strings, which can reduce the amount of inline Ruby code in
7
8
  # your views. These helper methods extend ActionView making them callable
8
- # within your template files as shown in the following example which truncates
9
- # the title of each post to 10 characters.
10
- #
11
- # <% @posts.each do |post| %>
12
- # # post == 'This is my title'
13
- # Title: <%= truncate(post.title, 10) %>
14
- # <% end %>
15
- # => Title: This is my...
16
- module TextHelper
9
+ # within your template files.
10
+ module TextHelper
17
11
  # The preferred method of outputting text in your views is to use the
18
12
  # <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
19
13
  # do not operate as expected in an eRuby code block. If you absolutely must
20
- # output text within a code block, you can use the concat method.
14
+ # output text within a non-output code block (i.e., <% %>), you can use the concat method.
21
15
  #
22
- # <% concat "hello", binding %>
23
- # is equivalent to using:
24
- # <%= "hello" %>
16
+ # ==== Examples
17
+ # <%
18
+ # concat "hello", binding
19
+ # # is the equivalent of <%= "hello" %>
20
+ #
21
+ # if (logged_in == true):
22
+ # concat "Logged in!", binding
23
+ # else
24
+ # concat link_to('login', :action => login), binding
25
+ # end
26
+ # # will either display "Logged in!" or a login link
27
+ # %>
25
28
  def concat(string, binding)
26
- eval("_erbout", binding).concat(string)
29
+ eval(ActionView::Base.erb_variable, binding) << string
27
30
  end
28
31
 
29
32
  # If +text+ is longer than +length+, +text+ will be truncated to the length of
30
- # +length+ and the last three characters will be replaced with the +truncate_string+.
33
+ # +length+ (defaults to 30) and the last characters will be replaced with the +truncate_string+
34
+ # (defaults to "...").
31
35
  #
36
+ # ==== Examples
32
37
  # truncate("Once upon a time in a world far far away", 14)
33
- # => Once upon a...
38
+ # # => Once upon a...
39
+ #
40
+ # truncate("Once upon a time in a world far far away")
41
+ # # => Once upon a time in a world f...
42
+ #
43
+ # truncate("And they found that many people were sleeping better.", 25, "(clipped)")
44
+ # # => And they found that many (clipped)
45
+ #
46
+ # truncate("And they found that many people were sleeping better.", 15, "... (continued)")
47
+ # # => And they found... (continued)
34
48
  def truncate(text, length = 30, truncate_string = "...")
35
49
  if text.nil? then return end
36
50
  l = length - truncate_string.chars.length
37
- text.chars.length > length ? text.chars[0...l] + truncate_string : text
51
+ (text.chars.length > length ? text.chars[0...l] + truncate_string : text).to_s
38
52
  end
39
53
 
40
- # Highlights +phrase+ everywhere it is found in +text+ by inserting it into
54
+ # Highlights one or more +phrases+ everywhere in +text+ by inserting it into
41
55
  # a +highlighter+ string. The highlighter can be specialized by passing +highlighter+
42
- # as a single-quoted string with \1 where the phrase is to be inserted.
56
+ # as a single-quoted string with \1 where the phrase is to be inserted (defaults to
57
+ # '<strong class="highlight">\1</strong>')
43
58
  #
59
+ # ==== Examples
44
60
  # highlight('You searched for: rails', 'rails')
45
- # => You searched for: <strong class="highlight">rails</strong>
46
- def highlight(text, phrase, highlighter = '<strong class="highlight">\1</strong>')
47
- if phrase.blank? then return text end
48
- text.gsub(/(#{Regexp.escape(phrase)})/i, highlighter) unless text.nil?
61
+ # # => You searched for: <strong class="highlight">rails</strong>
62
+ #
63
+ # highlight('You searched for: ruby, rails, dhh', 'actionpack')
64
+ # # => You searched for: ruby, rails, dhh
65
+ #
66
+ # highlight('You searched for: rails', ['for', 'rails'], '<em>\1</em>')
67
+ # # => You searched <em>for</em>: <em>rails</em>
68
+ #
69
+ # highlight('You searched for: rails', 'rails', "<a href='search?q=\1'>\1</a>")
70
+ # # => You searched for: <a href='search?q=rails>rails</a>
71
+ def highlight(text, phrases, highlighter = '<strong class="highlight">\1</strong>')
72
+ if text.blank? || phrases.blank?
73
+ text
74
+ else
75
+ match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
76
+ text.gsub(/(#{match})/i, highlighter)
77
+ end
49
78
  end
50
79
 
51
80
  # Extracts an excerpt from +text+ that matches the first instance of +phrase+.
52
- # The +radius+ expands the excerpt on each side of +phrase+ by the number of characters
53
- # defined in +radius+. If the excerpt radius overflows the beginning or end of the +text+,
81
+ # The +radius+ expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
82
+ # defined in +radius+ (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
54
83
  # then the +excerpt_string+ will be prepended/appended accordingly. If the +phrase+
55
84
  # isn't found, nil is returned.
56
85
  #
86
+ # ==== Examples
57
87
  # excerpt('This is an example', 'an', 5)
58
- # => "...s is an examp..."
88
+ # # => "...s is an examp..."
59
89
  #
60
90
  # excerpt('This is an example', 'is', 5)
61
- # => "This is an..."
91
+ # # => "This is an..."
92
+ #
93
+ # excerpt('This is an example', 'is')
94
+ # # => "This is an example"
95
+ #
96
+ # excerpt('This next thing is an example', 'ex', 2)
97
+ # # => "...next t..."
98
+ #
99
+ # excerpt('This is also an example', 'an', 8, '<chop> ')
100
+ # # => "<chop> is also an example"
62
101
  def excerpt(text, phrase, radius = 100, excerpt_string = "...")
63
102
  if text.nil? || phrase.nil? then return end
64
103
  phrase = Regexp.escape(phrase)
@@ -81,11 +120,20 @@ module ActionView
81
120
  # is loaded, it will use the Inflector to determine the plural form, otherwise
82
121
  # it will just add an 's' to the +singular+ word.
83
122
  #
84
- # pluralize(1, 'person') => 1 person
85
- # pluralize(2, 'person') => 2 people
86
- # pluralize(3, 'person', 'users') => 3 users
123
+ # ==== Examples
124
+ # pluralize(1, 'person')
125
+ # # => 1 person
126
+ #
127
+ # pluralize(2, 'person')
128
+ # # => 2 people
129
+ #
130
+ # pluralize(3, 'person', 'users')
131
+ # # => 3 users
132
+ #
133
+ # pluralize(0, 'person')
134
+ # # => 0 people
87
135
  def pluralize(count, singular, plural = nil)
88
- "#{count} " + if count == 1 || count == '1'
136
+ "#{count || 0} " + if count == 1 || count == '1'
89
137
  singular
90
138
  elsif plural
91
139
  plural
@@ -97,20 +145,48 @@ module ActionView
97
145
  end
98
146
 
99
147
  # Wraps the +text+ into lines no longer than +line_width+ width. This method
100
- # breaks on the first whitespace character that does not exceed +line_width+.
148
+ # breaks on the first whitespace character that does not exceed +line_width+
149
+ # (which is 80 by default).
101
150
  #
151
+ # ==== Examples
102
152
  # word_wrap('Once upon a time', 4)
103
- # => Once\nupon\na\ntime
153
+ # # => Once\nupon\na\ntime
154
+ #
155
+ # word_wrap('Once upon a time', 8)
156
+ # # => Once upon\na time
157
+ #
158
+ # word_wrap('Once upon a time')
159
+ # # => Once upon a time
160
+ #
161
+ # word_wrap('Once upon a time', 1)
162
+ # # => Once\nupon\na\ntime
104
163
  def word_wrap(text, line_width = 80)
105
- text.gsub(/\n/, "\n\n").gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip
164
+ text.split("\n").collect do |line|
165
+ line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
166
+ end * "\n"
106
167
  end
107
168
 
108
169
  begin
109
170
  require_library_or_gem "redcloth" unless Object.const_defined?(:RedCloth)
110
171
 
111
- # Returns the text with all the Textile codes turned into HTML tags.
172
+ # Returns the text with all the Textile[http://www.textism.com/tools/textile] codes turned into HTML tags.
173
+ #
174
+ # You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile].
112
175
  # <i>This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
113
176
  # is available</i>.
177
+ #
178
+ # ==== Examples
179
+ # textilize("*This is Textile!* Rejoice!")
180
+ # # => "<p><strong>This is Textile!</strong> Rejoice!</p>"
181
+ #
182
+ # textilize("I _love_ ROR(Ruby on Rails)!")
183
+ # # => "<p>I <em>love</em> <acronym title="Ruby on Rails">ROR</acronym>!</p>"
184
+ #
185
+ # textilize("h2. Textile makes markup -easy- simple!")
186
+ # # => "<h2>Textile makes markup <del>easy</del> simple!</h2>"
187
+ #
188
+ # textilize("Visit the Rails website "here":http://www.rubyonrails.org/.)
189
+ # # => "<p>Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>.</p>"
114
190
  def textilize(text)
115
191
  if text.blank?
116
192
  ""
@@ -123,8 +199,23 @@ module ActionView
123
199
 
124
200
  # Returns the text with all the Textile codes turned into HTML tags,
125
201
  # but without the bounding <p> tag that RedCloth adds.
202
+ #
203
+ # You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile].
126
204
  # <i>This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
127
205
  # is available</i>.
206
+ #
207
+ # ==== Examples
208
+ # textilize_without_paragraph("*This is Textile!* Rejoice!")
209
+ # # => "<strong>This is Textile!</strong> Rejoice!"
210
+ #
211
+ # textilize_without_paragraph("I _love_ ROR(Ruby on Rails)!")
212
+ # # => "I <em>love</em> <acronym title="Ruby on Rails">ROR</acronym>!"
213
+ #
214
+ # textilize_without_paragraph("h2. Textile makes markup -easy- simple!")
215
+ # # => "<h2>Textile makes markup <del>easy</del> simple!</h2>"
216
+ #
217
+ # textilize_without_paragraph("Visit the Rails website "here":http://www.rubyonrails.org/.)
218
+ # # => "Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>."
128
219
  def textilize_without_paragraph(text)
129
220
  textiled = textilize(text)
130
221
  if textiled[0..2] == "<p>" then textiled = textiled[3..-1] end
@@ -141,6 +232,20 @@ module ActionView
141
232
  # Returns the text with all the Markdown codes turned into HTML tags.
142
233
  # <i>This method is only available if BlueCloth[http://www.deveiate.org/projects/BlueCloth]
143
234
  # is available</i>.
235
+ #
236
+ # ==== Examples
237
+ # markdown("We are using __Markdown__ now!")
238
+ # # => "<p>We are using <strong>Markdown</strong> now!</p>"
239
+ #
240
+ # markdown("We like to _write_ `code`, not just _read_ it!")
241
+ # # => "<p>We like to <em>write</em> <code>code</code>, not just <em>read</em> it!</p>"
242
+ #
243
+ # markdown("The [Markdown website](http://daringfireball.net/projects/markdown/) has more information.")
244
+ # # => "<p>The <a href="http://daringfireball.net/projects/markdown/">Markdown website</a>
245
+ # # has more information.</p>"
246
+ #
247
+ # markdown('![The ROR logo](http://rubyonrails.com/images/rails.png "Ruby on Rails")')
248
+ # # => '<p><img src="http://rubyonrails.com/images/rails.png" alt="The ROR logo" title="Ruby on Rails" /></p>'
144
249
  def markdown(text)
145
250
  text.blank? ? "" : BlueCloth.new(text).to_html
146
251
  end
@@ -153,6 +258,20 @@ module ActionView
153
258
  # paragraph and wrapped in <tt><p></tt> tags. One newline (<tt>\n</tt>) is
154
259
  # considered as a linebreak and a <tt><br /></tt> tag is appended. This
155
260
  # method does not remove the newlines from the +text+.
261
+ #
262
+ # ==== Examples
263
+ # my_text = """Here is some basic text...
264
+ # ...with a line break."""
265
+ #
266
+ # simple_format(my_text)
267
+ # # => "<p>Here is some basic text...<br />...with a line break.</p>"
268
+ #
269
+ # more_text = """We want to put a paragraph...
270
+ #
271
+ # ...right there."""
272
+ #
273
+ # simple_format(more_text)
274
+ # # => "<p>We want to put a paragraph...</p><p>...right there.</p>"
156
275
  def simple_format(text)
157
276
  content_tag 'p', text.to_s.
158
277
  gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
@@ -160,139 +279,70 @@ module ActionView
160
279
  gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
161
280
  end
162
281
 
163
- # Turns all urls and email addresses into clickable links. The +link+ parameter
164
- # will limit what should be linked. You can add html attributes to the links using
282
+ # Turns all URLs and e-mail addresses into clickable links. The +link+ parameter
283
+ # will limit what should be linked. You can add HTML attributes to the links using
165
284
  # +href_options+. Options for +link+ are <tt>:all</tt> (default),
166
- # <tt>:email_addresses</tt>, and <tt>:urls</tt>.
285
+ # <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
286
+ # e-mail address is yielded and the result is used as the link text.
287
+ #
288
+ # ==== Examples
289
+ # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
290
+ # # => "Go to <a href="http://www.rubyonrails.org">http://www.rubyonrails.org</a> and
291
+ # # say hello to <a href="mailto:david@loudthinking.com">david@loudthinking.com</a>"
167
292
  #
168
- # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com") =>
169
- # Go to <a href="http://www.rubyonrails.org">http://www.rubyonrails.org</a> and
170
- # say hello to <a href="mailto:david@loudthinking.com">david@loudthinking.com</a>
293
+ # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :urls)
294
+ # # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
295
+ # # or e-mail david@loudthinking.com"
171
296
  #
172
- # If a block is given, each url and email address is yielded and the
173
- # result is used as the link text.
297
+ # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :email_addresses)
298
+ # # => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
174
299
  #
175
- # auto_link(post.body, :all, :target => '_blank') do |text|
300
+ # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
301
+ # auto_link(post_body, :all, :target => '_blank') do |text|
176
302
  # truncate(text, 15)
177
303
  # end
304
+ # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
305
+ # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
306
+ #
178
307
  def auto_link(text, link = :all, href_options = {}, &block)
179
308
  return '' if text.blank?
180
309
  case link
181
- when :all then auto_link_urls(auto_link_email_addresses(text, &block), href_options, &block)
310
+ when :all then auto_link_email_addresses(auto_link_urls(text, href_options, &block), &block)
182
311
  when :email_addresses then auto_link_email_addresses(text, &block)
183
312
  when :urls then auto_link_urls(text, href_options, &block)
184
313
  end
185
314
  end
186
-
187
- # Strips link tags from +text+ leaving just the link label.
188
- #
189
- # strip_links('<a href="http://www.rubyonrails.org">Ruby on Rails</a>')
190
- # => Ruby on Rails
191
- def strip_links(text)
192
- text.gsub(/<a\b.*?>(.*?)<\/a>/mi, '\1')
193
- end
194
-
195
- # Try to require the html-scanner library
196
- begin
197
- require 'html/tokenizer'
198
- require 'html/node'
199
- rescue LoadError
200
- # if there isn't a copy installed, use the vendor version in
201
- # ActionController
202
- $:.unshift File.join(File.dirname(__FILE__), "..", "..",
203
- "action_controller", "vendor", "html-scanner")
204
- require 'html/tokenizer'
205
- require 'html/node'
206
- end
207
-
208
- VERBOTEN_TAGS = %w(form script plaintext) unless defined?(VERBOTEN_TAGS)
209
- VERBOTEN_ATTRS = /^on/i unless defined?(VERBOTEN_ATTRS)
210
-
211
- # Sanitizes the +html+ by converting <form> and <script> tags into regular
212
- # text, and removing all "onxxx" attributes (so that arbitrary Javascript
213
- # cannot be executed). It also removes href= and src= attributes that start with
214
- # "javascript:". You can modify what gets sanitized by defining VERBOTEN_TAGS
215
- # and VERBOTEN_ATTRS before this Module is loaded.
216
- #
217
- # sanitize('<script> do_nasty_stuff() </script>')
218
- # => &lt;script> do_nasty_stuff() &lt;/script>
219
- # sanitize('<a href="javascript: sucker();">Click here for $100</a>')
220
- # => <a>Click here for $100</a>
221
- def sanitize(html)
222
- # only do this if absolutely necessary
223
- if html.index("<")
224
- tokenizer = HTML::Tokenizer.new(html)
225
- new_text = ""
226
-
227
- while token = tokenizer.next
228
- node = HTML::Node.parse(nil, 0, 0, token, false)
229
- new_text << case node
230
- when HTML::Tag
231
- if VERBOTEN_TAGS.include?(node.name)
232
- node.to_s.gsub(/</, "&lt;")
233
- else
234
- if node.closing != :close
235
- node.attributes.delete_if { |attr,v| attr =~ VERBOTEN_ATTRS }
236
- %w(href src).each do |attr|
237
- node.attributes.delete attr if node.attributes[attr] =~ /^javascript:/i
238
- end
239
- end
240
- node.to_s
241
- end
242
- else
243
- node.to_s.gsub(/</, "&lt;")
244
- end
245
- end
246
-
247
- html = new_text
248
- end
249
-
250
- html
251
- end
252
-
253
- # Strips all HTML tags from the +html+, including comments. This uses the
254
- # html-scanner tokenizer and so its HTML parsing ability is limited by
255
- # that of html-scanner.
256
- def strip_tags(html)
257
- return html if html.blank?
258
- if html.index("<")
259
- text = ""
260
- tokenizer = HTML::Tokenizer.new(html)
261
-
262
- while token = tokenizer.next
263
- node = HTML::Node.parse(nil, 0, 0, token, false)
264
- # result is only the content of any Text nodes
265
- text << node.to_s if node.class == HTML::Text
266
- end
267
- # strip any comments, and if they have a newline at the end (ie. line with
268
- # only a comment) strip that too
269
- text.gsub(/<!--(.*?)-->[\n]?/m, "")
270
- else
271
- html # already plain text
272
- end
273
- end
274
315
 
275
316
  # Creates a Cycle object whose _to_s_ method cycles through elements of an
276
317
  # array every time it is called. This can be used for example, to alternate
277
- # classes for table rows:
318
+ # classes for table rows. You can use named cycles to allow nesting in loops.
319
+ # Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
320
+ # named cycle. You can manually reset a cycle by calling reset_cycle and passing the
321
+ # name of the cycle.
278
322
  #
323
+ # ==== Examples
324
+ # # Alternate CSS classes for even and odd numbers...
325
+ # @items = [1,2,3,4]
326
+ # <table>
279
327
  # <% @items.each do |item| %>
280
328
  # <tr class="<%= cycle("even", "odd") -%>">
281
329
  # <td>item</td>
282
330
  # </tr>
283
331
  # <% end %>
332
+ # </table>
284
333
  #
285
- # You can use named cycles to allow nesting in loops. Passing a Hash as
286
- # the last parameter with a <tt>:name</tt> key will create a named cycle.
287
- # You can manually reset a cycle by calling reset_cycle and passing the
288
- # name of the cycle.
289
334
  #
335
+ # # Cycle CSS classes for rows, and text colors for values within each row
336
+ # @items = x = [{:first => 'Robert', :middle => 'Daniel', :last => 'James'},
337
+ # {:first => 'Emily', :middle => 'Shannon', :maiden => 'Pike', :last => 'Hicks'},
338
+ # {:first => 'June', :middle => 'Dae', :last => 'Jones'}]
290
339
  # <% @items.each do |item| %>
291
- # <tr class="<%= cycle("even", "odd", :name => "row_class")
340
+ # <tr class="<%= cycle("even", "odd", :name => "row_class") -%>">
292
341
  # <td>
293
342
  # <% item.values.each do |value| %>
343
+ # <%# Create a named cycle "colors" %>
294
344
  # <span style="color:<%= cycle("red", "green", "blue", :name => "colors") -%>">
295
- # value
345
+ # <%= value %>
296
346
  # </span>
297
347
  # <% end %>
298
348
  # <% reset_cycle("colors") %>
@@ -317,6 +367,23 @@ module ActionView
317
367
 
318
368
  # Resets a cycle so that it starts from the first element the next time
319
369
  # it is called. Pass in +name+ to reset a named cycle.
370
+ #
371
+ # ==== Example
372
+ # # Alternate CSS classes for even and odd numbers...
373
+ # @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]]
374
+ # <table>
375
+ # <% @items.each do |item| %>
376
+ # <tr class="<%= cycle("even", "odd") -%>">
377
+ # <% item.each do |value| %>
378
+ # <span style="color:<%= cycle("#333", "#666", "#999", :name => "colors") -%>">
379
+ # <%= value %>
380
+ # </span>
381
+ # <% end %>
382
+ #
383
+ # <% reset_cycle("colors") %>
384
+ # </tr>
385
+ # <% end %>
386
+ # </table>
320
387
  def reset_cycle(name = "default")
321
388
  cycle = get_cycle(name)
322
389
  cycle.reset unless cycle.nil?
@@ -340,7 +407,7 @@ module ActionView
340
407
  return value
341
408
  end
342
409
  end
343
-
410
+
344
411
  private
345
412
  # The cycle helpers need to store the cycles in a place that is
346
413
  # guaranteed to be reset every time a page is rendered, so it
@@ -369,8 +436,8 @@ module ActionView
369
436
  [-\w]+ # subdomain or domain
370
437
  (?:\.[-\w]+)* # remaining subdomains or domain
371
438
  (?::\d+)? # port
372
- (?:/(?:(?:[~\w\+%-]|(?:[,.;:][^\s$]))+)?)* # path
373
- (?:\?[\w\+%&=.;-]+)? # query string
439
+ (?:/(?:(?:[~\w\+@%-]|(?:[,.;:][^\s$]))+)?)* # path
440
+ (?:\?[\w\+@%&=.;-]+)? # query string
374
441
  (?:\#[\w\-]*)? # trailing anchor
375
442
  )
376
443
  ([[:punct:]]|\s|<|$) # trailing text
@@ -395,10 +462,16 @@ module ActionView
395
462
  # Turns all email addresses into clickable links. If a block is given,
396
463
  # each email is yielded and the result is used as the link text.
397
464
  def auto_link_email_addresses(text)
465
+ body = text.dup
398
466
  text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
399
467
  text = $1
400
- text = yield(text) if block_given?
401
- %{<a href="mailto:#{$1}">#{text}</a>}
468
+
469
+ if body.match(/<a\b[^>]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/)
470
+ text
471
+ else
472
+ display_text = (block_given?) ? yield(text) : text
473
+ %{<a href="mailto:#{text}">#{display_text}</a>}
474
+ end
402
475
  end
403
476
  end
404
477
  end