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).
<br /> # File actionpack/lib/action_view/helpers/text_helper.rb, line 49<br /> def truncate(text, length = 30, truncate_string = "...")<br /> if text<br /> l = length - truncate_string.chars.length<br /> chars = text.chars<br /> (chars.length > length ? chars[0...l] + truncate_string : text).to_s<br /> end<br /> end<br />
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.
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.
<br /> def truncate(text, length = 30, truncate_string = "...")<br /> if text<br /> l = length - truncate_string.chars.to_a.length<br /> chars = text.chars<br /> (chars.to_a.length > length ? chars.to_a[0...l].to_s + truncate_string : text).to_s<br /> end<br /> end<br />
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.
<br /> def truncate_string(text, length = 30, truncate_string = '...')<br /> if text.nil? then return end<br /> l = length - truncate_string.length<br /> text.length > length ? text[0...l] + truncate_string : text<br /> end<br />
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.