最近Ruby on Railsばかりなので、今回もRailsのお話。

何も考えずに特定のIDのレコードを見つける際は、「ActiveRecord::Base#find」という超基本メソッドを使うけれど、同時に、XXXというフィールドに対して「ActiveRecord::Base#find_by_XXX」なるメソッドも定義されているので、「ActiveRecord::Base#find_by_id」でも同じようにidに紐付けて見つけ出せる。

以上は当たり前の話だが、この同じような2つ、重要なところが違う。すなわち、そのIDに紐付くレコードが存在しない場合。

ActiveRecord::Base#find」は例外が生じる。

1
2
3
4
5
6
7
Post.find(1)
#=> ActiveRecord::RecordNotFound: Couldn't find Post with ID=1
#	from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1586:in `find_one'
#	from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1569:in `find_from_ids'
#	from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:616:in `find'
#	from (irb):6
#	from :0

ActiveRecord::Base#find_by_id」は nil を返す。

1
2
Post.find_by_id(1)
#=> nil

このお話はそれなりに有名なようですが、find_by_idを使えば、例えばこういうコードが書けて気分がいいですね。

1
(m = ItemMaster.find_by_id(params[:id])) ? m.item_name : params[:id]

このワンライナーが多発するぐらいなら、item_master.rb にクラスメソッドを定義するべきだとは思うけど。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class PostController < ApplicationController
  #例えばブログで
  def show
    @post = Post.find_by_id(params[:id])
    if !@post
      flash[:notice] = "ブログ記事が存在しません: #{params[:id]}"
      redirect_to :action => :index
      return
    end
 
    respond_to do |format|
      format.html 
      #...
    end
  end
end

こっちは幾分か実用的な気がする。IDが存在しないだけで 503 500 になって精神衛生状態が悪くなるのを回避できます。むしろ404を返してもいい(参考: http://d.hatena.ne.jp/NeoCat/20080604/1212599193)。

この挙動は、少なくともRails 2.2.2とRails 2.3.5でこうなることを確認しています、よしなに。

あと、ちゃんとソース読んでないのでなんですが、「find_by_XXX」系メソッドは内部でmethod_missingを呼んでるとかいう話なので、当然ただのfindよりオーバヘッドがかかります(特にRuby 1.9系)。なんでもfind_by_idに置き換えるべきではないでしょう。