RailsのApplicationController::RoutingErrorがたまらなくうざい

いやまぁ書いた通りなんだけど、ApplicationController::RoutingError の backtrace って全然役に立たないし、邪魔じゃないですか。できればこのエラーだけ backtrace をオフにできたらステキだなと思ったのでその辺の対処方法をまとめてみた。

この「RoutingErrorのログが邪魔問題」の解決方法は主に以下のような3つくらいのパターンがあるっぽい。

  1. routing で丸ごとキャッチしてしまってそもそも RoutingError が起きないようにして手動で 404 を返す
  2. Logger を差し替えてなかったことにする、DebugExceptions そのものを使わないようにする
  3. DebugExceptions の中でゴニョゴニョする

RoutingErrorが起きないようにroutes.rbで全部拾う

1 の方法については Stack Overflow や Qiita なんかにたくさん記事や回答がある。あるんだけど、ちょっとイマイチっぽい、みたいなニュアンス。

個人的にも想定外のリクエストがあってエラーが起きたという事実について情報量をコントロールしたいとは思うものの、すべて正常な処理で 404 が返っただけになってしまうのはちょっと意図と違うなという気がする。

Loggerやrack middlewareを差し替えてもみ消す

2 の方法はなかなか見つからないんだけど、例えば

stve/silencer: Easily suppress the Rails logger

みたいなやつ。

もっと大胆に DebugExceptions middleware を無効にしちゃうとか、 3 と組み合わせて自分で動的に Logger を差し替えちゃうという方法も Stack Overflow で見かけたが、middleware や Logger を全部差し替えたりするのは差し替え処理と差し替えた Logger ( middleware ) そのものが正しく動作するか検証しなくちゃいけないので重たいなーという印象。

そもそもエラーは起きているがそれが記録されないのは 1 よりもさらにイマイチな気がする。

DebugExceptions middleware + interceptorで好きなようにする

3 の方法については 2 の方法が見つかってあれこれコードを読んでいて自分で気がついたんだけど、実は DebugExceptions には interceptor という仕組みがあるのでそれが使える。

この方法を採用している事例は探した限り見つからなかったんだけど、結構使えるテクだと思うので紹介しておく。対象バージョンは

  • Rails 6.0.0

で、DebugExceptions は ActionDispatch の中の middleware で、rake middleware すると真ん中辺りに出てくるんじゃないかと思う。

この middleware のコードを追うと以下のように ActionDispatch::DebugExceptions.call の中で、事前に register された interceptor が次々に呼ばれることが分かる。interceptor には request と exception が渡ってくる。

module ActionDispatch
  # This middleware is responsible for logging exceptions and
  # showing a debugging page in case the request is local.
  class DebugExceptions
    cattr_reader :interceptors, instance_accessor: false, default: []

    def self.register_interceptor(object = nil, &block)
      interceptor = object || block
      interceptors << interceptor
    end

….

    def call(env)
      ...
    rescue Exception => exception
      invoke_interceptors(request, exception)
      raise exception unless request.show_exceptions?
      render_exception(request, exception)
    end

….

     def invoke_interceptors(request, exception)
       backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
       wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)

       @interceptors.each do |interceptor|
         interceptor.call(request, exception)
       rescue Exception
         log_error(request, wrapper)
       end
     end

ということで独自の interceptor を作ってその中で ActionController::RoutingError かどうか判別して何らかの加工を加えるなり処理を行うなどするとよさそう。

具体的には request オブジェクトを書き換えたり exception オブジェクトを書き換えると backtrace は無視するといったことが可能で、そのうえで必要な情報は独自にログに落とすとかどこかエラー収集の仕組みに投げるようなコードを書けばよいと思う。

具体的なIntercepterの例

上のコードにマッチすればよいので、以下のような形になる。

class FooInterceptor
  def call(request, exception)
    ..
    if exception.is_a? ActionController::RoutingError
      ..
      request.instance_eval {
        def show_exceptions? # もみ消す
          false
        end
      }
      ..
    }
  end
end

ActionDispatch::DebugExceptions.register_interceptor(FooIntercepter)

