2009-02-11 16 views
8

Tengo una página que enumerará artículos de noticias. Para reducir la longitud de la página, solo quiero mostrar un avance (las primeras 200 palabras/600 letras del artículo) y luego mostrar un enlace "más ..." que, al hacer clic, expandirá el resto del artículo en una forma jQuery/Javascript. Ahora, tengo todo lo que descubrí e incluso encontré el siguiente método de ayuda en una página de pegar, que asegurará que el artículo de noticias (cadena) no esté recortado justo en medio de una palabra:rails: obtener un avance/extracto de un artículo

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 

El problema que tengo es que los cuerpos de los artículos de noticias que obtengo de la BD tienen formato HTML. Entonces, si no tengo suerte, el asistente anterior cortará mi cadena de artículos justo en el medio de una etiqueta html e insertará la cadena "más ..." allí (por ejemplo, entre ""), lo que dañará mi html en la página .

¿Hay alguna forma de evitar esto o hay un complemento que pueda usar para generar extractos/avances de una cadena HTML?

Respuesta

2

¡Muchas gracias por sus respuestas! Sin embargo, mientras tanto tropecé con el jQuery HTML Truncator plugin, que se adapta perfectamente a mis propósitos y cambia el truncamiento al lado del cliente.No es más fácil :-)

1

tendrías que escribir un analizador sintáctico más complejo si no quieres dividirlo en el medio de los elementos html. Tendría que recordar si está en el medio de un bloque <> y si está entre dos etiquetas.

incluso si lo hiciera, aún tendría problemas. si algunos ponen el artículo completo en un elemento html, ya que el analizador no pudo dividirlo en ningún lado, debido a la etiqueta de cierre faltante.

si es posible, trataría de no poner ninguna etiqueta en los artículos o mantenerla en las etiquetas que no contienen nada (no <div> y así sucesivamente). de esa manera usted sólo tiene que comprobar si se encuentra en medio de una etiqueta que es bastante simple:

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 
3

My answer here debe hacer el trabajo. La pregunta original (error, preguntada por mí) fue sobre truncar el descuento, pero terminé convirtiendo el descuento en HTML y luego truncar eso, así que debería funcionar.

Por supuesto, si su sitio recibe mucho tráfico, debe almacenar en caché el fragmento (¿quizás cuando se crea o actualiza la publicación, puede almacenar el extracto en la base de datos?), Esto también significa que podría permitir al usuario modificar o entrar en su propio extracto

uso:

>> puts "<p><b><a href=\"hi\">Something</a></p>".truncate_html(5, at_end = "...") 
=> <p><b><a href="hi">Someth...</a></b></p> 

..y el código (copiado de la otra respuesta):

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 
+0

oh me gusta el tuyo, soluciona el problema con las etiquetas alrededor del texto – LDomagala

15

Se puede utilizar una combinación de Sanitize y Truncate.

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

Estoy haciendo una tarea similar en la que tengo entradas de blog y solo quiero mostrar un extracto rápido. Así que en mi opinión, simplemente hago:

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

que despoja a las etiquetas HTML, me da los primeros 150 caracteres y se maneja en la vista de lo que es amigable MVC.

¡Buena suerte!

+5

Esto podría funcionar, pero debes desinfectar y luego truncar. Si trunca y luego desinfecta, puede truncar en el medio de una etiqueta HTML y desinfectar dejará la etiqueta parcial visible. –

1

Habría desinfectado el HTML y extraído la primera frase. Asumiendo que tiene un modelo de artículo, con un atributo de 'cuerpo' que contiene el código 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 

Esto convertiría a "< b> Esta </b> es un < em> importante </em> Artículo Y! aquí está el resto del artículo ". en "Este es un artículo importante".

+0

Patch de mono Cadena para esto es un poco excesiva ... – DGM

0

que resolvieron este usando la solución siguiente

Instalar joya 'sanear'

gem install sanitize 

y usados ​​siguiente código, aquí cuerpo es un texto que contiene las etiquetas HTML.

<%= content_tag :div, Sanitize.clean(truncate(body, length: 200, separator: ' ', omission: "... #{ link_to '(continue)', '#' }"), Sanitize::Config::BASIC).html_safe %> 

Da un extracto con html válido. Espero que ayude a alguien.

0

Ahora hay una gema llamada HTMLTruncator que se ocupa de esto por usted. Lo he usado para mostrar extractos de publicaciones y similares, y es muy robusto.

Cuestiones relacionadas