Reputation: 15129
I wonder if there's a plugin to enable a sort of smart truncation. I need to truncate my text with a precision of a word or of a sentence.
For example:
Post.my_message.smart_truncate(
"Once upon a time in a world far far away. And they found that many people
were sleeping better.", :sentences => 1)
# => Once upon a time in a world far far away.
or
Post.my_message.smart_truncate(
"Once upon a time in a world far far away. And they found that many people
were sleeping better.", :words => 12)
# => Once upon a time in a world far far away. And they ...
Upvotes: 10
Views: 4749
Reputation: 81
This will truncate at the word boundary based on the char_limit length specified. So it will not truncate the sentence at weird places
def smart_truncate(text, char_limit)
size = 0
text.split().reject do |token|
size += token.size() + 1
size > char_limit
end.join(' ') + ( text.size() > char_limit ? ' ' + '...' : '' )
end
Upvotes: 8
Reputation: 2320
The gem truncate_html does this job. It also can skip over pieces of HTML – which can be quiet useful – and offers the possibility to customize the word boundary regex. Furthermore the defaults for all parameters can be configured eg in your config/environment.rb
.
Example:
some_html = '<ul><li><a href="http://whatever">This is a link</a></li></ul>'
truncate_html(some_html, :length => 15, :omission => '...(continued)')
=> <ul><li><a href="http://whatever">This...(continued)</a></li></ul>
Upvotes: 1
Reputation: 449
Working for quite a while now on some updates with different projects and I came up with these refinements of the code that seem much more usable in real life scenarios.
def smart_truncate_characters(text, char_limit)
text = text.to_s
text = text.squish
size = 0
new_text = text.mb_chars.split().reject do |token|
size+=token.size()
size>char_limit
end.join(" ")
if size > char_limit
return new_text += '…'
else
return new_text
end
end
def smart_truncate_sentences(text, sentence_limit)
text = text.to_s
text = text.squish
size = 0
arr = text.mb_chars.split(/(?:\.|\?|\!)(?= [^a-z]|$)/)
arr = arr[0...sentence_limit]
new_text = arr.join(".")
new_text += '.'
end
def smart_truncate(text, sentence_limit, char_limit)
text = smart_truncate_sentences(text, sentence_limit)
text = smart_truncate_characters(text, char_limit)
end
Upvotes: 0
Reputation: 449
Nice helper. Since I had a different experience I did change it so that it stops on the last word and work with character limit. I think this is much more real world scenario in most apps.
Update: Took the code above and updated it a bit. Seemed much nicer approach for old ruby and works with utf8.
def smart_truncate(text, char_limit)
text = text.squish
size = 0
text.mb_chars.split().reject do |token|
size+=token.size()
size>char_limit
end.join(" ")
end
Upvotes: 0
Reputation: 52316
I haven't seen such a plugin, but there was a similar question that could serve as the basis for a lib or helper function.
The way you show the function seems to put it as an extension to String: unless you really want to be able to do this outside of a view, I'd be inclined to go for a function in application_helper.rb
. Something like this, perhaps?
module ApplicationHelper
def smart_truncate(s, opts = {})
opts = {:words => 12}.merge(opts)
if opts[:sentences]
return s.split(/\.(\s|$)+/)[0, opts[:sentences]].map{|s| s.strip}.join('. ') + '.'
end
a = s.split(/\s/) # or /[ ]+/ to only split on spaces
n = opts[:words]
a[0...n].join(' ') + (a.size > n ? '...' : '')
end
end
smart_truncate("a b c. d e f. g h i.", :sentences => 2) #=> "a b c. d e f."
smart_truncate("apple blueberry cherry plum", :words => 3) #=> "apple blueberry cherry..."
Upvotes: 20