単に消してしまうだけだと上に書いたようにエラーが起きた事実も消えてしまってよくないので、なんらかの措置を行なったうえでもみ消してください。

RailsのfixtureでCSVを使う場合は空のセルに注意

  • CSV から fixture を読み込む場合、空つまり null になるはずのものが 0 になってしまう1
  • もちろん ,"", と空文字列であることを明示してあればこの限りではないが、Excel ではこのような安全なカラムを作ることができない2
    • 中にある文字列を解釈して必要に応じて " を付加してくれるが、空文字列を明示することはできない
  • 空になり得るカラムが最初から fixture に存在していない場合は当然問題は起きない

ということで

fixture は YAML が安全

ということに。ふーむ。

拙作 ModelDumper は CSV でも YAML でも出力できます。

cf.

A model dumper for rails console ( Rails 3 or later ) and .irbrc for rails env — Gist

  1. DBMSに依存してるかも。少なくともSQLiteでは発生する。 

  2. とりあえず Excel 2003 ではできない。 

複数の diff を一気に見る GUI のツールってやっぱりないの?

terminal 原理主義の自分は当然

svn diff
svn diff | awk '/^Index/'
svn diff | awk '/^Index/ {print $NF}'
for i in `svn diff | awk '/^Index/ {print $NF}'`; do svn diff $i | nkf -w ; done | less

みたいなことをしてるんだけど1、GUI のツールにこういうことのできるものを見かけたことがない。なんでだろう。

何が言いたいかというと、例えば TortoiseSVN などで commit する際、変更のあるファイルがリストアップされるでしょ? で、その diff を見ようと思ったら別な diff ツールに渡さなきゃいけないんだけど、そのとき一つ一つ開かないといけないのって面倒くさくない?って話。(これが間違いなら今回の話はもう全然意味ないです。)

ファイル一つだけで済んでるうちはいいんだけど、例えばメソッドを追加したのでプロダクトコードとテストコード両方いじりましたとか、テストデータもいじりましたとか、変更が複数のクラスに入りましたとか、あるわけじゃないですか2。このとき、GUI 使いの人たちはどうやってその変更を一覧するのだろう、という疑問です。

いやね、絶対一覧できなきゃいけないってわけじゃないんだけど、一覧を見てるうちに「あれ、そういえばさっきいじってたファイルを add し忘れてるな」とか「しまった、何の気なしにちょっといじっておいたものをそのまま commit するところだった」なんてことに気づくことがちょこちょこあるし、複数の diff の間でうまく対応が取れていない3ことに気づいたりするし、実際身近な GUI 使いの人の commit 忘れが自分より多い気がしてるのです。当然、使い手の個性もあるでしょうが。

第一、GUI の diff ツールって、確かにきれいに見えるんだけど実際の変更以外も全部表示されてるから、例えば変更の入った部分がファイルの末尾の方ばっかりとかだとスクロールがうざくないですか? コロコロコローって4。そういうのって設定次第でどうにかなるもんなの? それでもやっぱ開いたり閉じたりしてるうちにさっき見てた diff の内容が頭から飛んじゃわない? 飛ぶよね。なんかやっぱ GUI の diff ツールって、書いてる途中のファイルの diff が見れればそれでいいと思ってんじゃないかって気がすんだよなぁ。

まとめ大事だよ、まとめ。まとめて見ようよ。

  1. 最後のは文字コードの混ざったファイル群の diff を全部 UTF-8 にして読むため 

  2. 複数のクラスに同時に変更が入るのはどうなんだという意見もあるだろうけど、実際問題インターフェイスを変えたいとかあるわけで。 

  3. でもテストは通る 

  4. そりゃなんかキーバインドがあるんだろうけど 

cmd.exe を捨てて ckw.exe へ

Windows 以外も使う人が Windows を使うときに cmd.exe の存在は若干問題になるなぁと思う出来事があった。で、今回はこれを機会に自分の使う範囲内では cmd.exe をやめて ckw.exe にしようかなと思ったという話。

この ckw は以前も触れたことのある terminal emulator ck の別バージョンで、cmd.exe 互換なんだけどウィンドウの外観だけ違う程度のものだと思うと分かりやすい。実際、euc-jp なんかは表示できない。

