модуль ActionView::Helpers::TextHelper
Модуль 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><a href=\"http://example.com/\">Example</a></p>"
Публичные методы экземпляров
# File actionview/lib/action_view/helpers/text_helper.rb, line 54 def concat(string) output_buffer << string end
Предпочтительный метод вывода текста в ваших представлениях — использование синтаксиса eRuby <%= “текст” %>. Обычные методы puts и print не работают так, как ожидается, в блоке кода eRuby. Если вам абсолютно необходимо вывести текст в блоке кода без вывода (т. е., <% %>), вы можете использовать метод concat.
<%
concat "hello"
# is the equivalent of <%= "hello" %>
if logged_in
concat "Logged in!"
else
concat link_to('login', action: :login)
end
# will either display "Logged in!" or a login link
%> # File actionview/lib/action_view/helpers/text_helper.rb, line 378 def current_cycle(name = "default") cycle = get_cycle(name) cycle.current_value if cycle end
Возвращает текущую строку цикла после начала цикла. Полезно для сложного выделения таблиц или любой другой потребности дизайна, которая требует текущей строки цикла в нескольких местах.
# 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 354
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 Создает объект 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 = x = [{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 175
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 Извлекает выдержку из text, которая соответствует первому вхождению phrase. Опция :radius расширяет выдержку с каждой стороны первого вхождения phrase на количество символов, определенных в :radius (по умолчанию 100). Если радиус выдержки выходит за пределы начала или конца text, то опция :omission (по умолчанию «…») будет соответственно добавлена в начало/конец. Используйте опцию :separator для выбора разделителя. Результирующая строка в любом случае будет очищена. Если phrase не найдено, возвращается nil.
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 132
def highlight(text, phrases, options = {})
text = sanitize(text) if options.fetch(:sanitize, true)
if text.blank? || phrases.blank?
text || ""
else
match = Array(phrases).map do |p|
Regexp === p ? p.to_s : Regexp.escape(p)
end.join("|")
if block_given?
text.gsub(/(#{match})(?![^<]*?>)/i) { |found| yield found }
else
highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
text.gsub(/(#{match})(?![^<]*?>)/i, highlighter)
end
end.html_safe
end Выделяет одну или несколько phrases повсюду в text, вставляя её в строку :highlighter. Подсветчик можно специализировать, передавая :highlighter как строку в одинарных кавычках с \1, куда должна быть вставлена фраза (по умолчанию '<mark>1</mark>'), или передавая блок, который получает каждый сопоставленный термин. По умолчанию text саннитизируется, чтобы предотвратить возможные атаки XSS. Если входные данные надёжны, передача false для :sanitize отключит саннитизацию.
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, 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 230
def pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)
word = if count == 1 || count.to_s =~ /^1(\.0+)?$/
singular
else
plural || singular.pluralize(locale)
end
"#{count || 0} #{word}"
end Попытка склонить слово singular к множественному числу, если count не равно 1. Если plural указано, оно будет использоваться, когда количество больше 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 401 def reset_cycle(name = "default") cycle = get_cycle(name) cycle.reset if cycle end
Сбрасывает цикл, чтобы он начинал с первого элемента в следующий раз, когда он будет вызван. Передайте 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 58 def safe_concat(string) output_buffer.respond_to?(:safe_concat) ? output_buffer.safe_concat(string) : concat(string) end
# File actionview/lib/action_view/helpers/text_helper.rb, line 302
def simple_format(text, html_options = {}, options = {})
wrapper_tag = options.fetch(:wrapper_tag, :p)
text = sanitize(text) 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 Возвращает text преобразованный в HTML с помощью простых правил форматирования. Два или более последовательных символов новой строки (\n\n или \r\n\r\n) считаются абзацем и оборачиваются в теги <p>. Одна новая строка (\n или \r\n) считается разрывом строки и к ней добавляется тег <br
/>. Этот метод не удаляет символы новой строки из text.
Вы можете передать любые HTML-атрибуты в html_options . Они будут добавлены ко всем созданным абзацам.
Опции
-
:sanitize- Еслиfalse, не саннитизируетtext. -
:wrapper_tag- Строка представляющая тег-обёртку, по умолчанию"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>"
# File actionview/lib/action_view/helpers/text_helper.rb, line 94
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 Обрезает заданный text после заданного :length если text длиннее :length (по умолчанию 30). Последние символы будут заменены на :omission (по умолчанию «…») для общей длины, не превышающей :length.
Передайте :separator для обрезки text по естественному разрыву.
Передайте блок, если вы хотите показать дополнительное содержимое, когда текст обрезается.
Результат отмечен как безопасный для HTML, но по умолчанию он экранируется, если :escape не равно false. Следует быть осторожным, если text содержит HTML-теги или сущности, так как обрезка может привести к созданию невалидного HTML (например, несбалансированных или неполных тегов).
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>")
# => "<p>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 wo...<a href="#">Continue</a>"
# File actionview/lib/action_view/helpers/text_helper.rb, line 260
def word_wrap(text, line_width: 80, break_sequence: "\n")
text.split("\n").collect! do |line|
line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1#{break_sequence}").rstrip : line
end * break_sequence
end Обрезает 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
You can also specify a custom +break_sequence+ ("\n" by default)
word_wrap('Once upon a time', line_width: 1, break_sequence: "\r\n")
# => Once\r\nupon\r\na\r\ntime
© 2004–2019 David Heinemeier Hansson
Licensed under the MIT License.