gettext/rails が動かない

結論から言うと、Rubycgi の仕様と、Railscgi の仕様がミスマッチを起こしているようです。Ruby のバージョンアップ(1.8.6-p26以上)をすることで解決できます。Rubyのバージョンアップなんてやだよ!な人は原因に関しては続き参照してしかるべき対処を。

rescue_action_locally を呼び出しているところで、render_file を呼び出しているわけだが、再度Exceptionが投げられてしまう。

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/local/lib/ruby/1.8/cgi.rb:1166:in `[]'
    /usr/local/lib/ruby/gems/1.8/gems/gettext-1.10.0/lib/gettext/locale_cgi.rb:26:in `system'
    /usr/local/lib/ruby/gems/1.8/gems/gettext-1.10.0/lib/gettext/locale.rb:88:in `system'
    /usr/local/lib/ruby/gems/1.8/gems/gettext-1.10.0/lib/gettext/locale.rb:96:in `default'
    /usr/local/lib/ruby/gems/1.8/gems/gettext-1.10.0/lib/gettext/rails.rb:276:in `render_file'

該当は、gettext/rails で再定義しているrender_fileのここ。

      [locale.to_general, locale.to_s, locale.language, Locale.default.language].each do |v|

ここでqueryやheaderに何もLocale情報を渡さなかった場合に local.default が呼び出されているわけだが、locale.rb には

  def default
    @@default ? @@default : system
  end

とある。しかし、ここは問題がなさそうで、local_cgi.rb の

      if ret = cgi_["lang"] and ret.size > 0


が例外発生元。cgi_["lang"] が悪いらしい。cgi.rb を見ると、

    def [](key)
      params = @params[key]
      value = params[0]
      ...

となっており、value = params[0] が The error occurred while evaluating nil.[] を発生させている。ん?と思って、cgi.rb をよく読む。こんな記述がある。

# For instance, suppose the request contains the parameter 
# "favourite_colours" with the multiple values "blue" and "green".  The
# following behaviour would occur:
#
#   cgi.params["favourite_colours"]  # => ["blue", "green"]
#   cgi["favourite_colours"]         # => "blue"
#
# If a parameter does not exist, the former method will return an empty
# array, the latter an empty string.  The simplest way to test for existence
# of a parameter is by the #has_key? method.

つまり、cgi["lang"]で例外が発生する方がおかしい。まず、Rails 1.2.3 と 2.0.2 で

puts cgi.has_key?("lang")
puts cgi["lang"]

をして、langキーがないときに、Rails 2.0.X でのみnil例外が発生することを確認。なんでやねん!、というわけで、Railsデバッグ開始。どうも、Rails 2.0 でQueryExtensionによるinitizlie_query()が上書きされているのがあやしい。@params を初期化する重要なメソッド。

Rails 2.0 による initialize_query()

    def initialize_query
      # Fix some strange request environments.
      env_table['REQUEST_METHOD'] ||= 'GET'

      # POST assumes missing Content-Type is application/x-www-form-urlencoded.
      if env_table['CONTENT_TYPE'].blank? && env_table['REQUEST_METHOD'] == 'POST'
        env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
      end

      @cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE'])
      @params = {}
    end

で、cgi.rb の initialize_query()

    def initialize_query()
      if ("POST" == env_table['REQUEST_METHOD']) and
         %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(env_table['CONTENT_TYPE'])
        boundary = $1.dup
        @multipart = true
        @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
      else
        @multipart = false
        @params = CGI::parse(
                    case env_table['REQUEST_METHOD']
                    when "GET", "HEAD"
                      if defined?(MOD_RUBY)
                        Apache::request.args or ""
                      else
                        env_table['QUERY_STRING'] or ""
                      end
                    when "POST"
                      stdinput.binmode if defined? stdinput.binmode
                      stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
                    else
                      read_from_cmdline
                    end
                  )
      end

      @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
    end

ん、GETの挙動が違う。。cgi.rb の CGI::parse では、Hash.new([].freeze)で初期化しているハッシュなのでキーがない場合は空の配列を返す。Rails 2.0のほうは {} で初期化しているだけなので、cgi["key"] すると必ず例外が起こるようだ。Railsが悪いように見える。

で、Rails と GetText/Rails のどっち直そうかなぁと思ったところでググる

http://ko.meadowy.net/~nay/?Rails2.0.1%A5%E1%A5%E2

う。おおばさん。

GetText で 500 Internal Server Error

が出た。むとう様に情報をいただいて解決。Ruby 1.8.6 p26 より前で出る現象だった。Ruby のバージョンアップで解決。

* http://d.hatena.ne.jp/craccho/20071210
* http://zargony.com/2007/07/29/using-ruby-gettext-with-edge-rails/

あれ、1.8.6-p26 で解決するのかい。ここまできたら、直したやつみないと気が済まないので、http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/tags/v1_8_6_26/lib/cgi.rb?revision=12340&view=markup を参照すると、

    def [](key)
      params = @params[key]
      return '' unless params

ruby-dev(http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/30740)にはエラーになるとあるんだが。@params をちゃんと Hash.new([].freeze)でキーが存在しない場合のデフォルトを与えたHashにしてあげればエラーにならない気がする。cgi.params["key"] = nil の場合は想定してないのかな?

まぁ、うだうだいってても先に進まないので、とりあえず、Ruby 1.8.6-p26以降にする、という形にした。