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.
- data/CHANGELOG +1400 -20
- data/MIT-LICENSE +1 -1
- data/README +5 -5
- data/RUNNING_UNIT_TESTS +4 -5
- data/Rakefile +5 -6
- data/install.rb +2 -2
- data/lib/action_controller.rb +11 -15
- data/lib/action_controller/assertions.rb +12 -25
- data/lib/action_controller/assertions/dom_assertions.rb +18 -4
- data/lib/action_controller/assertions/model_assertions.rb +8 -1
- data/lib/action_controller/assertions/response_assertions.rb +35 -12
- data/lib/action_controller/assertions/routing_assertions.rb +56 -12
- data/lib/action_controller/assertions/selector_assertions.rb +105 -38
- data/lib/action_controller/assertions/tag_assertions.rb +28 -15
- data/lib/action_controller/base.rb +318 -250
- data/lib/action_controller/benchmarking.rb +33 -29
- data/lib/action_controller/caching.rb +130 -64
- data/lib/action_controller/cgi_ext.rb +16 -0
- data/lib/action_controller/cgi_ext/{cookie_performance_fix.rb → cookie.rb} +25 -40
- data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
- data/lib/action_controller/cgi_ext/session.rb +73 -0
- data/lib/action_controller/cgi_ext/stdinput.rb +23 -0
- data/lib/action_controller/cgi_process.rb +34 -57
- data/lib/action_controller/components.rb +19 -36
- data/lib/action_controller/cookies.rb +10 -9
- data/lib/action_controller/dispatcher.rb +195 -0
- data/lib/action_controller/filters.rb +35 -34
- data/lib/action_controller/flash.rb +30 -35
- data/lib/action_controller/helpers.rb +121 -47
- data/lib/action_controller/http_authentication.rb +126 -0
- data/lib/action_controller/integration.rb +105 -101
- data/lib/action_controller/layout.rb +59 -47
- data/lib/action_controller/mime_responds.rb +57 -68
- data/lib/action_controller/mime_type.rb +43 -80
- data/lib/action_controller/mime_types.rb +20 -0
- data/lib/action_controller/polymorphic_routes.rb +88 -0
- data/lib/action_controller/record_identifier.rb +91 -0
- data/lib/action_controller/request.rb +553 -88
- data/lib/action_controller/request_forgery_protection.rb +126 -0
- data/lib/action_controller/request_profiler.rb +138 -0
- data/lib/action_controller/rescue.rb +185 -69
- data/lib/action_controller/resources.rb +211 -172
- data/lib/action_controller/response.rb +49 -8
- data/lib/action_controller/routing.rb +359 -236
- data/lib/action_controller/routing_optimisation.rb +119 -0
- data/lib/action_controller/session/active_record_store.rb +3 -2
- data/lib/action_controller/session/cookie_store.rb +161 -0
- data/lib/action_controller/session/mem_cache_store.rb +9 -16
- data/lib/action_controller/session_management.rb +17 -8
- data/lib/action_controller/streaming.rb +6 -3
- data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
- data/lib/action_controller/templates/rescues/{_trace.rhtml → _trace.erb} +0 -0
- data/lib/action_controller/templates/rescues/{diagnostics.rhtml → diagnostics.erb} +2 -2
- data/lib/action_controller/templates/rescues/{layout.rhtml → layout.erb} +0 -0
- data/lib/action_controller/templates/rescues/{missing_template.rhtml → missing_template.erb} +0 -0
- data/lib/action_controller/templates/rescues/{routing_error.rhtml → routing_error.erb} +0 -0
- data/lib/action_controller/templates/rescues/{template_error.rhtml → template_error.erb} +2 -2
- data/lib/action_controller/templates/rescues/{unknown_action.rhtml → unknown_action.erb} +0 -0
- data/lib/action_controller/test_case.rb +53 -0
- data/lib/action_controller/test_process.rb +59 -46
- data/lib/action_controller/url_rewriter.rb +48 -24
- data/lib/action_controller/vendor/html-scanner/html/document.rb +7 -4
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +11 -6
- data/lib/action_controller/verification.rb +27 -21
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/version.rb +4 -4
- data/lib/action_view.rb +2 -3
- data/lib/action_view/base.rb +218 -63
- data/lib/action_view/compiled_templates.rb +1 -2
- data/lib/action_view/helpers/active_record_helper.rb +35 -17
- data/lib/action_view/helpers/asset_tag_helper.rb +395 -87
- data/lib/action_view/helpers/atom_feed_helper.rb +111 -0
- data/lib/action_view/helpers/benchmark_helper.rb +12 -5
- data/lib/action_view/helpers/cache_helper.rb +29 -0
- data/lib/action_view/helpers/capture_helper.rb +97 -63
- data/lib/action_view/helpers/date_helper.rb +295 -35
- data/lib/action_view/helpers/debug_helper.rb +6 -2
- data/lib/action_view/helpers/form_helper.rb +354 -111
- data/lib/action_view/helpers/form_options_helper.rb +171 -109
- data/lib/action_view/helpers/form_tag_helper.rb +332 -76
- data/lib/action_view/helpers/javascript_helper.rb +35 -11
- data/lib/action_view/helpers/javascripts/controls.js +484 -354
- data/lib/action_view/helpers/javascripts/dragdrop.js +88 -58
- data/lib/action_view/helpers/javascripts/effects.js +396 -364
- data/lib/action_view/helpers/javascripts/prototype.js +2817 -1107
- data/lib/action_view/helpers/number_helper.rb +84 -60
- data/lib/action_view/helpers/prototype_helper.rb +419 -43
- data/lib/action_view/helpers/record_identification_helper.rb +20 -0
- data/lib/action_view/helpers/record_tag_helper.rb +59 -0
- data/lib/action_view/helpers/sanitize_helper.rb +223 -0
- data/lib/action_view/helpers/scriptaculous_helper.rb +63 -4
- data/lib/action_view/helpers/tag_helper.rb +69 -39
- data/lib/action_view/helpers/text_helper.rb +221 -148
- data/lib/action_view/helpers/url_helper.rb +283 -165
- data/lib/action_view/partials.rb +134 -62
- data/lib/action_view/template_error.rb +4 -12
- data/lib/actionpack.rb +1 -0
- data/test/abstract_unit.rb +21 -1
- data/test/action_view_test.rb +26 -0
- data/test/active_record_unit.rb +12 -20
- data/test/activerecord/active_record_store_test.rb +2 -2
- data/test/activerecord/render_partial_with_record_identification_test.rb +74 -0
- data/test/controller/action_pack_assertions_test.rb +21 -152
- data/test/controller/addresses_render_test.rb +2 -7
- data/test/controller/assert_select_test.rb +120 -14
- data/test/controller/base_test.rb +11 -13
- data/test/controller/caching_test.rb +125 -5
- data/test/controller/capture_test.rb +23 -16
- data/test/controller/cgi_test.rb +66 -391
- data/test/controller/components_test.rb +31 -42
- data/test/controller/content_type_test.rb +1 -1
- data/test/controller/cookie_test.rb +42 -14
- data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -42
- data/test/controller/dispatcher_test.rb +123 -0
- data/test/controller/fake_models.rb +5 -0
- data/test/controller/filters_test.rb +44 -7
- data/test/controller/flash_test.rb +46 -2
- data/test/controller/fragment_store_setting_test.rb +10 -8
- data/test/controller/helper_test.rb +19 -2
- data/test/controller/html-scanner/document_test.rb +124 -0
- data/test/controller/html-scanner/node_test.rb +69 -0
- data/test/controller/html-scanner/sanitizer_test.rb +250 -0
- data/test/controller/html-scanner/tag_node_test.rb +239 -0
- data/test/controller/html-scanner/text_node_test.rb +51 -0
- data/test/controller/html-scanner/tokenizer_test.rb +125 -0
- data/test/controller/http_authentication_test.rb +54 -0
- data/test/controller/integration_test.rb +12 -26
- data/test/controller/layout_test.rb +64 -12
- data/test/controller/mime_responds_test.rb +193 -38
- data/test/controller/mime_type_test.rb +30 -8
- data/test/controller/new_render_test.rb +104 -22
- data/test/controller/polymorphic_routes_test.rb +98 -0
- data/test/controller/record_identifier_test.rb +103 -0
- data/test/controller/redirect_test.rb +120 -18
- data/test/controller/render_test.rb +195 -45
- data/test/controller/request_forgery_protection_test.rb +217 -0
- data/test/controller/request_test.rb +545 -27
- data/test/controller/rescue_test.rb +501 -0
- data/test/controller/resources_test.rb +258 -132
- data/test/controller/routing_test.rb +502 -106
- data/test/controller/selector_test.rb +5 -5
- data/test/controller/send_file_test.rb +17 -7
- data/test/controller/session/cookie_store_test.rb +246 -0
- data/test/controller/session/mem_cache_store_test.rb +182 -0
- data/test/controller/session_fixation_test.rb +8 -11
- data/test/controller/session_management_test.rb +7 -7
- data/test/controller/test_test.rb +150 -38
- data/test/controller/url_rewriter_test.rb +87 -12
- data/test/controller/verification_test.rb +11 -0
- data/test/controller/view_paths_test.rb +137 -0
- data/test/controller/webservice_test.rb +11 -75
- data/test/fixtures/addresses/{list.rhtml → list.erb} +0 -0
- data/test/fixtures/db_definitions/sqlite.sql +2 -1
- data/test/fixtures/developer.rb +2 -0
- data/test/fixtures/fun/games/{hello_world.rhtml → hello_world.erb} +0 -0
- data/test/fixtures/helpers/fun/pdf_helper.rb +1 -1
- data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
- data/test/fixtures/layouts/{builder.rxml → builder.builder} +0 -0
- data/test/fixtures/layouts/{standard.rhtml → standard.erb} +0 -0
- data/test/fixtures/layouts/{talk_from_action.rhtml → talk_from_action.erb} +0 -0
- data/test/fixtures/layouts/{yield.rhtml → yield.erb} +0 -0
- data/test/fixtures/multipart/binary_file +0 -0
- data/test/fixtures/multipart/bracketed_param +5 -0
- data/test/fixtures/override/test/hello_world.erb +1 -0
- data/test/fixtures/override2/layouts/test/sub.erb +1 -0
- data/test/fixtures/post_test/layouts/post.html.erb +1 -0
- data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
- data/test/fixtures/post_test/post/index.html.erb +1 -0
- data/test/fixtures/post_test/post/index.iphone.erb +1 -0
- data/test/fixtures/post_test/super_post/index.html.erb +1 -0
- data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
- data/test/fixtures/public/404.html +1 -0
- data/test/fixtures/public/500.html +1 -0
- data/test/fixtures/public/javascripts/application.js +0 -1
- data/test/fixtures/public/javascripts/bank.js +1 -0
- data/test/fixtures/public/javascripts/robber.js +1 -0
- data/test/fixtures/public/stylesheets/bank.css +1 -0
- data/test/fixtures/public/stylesheets/robber.css +1 -0
- data/test/fixtures/replies.yml +2 -0
- data/test/fixtures/reply.rb +2 -1
- data/test/fixtures/respond_to/{all_types_with_layout.rhtml → all_types_with_layout.html.erb} +0 -0
- data/test/fixtures/respond_to/{all_types_with_layout.rjs → all_types_with_layout.js.rjs} +0 -0
- data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
- data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
- data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
- data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
- data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
- data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
- data/test/fixtures/respond_to/{using_defaults.rhtml → using_defaults.html.erb} +0 -0
- data/test/fixtures/respond_to/{using_defaults.rjs → using_defaults.js.rjs} +0 -0
- data/test/fixtures/respond_to/{using_defaults.rxml → using_defaults.xml.builder} +0 -0
- data/test/fixtures/respond_to/{using_defaults_with_type_list.rhtml → using_defaults_with_type_list.html.erb} +0 -0
- data/test/fixtures/respond_to/{using_defaults_with_type_list.rjs → using_defaults_with_type_list.js.rjs} +0 -0
- data/test/fixtures/respond_to/{using_defaults_with_type_list.rxml → using_defaults_with_type_list.xml.builder} +0 -0
- data/test/fixtures/scope/test/{modgreet.rhtml → modgreet.erb} +0 -0
- data/test/fixtures/test/{_customer.rhtml → _customer.erb} +0 -0
- data/test/fixtures/test/{_customer_greeting.rhtml → _customer_greeting.erb} +0 -0
- data/test/fixtures/test/_hash_greeting.erb +1 -0
- data/test/fixtures/test/_hash_object.erb +2 -0
- data/test/fixtures/test/{_hello.rxml → _hello.builder} +0 -0
- data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
- data/test/fixtures/test/_partial.erb +1 -0
- data/test/fixtures/test/_partial.html.erb +1 -0
- data/test/fixtures/test/_partial.js.erb +1 -0
- data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
- data/test/fixtures/test/{_partial_only.rhtml → _partial_only.erb} +0 -0
- data/test/fixtures/test/{_person.rhtml → _person.erb} +0 -0
- data/test/fixtures/test/{action_talk_to_layout.rhtml → action_talk_to_layout.erb} +0 -0
- data/test/fixtures/test/{block_content_for.rhtml → block_content_for.erb} +0 -0
- data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
- data/test/fixtures/test/{capturing.rhtml → capturing.erb} +0 -0
- data/test/fixtures/test/{content_for.rhtml → content_for.erb} +0 -0
- data/test/fixtures/test/content_for_concatenated.erb +3 -0
- data/test/fixtures/test/content_for_with_parameter.erb +2 -0
- data/test/fixtures/test/dot.directory/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
- data/test/fixtures/test/{erb_content_for.rhtml → erb_content_for.erb} +0 -0
- data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
- data/test/fixtures/test/formatted_xml_erb.builder +1 -0
- data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
- data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
- data/test/fixtures/test/{greeting.rhtml → greeting.erb} +0 -0
- data/test/fixtures/test/{hello.rxml → hello.builder} +0 -0
- data/test/fixtures/test/{hello_world.rxml → hello_world.builder} +0 -0
- data/test/fixtures/test/{hello_world.rhtml → hello_world.erb} +0 -0
- data/test/fixtures/test/{hello_world_container.rxml → hello_world_container.builder} +0 -0
- data/test/fixtures/test/{hello_world_with_layout_false.rhtml → hello_world_with_layout_false.erb} +0 -0
- data/test/fixtures/test/{hello_xml_world.rxml → hello_xml_world.builder} +0 -0
- data/test/fixtures/test/list.erb +1 -0
- data/test/fixtures/test/{non_erb_block_content_for.rxml → non_erb_block_content_for.builder} +0 -0
- data/test/fixtures/test/{potential_conflicts.rhtml → potential_conflicts.erb} +0 -0
- data/test/fixtures/test/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
- data/test/fixtures/test/{render_file_with_locals.rhtml → render_file_with_locals.erb} +0 -0
- data/test/fixtures/test/{render_to_string_test.rhtml → render_to_string_test.erb} +0 -0
- data/test/fixtures/test/{update_element_with_capture.rhtml → update_element_with_capture.erb} +0 -0
- data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
- data/test/fixtures/topic.rb +1 -1
- data/test/template/active_record_helper_test.rb +67 -20
- data/test/template/asset_tag_helper_test.rb +222 -54
- data/test/template/atom_feed_helper_test.rb +101 -0
- data/test/template/benchmark_helper_test.rb +2 -2
- data/test/template/compiled_templates_test.rb +76 -32
- data/test/template/date_helper_test.rb +125 -9
- data/test/template/form_helper_test.rb +326 -33
- data/test/template/form_options_helper_test.rb +822 -15
- data/test/template/form_tag_helper_test.rb +96 -30
- data/test/template/javascript_helper_test.rb +61 -13
- data/test/template/number_helper_test.rb +12 -11
- data/test/template/prototype_helper_test.rb +185 -24
- data/test/template/sanitize_helper_test.rb +49 -0
- data/test/template/scriptaculous_helper_test.rb +9 -3
- data/test/template/tag_helper_test.rb +13 -2
- data/test/template/text_helper_test.rb +38 -52
- data/test/template/url_helper_test.rb +216 -46
- metadata +144 -116
- data/examples/.htaccess +0 -24
- data/examples/address_book/index.rhtml +0 -33
- data/examples/address_book/layout.rhtml +0 -8
- data/examples/address_book_controller.cgi +0 -9
- data/examples/address_book_controller.fcgi +0 -6
- data/examples/address_book_controller.rb +0 -52
- data/examples/address_book_controller.rbx +0 -4
- data/examples/benchmark.rb +0 -52
- data/examples/benchmark_with_ar.fcgi +0 -89
- data/examples/blog_controller.cgi +0 -53
- data/examples/debate/index.rhtml +0 -14
- data/examples/debate/new_topic.rhtml +0 -22
- data/examples/debate/topic.rhtml +0 -32
- data/examples/debate_controller.cgi +0 -57
- data/lib/action_controller/assertions/deprecated_assertions.rb +0 -228
- data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -36
- data/lib/action_controller/cgi_ext/cgi_methods.rb +0 -211
- data/lib/action_controller/cgi_ext/pstore_performance_fix.rb +0 -30
- data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +0 -95
- data/lib/action_controller/cgi_ext/session_performance_fix.rb +0 -30
- data/lib/action_controller/deprecated_dependencies.rb +0 -65
- data/lib/action_controller/deprecated_redirects.rb +0 -17
- data/lib/action_controller/deprecated_request_methods.rb +0 -34
- data/lib/action_controller/macros/auto_complete.rb +0 -53
- data/lib/action_controller/macros/in_place_editing.rb +0 -33
- data/lib/action_controller/pagination.rb +0 -408
- data/lib/action_controller/scaffolding.rb +0 -189
- data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -44
- data/lib/action_controller/templates/scaffolds/edit.rhtml +0 -7
- data/lib/action_controller/templates/scaffolds/layout.rhtml +0 -69
- data/lib/action_controller/templates/scaffolds/list.rhtml +0 -27
- data/lib/action_controller/templates/scaffolds/new.rhtml +0 -6
- data/lib/action_controller/templates/scaffolds/show.rhtml +0 -9
- data/lib/action_controller/vendor/xml_node.rb +0 -97
- data/lib/action_view/helpers/deprecated_helper.rb +0 -37
- data/lib/action_view/helpers/java_script_macros_helper.rb +0 -233
- data/lib/action_view/helpers/pagination_helper.rb +0 -86
- data/test/activerecord/active_record_assertions_test.rb +0 -92
- data/test/activerecord/pagination_test.rb +0 -165
- data/test/controller/deprecated_instance_variables_test.rb +0 -48
- data/test/controller/raw_post_test.rb +0 -68
- data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +0 -1
- data/test/fixtures/respond_to/layouts/standard.rhtml +0 -1
- data/test/fixtures/test/_hash_object.rhtml +0 -1
- data/test/fixtures/test/list.rhtml +0 -1
- data/test/template/deprecated_helper_test.rb +0 -36
- data/test/template/deprecated_instance_variables_test.rb +0 -43
- data/test/template/java_script_macros_helper_test.rb +0 -109
@@ -1,64 +1,103 @@
|
|
1
|
-
require
|
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
|
6
|
-
# and transforming strings
|
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
|
9
|
-
|
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
|
-
#
|
23
|
-
#
|
24
|
-
#
|
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(
|
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
|
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
|
-
#
|
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 +
|
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
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
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
|
-
#
|
88
|
+
# # => "...s is an examp..."
|
59
89
|
#
|
60
90
|
# excerpt('This is an example', 'is', 5)
|
61
|
-
#
|
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
|
-
#
|
85
|
-
# pluralize(
|
86
|
-
#
|
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
|
-
#
|
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.
|
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
|
164
|
-
# will limit what should be linked. You can add
|
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("
|
169
|
-
#
|
170
|
-
#
|
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
|
-
#
|
173
|
-
#
|
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
|
-
#
|
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(
|
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
|
-
# => <script> do_nasty_stuff() </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(/</, "<")
|
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(/</, "<")
|
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
|
373
|
-
(?:\?[\w
|
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
|
-
|
401
|
-
|
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
|