で、今回の話、いったい何が理由かというと、

cmd.exe のウィンドウでは IME ツールバーが機能しない

というのが発端。cmd.exe ではウィンドウ内で IME が機能するようにできているためで、こうなると何が困るってマウスで IME を ON/OFF させることができない。そんなの別にいいじゃんて思うかもしれないけど、リモートでの作業やバーチャルマシン上の作業の場合はマウスで操作できると便利なことがあるのだ。

リモートでの作業やバーチャルマシン上の作業の場合、IME の ON/OFF などのキーコンビネーションをローカルの実機と異なる状態にしておくことがある。これは意図しない機械に命令が影響しないようにするための工夫でもあるけれど、例えば Mac から Windows を操作する場合は Windows の標準のキーコンビネーションを Mac のキーボードで再現できなかったりするので、必然的に「割り当て」を行う必要がある。

これがそもそも面倒くさい。つかどのキーに割り当てたのか忘れちゃう。

だからマウスで操作できると嬉しい。それと、

cmd.exe 内で動く IME には Windows 全体のキーコンビネーションの設定が効かない。

例えば X 使いの人が shift + space に IME の ON/OFF を割り当てていても、cmd.exe 内では標準の [全角/半角] を押さないといけない。これは面白くない。

ということで ckw.exe

これを使っても特別何が便利ということはない。ウィンドウの外観の設定やコピペの動作が標準と違うってだけで、特別な機能は付加されていない。それでも、

cmd.exe と違って通常のウィンドウである

というアドバンテージがある。通常のウィンドウなので IME ツールバーが他のアプリと同じように機能する。これが、大きかった。

はげどう。の話。

RD のリモコンはでかくていやだから小さいの売ってほしいって意見

いやまったくほんとにその通り。わたしゃ最初マジで「このリモコンは分割できるようにすべきだ」と思いましたよ。うちは H1 じゃなくて XS ですけどね。

東芝ちゃん、藤原紀香に魔法掛けられてる場合じゃない。気づくの遅すぎ。あとブラウザからサクッとタイトル削除もできるようにしてください。でもファームウェア上げて変なフリーズに悩むのはいやですので、その辺もしっかりやってください。

cygwin 上の w3m と screen と SHELL

なんかカーソルキーで移動するとリンクテキストにしか移動できないと思っていたけど、

export SHELL=/bin/bash

って書いたらフリーカーソルになったような気がする。なぜだろう。

ついでに、screen 上でプロンプトが正しく表示されなかったのも直った。そういうもんなのか。でも .bashrc 上で SHELL=/bin/bash を指定するのって、なんかおかしくね?

screen on cygwin

先日の話を実際に試してみた。結果、以下のようになった。

versiontest
3.9.10NG
3.9.13Good!
3.9.15maybe
4.0.2NG

3.9.15 はなんかちょっと不安定、残りの2つは全然ダメ。3.9.13 はどこから binary を持ってきたのか分からない。。。

4.0.2 はここから

http://www.fredlwm.hpg.ig.com.br/cygwin/screen/

これに 2ch にあったパッチを当てると detach, attach できる。以下、保全コピペ

from http://pc5.2ch.net/test/read.cgi/unix/1048030339/914-915

