eactionpack 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. data/CHANGELOG +7 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +469 -0
  4. data/RUNNING_UNIT_TESTS +24 -0
  5. data/Rakefile +146 -0
  6. data/install.rb +30 -0
  7. data/lib/action_controller.rb +79 -0
  8. data/lib/action_controller/assertions.rb +69 -0
  9. data/lib/action_controller/assertions/dom_assertions.rb +39 -0
  10. data/lib/action_controller/assertions/model_assertions.rb +20 -0
  11. data/lib/action_controller/assertions/response_assertions.rb +172 -0
  12. data/lib/action_controller/assertions/routing_assertions.rb +146 -0
  13. data/lib/action_controller/assertions/selector_assertions.rb +491 -0
  14. data/lib/action_controller/assertions/tag_assertions.rb +130 -0
  15. data/lib/action_controller/base.rb +1288 -0
  16. data/lib/action_controller/benchmarking.rb +94 -0
  17. data/lib/action_controller/caching.rb +72 -0
  18. data/lib/action_controller/caching/actions.rb +144 -0
  19. data/lib/action_controller/caching/fragments.rb +138 -0
  20. data/lib/action_controller/caching/pages.rb +154 -0
  21. data/lib/action_controller/caching/sql_cache.rb +18 -0
  22. data/lib/action_controller/caching/sweeping.rb +97 -0
  23. data/lib/action_controller/cgi_ext.rb +16 -0
  24. data/lib/action_controller/cgi_ext/cookie.rb +110 -0
  25. data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
  26. data/lib/action_controller/cgi_ext/session.rb +73 -0
  27. data/lib/action_controller/cgi_ext/stdinput.rb +24 -0
  28. data/lib/action_controller/cgi_process.rb +223 -0
  29. data/lib/action_controller/components.rb +166 -0
  30. data/lib/action_controller/cookies.rb +96 -0
  31. data/lib/action_controller/dispatcher.rb +162 -0
  32. data/lib/action_controller/filters.rb +642 -0
  33. data/lib/action_controller/flash.rb +172 -0
  34. data/lib/action_controller/headers.rb +31 -0
  35. data/lib/action_controller/helpers.rb +221 -0
  36. data/lib/action_controller/http_authentication.rb +124 -0
  37. data/lib/action_controller/integration.rb +634 -0
  38. data/lib/action_controller/layout.rb +309 -0
  39. data/lib/action_controller/mime_responds.rb +173 -0
  40. data/lib/action_controller/mime_type.rb +186 -0
  41. data/lib/action_controller/mime_types.rb +20 -0
  42. data/lib/action_controller/polymorphic_routes.rb +191 -0
  43. data/lib/action_controller/record_identifier.rb +102 -0
  44. data/lib/action_controller/request.rb +764 -0
  45. data/lib/action_controller/request_forgery_protection.rb +140 -0
  46. data/lib/action_controller/request_profiler.rb +169 -0
  47. data/lib/action_controller/rescue.rb +258 -0
  48. data/lib/action_controller/resources.rb +572 -0
  49. data/lib/action_controller/response.rb +76 -0
  50. data/lib/action_controller/routing.rb +387 -0
  51. data/lib/action_controller/routing/builder.rb +203 -0
  52. data/lib/action_controller/routing/optimisations.rb +120 -0
  53. data/lib/action_controller/routing/recognition_optimisation.rb +162 -0
  54. data/lib/action_controller/routing/route.rb +240 -0
  55. data/lib/action_controller/routing/route_set.rb +436 -0
  56. data/lib/action_controller/routing/routing_ext.rb +46 -0
  57. data/lib/action_controller/routing/segments.rb +283 -0
  58. data/lib/action_controller/session/active_record_store.rb +340 -0
  59. data/lib/action_controller/session/cookie_store.rb +166 -0
  60. data/lib/action_controller/session/drb_server.rb +32 -0
  61. data/lib/action_controller/session/drb_store.rb +35 -0
  62. data/lib/action_controller/session/mem_cache_store.rb +98 -0
  63. data/lib/action_controller/session_management.rb +158 -0
  64. data/lib/action_controller/status_codes.rb +88 -0
  65. data/lib/action_controller/streaming.rb +155 -0
  66. data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
  67. data/lib/action_controller/templates/rescues/_trace.erb +26 -0
  68. data/lib/action_controller/templates/rescues/diagnostics.erb +11 -0
  69. data/lib/action_controller/templates/rescues/layout.erb +29 -0
  70. data/lib/action_controller/templates/rescues/missing_template.erb +2 -0
  71. data/lib/action_controller/templates/rescues/routing_error.erb +10 -0
  72. data/lib/action_controller/templates/rescues/template_error.erb +21 -0
  73. data/lib/action_controller/templates/rescues/unknown_action.erb +2 -0
  74. data/lib/action_controller/test_case.rb +83 -0
  75. data/lib/action_controller/test_process.rb +526 -0
  76. data/lib/action_controller/url_rewriter.rb +142 -0
  77. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  78. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  79. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
  80. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  81. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  82. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  83. data/lib/action_controller/verification.rb +130 -0
  84. data/lib/action_pack.rb +24 -0
  85. data/lib/action_pack/version.rb +9 -0
  86. data/lib/action_view.rb +44 -0
  87. data/lib/action_view/base.rb +335 -0
  88. data/lib/action_view/helpers/active_record_helper.rb +276 -0
  89. data/lib/action_view/helpers/asset_tag_helper.rb +599 -0
  90. data/lib/action_view/helpers/atom_feed_helper.rb +143 -0
  91. data/lib/action_view/helpers/benchmark_helper.rb +33 -0
  92. data/lib/action_view/helpers/cache_helper.rb +40 -0
  93. data/lib/action_view/helpers/capture_helper.rb +161 -0
  94. data/lib/action_view/helpers/date_helper.rb +711 -0
  95. data/lib/action_view/helpers/debug_helper.rb +31 -0
  96. data/lib/action_view/helpers/form_helper.rb +767 -0
  97. data/lib/action_view/helpers/form_options_helper.rb +458 -0
  98. data/lib/action_view/helpers/form_tag_helper.rb +458 -0
  99. data/lib/action_view/helpers/javascript_helper.rb +148 -0
  100. data/lib/action_view/helpers/number_helper.rb +186 -0
  101. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  102. data/lib/action_view/helpers/record_tag_helper.rb +59 -0
  103. data/lib/action_view/helpers/sanitize_helper.rb +229 -0
  104. data/lib/action_view/helpers/tag_helper.rb +134 -0
  105. data/lib/action_view/helpers/text_helper.rb +507 -0
  106. data/lib/action_view/helpers/url_helper.rb +573 -0
  107. data/lib/action_view/inline_template.rb +20 -0
  108. data/lib/action_view/partial_template.rb +70 -0
  109. data/lib/action_view/partials.rb +158 -0
  110. data/lib/action_view/template.rb +125 -0
  111. data/lib/action_view/template_error.rb +110 -0
  112. data/lib/action_view/template_finder.rb +176 -0
  113. data/lib/action_view/template_handler.rb +34 -0
  114. data/lib/action_view/template_handlers/builder.rb +27 -0
  115. data/lib/action_view/template_handlers/compilable.rb +128 -0
  116. data/lib/action_view/template_handlers/erb.rb +56 -0
  117. data/lib/action_view/test_case.rb +58 -0
  118. data/lib/actionpack.rb +1 -0
  119. data/test/abstract_unit.rb +36 -0
  120. data/test/active_record_unit.rb +105 -0
  121. data/test/activerecord/active_record_store_test.rb +141 -0
  122. data/test/activerecord/render_partial_with_record_identification_test.rb +191 -0
  123. data/test/adv_attr_test.rb +20 -0
  124. data/test/controller/action_pack_assertions_test.rb +543 -0
  125. data/test/controller/addresses_render_test.rb +43 -0
  126. data/test/controller/assert_select_test.rb +331 -0
  127. data/test/controller/base_test.rb +219 -0
  128. data/test/controller/benchmark_test.rb +32 -0
  129. data/test/controller/caching_test.rb +581 -0
  130. data/test/controller/capture_test.rb +89 -0
  131. data/test/controller/cgi_test.rb +116 -0
  132. data/test/controller/components_test.rb +140 -0
  133. data/test/controller/content_type_test.rb +139 -0
  134. data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
  135. data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
  136. data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
  137. data/test/controller/cookie_test.rb +146 -0
  138. data/test/controller/custom_handler_test.rb +45 -0
  139. data/test/controller/deprecation/deprecated_base_methods_test.rb +37 -0
  140. data/test/controller/dispatcher_test.rb +105 -0
  141. data/test/controller/fake_controllers.rb +33 -0
  142. data/test/controller/fake_models.rb +11 -0
  143. data/test/controller/filter_params_test.rb +49 -0
  144. data/test/controller/filters_test.rb +881 -0
  145. data/test/controller/flash_test.rb +146 -0
  146. data/test/controller/header_test.rb +14 -0
  147. data/test/controller/helper_test.rb +210 -0
  148. data/test/controller/html-scanner/cdata_node_test.rb +15 -0
  149. data/test/controller/html-scanner/document_test.rb +148 -0
  150. data/test/controller/html-scanner/node_test.rb +89 -0
  151. data/test/controller/html-scanner/sanitizer_test.rb +269 -0
  152. data/test/controller/html-scanner/tag_node_test.rb +238 -0
  153. data/test/controller/html-scanner/text_node_test.rb +50 -0
  154. data/test/controller/html-scanner/tokenizer_test.rb +131 -0
  155. data/test/controller/http_authentication_test.rb +54 -0
  156. data/test/controller/integration_test.rb +252 -0
  157. data/test/controller/integration_upload_test.rb +43 -0
  158. data/test/controller/layout_test.rb +255 -0
  159. data/test/controller/mime_responds_test.rb +514 -0
  160. data/test/controller/mime_type_test.rb +84 -0
  161. data/test/controller/new_render_test.rb +843 -0
  162. data/test/controller/polymorphic_routes_test.rb +174 -0
  163. data/test/controller/record_identifier_test.rb +139 -0
  164. data/test/controller/redirect_test.rb +289 -0
  165. data/test/controller/render_test.rb +484 -0
  166. data/test/controller/request_forgery_protection_test.rb +305 -0
  167. data/test/controller/request_test.rb +928 -0
  168. data/test/controller/rescue_test.rb +517 -0
  169. data/test/controller/resources_test.rb +873 -0
  170. data/test/controller/routing_test.rb +2464 -0
  171. data/test/controller/selector_test.rb +628 -0
  172. data/test/controller/send_file_test.rb +138 -0
  173. data/test/controller/session/cookie_store_test.rb +258 -0
  174. data/test/controller/session/mem_cache_store_test.rb +181 -0
  175. data/test/controller/session_fixation_test.rb +89 -0
  176. data/test/controller/session_management_test.rb +178 -0
  177. data/test/controller/test_test.rb +695 -0
  178. data/test/controller/url_rewriter_test.rb +310 -0
  179. data/test/controller/verification_test.rb +270 -0
  180. data/test/controller/view_paths_test.rb +140 -0
  181. data/test/controller/webservice_test.rb +229 -0
  182. data/test/fixtures/addresses/list.erb +1 -0
  183. data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
  184. data/test/fixtures/companies.yml +24 -0
  185. data/test/fixtures/company.rb +10 -0
  186. data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
  187. data/test/fixtures/content_type/render_default_for_js.js.erb +1 -0
  188. data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
  189. data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
  190. data/test/fixtures/customers/_customer.html.erb +1 -0
  191. data/test/fixtures/db_definitions/sqlite.sql +49 -0
  192. data/test/fixtures/developer.rb +9 -0
  193. data/test/fixtures/developers.yml +21 -0
  194. data/test/fixtures/developers_projects.yml +13 -0
  195. data/test/fixtures/fun/games/hello_world.erb +1 -0
  196. data/test/fixtures/functional_caching/_partial.erb +3 -0
  197. data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
  198. data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
  199. data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
  200. data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
  201. data/test/fixtures/helpers/abc_helper.rb +5 -0
  202. data/test/fixtures/helpers/fun/games_helper.rb +3 -0
  203. data/test/fixtures/helpers/fun/pdf_helper.rb +3 -0
  204. data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
  205. data/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml +1 -0
  206. data/test/fixtures/layout_tests/layouts/item.rhtml +1 -0
  207. data/test/fixtures/layout_tests/layouts/layout_test.rhtml +1 -0
  208. data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
  209. data/test/fixtures/layout_tests/layouts/third_party_template_library.mab +1 -0
  210. data/test/fixtures/layout_tests/views/hello.rhtml +1 -0
  211. data/test/fixtures/layouts/block_with_layout.erb +3 -0
  212. data/test/fixtures/layouts/builder.builder +3 -0
  213. data/test/fixtures/layouts/partial_with_layout.erb +3 -0
  214. data/test/fixtures/layouts/standard.erb +1 -0
  215. data/test/fixtures/layouts/talk_from_action.erb +2 -0
  216. data/test/fixtures/layouts/yield.erb +2 -0
  217. data/test/fixtures/mascot.rb +3 -0
  218. data/test/fixtures/mascots.yml +4 -0
  219. data/test/fixtures/mascots/_mascot.html.erb +1 -0
  220. data/test/fixtures/multipart/binary_file +0 -0
  221. data/test/fixtures/multipart/boundary_problem_file +10 -0
  222. data/test/fixtures/multipart/bracketed_param +5 -0
  223. data/test/fixtures/multipart/large_text_file +10 -0
  224. data/test/fixtures/multipart/mixed_files +0 -0
  225. data/test/fixtures/multipart/mona_lisa.jpg +0 -0
  226. data/test/fixtures/multipart/single_parameter +5 -0
  227. data/test/fixtures/multipart/text_file +10 -0
  228. data/test/fixtures/override/test/hello_world.erb +1 -0
  229. data/test/fixtures/override2/layouts/test/sub.erb +1 -0
  230. data/test/fixtures/post_test/layouts/post.html.erb +1 -0
  231. data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
  232. data/test/fixtures/post_test/post/index.html.erb +1 -0
  233. data/test/fixtures/post_test/post/index.iphone.erb +1 -0
  234. data/test/fixtures/post_test/super_post/index.html.erb +1 -0
  235. data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
  236. data/test/fixtures/project.rb +3 -0
  237. data/test/fixtures/projects.yml +7 -0
  238. data/test/fixtures/public/404.html +1 -0
  239. data/test/fixtures/public/500.html +1 -0
  240. data/test/fixtures/public/images/rails.png +0 -0
  241. data/test/fixtures/public/javascripts/application.js +1 -0
  242. data/test/fixtures/public/javascripts/bank.js +1 -0
  243. data/test/fixtures/public/javascripts/robber.js +1 -0
  244. data/test/fixtures/public/javascripts/version.1.0.js +1 -0
  245. data/test/fixtures/public/stylesheets/bank.css +1 -0
  246. data/test/fixtures/public/stylesheets/robber.css +1 -0
  247. data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
  248. data/test/fixtures/replies.yml +15 -0
  249. data/test/fixtures/reply.rb +7 -0
  250. data/test/fixtures/respond_to/all_types_with_layout.html.erb +1 -0
  251. data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
  252. data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
  253. data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
  254. data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
  255. data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
  256. data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
  257. data/test/fixtures/respond_to/using_defaults.html.erb +1 -0
  258. data/test/fixtures/respond_to/using_defaults.js.rjs +1 -0
  259. data/test/fixtures/respond_to/using_defaults.xml.builder +1 -0
  260. data/test/fixtures/respond_to/using_defaults_with_type_list.html.erb +1 -0
  261. data/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs +1 -0
  262. data/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder +1 -0
  263. data/test/fixtures/scope/test/modgreet.erb +1 -0
  264. data/test/fixtures/shared.html.erb +1 -0
  265. data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
  266. data/test/fixtures/test/_customer.erb +1 -0
  267. data/test/fixtures/test/_customer_counter.erb +1 -0
  268. data/test/fixtures/test/_customer_greeting.erb +1 -0
  269. data/test/fixtures/test/_form.erb +1 -0
  270. data/test/fixtures/test/_hash_greeting.erb +1 -0
  271. data/test/fixtures/test/_hash_object.erb +2 -0
  272. data/test/fixtures/test/_hello.builder +1 -0
  273. data/test/fixtures/test/_labelling_form.erb +1 -0
  274. data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
  275. data/test/fixtures/test/_partial.erb +1 -0
  276. data/test/fixtures/test/_partial.html.erb +1 -0
  277. data/test/fixtures/test/_partial.js.erb +1 -0
  278. data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
  279. data/test/fixtures/test/_partial_only.erb +1 -0
  280. data/test/fixtures/test/_person.erb +2 -0
  281. data/test/fixtures/test/_raise.html.erb +1 -0
  282. data/test/fixtures/test/action_talk_to_layout.erb +2 -0
  283. data/test/fixtures/test/block_content_for.erb +2 -0
  284. data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
  285. data/test/fixtures/test/capturing.erb +4 -0
  286. data/test/fixtures/test/content_for.erb +2 -0
  287. data/test/fixtures/test/content_for_concatenated.erb +3 -0
  288. data/test/fixtures/test/content_for_with_parameter.erb +2 -0
  289. data/test/fixtures/test/delete_with_js.rjs +2 -0
  290. data/test/fixtures/test/dot.directory/render_file_with_ivar.erb +1 -0
  291. data/test/fixtures/test/enum_rjs_test.rjs +6 -0
  292. data/test/fixtures/test/erb_content_for.erb +2 -0
  293. data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
  294. data/test/fixtures/test/formatted_xml_erb.builder +1 -0
  295. data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
  296. data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
  297. data/test/fixtures/test/greeting.erb +1 -0
  298. data/test/fixtures/test/greeting.js.rjs +1 -0
  299. data/test/fixtures/test/hello.builder +4 -0
  300. data/test/fixtures/test/hello_world.erb +1 -0
  301. data/test/fixtures/test/hello_world_container.builder +3 -0
  302. data/test/fixtures/test/hello_world_from_rxml.builder +4 -0
  303. data/test/fixtures/test/hello_world_with_layout_false.erb +1 -0
  304. data/test/fixtures/test/hello_xml_world.builder +11 -0
  305. data/test/fixtures/test/list.erb +1 -0
  306. data/test/fixtures/test/non_erb_block_content_for.builder +4 -0
  307. data/test/fixtures/test/potential_conflicts.erb +4 -0
  308. data/test/fixtures/test/render_file_from_template.html.erb +1 -0
  309. data/test/fixtures/test/render_file_with_ivar.erb +1 -0
  310. data/test/fixtures/test/render_file_with_locals.erb +1 -0
  311. data/test/fixtures/test/render_to_string_test.erb +1 -0
  312. data/test/fixtures/test/update_element_with_capture.erb +9 -0
  313. data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
  314. data/test/fixtures/topic.rb +3 -0
  315. data/test/fixtures/topics.yml +22 -0
  316. data/test/fixtures/topics/_topic.html.erb +1 -0
  317. data/test/template/active_record_helper_test.rb +268 -0
  318. data/test/template/asset_tag_helper_test.rb +514 -0
  319. data/test/template/atom_feed_helper_test.rb +179 -0
  320. data/test/template/benchmark_helper_test.rb +60 -0
  321. data/test/template/date_helper_test.rb +1791 -0
  322. data/test/template/deprecated_erb_variable_test.rb +9 -0
  323. data/test/template/erb_util_test.rb +24 -0
  324. data/test/template/form_helper_test.rb +885 -0
  325. data/test/template/form_options_helper_test.rb +1333 -0
  326. data/test/template/form_tag_helper_test.rb +272 -0
  327. data/test/template/javascript_helper_test.rb +73 -0
  328. data/test/template/number_helper_test.rb +97 -0
  329. data/test/template/record_tag_helper_test.rb +54 -0
  330. data/test/template/sanitize_helper_test.rb +48 -0
  331. data/test/template/tag_helper_test.rb +77 -0
  332. data/test/template/template_finder_test.rb +73 -0
  333. data/test/template/template_object_test.rb +95 -0
  334. data/test/template/test_test.rb +56 -0
  335. data/test/template/text_helper_test.rb +367 -0
  336. data/test/template/url_helper_test.rb +544 -0
  337. data/test/testing_sandbox.rb +15 -0
  338. metadata +469 -0
