※「原因」に加筆し,タイトルの「Windows」を「Windows 7」に変えた(2015/02/04)
Rails アプリが軒並み死んだ
ある日突然,Rails がエラーを吐くようになった。
ExecJS::ProgramError TypeError: オブジェクトでサポートされていないプロパティまたはメソッドです。
ちなみに英語だと
ExecJS::ProgramError TypeError: Object doesn't support this property or method
のようだ。
bundle update
をやったタイミングなのかどうか当初は分らなかったが,その日あたりからあらゆる Rails アプリが動かなくなっていった。
現象としては,app/assets/javascripts
に *.coffee
なファイルが一つでも入っているとエラーが出る。
ただ,「ある日突然」よりも前に *.coffee
からコンパイルされて出来た JavaScript が tmp/cache
に残っていて,生きている(元の *.coffee
が変更されていない)限りはエラーは出ないようだった。
CentOS 上では大丈夫だった。
異常が起こったのはこんな環境:
- Windows 7
- ruby 2.1.5p273 (2014-11-13 revision 48405) [i386-mingw32]
- Rails 4.2, 4.1.8
CoffeeScript が 1.8.0 から 1.9.0 になってた
Rails がエラーを吐くようになったのは,どうも 1 月末あたりっぽい。
そこで,この頃にアップデートした gem が何かを調べてみた。すると,coffee-script-source が 1 月 29 日に 1.8.0 から 1.9.0 に上がってた。これが怪しい。
この gem は,CoffeeScript の処理系を提供するためのものらしい。つまり,CoffeeScript スクリプトを読み込んで JavaScript スクリプトに変換してくれるコンパイラー(JavaScript で書かれている)がこの gem の中に入っている。
gem のバージョン番号は CoffeeScript のバージョン番号に合わせてあるようだ。
coffee-script-source を 1.8.0 に戻す
Rails の Gemfile には coffee-script-source はあらわには書かれていない。
Gemfile に書かれているのは coffee-rails で,こいつが coffee-script に依存し,coffee-script が coffee-script-source に依存しているのだった。
そこで,Gemfile にあらわに
gem 'coffee-script-source', '1.8.0'
と追記してやると,正常に動くようになった。
ふぅ。この問題で半日以上つぶれた。
原因
TypeError が出た原因は私には分らない。
CoffeeScript 1.9.0 で何か非互換な変更が行われたのだろう。
Windows で問題が起こり,CentOS で起こらなかったのは,JavaScript の処理系の違いなのかな。
Rails は CoffeeScript を処理するため,JavaScript の処理系を必要とする。実際に JavaScript を実行させるのは ExecJS という gem の役目らしい。
ExecJS はその環境で使える JavaScript の処理系を自動的に選んで動かしてくれるのだとか。
Windows と CentOS で選ばれた JavaScript 処理系が違ったのではないかと思うが,詳細は分らない。
ともかく Rails は関係なくて,
gem "coffee-script-source", "1.9.0"
require "coffee-script"
p CoffeeScript.compile("x=1")
という簡単なコードでエラーが再現できることが分った。(1.9.0
を 1.8.0
にするとエラーは出ない)
追記:もうちょっと分った(2015/02/04)
TypeError の直接の原因は,CoffeeScript 1.9.0 で Object.create
を使っていることっぽい。1.8.0 ではこれは使っていなかった。
ちなみにブラウザーの JavaScript エンジンで Object.create
がサポートされたのは,Firefox なら 4.0,InternetExplorer なら 9.0(いずれも 2011 年リリース)から。
ということは,話がそれるけど,ブラウザー上で CoffeeScript の処理系を直接動かすような使い方をすると,IE8 以下は切り捨てることになるのだろう。
それから,ExecJS が Windows の cscript というコマンドを使って OS の JScript 処理系を呼び出していることも分った。cscript
に //E:jscript
というパラメーターを渡し,スクリプトエンジンとして JScript を指定しているらしい。
Windows 7 が出たのは IE8 の時代だから,cscript
で呼ばれる JScript が Object.create
に対応してないバージョンというのは,原因としていかにもありそう。
新しい IE を入れても cscript
で新しい JScript が呼ばれたりはしないのね。
追記:Windows 10 もダメ(2016/04/20)
Windows 10 に上げたので同じことをやってみたら,やっぱりダメだった。
「JavaScript の処理系が新しくなって,CoffeeScript 1.9.0 以上でもエラーが出なくなる」と予測していたので,これは意外だった。
教訓
気軽に
bundle update
とかやるとエラい目に遭うこともあると分った。
ただ,気軽に bundle update
してなかったら,問題に気づくのがずっと先になり,今より対処に時間がかかっていたかもしれない。変更になった gem も多かっただろうし。
だから,やってよかったのだけど,タイミングがまずかった。他の仕事で忙殺されてたり,気持ちに余裕のないときにやるべきではなかった。
bundle update
した直後にエラーが出るようになったんなら,私でもすぐに分ったかもしれないけど,今回は JavaScript のキャッシュが生きている(?)間はエラーが出なかった(らしい)のがつらかった。