actionpack 1.12.5 → 1.13.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 +517 -15
- data/MIT-LICENSE +1 -1
- data/README +18 -20
- data/Rakefile +7 -4
- data/examples/address_book_controller.rb +3 -3
- data/examples/blog_controller.cgi +3 -3
- data/examples/debate_controller.cgi +5 -5
- data/lib/action_controller.rb +2 -2
- data/lib/action_controller/assertions.rb +73 -311
- data/lib/action_controller/{deprecated_assertions.rb → assertions/deprecated_assertions.rb} +32 -8
- data/lib/action_controller/assertions/dom_assertions.rb +25 -0
- data/lib/action_controller/assertions/model_assertions.rb +12 -0
- data/lib/action_controller/assertions/response_assertions.rb +140 -0
- data/lib/action_controller/assertions/routing_assertions.rb +82 -0
- data/lib/action_controller/assertions/selector_assertions.rb +571 -0
- data/lib/action_controller/assertions/tag_assertions.rb +117 -0
- data/lib/action_controller/base.rb +334 -163
- data/lib/action_controller/benchmarking.rb +3 -6
- data/lib/action_controller/caching.rb +83 -22
- data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -7
- data/lib/action_controller/cgi_ext/cgi_methods.rb +167 -173
- data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +43 -22
- data/lib/action_controller/cgi_process.rb +50 -27
- data/lib/action_controller/components.rb +21 -25
- data/lib/action_controller/cookies.rb +10 -9
- data/lib/action_controller/{dependencies.rb → deprecated_dependencies.rb} +9 -27
- data/lib/action_controller/filters.rb +448 -225
- data/lib/action_controller/flash.rb +24 -20
- data/lib/action_controller/helpers.rb +2 -5
- data/lib/action_controller/integration.rb +40 -16
- data/lib/action_controller/layout.rb +11 -8
- data/lib/action_controller/macros/auto_complete.rb +3 -2
- data/lib/action_controller/macros/in_place_editing.rb +3 -2
- data/lib/action_controller/mime_responds.rb +41 -29
- data/lib/action_controller/mime_type.rb +68 -10
- data/lib/action_controller/pagination.rb +4 -3
- data/lib/action_controller/request.rb +22 -14
- data/lib/action_controller/rescue.rb +25 -22
- data/lib/action_controller/resources.rb +302 -0
- data/lib/action_controller/response.rb +20 -2
- data/lib/action_controller/response.rb.rej +17 -0
- data/lib/action_controller/routing.rb +1165 -567
- data/lib/action_controller/scaffolding.rb +30 -31
- data/lib/action_controller/session/active_record_store.rb +2 -0
- data/lib/action_controller/session/drb_store.rb +4 -0
- data/lib/action_controller/session/mem_cache_store.rb +4 -0
- data/lib/action_controller/session_management.rb +6 -9
- data/lib/action_controller/status_codes.rb +89 -0
- data/lib/action_controller/streaming.rb +6 -15
- data/lib/action_controller/templates/rescues/_request_and_response.rhtml +5 -5
- data/lib/action_controller/templates/rescues/diagnostics.rhtml +2 -2
- data/lib/action_controller/templates/rescues/routing_error.rhtml +4 -4
- data/lib/action_controller/templates/rescues/template_error.rhtml +1 -1
- data/lib/action_controller/templates/scaffolds/list.rhtml +1 -1
- data/lib/action_controller/test_process.rb +52 -30
- data/lib/action_controller/url_rewriter.rb +63 -29
- data/lib/action_controller/vendor/html-scanner/html/document.rb +1 -0
- data/lib/action_controller/vendor/html-scanner/html/node.rb +3 -4
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +822 -0
- data/lib/action_controller/verification.rb +22 -11
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/version.rb +2 -2
- data/lib/action_view.rb +1 -1
- data/lib/action_view/base.rb +46 -43
- data/lib/action_view/compiled_templates.rb +1 -1
- data/lib/action_view/helpers/active_record_helper.rb +54 -17
- data/lib/action_view/helpers/asset_tag_helper.rb +97 -46
- data/lib/action_view/helpers/capture_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +258 -136
- data/lib/action_view/helpers/debug_helper.rb +1 -1
- data/lib/action_view/helpers/deprecated_helper.rb +34 -0
- data/lib/action_view/helpers/form_helper.rb +75 -35
- data/lib/action_view/helpers/form_options_helper.rb +7 -5
- data/lib/action_view/helpers/form_tag_helper.rb +44 -6
- data/lib/action_view/helpers/java_script_macros_helper.rb +59 -46
- data/lib/action_view/helpers/javascript_helper.rb +71 -10
- data/lib/action_view/helpers/javascripts/controls.js +41 -23
- data/lib/action_view/helpers/javascripts/dragdrop.js +105 -76
- data/lib/action_view/helpers/javascripts/effects.js +293 -163
- data/lib/action_view/helpers/javascripts/prototype.js +897 -389
- data/lib/action_view/helpers/javascripts/prototype.js.rej +561 -0
- data/lib/action_view/helpers/number_helper.rb +111 -65
- data/lib/action_view/helpers/prototype_helper.rb +84 -109
- data/lib/action_view/helpers/scriptaculous_helper.rb +5 -0
- data/lib/action_view/helpers/tag_helper.rb +69 -16
- data/lib/action_view/helpers/text_helper.rb +149 -112
- data/lib/action_view/helpers/url_helper.rb +200 -107
- data/lib/action_view/template_error.rb +66 -42
- data/test/abstract_unit.rb +4 -2
- data/test/active_record_unit.rb +84 -56
- data/test/activerecord/active_record_assertions_test.rb +26 -18
- data/test/activerecord/active_record_store_test.rb +4 -36
- data/test/activerecord/pagination_test.rb +1 -6
- data/test/controller/action_pack_assertions_test.rb +230 -113
- data/test/controller/addresses_render_test.rb +2 -6
- data/test/controller/assert_select_test.rb +576 -0
- data/test/controller/base_test.rb +73 -3
- data/test/controller/caching_test.rb +228 -0
- data/test/controller/capture_test.rb +12 -10
- data/test/controller/cgi_test.rb +89 -12
- data/test/controller/components_test.rb +24 -2
- data/test/controller/content_type_test.rb +139 -0
- data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
- data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
- data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
- data/test/controller/cookie_test.rb +33 -25
- data/test/controller/deprecated_instance_variables_test.rb +48 -0
- data/test/controller/deprecation/deprecated_base_methods_test.rb +60 -0
- data/test/controller/fake_controllers.rb +0 -1
- data/test/controller/filters_test.rb +301 -16
- data/test/controller/flash_test.rb +19 -2
- data/test/controller/helper_test.rb +2 -2
- data/test/controller/integration_test.rb +154 -0
- data/test/controller/layout_test.rb +115 -1
- data/test/controller/mime_responds_test.rb +94 -0
- data/test/controller/mime_type_test.rb +9 -0
- data/test/controller/new_render_test.rb +161 -11
- data/test/controller/raw_post_test.rb +52 -15
- data/test/controller/redirect_test.rb +27 -14
- data/test/controller/render_test.rb +76 -29
- data/test/controller/request_test.rb +55 -4
- data/test/controller/resources_test.rb +274 -0
- data/test/controller/routing_test.rb +1533 -824
- data/test/controller/selector_test.rb +628 -0
- data/test/controller/send_file_test.rb +9 -1
- data/test/controller/session_management_test.rb +51 -0
- data/test/controller/test_test.rb +113 -29
- data/test/controller/url_rewriter_test.rb +86 -17
- data/test/controller/verification_test.rb +19 -17
- data/test/controller/webservice_test.rb +0 -7
- data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
- data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
- data/test/fixtures/content_type/render_default_for_rjs.rjs +1 -0
- data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
- data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +1 -0
- data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +1 -0
- data/test/fixtures/multipart/binary_file +0 -0
- data/test/fixtures/public/javascripts/application.js +1 -0
- data/test/fixtures/test/_hello.rxml +1 -0
- data/test/fixtures/test/hello_world_container.rxml +3 -0
- data/test/fixtures/topic.rb +2 -2
- data/test/template/active_record_helper_test.rb +83 -12
- data/test/template/asset_tag_helper_test.rb +75 -95
- data/test/template/compiled_templates_test.rb +1 -0
- data/test/template/date_helper_test.rb +873 -181
- data/test/template/deprecated_helper_test.rb +36 -0
- data/test/template/deprecated_instance_variables_test.rb +43 -0
- data/test/template/form_helper_test.rb +77 -1
- data/test/template/form_options_helper_test.rb +4 -0
- data/test/template/form_tag_helper_test.rb +66 -2
- data/test/template/java_script_macros_helper_test.rb +4 -1
- data/test/template/javascript_helper_test.rb +29 -0
- data/test/template/number_helper_test.rb +63 -27
- data/test/template/prototype_helper_test.rb +77 -34
- data/test/template/tag_helper_test.rb +34 -6
- data/test/template/text_helper_test.rb +69 -34
- data/test/template/url_helper_test.rb +168 -16
- data/test/testing_sandbox.rb +7 -22
- metadata +66 -20
- data/filler.txt +0 -50
- data/lib/action_controller/code_generation.rb +0 -235
- data/lib/action_controller/vendor/xml_simple.rb +0 -1019
- data/test/controller/caching_filestore.rb +0 -74
- data/test/fixtures/application_root/app/controllers/a_class_that_contains_a_controller/poorly_placed_controller.rb +0 -7
- data/test/fixtures/application_root/app/controllers/module_that_holds_controllers/nested_controller.rb +0 -3
- data/test/fixtures/application_root/app/models/a_class_that_contains_a_controller.rb +0 -7
- data/test/fixtures/dont_load.rb +0 -3
@@ -69,6 +69,11 @@ module ActionView
|
|
69
69
|
# containing the values of the ids of elements the sortable consists
|
70
70
|
# of, in the current order.
|
71
71
|
#
|
72
|
+
# Important: For this to work, the sortable elements must have id
|
73
|
+
# attributes in the form "string_identifier". For example, "item_1". Only
|
74
|
+
# the identifier part of the id attribute will be serialized.
|
75
|
+
#
|
76
|
+
#
|
72
77
|
# You can change the behaviour with various options, see
|
73
78
|
# http://script.aculo.us for more documentation.
|
74
79
|
def sortable_element(element_id, options = {})
|
@@ -2,39 +2,87 @@ require 'cgi'
|
|
2
2
|
require 'erb'
|
3
3
|
|
4
4
|
module ActionView
|
5
|
-
module Helpers
|
6
|
-
#
|
5
|
+
module Helpers #:nodoc:
|
6
|
+
# Use these methods to generate HTML tags programmatically when you can't use
|
7
|
+
# a Builder. By default, they output XHTML compliant tags.
|
7
8
|
module TagHelper
|
8
9
|
include ERB::Util
|
9
10
|
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
11
|
+
# Returns an empty HTML tag of type +name+ which by default is XHTML
|
12
|
+
# compliant. Setting +open+ to true will create an open tag compatible
|
13
|
+
# with HTML 4.0 and below. Add HTML attributes by passing an attributes
|
14
|
+
# hash to +options+. For attributes with no value like (disabled and
|
15
|
+
# readonly), give it a value of true in the +options+ hash. You can use
|
16
|
+
# symbols or strings for the attribute names.
|
17
|
+
#
|
18
|
+
# tag("br")
|
19
|
+
# # => <br />
|
20
|
+
# tag("br", nil, true)
|
21
|
+
# # => <br>
|
22
|
+
# tag("input", { :type => 'text', :disabled => true })
|
23
|
+
# # => <input type="text" disabled="disabled" />
|
13
24
|
def tag(name, options = nil, open = false)
|
14
|
-
"<#{name}#{tag_options(options
|
25
|
+
"<#{name}#{tag_options(options) if options}" + (open ? ">" : " />")
|
15
26
|
end
|
16
27
|
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
|
22
|
-
|
28
|
+
# Returns an HTML block tag of type +name+ surrounding the +content+. Add
|
29
|
+
# HTML attributes by passing an attributes hash to +options+. For attributes
|
30
|
+
# with no value like (disabled and readonly), give it a value of true in
|
31
|
+
# the +options+ hash. You can use symbols or strings for the attribute names.
|
32
|
+
#
|
33
|
+
# content_tag(:p, "Hello world!")
|
34
|
+
# # => <p>Hello world!</p>
|
35
|
+
# content_tag(:div, content_tag(:p, "Hello world!"), :class => "strong")
|
36
|
+
# # => <div class="strong"><p>Hello world!</p></div>
|
37
|
+
# content_tag("select", options, :multiple => true)
|
38
|
+
# # => <select multiple="multiple">...options...</select>
|
39
|
+
#
|
40
|
+
# Instead of passing the content as an argument, you can also use a block
|
41
|
+
# in which case, you pass your +options+ as the second parameter.
|
42
|
+
#
|
43
|
+
# <% content_tag :div, :class => "strong" do -%>
|
44
|
+
# Hello world!
|
45
|
+
# <% end -%>
|
46
|
+
# # => <div class="strong"><p>Hello world!</p></div>
|
47
|
+
def content_tag(name, content_or_options_with_block = nil, options = nil, &block)
|
48
|
+
if block_given?
|
49
|
+
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
|
50
|
+
content = capture(&block)
|
51
|
+
concat(content_tag_string(name, content, options), block.binding)
|
52
|
+
else
|
53
|
+
content = content_or_options_with_block
|
54
|
+
content_tag_string(name, content, options)
|
55
|
+
end
|
23
56
|
end
|
24
57
|
|
25
|
-
# Returns a CDATA section
|
58
|
+
# Returns a CDATA section with the given +content+. CDATA sections
|
26
59
|
# are used to escape blocks of text containing characters which would
|
27
60
|
# otherwise be recognized as markup. CDATA sections begin with the string
|
28
|
-
# <tt
|
29
|
-
#
|
61
|
+
# <tt><![CDATA[</tt> and end with (and may not contain) the string <tt>]]></tt>.
|
62
|
+
#
|
63
|
+
# cdata_section("<hello world>")
|
64
|
+
# # => <![CDATA[<hello world>]]>
|
30
65
|
def cdata_section(content)
|
31
66
|
"<![CDATA[#{content}]]>"
|
32
67
|
end
|
33
68
|
|
69
|
+
# Returns the escaped +html+ without affecting existing escaped entities.
|
70
|
+
#
|
71
|
+
# escape_once("1 > 2 & 3")
|
72
|
+
# # => "1 < 2 & 3"
|
73
|
+
def escape_once(html)
|
74
|
+
fix_double_escape(html_escape(html.to_s))
|
75
|
+
end
|
76
|
+
|
34
77
|
private
|
78
|
+
def content_tag_string(name, content, options)
|
79
|
+
tag_options = options ? tag_options(options) : ""
|
80
|
+
"<#{name}#{tag_options}>#{content}</#{name}>"
|
81
|
+
end
|
82
|
+
|
35
83
|
def tag_options(options)
|
36
84
|
cleaned_options = convert_booleans(options.stringify_keys.reject {|key, value| value.nil?})
|
37
|
-
' ' + cleaned_options.map {|key, value| %(#{key}="#{
|
85
|
+
' ' + cleaned_options.map {|key, value| %(#{key}="#{escape_once(value)}")}.sort * ' ' unless cleaned_options.empty?
|
38
86
|
end
|
39
87
|
|
40
88
|
def convert_booleans(options)
|
@@ -45,6 +93,11 @@ module ActionView
|
|
45
93
|
def boolean_attribute(options, attribute)
|
46
94
|
options[attribute] ? options[attribute] = attribute : options.delete(attribute)
|
47
95
|
end
|
96
|
+
|
97
|
+
# Fix double-escaped entities, such as &amp;, &#123;, etc.
|
98
|
+
def fix_double_escape(escaped)
|
99
|
+
escaped.gsub(/&([a-z]+|(#\d+));/i) { "&#{$1};" }
|
100
|
+
end
|
48
101
|
end
|
49
102
|
end
|
50
103
|
end
|
@@ -2,65 +2,90 @@ require File.dirname(__FILE__) + '/tag_helper'
|
|
2
2
|
|
3
3
|
module ActionView
|
4
4
|
module Helpers #:nodoc:
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
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
|
7
|
+
# 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) %>
|
10
14
|
# <% end %>
|
15
|
+
# => Title: This is my...
|
11
16
|
module TextHelper
|
12
|
-
# The
|
13
|
-
#
|
14
|
-
#
|
17
|
+
# The preferred method of outputting text in your views is to use the
|
18
|
+
# <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
|
19
|
+
# 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.
|
21
|
+
#
|
22
|
+
# <% concat "hello", binding %>
|
23
|
+
# is equivalent to using:
|
24
|
+
# <%= "hello" %>
|
15
25
|
def concat(string, binding)
|
16
26
|
eval("_erbout", binding).concat(string)
|
17
27
|
end
|
18
28
|
|
19
|
-
#
|
20
|
-
#
|
29
|
+
# 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+.
|
31
|
+
#
|
32
|
+
# truncate("Once upon a time in a world far far away", 14)
|
33
|
+
# => Once upon a...
|
21
34
|
def truncate(text, length = 30, truncate_string = "...")
|
22
35
|
if text.nil? then return end
|
23
|
-
l = length - truncate_string.length
|
24
|
-
|
25
|
-
text.length > length ? text[0...l] + truncate_string : text
|
26
|
-
else
|
27
|
-
chars = text.split(//)
|
28
|
-
chars.length > length ? chars[0...l].join + truncate_string : text
|
29
|
-
end
|
36
|
+
l = length - truncate_string.chars.length
|
37
|
+
text.chars.length > length ? text.chars[0...l] + truncate_string : text
|
30
38
|
end
|
31
39
|
|
32
|
-
# Highlights
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
40
|
+
# Highlights +phrase+ everywhere it is found in +text+ by inserting it into
|
41
|
+
# 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.
|
43
|
+
#
|
44
|
+
# highlight('You searched for: rails', 'rails')
|
45
|
+
# => You searched for: <strong class="highlight">rails</strong>
|
36
46
|
def highlight(text, phrase, highlighter = '<strong class="highlight">\1</strong>')
|
37
47
|
if phrase.blank? then return text end
|
38
48
|
text.gsub(/(#{Regexp.escape(phrase)})/i, highlighter) unless text.nil?
|
39
49
|
end
|
40
50
|
|
41
|
-
# Extracts an excerpt from
|
42
|
-
#
|
43
|
-
#
|
51
|
+
# 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+,
|
54
|
+
# then the +excerpt_string+ will be prepended/appended accordingly. If the +phrase+
|
55
|
+
# isn't found, nil is returned.
|
56
|
+
#
|
57
|
+
# excerpt('This is an example', 'an', 5)
|
58
|
+
# => "...s is an examp..."
|
59
|
+
#
|
60
|
+
# excerpt('This is an example', 'is', 5)
|
61
|
+
# => "This is an..."
|
44
62
|
def excerpt(text, phrase, radius = 100, excerpt_string = "...")
|
45
63
|
if text.nil? || phrase.nil? then return end
|
46
64
|
phrase = Regexp.escape(phrase)
|
47
65
|
|
48
|
-
if found_pos = text =~ /(#{phrase})/i
|
66
|
+
if found_pos = text.chars =~ /(#{phrase})/i
|
49
67
|
start_pos = [ found_pos - radius, 0 ].max
|
50
|
-
end_pos = [ found_pos + phrase.length + radius, text.length ].min
|
68
|
+
end_pos = [ found_pos + phrase.chars.length + radius, text.chars.length ].min
|
51
69
|
|
52
70
|
prefix = start_pos > 0 ? excerpt_string : ""
|
53
|
-
postfix = end_pos < text.length ? excerpt_string : ""
|
71
|
+
postfix = end_pos < text.chars.length ? excerpt_string : ""
|
54
72
|
|
55
|
-
prefix + text[start_pos..end_pos].strip + postfix
|
73
|
+
prefix + text.chars[start_pos..end_pos].strip + postfix
|
56
74
|
else
|
57
75
|
nil
|
58
76
|
end
|
59
77
|
end
|
60
78
|
|
61
|
-
# Attempts to pluralize the +singular+ word unless +count+ is 1.
|
79
|
+
# Attempts to pluralize the +singular+ word unless +count+ is 1. If +plural+
|
80
|
+
# is supplied, it will use that when count is > 1, if the ActiveSupport Inflector
|
81
|
+
# is loaded, it will use the Inflector to determine the plural form, otherwise
|
82
|
+
# it will just add an 's' to the +singular+ word.
|
83
|
+
#
|
84
|
+
# pluralize(1, 'person') => 1 person
|
85
|
+
# pluralize(2, 'person') => 2 people
|
86
|
+
# pluralize(3, 'person', 'users') => 3 users
|
62
87
|
def pluralize(count, singular, plural = nil)
|
63
|
-
"#{count} " + if count == 1
|
88
|
+
"#{count} " + if count == 1 || count == '1'
|
64
89
|
singular
|
65
90
|
elsif plural
|
66
91
|
plural
|
@@ -71,7 +96,11 @@ module ActionView
|
|
71
96
|
end
|
72
97
|
end
|
73
98
|
|
74
|
-
#
|
99
|
+
# 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+.
|
101
|
+
#
|
102
|
+
# word_wrap('Once upon a time', 4)
|
103
|
+
# => Once\nupon\na\ntime
|
75
104
|
def word_wrap(text, line_width = 80)
|
76
105
|
text.gsub(/\n/, "\n\n").gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip
|
77
106
|
end
|
@@ -79,8 +108,9 @@ module ActionView
|
|
79
108
|
begin
|
80
109
|
require_library_or_gem "redcloth" unless Object.const_defined?(:RedCloth)
|
81
110
|
|
82
|
-
# Returns the text with all the Textile codes turned into HTML
|
83
|
-
# <i>This method is only available if RedCloth
|
111
|
+
# Returns the text with all the Textile codes turned into HTML tags.
|
112
|
+
# <i>This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
|
113
|
+
# is available</i>.
|
84
114
|
def textilize(text)
|
85
115
|
if text.blank?
|
86
116
|
""
|
@@ -91,8 +121,10 @@ module ActionView
|
|
91
121
|
end
|
92
122
|
end
|
93
123
|
|
94
|
-
# Returns the text with all the Textile codes turned into HTML
|
95
|
-
#
|
124
|
+
# Returns the text with all the Textile codes turned into HTML tags,
|
125
|
+
# but without the bounding <p> tag that RedCloth adds.
|
126
|
+
# <i>This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
|
127
|
+
# is available</i>.
|
96
128
|
def textilize_without_paragraph(text)
|
97
129
|
textiled = textilize(text)
|
98
130
|
if textiled[0..2] == "<p>" then textiled = textiled[3..-1] end
|
@@ -106,8 +138,9 @@ module ActionView
|
|
106
138
|
begin
|
107
139
|
require_library_or_gem "bluecloth" unless Object.const_defined?(:BlueCloth)
|
108
140
|
|
109
|
-
# Returns the text with all the Markdown codes turned into HTML
|
110
|
-
# <i>This method is only available if BlueCloth
|
141
|
+
# Returns the text with all the Markdown codes turned into HTML tags.
|
142
|
+
# <i>This method is only available if BlueCloth[http://www.deveiate.org/projects/BlueCloth]
|
143
|
+
# is available</i>.
|
111
144
|
def markdown(text)
|
112
145
|
text.blank? ? "" : BlueCloth.new(text).to_html
|
113
146
|
end
|
@@ -115,29 +148,30 @@ module ActionView
|
|
115
148
|
# We can't really help what's not there
|
116
149
|
end
|
117
150
|
|
118
|
-
# Returns +text+ transformed into HTML using
|
119
|
-
#
|
120
|
-
#
|
121
|
-
# considered a linebreak
|
151
|
+
# Returns +text+ transformed into HTML using simple formatting rules.
|
152
|
+
# Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
|
153
|
+
# paragraph and wrapped in <tt><p></tt> tags. One newline (<tt>\n</tt>) is
|
154
|
+
# considered as a linebreak and a <tt><br /></tt> tag is appended. This
|
155
|
+
# method does not remove the newlines from the +text+.
|
122
156
|
def simple_format(text)
|
123
|
-
text.
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
content_tag("p", text)
|
157
|
+
content_tag 'p', text.to_s.
|
158
|
+
gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
|
159
|
+
gsub(/\n\n+/, "</p>\n\n<p>"). # 2+ newline -> paragraph
|
160
|
+
gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
|
129
161
|
end
|
130
162
|
|
131
|
-
# Turns all urls and email addresses into clickable links. The +link+ parameter
|
132
|
-
#
|
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
|
165
|
+
# +href_options+. Options for +link+ are <tt>:all</tt> (default),
|
166
|
+
# <tt>:email_addresses</tt>, and <tt>:urls</tt>.
|
133
167
|
#
|
134
|
-
#
|
135
|
-
#
|
136
|
-
# Go to <a href="http://www.rubyonrails.com">http://www.rubyonrails.com</a> and
|
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
|
137
170
|
# say hello to <a href="mailto:david@loudthinking.com">david@loudthinking.com</a>
|
138
171
|
#
|
139
172
|
# If a block is given, each url and email address is yielded and the
|
140
|
-
# result is used as the link text.
|
173
|
+
# result is used as the link text.
|
174
|
+
#
|
141
175
|
# auto_link(post.body, :all, :target => '_blank') do |text|
|
142
176
|
# truncate(text, 15)
|
143
177
|
# end
|
@@ -150,9 +184,12 @@ module ActionView
|
|
150
184
|
end
|
151
185
|
end
|
152
186
|
|
153
|
-
#
|
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
|
154
191
|
def strip_links(text)
|
155
|
-
text.gsub(/<a
|
192
|
+
text.gsub(/<a\b.*?>(.*?)<\/a>/mi, '\1')
|
156
193
|
end
|
157
194
|
|
158
195
|
# Try to require the html-scanner library
|
@@ -161,22 +198,26 @@ module ActionView
|
|
161
198
|
require 'html/node'
|
162
199
|
rescue LoadError
|
163
200
|
# if there isn't a copy installed, use the vendor version in
|
164
|
-
#
|
201
|
+
# ActionController
|
165
202
|
$:.unshift File.join(File.dirname(__FILE__), "..", "..",
|
166
203
|
"action_controller", "vendor", "html-scanner")
|
167
204
|
require 'html/tokenizer'
|
168
205
|
require 'html/node'
|
169
206
|
end
|
170
207
|
|
171
|
-
VERBOTEN_TAGS = %w(form script) unless defined?(VERBOTEN_TAGS)
|
208
|
+
VERBOTEN_TAGS = %w(form script plaintext) unless defined?(VERBOTEN_TAGS)
|
172
209
|
VERBOTEN_ATTRS = /^on/i unless defined?(VERBOTEN_ATTRS)
|
173
210
|
|
174
|
-
# Sanitizes the
|
211
|
+
# Sanitizes the +html+ by converting <form> and <script> tags into regular
|
175
212
|
# text, and removing all "onxxx" attributes (so that arbitrary Javascript
|
176
|
-
# cannot be executed).
|
177
|
-
# "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.
|
178
216
|
#
|
179
|
-
#
|
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>
|
180
221
|
def sanitize(html)
|
181
222
|
# only do this if absolutely necessary
|
182
223
|
if html.index("<")
|
@@ -192,8 +233,8 @@ module ActionView
|
|
192
233
|
else
|
193
234
|
if node.closing != :close
|
194
235
|
node.attributes.delete_if { |attr,v| attr =~ VERBOTEN_ATTRS }
|
195
|
-
|
196
|
-
node.attributes.delete
|
236
|
+
%w(href src).each do |attr|
|
237
|
+
node.attributes.delete attr if node.attributes[attr] =~ /^javascript:/i
|
197
238
|
end
|
198
239
|
end
|
199
240
|
node.to_s
|
@@ -209,11 +250,11 @@ module ActionView
|
|
209
250
|
html
|
210
251
|
end
|
211
252
|
|
212
|
-
# Strips all HTML tags from the
|
213
|
-
# tokenizer and so
|
214
|
-
#
|
215
|
-
|
216
|
-
|
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?
|
217
258
|
if html.index("<")
|
218
259
|
text = ""
|
219
260
|
tokenizer = HTML::Tokenizer.new(html)
|
@@ -231,32 +272,33 @@ module ActionView
|
|
231
272
|
end
|
232
273
|
end
|
233
274
|
|
234
|
-
#
|
235
|
-
# array every time it is called. This can be used to alternate
|
236
|
-
# for table rows:
|
275
|
+
# Creates a Cycle object whose _to_s_ method cycles through elements of an
|
276
|
+
# array every time it is called. This can be used for example, to alternate
|
277
|
+
# classes for table rows:
|
237
278
|
#
|
238
|
-
#
|
239
|
-
# <tr class="<%= cycle("even", "odd")
|
240
|
-
#
|
279
|
+
# <% @items.each do |item| %>
|
280
|
+
# <tr class="<%= cycle("even", "odd") -%>">
|
281
|
+
# <td>item</td>
|
241
282
|
# </tr>
|
242
|
-
#
|
283
|
+
# <% end %>
|
243
284
|
#
|
244
|
-
# You can use named cycles to
|
245
|
-
#
|
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.
|
246
289
|
#
|
247
|
-
#
|
290
|
+
# <% @items.each do |item| %>
|
248
291
|
# <tr class="<%= cycle("even", "odd", :name => "row_class")
|
249
292
|
# <td>
|
250
|
-
#
|
251
|
-
# <span style="color
|
252
|
-
#
|
253
|
-
# item
|
293
|
+
# <% item.values.each do |value| %>
|
294
|
+
# <span style="color:<%= cycle("red", "green", "blue", :name => "colors") -%>">
|
295
|
+
# value
|
254
296
|
# </span>
|
255
|
-
#
|
256
|
-
#
|
297
|
+
# <% end %>
|
298
|
+
# <% reset_cycle("colors") %>
|
257
299
|
# </td>
|
258
300
|
# </tr>
|
259
|
-
#
|
301
|
+
# <% end %>
|
260
302
|
def cycle(first_value, *values)
|
261
303
|
if (values.last.instance_of? Hash)
|
262
304
|
params = values.pop
|
@@ -273,12 +315,11 @@ module ActionView
|
|
273
315
|
return cycle.to_s
|
274
316
|
end
|
275
317
|
|
276
|
-
# Resets a cycle so that it starts from the first element
|
277
|
-
#
|
318
|
+
# Resets a cycle so that it starts from the first element the next time
|
319
|
+
# it is called. Pass in +name+ to reset a named cycle.
|
278
320
|
def reset_cycle(name = "default")
|
279
321
|
cycle = get_cycle(name)
|
280
|
-
|
281
|
-
cycle.reset
|
322
|
+
cycle.reset unless cycle.nil?
|
282
323
|
end
|
283
324
|
|
284
325
|
class Cycle #:nodoc:
|
@@ -305,42 +346,42 @@ module ActionView
|
|
305
346
|
# guaranteed to be reset every time a page is rendered, so it
|
306
347
|
# uses an instance variable of ActionView::Base.
|
307
348
|
def get_cycle(name)
|
308
|
-
@_cycles = Hash.new
|
349
|
+
@_cycles = Hash.new unless defined?(@_cycles)
|
309
350
|
return @_cycles[name]
|
310
351
|
end
|
311
352
|
|
312
353
|
def set_cycle(name, cycle_object)
|
313
|
-
@_cycles = Hash.new
|
354
|
+
@_cycles = Hash.new unless defined?(@_cycles)
|
314
355
|
@_cycles[name] = cycle_object
|
315
356
|
end
|
316
357
|
|
317
|
-
AUTO_LINK_RE =
|
318
|
-
(
|
319
|
-
<\w+.*?>|
|
320
|
-
[^=!:'"
|
321
|
-
^
|
358
|
+
AUTO_LINK_RE = %r{
|
359
|
+
( # leading text
|
360
|
+
<\w+.*?>| # leading HTML tag, or
|
361
|
+
[^=!:'"/]| # leading punctuation, or
|
362
|
+
^ # beginning of line
|
322
363
|
)
|
323
364
|
(
|
324
|
-
(?:
|
325
|
-
(?:www\.)
|
365
|
+
(?:https?://)| # protocol spec, or
|
366
|
+
(?:www\.) # www.*
|
326
367
|
)
|
327
368
|
(
|
328
|
-
|
329
|
-
|
330
|
-
(
|
369
|
+
[-\w]+ # subdomain or domain
|
370
|
+
(?:\.[-\w]+)* # remaining subdomains or domain
|
371
|
+
(?::\d+)? # port
|
372
|
+
(?:/(?:[~\w%.;-]+)?)* # path
|
373
|
+
(?:\?[\w%&=.;-]+)? # query string
|
374
|
+
(?:\#\w*)? # trailing anchor
|
331
375
|
)
|
332
|
-
([[:punct:]]|\s|<|$)
|
333
|
-
|
376
|
+
([[:punct:]]|\s|<|$) # trailing text
|
377
|
+
}x unless const_defined?(:AUTO_LINK_RE)
|
334
378
|
|
335
379
|
# Turns all urls into clickable links. If a block is given, each url
|
336
|
-
# is yielded and the result is used as the link text.
|
337
|
-
# auto_link_urls(post.body, :all, :target => '_blank') do |text|
|
338
|
-
# truncate(text, 15)
|
339
|
-
# end
|
380
|
+
# is yielded and the result is used as the link text.
|
340
381
|
def auto_link_urls(text, href_options = {})
|
341
382
|
extra_options = tag_options(href_options.stringify_keys) || ""
|
342
383
|
text.gsub(AUTO_LINK_RE) do
|
343
|
-
all, a, b, c, d = $&, $1, $2, $3, $
|
384
|
+
all, a, b, c, d = $&, $1, $2, $3, $4
|
344
385
|
if a =~ /<a\s/i # don't replace URL's that are already linked
|
345
386
|
all
|
346
387
|
else
|
@@ -353,10 +394,6 @@ module ActionView
|
|
353
394
|
|
354
395
|
# Turns all email addresses into clickable links. If a block is given,
|
355
396
|
# each email is yielded and the result is used as the link text.
|
356
|
-
# Example:
|
357
|
-
# auto_link_email_addresses(post.body) do |text|
|
358
|
-
# truncate(text, 15)
|
359
|
-
# end
|
360
397
|
def auto_link_email_addresses(text)
|
361
398
|
text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
|
362
399
|
text = $1
|