Rails 4.2 added a new feature called truncate words. It allows you to do things like this.
[ruby]"In a world where everything is awesome".truncate_words(3)
"In a world…"[/ruby]
I wanted to step through this method to look at a few things I found in the code. Here is the full code
[ruby]
def truncate_words(words_count, options = {})
sep = options[:separator] || /\s+/
sep = Regexp.escape(sep.to_s) unless Regexp === sep
if self =~ /\A((?:.+?#{sep}){#{words_count – 1}}.+?)#{sep}.*/m
$1 + (options[:omission] || ‘…’)
else
dup
end
end[/ruby]
The first thing that I noticed is that it recognizes two additional options
:separator allows you to change the thing that splits up words. For instance, you could do something like cut off the string at the third i. That would look like this.
[ruby]"Once i went to india and ate ice".truncate_words(3, {separator: ‘i’})
"Once i went to india and ate …"[/ruby]
There is also :omission, which allows you to add something besides … at the end of the truncation.
Now that we know the options, lets look at the code.
The first two lines setup the separator that is used to count the number of words. The first line either takes it from the options hash passed in or defaults it to a space. The second line converts the separator into regular expression language by escaping it (i.e. adding 1 or more \) UNLESS the separator is already of class Regexp. I suspect the unless is so that we don’t get double escaping that isn’t handled already in the Regexp library.
[ruby]sep = options[:separator] || /\s+/
sep = Regexp.escape(sep.to_s) unless Regexp === sep[/ruby]
Now that we have our separator Regex’ed out, we check the string for it’s match. Self is going to be some sort of string that responds to matching a regex. If it finds a match the truncate_words will return the match (which is stored in the $1 global variable) along with either the specified omission or ‘…’ at the end. If we don’t get enough matches, then the string is just duplicated out.
[ruby]
if self =~ /\A((?:.+?#{sep}){#{words_count – 1}}.+?)#{sep}.*/m
$1 + (options[:omission] || ‘…’)
else
dup
end
[/ruby]
Something I’m wondering about is why do they duplicate the string at the end if no match is found? Wouldn’t just sending out self work and not eat up a tiny bit more memory?
In my next post I analyze the regex that is used to do matching on this string.