読者です 読者をやめる 読者になる 読者になる

第7回Gotanda.rb

ruby

もう何回目か数えるのもめんどくさくなってきましたが、今日もひたすら前回の続きをしてました。

Rackの上で書いていると、

def action
  return not_found if cond1
  return not_found if cond2
  return not_found if cond3
  [ 200, { 'Content-Type' => 'text/plain' }, 'Found' ]
end

def not_found
  [404, { 'Content-Type' => 'text/plain' }, 'Not Found' ]
end  

みたいなコードを書きたくなるときがありますが、not_foundメソッドを呼ぶときは必ずreturnを呼ばないとそこで処理が終らずに下の処理が実行されてしまいます。

ここまではまぁ当たり前の話なのですが、cond1, cond2, cond3までチェックするという部分が複数のアクションで共通だったりするとメソッドにくくりたくなります。

def check
  return not_found if cond1
  return not_found if cond2
  return not_found if cond3
end

def action1
  check
  [ 200, {'Content-Type' => 'text/plain' }, 'Found' ]
end

def action2
  check
  return not_found if cond4
  [ 200, {'Content-Type' => 'text/plain' }, 'Found' ]
end

こういうふうにしたときに、not_foundは大域脱出できるようなメソッドとして作ってないと、action1では200が結果として返ってしまいます。

ということでこんなふうにしてみました。

class ActionError < Exception; end
class NotFound < ActionError; end

def action
  check
  [ 200, {'Content-Type' => 'text/plain' }, 'Found' ]
rescue ActionError => e
  e.message
end

def not_found!
 raise NotFound, [ 404, { 'Content-Type' => 'text/plain' }, 'Not Found' ]
end

例外オブジェクトは文字列を渡すことになっているのですが、いちおうそれ以外も渡せるのを利用してます。

前似たようなことをやろうとしたときに、例外だと何かに引っかかってcatch..throw方式を試していた記憶があるんですが、ちょっと思い出せないのと、throwはSymbolで投げるので、内部的に使ってるライブラリとかとsymbolが万が一被るとやっかいなことになりそうなので、例外方式がよいのかなといった感じです。

not_found!とメソッド名をふつうのメソッドのフローとは違うとわかるようにしています。(キモいと言われそうですが、本当はNotFoundにしようかと思っていたのですが、NotFoundだけだと定数と解釈されてしまうのでNotFound()とカッコをつけないといけなくて、目立つのでそれでもいいかなと思ったのですが慣習に従ってみた次第です)