跳至内容 跳至搜索

Action View 文本帮助器

The TextHelper 模块提供了一组用于过滤、格式化和转换字符串的方法,可以减少视图中的内联 Ruby 代码。这些帮助器方法扩展了 Action View,使它们可以在模板文件中调用。

清理

大多数生成 HTML 输出的文本帮助器默认情况下会对给定的输入进行清理,但不会对其进行转义。这意味着 HTML 标签将出现在页面中,但所有恶意代码将被删除。让我们看一些使用 simple_format 方法的示例

simple_format('<a href="http://example.com/">Example</a>')
# => "<p><a href=\"http://example.com/\">Example</a></p>"

simple_format('<a href="javascript:alert(\'no!\')">Example</a>')
# => "<p><a>Example</a></p>"

如果要转义所有内容,应在调用文本帮助器之前调用 h 方法。

simple_format h('<a href="http://example.com/">Example</a>')
# => "<p>&lt;a href=\"http://example.com/\"&gt;Example&lt;/a&gt;</p>"
方法
C
E
H
P
R
S
T
W
包含的模块

实例公有方法

concat(string)

在视图中输出文本的首选方法是使用 <%= "text" %> eRuby 语法。常规的 putsprint 方法在 eRuby 代码块中无法按预期工作。如果您必须在非输出代码块(即 <% %>)中输出文本,可以使用 concat 方法。

<% concat "hello" %> is equivalent to <%= "hello" %>

<%
   unless signed_in?
     concat link_to("Sign In", action: :sign_in)
   end
%>

is equivalent to

<% unless signed_in? %>
  <%= link_to "Sign In", action: :sign_in %>
<% end %>
# File actionview/lib/action_view/helpers/text_helper.rb, line 63
def concat(string)
  output_buffer << string
end

current_cycle(name = "default")

在循环开始后返回当前循环字符串。对于需要在多个地方使用当前循环字符串的复杂表格突出显示或任何其他设计需求很有用。

<%# Alternate background colors %>
<% @items = [1,2,3,4] %>
<% @items.each do |item| %>
  <div style="background-color:<%= cycle("red","white","blue") %>">
    <span style="background-color:<%= current_cycle %>"><%= item %></span>
  </div>
<% end %>
# File actionview/lib/action_view/helpers/text_helper.rb, line 454
def current_cycle(name = "default")
  cycle = get_cycle(name)
  cycle.current_value if cycle
end

cycle(first_value, *values)

创建一个 Cycle 对象,其 to_s 方法在每次调用时循环遍历数组的元素。例如,这可以用于交替表格行的类。可以使用命名循环来允许在循环中嵌套。将 Hash 作为最后一个参数传递,其中包含一个 :name 键,将创建一个命名循环。对于没有 :name 键的循环,默认名称为 "default"。您可以通过调用 reset_cycle 并传递循环名称来手动重置循环。可以使用 current_cycle 方法随时获取当前循环字符串。

 <%# Alternate CSS classes for even and odd numbers... %>
 <% @items = [1,2,3,4] %>
 <table>
 <% @items.each do |item| %>
   <tr class="<%= cycle("odd", "even") -%>">
     <td><%= item %></td>
   </tr>
 <% end %>
 </table>

 <%# Cycle CSS classes for rows, and text colors for values within each row %>
 <% @items = [
   { first: "Robert", middle: "Daniel", last: "James" },
   { first: "Emily", middle: "Shannon", maiden: "Pike", last: "Hicks" },
   { first: "June", middle: "Dae", last: "Jones" },
 ] %>
 <% @items.each do |item| %>
   <tr class="<%= cycle("odd", "even", name: "row_class") -%>">
     <td>
       <% item.values.each do |value| %>
         <%# Create a named cycle "colors" %>
         <span style="color:<%= cycle("red", "green", "blue", name: "colors") -%>">
           <%= value %>
         </span>
       <% end %>
       <% reset_cycle("colors") %>
     </td>
  </tr>
<% end %>
# File actionview/lib/action_view/helpers/text_helper.rb, line 430
def cycle(first_value, *values)
  options = values.extract_options!
  name = options.fetch(:name, "default")

  values.unshift(*first_value)

  cycle = get_cycle(name)
  unless cycle && cycle.values == values
    cycle = set_cycle(name, Cycle.new(*values))
  end
  cycle.to_s