begin-base64 644 screen-4.0.2-cygwin.patch.gz
H4sICFfMf0ACA3NjcmVlbi00LjAuMi1jeWd3aW4ucGF0Y2gAzVdtb5tIEP4c
/4pJovYgBgeInfjlUqWJOV/U1I5sp1VOJyECaxuVgAVLmlyb/34zy4shtqtU
lU5nWSzMzgwzzzO7O7jebAZqlEDsRIwFarOhNYzDey92Gk5FpjpP869ekE3V
VFXdZLJjaNqRqhuq1gLD6DZb3abe0PIf1LWOptXq9foPXJOLpop//RgMvau1
uoa25uLsDNRj/Ug5gXo6nJ3VAH8HhzXYBxa43gwOD2Bomv2JOTWHn9IZ5seM
5DcTcyVW9wG1XTbzAuZKvhckjzJ8/15ILMsJgwf2aFlCLJ1P+vDuFPROR9eO
5Fr9l8yrmhe3g8+XQ9SkZGLGWfAgPdiRAg+2nzAFdLkn8sM0NmrgtLuR0CV/
2sKnmFmnU4hTNrWOqrXBOOoax129/Wo2Cw/rZOqtY6NVJfOoSVyKK1G5Lyis
QY3YmoURxKFvR14MRkNX4CbwHr/aEQNp8mncbBgy2IELyzCOvTv/CUK+YFGc
UuvNEF348/0n0yJd63p6O0HKSoxV52R4+xZ2NzKCPAeOn7gMfo+f4sOYR+GS
x43Fu1K4m9GPQ+cL41sIyCfXOchn1mloaa+moezkVUy0Tk6UY6jTUCwr/EWM
J1EAqt5LJc/FQqvVAVgUBSGcgiZmEV0Jiz5gDpdiBSSEKnE4UCi260ZwgCjb
CqDYZ4E0QfG1zRcy1AGpPD3Fl8jpS77lbyeP+A45jbDdEhG2j5VmqxSi44cx
k2K5lwtiOC0HXCc/UhbqKZiXw+vxaDA2JxOZ5uh19dRw5lq4tnDo5RIv4AQB
PueSP/rWX+Z4JL2duXKvJMSNhZKuiNESQ4mZLxDBPLGIhzdXV0Itv6XrygQj
Jatd3CnkXFaKMFMRmJREAB/jeZqiAntv4m720j0FCph7ZQOX3SVzae+j/YVd
+B4L+ETUSxcy/mBmez5zG38He1XDFdglYQ54IXrOb8WWtTkNKgusA4sDrie8
qXgk3HFrq8hSNQLU+4eFM9z//GoYCMyccfKLqkTGZHRlTUYXH8wp3VvmeDwa
I/ZoiNfUXVF4FTS/VR+3YL4J91UAW7H/Jfy3crCRhwoXWR6YPZWX9kspCwhF
wlnA/8Nss1uxY6XnAZ7/tE7Nm8s+bQ6PdJAmniux2czCMd1BhHSeSedCSrtP
R2vTUdVpthVdF7vPTgqFhkC859x2FmBzzu6XHL56fAF3Np5N6OaNK+8iOveN
+4Yt1Br2Mn9X/ruLmP0FJTvP4pCiWEfWcHQxnd6uNgaQJE9sJ064xP0THVqc
PymoOe5/HsN3YTI8v8KCzx7IXkGmZfhd8L2fdxA/7bDqJT31dr4VGOQrIMWh
Cxdh4rsQhLS2ccm+iTMEyD9ljmYfPN+XXoCCNXQ5sM5vza0NTcLvl1sO1HRq
/ThN5S8P05OufvLqw3Tl4lVHabtDlSKuVCcxt7nniC0NLx4nb2BZ15L0EGIh
EB6ZCj1Td5dgf8dfqGSoq/vrXSe2LrlkYE5vsMGdymmzI/2oPy16nC0e8qiy
Y1yEffC4TDA8dMfS+CqTdE2DzdzBSntlWORCWOmaQWClQ7UFfNGhF4FRtFLR
q1EL9yKxxRJzfdmh/6z5jxrCsl+hTe2qEyXMB3rEc99nNn5yYNecfZrAOiBW
GRBYfbwID2iVtjyilFqlWirDjUiLVilR3yXc4k9Lhsu5b77vW9jhXGCD00vr
ZXd7GZgfsf3NqmX3teWybl8EwR493mAWZ9G9F2D5hEHRHlYUaMhmStXQbolN
Nh0o3+fKd935zWBwi0RejQaXQ4Hsf7oe9iEJaGsuESc+VGw7mi8UED5/KzGc
9s6xWNa7FO6/HqPybnYPAAA=
====

3.9.13 よりも速い気がする。快適。

※ と思ったけど Emacs を使っていると死ぬ。やっぱだめか。

About

例によって個人のなんちゃらです