Butt Biter: Rails ‘truncate’ Method Broken in Ruby 1.8.7

This one threw me for far too many minutes. In ActionView::Helpers::TextHelper the truncate method is broken for Ruby 1.8.7. I’m not exactly sure the version of ruby that it stopped working for, but I was previously on 1.8.6, and I don’t recall it not working. But after I upgraded to the latest patchlevel of 1.8.7, truncate stopped working.

Update 12/2/08: Fixed in Rails 2.2! (read more)

The error that I received was thus:

ActionView::TemplateError (undefined method `length' for #) on line #83 of dashboard/index.html.erb:

Fortunately, someone has submitted a ticket to Rails core. I hope it’s applied in the Rails 2.2.0 release.

Current Method, Done Busted

As of Rails 2.1.0, this is the current method (Rails 2.0.2 is similar, and also broken).

[sourcecode lang=’ruby’]
# File actionpack/lib/action_view/helpers/text_helper.rb, line 49
def truncate(text, length = 30, truncate_string = “…”)
if text
l = length – truncate_string.chars.length
chars = text.chars
(chars.length > length ? chars[0…l] + truncate_string : text).to_s
end
end
[/sourcecode]

The problem is that chars method called on the string object (truncate_string) returns an <Enumerable::Enumerator> object. And according to the Ruby 1.8.7 RDoc, there is no ‘length’ method in Enumerable::Enumerator. To be honest, I can’t find the ‘chars’ method in any of the Ruby 1.8.6 docs or 1.8.7 docs. I guessing that it may have been Rails method at some time.

Fix #1

The fix is to get rid of all the chars methods. On the bug ticket, the author gives his fix, but you are going to have to monkey-patch rails, or change the method name.

[sourcecode lang=’ruby’]
def truncate(text, length = 30, truncate_string = “…”)
if text
l = length – truncate_string.chars.to_a.length
chars = text.chars
(chars.to_a.length > length ? chars.to_a[0…l].to_s + truncate_string : text).to_s
end
end
[/sourcecode]

Fix #2 (mine, tiny bit better?)

I’m not exactly sure why the string needs to be converted to an array, when the length function works just dandy on string objects. So I added this to my application_helper.rb file. Also, in the submitted patch, the result of the ternary is converted to a string (.to_s), when all the resulting methods of the ternary return strings. Unnecessary IMHO and probably left over from when the string object was converted to an enumerable. Note that I started with the Rail 2.0.1 version, so it’s slightly different than if I started with the 2.1.0 version.

[sourcecode lang=’ruby’]
def truncate_string(text, length = 30, truncate_string = ‘…’)
if text.nil? then return end
l = length – truncate_string.length
text.length > length ? text[0…l] + truncate_string : text
end
[/sourcecode]

Yeah, yeah, I could have monkey-patched the TextHelper module, but why tempt fate if you don’t have to. A quick find/replace on ‘truncate’ to ‘truncate_string’ is all that was necessary. Works for me.

Posted in Rails. Tagged with , .

3 Responses

Comments always appreciated...