рельсы: получить тизер / отрывок для статьи

Asked
Viewd6154

9

У меня есть страница со списком новостных статей. Чтобы сократить длину страницы, я хочу отобразить только тизер (первые 200 слов / 600 букв статьи), а затем отобразить ссылку «еще ...», при нажатии на которую будет развернута остальная часть статью в формате jQuery / Javascript. Теперь я все это выяснил и даже нашел следующий вспомогательный метод на некоторой странице вставки, который гарантирует, что новостная статья (строка) не будет разрезана прямо в середине слова:

  def shorten (string, count = 30)
    if string.length >= count
      shortened = string[0, count]
      splitted = shortened.split(/\s/)
      words = splitted.length
      splitted[0, words-1].join(" ") + ' ...'
    else
      string
    end
  end
 

Моя проблема заключается в том, что тела новостных статей, которые я получаю из БД, имеют формат HTML. Так что, если мне не повезет, указанный выше помощник нарежет мою строку статьи прямо в середине тега html и вставит туда строку "more ..." (например, между ""), что приведет к повреждению моего html на странице. .

Есть ли способ обойти это или есть плагин, который я могу использовать для создания отрывков / тизеров из строки HTML?

7 ответов

2

Спасибо за ответы! Тем не менее, тем временем я наткнулся на плагин jQuery HTML Truncator , который идеально подходит для моих целей. и переносит усечение на клиентскую сторону. Легче не становится :-)

1

Я бы продезинфицировал HTML и извлек первое предложение. Предположим, у вас есть модель статьи с атрибутом body, содержащим HTML:

 # lib/core_ext/string.rb
class String
  def first_sentence
    self[/(\A[^.|!|?]+)/, 1]
  end
end

# app/models/article.rb
def teaser
  HTML::FullSanitizer.new.sanitize(body).first_sentence
end
 

Это преобразовало бы « Эта - важная статья! А вот и остальная часть статьи». в «Это важная статья».

16

Вы можете использовать комбинацию Sanitize и Усечь .

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

Я выполняю аналогичную задачу, когда у меня есть сообщения в блоге, и я просто хочу показать небольшой отрывок. На мой взгляд, я просто делаю:

 sanitize(truncate(blog_post.body, length: 150))
 

Это удаляет HTML-теги, дает мне первые 150 символов и обрабатывается в представлении, поэтому он удобен для MVC.

Удачи!

3

Мой ответ здесь должен работать. Первоначальный вопрос (ошибся, заданный мной) касался усечения уценки, но в итоге я преобразовал уценку в 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
 
  • oh i like yours, it fixes the problem with tags around the text

    LDomagala11 февраля 2009, 14:01
1

вам придется писать более сложные парсеры, если вы не хотите разделять элементы html посередине. ему нужно будет помнить, находится ли он в середине блока <> и между двумя тегами.

даже если вы это сделаете, у вас все равно будут проблемы. если некоторые помещают всю статью в элемент html, поскольку парсер не может разделить ее где-либо из-за отсутствия закрывающего тега.

Если это вообще возможно, я бы постарался не помещать никаких тегов в статьи или сохранять их в тегах, которые ничего не содержат (не <div> и т. д.). таким образом вам нужно будет только проверить, находитесь ли вы в середине тега, что довольно просто:

   def shorten (string, count = 30)
     if string.length >= count
       shortened = string[0, count]
       splitted = shortened.split(/\s/)
       words = splitted.length
       if(splitted[words-1].include? "<")
         splitted[0,words-2].join(" ") + ' ...'
       else
         splitted[0, words-1].join(" ") + ' ...'
     else
       string
     end   
  end