@@ -0,0 +1,134 @@
1
+ require 'cgi'
2
+ require 'erb'
3
+ require 'set'
4
+
5
+ module ActionView
6
+ module Helpers #:nodoc:
7
+ # Provides methods to generate HTML tags programmatically when you can't use
8
+ # a Builder. By default, they output XHTML compliant tags.
9
+ module TagHelper
10
+ include ERB::Util
11
+
12
+ BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple).to_set
13
+ BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
14
+
15
+ # Returns an empty HTML tag of type +name+ which by default is XHTML
16
+ # compliant. Set +open+ to true to create an open tag compatible
17
+ # with HTML 4.0 and below. Add HTML attributes by passing an attributes
18
+ # hash to +options+. Set +escape+ to false to disable attribute value
19
+ # escaping.
20
+ #
21
+ # ==== Options
22
+ # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
23
+ # <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
24
+ # symbols or strings for the attribute names.
25
+ #
26
+ # ==== Examples
27
+ # tag("br")
28
+ # # => <br />
29
+ #
30
+ # tag("br", nil, true)
31
+ # # => <br>
32
+ #
33
+ # tag("input", { :type => 'text', :disabled => true })
34
+ # # => <input type="text" disabled="disabled" />
35
+ #
36
+ # tag("img", { :src => "open & shut.png" })
37
+ # # => <img src="open &amp; shut.png" />
38
+ #
39
+ # tag("img", { :src => "open &amp; shut.png" }, false, false)
40
+ # # => <img src="open &amp; shut.png" />
41
+ def tag(name, options = nil, open = false, escape = true)
42
+ "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}"
43
+ end
44
+
45
+ # Returns an HTML block tag of type +name+ surrounding the +content+. Add
46
+ # HTML attributes by passing an attributes hash to +options+.
47
+ # Instead of passing the content as an argument, you can also use a block
48
+ # in which case, you pass your +options+ as the second parameter.
49
+ # Set escape to false to disable attribute value escaping.
50
+ #
51
+ # ==== Options
52
+ # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
53
+ # <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
54
+ # symbols or strings for the attribute names.
55
+ #
56
+ # ==== Examples
57
+ # content_tag(:p, "Hello world!")
58
+ # # => <p>Hello world!</p>
59
+ # content_tag(:div, content_tag(:p, "Hello world!"), :class => "strong")
60
+ # # => <div class="strong"><p>Hello world!</p></div>
61
+ # content_tag("select", options, :multiple => true)
62
+ # # => <select multiple="multiple">...options...</select>
63
+ #
64
+ # <% content_tag :div, :class => "strong" do -%>
65
+ # Hello world!
66
+ # <% end -%>
67
+ # # => <div class="strong"><p>Hello world!</p></div>
68
+ def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
69
+ if block_given?
70
+ options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
71
+ content = capture(&block)
72
+ content_tag = content_tag_string(name, content, options, escape)
73
+ block_is_within_action_view?(block) ? concat(content_tag, block.binding) : content_tag
74
+ else
75
+ content = content_or_options_with_block
76
+ content_tag_string(name, content, options, escape)
77
+ end
78
+ end
79
+
80
+ # Returns a CDATA section with the given +content+. CDATA sections
81
+ # are used to escape blocks of text containing characters which would
82
+ # otherwise be recognized as markup. CDATA sections begin with the string
83
+ # <tt><![CDATA[</tt> and end with (and may not contain) the string <tt>]]></tt>.
84
+ #
85
+ # ==== Examples
86
+ # cdata_section("<hello world>")
87
+ # # => <![CDATA[<hello world>]]>
88
+ #
89
+ # cdata_section(File.read("hello_world.txt"))
90
+ # # => <![CDATA[<hello from a text file]]>
91
+ def cdata_section(content)
92
+ "<![CDATA[#{content}]]>"
93
+ end
94
+
95
+ # Returns an escaped version of +html+ without affecting existing escaped entities.
96
+ #
97
+ # ==== Examples
98
+ # escape_once("1 > 2 &amp; 3")
99
+ # # => "1 &lt; 2 &amp; 3"
100
+ #
101
+ # escape_once("&lt;&lt; Accept & Checkout")
102
+ # # => "&lt;&lt; Accept &amp; Checkout"
103
+ def escape_once(html)
104
+ html.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] }
105
+ end
106
+
107
+ private
108
+ def content_tag_string(name, content, options, escape = true)
109
+ tag_options = tag_options(options, escape) if options
110
+ "<#{name}#{tag_options}>#{content}</#{name}>"
111
+ end
112
+
113
+ def tag_options(options, escape = true)
114
+ unless options.blank?
115
+ attrs = []
116
+ if escape
117
+ options.each do |key, value|
118
+ next unless value
119
+ value = BOOLEAN_ATTRIBUTES.include?(key) ? key : escape_once(value)
120
+ attrs << %(#{key}="#{value}")
121
+ end
122
+ else
123
+ attrs = options.map { |key, value| %(#{key}="#{value}") }
124
+ end
125
+ " #{attrs.sort * ' '}" unless attrs.empty?
126
+ end
127
+ end
128
+
129
+ def block_is_within_action_view?(block)
130
+ eval("defined? _erbout", block.binding)
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,507 @@
1
+ require 'action_view/helpers/tag_helper'
2
+ require 'html/document'
3
+
4
+ module ActionView
5
+ module Helpers #:nodoc:
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
8
+ # your views. These helper methods extend ActionView making them callable
9
+ # within your template files.
10
+ module TextHelper
11
+ # The preferred method of outputting text in your views is to use the
12
+ # <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
13
+ # do not operate as expected in an eRuby code block. If you absolutely must
14
+ # output text within a non-output code block (i.e., <% %>), you can use the concat method.
15
+ #
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
+ # %>
28
+ def concat(string, binding)
29
+ eval(ActionView::Base.erb_variable, binding) << string
30
+ end
31
+
32
+ if RUBY_VERSION < '1.9'
33
+ # If +text+ is longer than +length+, +text+ will be truncated to the length of
34
+ # +length+ (defaults to 30) and the last characters will be replaced with the +truncate_string+
35
+ # (defaults to "...").
36
+ #
37
+ # ==== Examples
38
+ # truncate("Once upon a time in a world far far away", 14)
39
+ # # => Once upon a...
40
+ #
41
+ # truncate("Once upon a time in a world far far away")
42
+ # # => Once upon a time in a world f...
43
+ #
44
+ # truncate("And they found that many people were sleeping better.", 25, "(clipped)")
45
+ # # => And they found that many (clipped)
46
+ #
47
+ # truncate("And they found that many people were sleeping better.", 15, "... (continued)")
48
+ # # => And they found... (continued)
49
+ def truncate(text, length = 30, truncate_string = "...")
50
+ if text
51
+ l = length - truncate_string.chars.length
52
+ chars = text.chars
53
+ (chars.length > length ? chars[0...l] + truncate_string : text).to_s
54
+ end
55
+ end
56
+ else
57
+ def truncate(text, length = 30, truncate_string = "...") #:nodoc:
58
+ if text
59
+ l = length - truncate_string.length
60
+ (text.length > length ? text[0...l] + truncate_string : text).to_s
61
+ end
62
+ end
63
+ end
64
+
65
+ # Highlights one or more +phrases+ everywhere in +text+ by inserting it into
66
+ # a +highlighter+ string. The highlighter can be specialized by passing +highlighter+
67
+ # as a single-quoted string with \1 where the phrase is to be inserted (defaults to
68
+ # '<strong class="highlight">\1</strong>')
69
+ #
70
+ # ==== Examples
71
+ # highlight('You searched for: rails', 'rails')
72
+ # # => You searched for: <strong class="highlight">rails</strong>
73
+ #
74
+ # highlight('You searched for: ruby, rails, dhh', 'actionpack')
75
+ # # => You searched for: ruby, rails, dhh
76
+ #
77
+ # highlight('You searched for: rails', ['for', 'rails'], '<em>\1</em>')
78
+ # # => You searched <em>for</em>: <em>rails</em>
79
+ #
80
+ # highlight('You searched for: rails', 'rails', "<a href='search?q=\1'>\1</a>")
81
+ # # => You searched for: <a href='search?q=rails>rails</a>
82
+ def highlight(text, phrases, highlighter = '<strong class="highlight">\1</strong>')
83
+ if text.blank? || phrases.blank?
84
+ text
85
+ else
86
+ match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
87
+ text.gsub(/(#{match})/i, highlighter)
88
+ end
89
+ end
90
+
91
+ if RUBY_VERSION < '1.9'
92
+ # Extracts an excerpt from +text+ that matches the first instance of +phrase+.
93
+ # The +radius+ expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
94
+ # defined in +radius+ (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
95
+ # then the +excerpt_string+ will be prepended/appended accordingly. The resulting string will be stripped in any case.
96
+ # If the +phrase+ isn't found, nil is returned.
97
+ #
98
+ # ==== Examples
99
+ # excerpt('This is an example', 'an', 5)
100
+ # # => "...s is an exam..."
101
+ #
102
+ # excerpt('This is an example', 'is', 5)
103
+ # # => "This is a..."
104
+ #
105
+ # excerpt('This is an example', 'is')
106
+ # # => "This is an example"
107
+ #
108
+ # excerpt('This next thing is an example', 'ex', 2)
109
+ # # => "...next..."
110
+ #
111
+ # excerpt('This is also an example', 'an', 8, '<chop> ')
112
+ # # => "<chop> is also an example"
113
+ def excerpt(text, phrase, radius = 100, excerpt_string = "...")
114
+ if text && phrase
115
+ phrase = Regexp.escape(phrase)
116
+
117
+ if found_pos = text.chars =~ /(#{phrase})/i
118
+ start_pos = [ found_pos - radius, 0 ].max
119
+ end_pos = [ [ found_pos + phrase.chars.length + radius - 1, 0].max, text.chars.length ].min
120
+
121
+ prefix = start_pos > 0 ? excerpt_string : ""
122
+ postfix = end_pos < text.chars.length - 1 ? excerpt_string : ""
123
+
124
+ prefix + text.chars[start_pos..end_pos].strip + postfix
125
+ else
126
+ nil
127
+ end
128
+ end
129
+ end
130
+ else
131
+ def excerpt(text, phrase, radius = 100, excerpt_string = "...") #:nodoc:
132
+ if text && phrase
133
+ phrase = Regexp.escape(phrase)
134
+
135
+ if found_pos = text =~ /(#{phrase})/i
136
+ start_pos = [ found_pos - radius, 0 ].max
137
+ end_pos = [ [ found_pos + phrase.length + radius - 1, 0].max, text.length ].min
138
+
139
+ prefix = start_pos > 0 ? excerpt_string : ""
140
+ postfix = end_pos < text.length - 1 ? excerpt_string : ""
141
+
142
+ prefix + text[start_pos..end_pos].strip + postfix
143
+ else
144
+ nil
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ # Attempts to pluralize the +singular+ word unless +count+ is 1. If
151
+ # +plural+ is supplied, it will use that when count is > 1, otherwise
152
+ # it will use the Inflector to determine the plural form
153
+ #
154
+ # ==== Examples
155
+ # pluralize(1, 'person')
156
+ # # => 1 person
157
+ #
158
+ # pluralize(2, 'person')
159
+ # # => 2 people
160
+ #
161
+ # pluralize(3, 'person', 'users')
162
+ # # => 3 users
163
+ #
164
+ # pluralize(0, 'person')
165
+ # # => 0 people
166
+ def pluralize(count, singular, plural = nil)
167
+ "#{count || 0} " + ((count == 1 || count == '1') ? singular : (plural || singular.pluralize))
168
+ end
169
+
170
+ # Wraps the +text+ into lines no longer than +line_width+ width. This method
171
+ # breaks on the first whitespace character that does not exceed +line_width+
172
+ # (which is 80 by default).
173
+ #
174
+ # ==== Examples
175
+ # word_wrap('Once upon a time', 4)
176
+ # # => Once\nupon\na\ntime
177
+ #
178
+ # word_wrap('Once upon a time', 8)
179
+ # # => Once upon\na time
180
+ #
181
+ # word_wrap('Once upon a time')
182
+ # # => Once upon a time
183
+ #
184
+ # word_wrap('Once upon a time', 1)
185
+ # # => Once\nupon\na\ntime
186
+ def word_wrap(text, line_width = 80)
187
+ text.split("\n").collect do |line|
188
+ line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
189
+ end * "\n"
190
+ end
191
+
192
+ begin
193
+ require_library_or_gem "redcloth" unless Object.const_defined?(:RedCloth)
194
+
195
+ # Returns the text with all the Textile[http://www.textism.com/tools/textile] codes turned into HTML tags.
196
+ #
197
+ # You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile].
198
+ # <i>This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
199
+ # is available</i>.
200
+ #
201
+ # ==== Examples
202
+ # textilize("*This is Textile!* Rejoice!")
203
+ # # => "<p><strong>This is Textile!</strong> Rejoice!</p>"
204
+ #
205
+ # textilize("I _love_ ROR(Ruby on Rails)!")
206
+ # # => "<p>I <em>love</em> <acronym title="Ruby on Rails">ROR</acronym>!</p>"
207
+ #
208
+ # textilize("h2. Textile makes markup -easy- simple!")
209
+ # # => "<h2>Textile makes markup <del>easy</del> simple!</h2>"
210
+ #
211
+ # textilize("Visit the Rails website "here":http://www.rubyonrails.org/.)
212
+ # # => "<p>Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>.</p>"
213
+ def textilize(text)
214
+ if text.blank?
215
+ ""
216
+ else
217
+ textilized = RedCloth.new(text, [ :hard_breaks ])
218
+ textilized.hard_breaks = true if textilized.respond_to?("hard_breaks=")
219
+ textilized.to_html
220
+ end
221
+ end
222
+
223
+ # Returns the text with all the Textile codes turned into HTML tags,
224
+ # but without the bounding <p> tag that RedCloth adds.
225
+ #
226
+ # You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile].
227
+ # <i>This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
228
+ # is available</i>.
229
+ #
230
+ # ==== Examples
231
+ # textilize_without_paragraph("*This is Textile!* Rejoice!")
232
+ # # => "<strong>This is Textile!</strong> Rejoice!"
233
+ #
234
+ # textilize_without_paragraph("I _love_ ROR(Ruby on Rails)!")
235
+ # # => "I <em>love</em> <acronym title="Ruby on Rails">ROR</acronym>!"
236
+ #
237
+ # textilize_without_paragraph("h2. Textile makes markup -easy- simple!")
238
+ # # => "<h2>Textile makes markup <del>easy</del> simple!</h2>"
239
+ #
240
+ # textilize_without_paragraph("Visit the Rails website "here":http://www.rubyonrails.org/.)
241
+ # # => "Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>."
242
+ def textilize_without_paragraph(text)
243
+ textiled = textilize(text)
244
+ if textiled[0..2] == "<p>" then textiled = textiled[3..-1] end
245
+ if textiled[-4..-1] == "</p>" then textiled = textiled[0..-5] end
246
+ return textiled
247
+ end
248
+ rescue LoadError
249
+ # We can't really help what's not there
250
+ end
251
+
252
+ begin
253
+ require_library_or_gem "bluecloth" unless Object.const_defined?(:BlueCloth)
254
+
255
+ # Returns the text with all the Markdown codes turned into HTML tags.
256
+ # <i>This method is only available if BlueCloth[http://www.deveiate.org/projects/BlueCloth]
257
+ # is available</i>.
258
+ #
259
+ # ==== Examples
260
+ # markdown("We are using __Markdown__ now!")
261
+ # # => "<p>We are using <strong>Markdown</strong> now!</p>"
262
+ #
263
+ # markdown("We like to _write_ `code`, not just _read_ it!")
264
+ # # => "<p>We like to <em>write</em> <code>code</code>, not just <em>read</em> it!</p>"
265
+ #
266
+ # markdown("The [Markdown website](http://daringfireball.net/projects/markdown/) has more information.")
267
+ # # => "<p>The <a href="http://daringfireball.net/projects/markdown/">Markdown website</a>
268
+ # # has more information.</p>"
269
+ #
270
+ # markdown('![The ROR logo](http://rubyonrails.com/images/rails.png "Ruby on Rails")')
271
+ # # => '<p><img src="http://rubyonrails.com/images/rails.png" alt="The ROR logo" title="Ruby on Rails" /></p>'
272
+ def markdown(text)
273
+ text.blank? ? "" : BlueCloth.new(text).to_html
274
+ end
275
+ rescue LoadError
276
+ # We can't really help what's not there
277
+ end
278
+
279
+ # Returns +text+ transformed into HTML using simple formatting rules.
280
+ # Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
281
+ # paragraph and wrapped in <tt><p></tt> tags. One newline (<tt>\n</tt>) is
282
+ # considered as a linebreak and a <tt><br /></tt> tag is appended. This
283
+ # method does not remove the newlines from the +text+.
284
+ #
285
+ # You can pass any HTML attributes into <tt>html_options</tt>. These
286
+ # will be added to all created paragraphs.
287
+ # ==== Examples
288
+ # my_text = "Here is some basic text...\n...with a line break."
289
+ #
290
+ # simple_format(my_text)
291
+ # # => "<p>Here is some basic text...\n<br />...with a line break.</p>"
292
+ #
293
+ # more_text = "We want to put a paragraph...\n\n...right there."
294
+ #
295
+ # simple_format(more_text)
296
+ # # => "<p>We want to put a paragraph...</p>\n\n<p>...right there.</p>"
297
+ #
298
+ # simple_format("Look ma! A class!", :class => 'description')
299
+ # # => "<p class='description'>Look ma! A class!</p>"
300
+ def simple_format(text, html_options={})
301
+ start_tag = tag('p', html_options, true)
302
+ text = text.to_s.dup
303
+ text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n
304
+ text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph
305
+ text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
306
+ text.insert 0, start_tag
307
+ text << "</p>"
308
+ end
309
+
310
+ # Turns all URLs and e-mail addresses into clickable links. The +link+ parameter
311
+ # will limit what should be linked. You can add HTML attributes to the links using
312
+ # +href_options+. Options for +link+ are <tt>:all</tt> (default),
313
+ # <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
314
+ # e-mail address is yielded and the result is used as the link text.
315
+ #
316
+ # ==== Examples
317
+ # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
318
+ # # => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
319
+ # # say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
320
+ #
321
+ # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :urls)
322
+ # # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
323
+ # # or e-mail david@loudthinking.com"
324
+ #
325
+ # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :email_addresses)
326
+ # # => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
327
+ #
328
+ # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
329
+ # auto_link(post_body, :all, :target => '_blank') do |text|
330
+ # truncate(text, 15)
331
+ # end
332
+ # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
333
+ # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
334
+ #
335
+ def auto_link(text, link = :all, href_options = {}, &block)
336
+ return '' if text.blank?
337
+ case link
338
+ when :all then auto_link_email_addresses(auto_link_urls(text, href_options, &block), &block)
339
+ when :email_addresses then auto_link_email_addresses(text, &block)
340
+ when :urls then auto_link_urls(text, href_options, &block)
341
+ end
342
+ end
343
+
344
+ # Creates a Cycle object whose _to_s_ method cycles through elements of an
345
+ # array every time it is called. This can be used for example, to alternate
346
+ # classes for table rows. You can use named cycles to allow nesting in loops.
347
+ # Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
348
+ # named cycle. You can manually reset a cycle by calling reset_cycle and passing the
349
+ # name of the cycle.
350
+ #
351
+ # ==== Examples
352
+ # # Alternate CSS classes for even and odd numbers...
353
+ # @items = [1,2,3,4]
354
+ # <table>
355
+ # <% @items.each do |item| %>
356
+ # <tr class="<%= cycle("even", "odd") -%>">
357
+ # <td>item</td>
358
+ # </tr>
359
+ # <% end %>
360
+ # </table>
361
+ #
362
+ #
363
+ # # Cycle CSS classes for rows, and text colors for values within each row
364
+ # @items = x = [{:first => 'Robert', :middle => 'Daniel', :last => 'James'},
365
+ # {:first => 'Emily', :middle => 'Shannon', :maiden => 'Pike', :last => 'Hicks'},
366
+ # {:first => 'June', :middle => 'Dae', :last => 'Jones'}]
367
+ # <% @items.each do |item| %>
368
+ # <tr class="<%= cycle("even", "odd", :name => "row_class") -%>">
369
+ # <td>
370
+ # <% item.values.each do |value| %>
371
+ # <%# Create a named cycle "colors" %>
372
+ # <span style="color:<%= cycle("red", "green", "blue", :name => "colors") -%>">
373
+ # <%= value %>
374
+ # </span>
375
+ # <% end %>
376
+ # <% reset_cycle("colors") %>
377
+ # </td>
378
+ # </tr>
379
+ # <% end %>
380
+ def cycle(first_value, *values)
381
+ if (values.last.instance_of? Hash)
382
+ params = values.pop
383
+ name = params[:name]
384
+ else
385
+ name = "default"
386
+ end
387
+ values.unshift(first_value)
388
+
389
+ cycle = get_cycle(name)
390
+ if (cycle.nil? || cycle.values != values)
391
+ cycle = set_cycle(name, Cycle.new(*values))
392
+ end
393
+ return cycle.to_s
394
+ end
395
+
396
+ # Resets a cycle so that it starts from the first element the next time
397
+ # it is called. Pass in +name+ to reset a named cycle.
398
+ #
399
+ # ==== Example
400
+ # # Alternate CSS classes for even and odd numbers...
401
+ # @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]]
402
+ # <table>
403
+ # <% @items.each do |item| %>
404
+ # <tr class="<%= cycle("even", "odd") -%>">
405
+ # <% item.each do |value| %>
406
+ # <span style="color:<%= cycle("#333", "#666", "#999", :name => "colors") -%>">
407
+ # <%= value %>
408
+ # </span>
409
+ # <% end %>
410
+ #
411
+ # <% reset_cycle("colors") %>
412
+ # </tr>
413
+ # <% end %>
414
+ # </table>
415
+ def reset_cycle(name = "default")
416
+ cycle = get_cycle(name)
417
+ cycle.reset unless cycle.nil?
418
+ end
419
+
420
+ class Cycle #:nodoc:
421
+ attr_reader :values
422
+
423
+ def initialize(first_value, *values)
424
+ @values = values.unshift(first_value)
425
+ reset
426
+ end
427
+
428
+ def reset
429
+ @index = 0
430
+ end
431
+
432
+ def to_s
433
+ value = @values[@index].to_s
434
+ @index = (@index + 1) % @values.size
435
+ return value
436
+ end
437
+ end
438
+
439
+ private
440
+ # The cycle helpers need to store the cycles in a place that is
441
+ # guaranteed to be reset every time a page is rendered, so it
442
+ # uses an instance variable of ActionView::Base.
443
+ def get_cycle(name)
444
+ @_cycles = Hash.new unless defined?(@_cycles)
445
+ return @_cycles[name]
446
+ end
447
+
448
+ def set_cycle(name, cycle_object)
449
+ @_cycles = Hash.new unless defined?(@_cycles)
450
+ @_cycles[name] = cycle_object
451
+ end
452
+
453
+ AUTO_LINK_RE = %r{
454
+ ( # leading text
455
+ <\w+.*?>| # leading HTML tag, or
456
+ [^=!:'"/]| # leading punctuation, or
457
+ ^ # beginning of line
458
+ )
459
+ (
460
+ (?:https?://)| # protocol spec, or
461
+ (?:www\.) # www.*
462
+ )
463
+ (
464
+ [-\w]+ # subdomain or domain
465
+ (?:\.[-\w]+)* # remaining subdomains or domain
466
+ (?::\d+)? # port
467
+ (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))+)?)* # path
468
+ (?:\?[\w\+@%&=.;-]+)? # query string
469
+ (?:\#[\w\-]*)? # trailing anchor
470
+ )
471
+ ([[:punct:]]|<|$|) # trailing text
472
+ }x unless const_defined?(:AUTO_LINK_RE)
473
+
474
+ # Turns all urls into clickable links. If a block is given, each url
475
+ # is yielded and the result is used as the link text.
476
+ def auto_link_urls(text, href_options = {})
477
+ extra_options = tag_options(href_options.stringify_keys) || ""
478
+ text.gsub(AUTO_LINK_RE) do
479
+ all, a, b, c, d = $&, $1, $2, $3, $4
480
+ if a =~ /<a\s/i # don't replace URL's that are already linked
481
+ all
482
+ else
483
+ text = b + c
484
+ text = yield(text) if block_given?
485
+ %(#{a}<a href="#{b=="www."?"http://www.":b}#{c}"#{extra_options}>#{text}</a>#{d})
486
+ end
487
+ end
488
+ end
489
+
490
+ # Turns all email addresses into clickable links. If a block is given,
491
+ # each email is yielded and the result is used as the link text.
492
+ def auto_link_email_addresses(text)
493
+ body = text.dup
494
+ text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
495
+ text = $1
496
+
497
+ if body.match(/<a\b[^>]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/)
498
+ text
499
+ else
500
+ display_text = (block_given?) ? yield(text) : text
501
+ %{<a href="mailto:#{text}">#{display_text}</a>}
502
+ end
503
+ end
504
+ end
505
+ end
506
+ end
507
+ end