zargony.com

#![desc = "Random thoughts of a software engineer"]

Using ruby-gettext with Edge Rails

The more I read about the various new features coming up in Rails 2.0, the more I couldn't resist... and finally, a few days ago, I switched over to Edge Rails with my current project.

Switching to Edge Rails was easily done, however after fireing up /script/server and doing the first request, I was presented with a 500 Internal Server Error.

DISPATCHER FAILSAFE RESPONSE (has cgi) Sun Jul 29 12:43:56 +0200 2007
  Status: 500 Internal Server Error
  You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.\[]
    /usr/lib64/ruby/1.8/cgi.rb:1165:in '\[]'
    /usr/lib64/ruby/gems/1.8/gems/gettext-1.10.0/lib/gettext/locale_cgi.rb:26:in 'system'
    /usr/lib64/ruby/gems/1.8/gems/gettext-1.10.0/lib/gettext/locale.rb:88:in 'system'
    /usr/lib64/ruby/gems/1.8/gems/gettext-1.10.0/lib/gettext/locale.rb:96:in 'default'

After some time of debugging, it turned out to be ruby-gettext, which I use for I18N, that causes a NoMethodError on each request during init-gettext.

What happened here is, that ruby-gettext tries to figure out the current visitor's language by looking for an URL parameter or a cookie named lang or the HTTP Accept header. Looking for the URL parameter seems to be the problem in this case. Locale::SystemCGI.system calls cgi["lang"] to look for the URL parameter, but CGI#[] raises a NoMethodError, because the @params hash does not contain a key named lang. Here are the first few lines of CGI#[] (starting at line 1163 in cgi.rb):

def [](key)                     # cgi.rb:1163
  params = @params[key]         # nil gets assigned to params
  value = params[0]             # raises NoMethodError, because params is nil
  if @multipart
    if value
      return value

The solution

The problem could be fixed by patching cgi.rb of the ruby standard library or locale_cgi.rb of ruby-gettext (like somebody with a similar problem suggested in this mail). However this isn't usually applicable on production servers. Instead, you can work around this problem by modifying CGI#[] from within your rails application temporarily, until the bug is fixed.

With Edge Rails, you don't place your own statements in config/environment.rb anymore. Instead, you can write an initializer file and place it into config/initializers. For ruby-gettext, I created config/initializers/gettext.rb, which contains:

require 'gettext/rails'

To work around the above problem, add the following code to your gettext initializer, right after the require statement:

class CGI
  module QueryExtension
    alias index_without_fix :[]
    def [] (key)
      return nil unless @params[key]
      index_without_fix(key)
    end
  end
end

This modifies CGI#[] to return nil if the requested param does not exist.

Update (2007-10-20): The above problem was fixed in Ruby 1.8.6-p26.

Finally, my application runs with Edge Rails and I can now start to explore all the new stuff (and probably do a lot of refactoring to use those features).

New-style .html.erb templates

Update (2007-07-31): Ruby-gettext is currently unable to recognize new-style .html.erb template files (since it doesn't know that .erb files should be parsed by GetText::ErbParser). I reported this to Masao, the author of ruby-gettext (so it'll be probably fixed in future versions). Since then, you can work around it by modifying your lib/tasks/gettext.rake like this:

require 'gettext/utils'
 # Tell ruby-gettext's ErbParser to parse .erb files as well
 # See also https://zargony.com/2007/07/29/using-ruby-gettext-with-edge-rails
GetText::ErbParser.init(:extnames => ['.rhtml', '.erb'])
desc 'Update pot/po files'
task :updatepo do
  GetText.update_pofiles('messages', Dir.glob("{app,lib}/**/*.{rb,rhtml,erb,rjs}"), 'MyApp')
end
desc 'Create mo-files'
task :makemo do
  GetText.create_mofiles(true, 'po', 'locale')
end

Update (2007-08-01): Masao pointed out, that it's better to use the GetText::ErbParser.init method rather than overriding ErbParser. Thanks Masao.

Update (2008-02-25): .erb files are now parsed by default, so the above fix isn't necessary anymore. Scanning .erb files was added on July 31, 2007 and is included in ruby-gettext 1.90.0 which was released on Feb 02, 2008.