end

excerpt(text, phrase, options = {})

text 中提取 phrase 的第一次出现及其周围的文本。如果结果的开始/结束与 text 的开始/结束不一致,则会添加省略号标记。无论如何,结果始终会被剥离。如果找不到 phrase,则返回 nil

选项

:radius

要包含在结果中的 phrase 周围的字符数(或标记 - 请参阅 :separator 选项)。默认为 100。

:omission

当摘要的开始/结束与 text 的开始/结束不一致时要添加的标记。默认为 "..."

:separator

用于 :radius 的标记之间的分隔符。默认为 "",将每个字符视为一个标记。

示例

excerpt('This is an example', 'an', radius: 5)
# => "...s is an exam..."

excerpt('This is an example', 'is', radius: 5)
# => "This is a..."

excerpt('This is an example', 'is')
# => "This is an example"

excerpt('This next thing is an example', 'ex', radius: 2)
# => "...next..."

excerpt('This is also an example', 'an', radius: 8, omission: '<chop> ')
# => "<chop> is also an example"

excerpt('This is a very beautiful morning', 'very', separator: ' ', radius: 1)
# => "...a very beautiful..."
# File actionview/lib/action_view/helpers/text_helper.rb, line 235
def excerpt(text, phrase, options = {})
  return unless text && phrase

  separator = options.fetch(:separator, nil) || ""
  case phrase
  when Regexp
    regex = phrase
  else
    regex = /#{Regexp.escape(phrase)}/i
  end

  return unless matches = text.match(regex)
  phrase = matches[0]

  unless separator.empty?
    text.split(separator).each do |value|
      if value.match?(regex)
        phrase = value
        break
      end
    end
  end

  first_part, second_part = text.split(phrase, 2)

  prefix, first_part   = cut_excerpt_part(:first, first_part, separator, options)
  postfix, second_part = cut_excerpt_part(:second, second_part, separator, options)

  affix = [first_part, separator, phrase, separator, second_part].join.strip
  [prefix, affix, postfix].join
end

highlight(text, phrases, options = {}, &block)

通过使用突出显示字符串对它们进行格式化,突出显示 textphrases 的出现。phrases 可以是一个或多个字符串或正则表达式。结果将被标记为 HTML 安全。默认情况下,text 会在突出显示之前进行清理,以防止可能的 XSS 攻击。

如果指定了块,则将使用它而不是突出显示字符串。phrases 的每次出现都将传递给块,并且其返回值将被插入到最终结果中。

选项

:highlighter

突出显示字符串。使用 \1 作为短语的占位符,类似于 +String#sub+。默认为 "<mark>\1</mark>"。如果指定了块,则忽略此选项。

:sanitize

是否在突出显示之前清理 text。默认为 true。

示例

highlight('You searched for: rails', 'rails')
# => "You searched for: <mark>rails</mark>"

highlight('You searched for: rails', /for|rails/)
# => "You searched <mark>for</mark>: <mark>rails</mark>"

highlight('You searched for: ruby, rails, dhh', 'actionpack')
# => "You searched for: ruby, rails, dhh"

highlight('You searched for: rails', ['for', 'rails'], highlighter: '<em>\1</em>')
# => "You searched <em>for</em>: <em>rails</em>"

highlight('You searched for: rails', 'rails', highlighter: '<a href="search?q=\1">\1</a>')
# => "You searched for: <a href=\"search?q=rails\">rails</a>"

highlight('You searched for: rails', 'rails') { |match| link_to(search_path(q: match)) }
# => "You searched for: <a href=\"search?q=rails\">rails</a>"

