Усечь уценку?
У меня есть сайт Rails, где содержание написано в уценке. Я хочу отобразить фрагмент каждого из них со ссылкой "Подробнее...".
Как мне это сделать? Например, простое усечение необработанного текста не сработает.
>> "This is an [example](http://example.com)"[0..25]
=> "This is an [example](http:"
В идеале я хочу позволить автору (необязательно) вставить маркер, чтобы указать, что использовать в качестве "фрагмента", если нет, то это займет 250 слов, и добавить "..." - например.
This article is an example of something or other.
This segment will be used as the snippet on the index page.
^^^^^^^^^^^^^^^
This text will be visible once clicking the "Read more.." link
Маркер можно рассматривать как маркер EOF (который можно игнорировать при отображении полного документа)
Я использую maruku для обработки Markdown (RedCloth очень склонен к Textile, BlueCloth чрезвычайно глючит, и я хотел парсер нативного Ruby, который исключал peg-markdown и RDiscount)
В качестве альтернативы (поскольку Markdown в любом случае переводится в HTML) правильное усечение HTML было бы вариантом, хотя было бы предпочтительнее markdown()
весь документ, только чтобы получить первые несколько строк.
Итак, варианты, которые я могу придумать (в порядке предпочтения)..
- Добавьте опцию "усечь" в парсер maruku, который будет анализировать только первые x слов или до маркера "отрывка".
- Написать / найти синтаксический анализатор Markdown truncate'r
- Написать / найти интеллектуальную функцию усечения HTML
7 ответов
- Написать / найти интеллектуальную функцию усечения HTML
Следующее из http://mikeburnscoder.wordpress.com/2006/11/11/truncating-html-in-ruby/, с некоторыми изменениями, будет корректно обрезать HTML и легко позволит добавлять строку перед закрывающими тегами.
>> puts "<p><b><a href=\"hi\">Something</a></p>".truncate_html(5, at_end = "...")
=> <p><b><a href="hi">Someth...</a></b></p>
Модифицированный код:
require 'rexml/parsers/pullparser'
class String
def truncate_html(len = 30, at_end = nil)
p = REXML::Parsers::PullParser.new(self)
tags = []
new_len = len
results = ''
while p.has_next? && new_len > 0
p_e = p.pull
case p_e.event_type
when :start_element
tags.push p_e[0]
results << "<#{tags.last}#{attrs_to_s(p_e[1])}>"
when :end_element
results << "</#{tags.pop}>"
when :text
results << p_e[0][0..new_len]
new_len -= p_e[0].length
else
results << "<!-- #{p_e.inspect} -->"
end
end
if at_end
results << "..."
end
tags.reverse.each do |tag|
results << "</#{tag}>"
end
results
end
private
def attrs_to_s(attrs)
if attrs.empty?
''
else
' ' + attrs.to_a.map { |attr| %{#{attr[0]}="#{attr[1]}"} }.join(' ')
end
end
end
Вот решение, которое работает для меня с текстилем.
- Преобразуйте это в HTML
- Обрежь это.
Удалите все HTML-теги, которые были разрезаны пополам
html_string.gsub(/<[^>]*$/, "")
Затем использует Hpricot для очистки и закрытия незакрытых тегов.
html_string = Hpricot( html_string ).to_s
Я делаю это помощником, и с кэшированием нет проблем с производительностью.
Вы можете использовать регулярное выражение, чтобы найти строку, состоящую только из символов "^":
markdown_string = <<-eos
This article is an example of something or other.
This segment will be used as the snippet on the index page.
^^^^^^^^^^^^^^^
This text will be visible once clicking the "Read more.." link
eos
preview = markdown_string[0...(markdown_string =~ /^\^+$/)]
puts preview
Вместо того, чтобы пытаться обрезать текст, почему бы не иметь 2 поля ввода, одно для "раскрывающегося объявления" и одно для основных "кишок". Таким образом, ваши авторы будут точно знать, что показывается, без необходимости полагаться на какой-то забавный маркер EOF.
Не уверен, относится ли это к этому случаю, но добавим приведенное ниже решение для полноты картины. Вы можете использовать метод strip_tags, если вы усекаете содержимое Markdown:
truncate(strip_tags(markdown(article.contents)), length: 50)
Источник: http://devblog.boonecommunitynetwork.com/rails-and-markdown/
Более простой вариант, который просто работает:
truncate(markdown(item.description), length: 100, escape: false)
Мне придется согласиться с подходом "двух входов", и создателю контента не нужно будет беспокоиться, так как вы можете изменить фоновую логику, чтобы смешивать два входа в один при отображении полного контента.
full_content = input1 + input2 // perhaps with some complementary html, for a better formatting