highlight('<a href="javascript:alert(\'no!\')">ruby</a> on rails', 'rails', sanitize: false)
# => "<a href=\"javascript:alert('no!')\">ruby</a> on <mark>rails</mark>"
# File actionview/lib/action_view/helpers/text_helper.rb, line 174
def highlight(text, phrases, options = {}, &block)
  text = sanitize(text) if options.fetch(:sanitize, true)

  if text.blank? || phrases.blank?
    text || ""
  else
    patterns = Array(phrases).map { |phrase| Regexp === phrase ? phrase : Regexp.escape(phrase) }
    pattern = /(#{patterns.join("|")})/i
    highlighter = options.fetch(:highlighter, '<mark>\1</mark>') unless block

    text.scan(/<[^>]*|[^<]+/).each do |segment|
      if !segment.start_with?("<")
        if block
          segment.gsub!(pattern, &block)
        else
          segment.gsub!(pattern, highlighter)
        end
      end
    end.join
  end.html_safe
end

pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)

尝试将 singular 单词复数化,除非 count 为 1。如果提供了 plural,则在 count > 1 时将使用它,否则它将使用 Inflector 为给定的区域设置确定复数形式,该区域设置默认为 I18n.locale

该单词将使用为区域设置定义的规则进行复数化(您必须为英语以外的语言定义自己的词形变化规则)。参见 ActiveSupport::Inflector.pluralize

pluralize(1, 'person')
# => "1 person"

pluralize(2, 'person')
# => "2 people"

pluralize(3, 'person', plural: 'users')
# => "3 users"

pluralize(0, 'person')
# => "0 people"

pluralize(2, 'Person', locale: :de)
# => "2 Personen"
# File actionview/lib/action_view/helpers/text_helper.rb, line 290
def pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)
  word = if count == 1 || count.to_s.match?(/^1(\.0+)?$/)
    singular
  else
    plural || singular.pluralize(locale)
  end

  "#{count || 0} #{word}"
end

reset_cycle(name = "default")

重置循环,以便下次调用时从第一个元素开始。传入 name 以重置命名循环。

<%# Alternate CSS classes for even and odd numbers... %>
<% @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]] %>
<table>
<% @items.each do |item| %>
  <tr class="<%= cycle("even", "odd") -%>">
      <% item.each do |value| %>
        <span style="color:<%= cycle("#333", "#666", "#999", name: "colors") -%>">
          <%= value %>
        </span>
      <% end %>

      <% reset_cycle("colors") %>
  </tr>
<% end %>
</table>
# File actionview/lib/action_view/helpers/text_helper.rb, line 477
def reset_cycle(name = "default")
  cycle = get_cycle(name)
  cycle.reset if cycle
end

safe_concat(string)

# File actionview/lib/action_view/helpers/text_helper.rb, line 67
def safe_concat(string)
  output_buffer.respond_to?(:safe_concat) ? output_buffer.safe_concat(string) : concat(string)
end

simple_format(text, html_options = {}, options = {})

返回使用简单格式化规则转换为 HTML 的 text。两个或更多个连续的换行符 (\n\n\r\n\r\n) 被视为一个段落,并用 <p> 标签包装。一个换行符 (\n\r\n) 被视为一个换行符,并追加一个 <br /> 标签。此方法不会从 text 中删除换行符。

可以将任何 HTML 属性传递给 html_options。这些将被添加到所有创建的段落中。

选项

  • :sanitize - 如果为 false,则不会清理 text

  • :sanitize_options - 您要添加到清理中的任何其他选项。

  • :wrapper_tag - String 代表包装标签,默认为 "p"

示例

my_text = "Here is some basic text...\n...with a line break."

simple_format(my_text)
# => "<p>Here is some basic text...\n<br />...with a line break.</p>"

simple_format(my_text, {}, wrapper_tag: "div")
# => "<div>Here is some basic text...\n<br />...with a line break.</div>"

more_text = "We want to put a paragraph...\n\n...right there."

simple_format(more_text)
# => "<p>We want to put a paragraph...</p>\n\n<p>...right there.</p>"

simple_format("Look ma! A class!", class: 'description')
# => "<p class='description'>Look ma! A class!</p>"

simple_format("<blink>Unblinkable.</blink>")
# => "<p>Unblinkable.</p>"

simple_format("<blink>Blinkable!</blink> It's true.", {}, sanitize: false)
# => "<p><blink>Blinkable!</blink> It's true.</p>"

simple_format("<a target=\"_blank\" href=\"http://example.com\">Continue</a>", {}, { sanitize_options: { attributes: %w[target href] } })
# => "<p><a target=\"_blank\" href=\"http://example.com\">Continue</a></p>"
# File actionview/lib/action_view/helpers/text_helper.rb, line 376
def simple_format(text, html_options = {}, options = {})
  wrapper_tag = options[:wrapper_tag] || "p"

  text = sanitize(text, options.fetch(:sanitize_options, {})) if options.fetch(:sanitize, true)
  paragraphs = split_paragraphs(text)

  if paragraphs.empty?
    content_tag(wrapper_tag, nil, html_options)
  else
    paragraphs.map! { |paragraph|
      content_tag(wrapper_tag, raw(paragraph), html_options)
    }.join("\n\n").html_safe
  end
end

truncate(text, options = {}, &block)

如果 text 长于指定的 :length,则截断 text。如果截断了 text,则会将省略号标记追加到结果中,其总长度不超过 :length

您还可以传递一个块来渲染并在截断 text 后将额外的内容追加到省略号标记。但是,此内容 *可能* 会导致总长度超过 :length 个字符。

除非指定了 escape: false,否则结果将被转义。无论如何,结果将被标记为 HTML 安全。如果 text 可能包含 HTML 标签或实体,则应谨慎,因为截断可能会产生无效的 HTML,例如不平衡或不完整的标签。

选项

:length

应返回的最大字符数,不包括来自块的任何额外内容。默认为 30。

:omission

截断后要追加的字符串。默认为 "..."

:separator

用于查找截断断点的字符串或正则表达式。默认情况下,截断可以在 text 中的任何字符处发生。

:escape

是否转义结果。默认为 true。

示例

truncate("Once upon a time in a world far far away")
# => "Once upon a time in a world..."

truncate("Once upon a time in a world far far away", length: 17)
# => "Once upon a ti..."

truncate("Once upon a time in a world far far away", length: 17, separator: ' ')
# => "Once upon a..."

truncate("And they found that many people were sleeping better.", length: 25, omission: '... (continued)')
# => "And they f... (continued)"

truncate("<p>Once upon a time in a world far far away</p>")
# => "&lt;p&gt;Once upon a time in a wo..."

truncate("<p>Once upon a time in a world far far away</p>", escape: false)
# => "<p>Once upon a time in a wo..."

truncate("Once upon a time in a world far far away") { link_to "Continue", "#" }
# => "Once upon a time in a world...<a href=\"#\">Continue</a>"
# File actionview/lib/action_view/helpers/text_helper.rb, line 122
def truncate(text, options = {}, &block)
  if text
    length  = options.fetch(:length, 30)

    content = text.truncate(length, options)
    content = options[:escape] == false ? content.html_safe : ERB::Util.html_escape(content)
    content << capture(&block) if block_given? && text.length > length
    content
  end
end

word_wrap(text, line_width: 80, break_sequence: "\n")

text 包装成不超过 line_width 宽度行的行。此方法在第一个不超过 line_width(默认值为 80)的空白字符处断开。

word_wrap('Once upon a time')
# => "Once upon a time"

word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...')
# => "Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\na successor to the throne turned out to be more trouble than anyone could have\nimagined..."

word_wrap('Once upon a time', line_width: 8)
# => "Once\nupon a\ntime"

word_wrap('Once upon a time', line_width: 1)
# => "Once\nupon\na\ntime"

您还可以指定自定义 break_sequence(默认值为“n”)。

word_wrap('Once upon a time', line_width: 1, break_sequence: "\r\n")
# => "Once\r\nupon\r\na\r\ntime"
# File actionview/lib/action_view/helpers/text_helper.rb, line 320
def word_wrap(text, line_width: 80, break_sequence: "\n")
  return +"" if text.empty?

  # Match up to `line_width` characters, followed by one of
  #   (1) non-newline whitespace plus an optional newline
  #   (2) the end of the string, ignoring any trailing newlines
  #   (3) a newline
  #
  # -OR-
  #
  # Match an empty line
  pattern = /(.{1,#{line_width}})(?:[^\S\n]+\n?|\n*\Z|\n)|\n/

  text.gsub(pattern, "\\1#{break_sequence}").chomp!(break_sequence)
end