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

破いて捨てたノート

Webやテクノロジーやそれ以外に関する思いつき

Rails で`find_by_id`に渡す引数はString でもいけるんだって

Rails
# 普通のやつ
User.find_by_id 1
=> #<User id: 1, name: "yachibit", ...>
# Stringを引数に渡すと...
User.find_by_id '1'
=> #<User id: 1, name: "yachibit", ...>
# このへんも同じく
User.find '1'
=> #<User id: 1, name: "yachibit", ...>

SUGEEE!! つい最近知りました。
よくよく考えたらいつも使わせて頂いてたわけなんですが...
パラメーターで渡ってくる値はString なのでController でレコードを引くときに、いつもお世話になっています。

# params[:id]
# => '1'
User.find params[:id]

Rails さんは本当によく出来ているなあと、ただただ感嘆させられるばかりです。 パラメーターを渡すだけで、いい感じによろしくやってくれるあたりの仕事ぶりは、自分も見習わなければならないなと襟を正されます。

余談はさておき、

User.find ""
  User Load (0.4ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 0 LIMIT 1

WHERE 'users'.'id' = 0 と言ってやがるということは、やはりRails は何処かでto_iしてるはずだ。ぜひとも、お目にかかりたと思い、Rails のコードを読み始めたのですがアレ?? 実際to_iしている箇所は見当たりませんでした(たぶん)。
どうやらActiveRecord::FinderMethodsのこの辺りが怪しそうですが。
うーん、ちょっとよくわからないです。

def find_with_ids(*ids)
  raise UnknownPrimaryKey.new(@klass) if primary_key.nil?

  expects_array = ids.first.kind_of?(Array)
  return ids.first if expects_array && ids.first.empty?

  ids = ids.flatten.compact.uniq

  case ids.size
  when 0 
    raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
  when 1 
    result = find_one(ids.first)
    expects_array ? [ result ] : result
  else
    find_some(ids)
  end
end

def find_one(id)
  if ActiveRecord::Base === id
    id = id.id
    ActiveSupport::Deprecation.warn "You are passing an instance of ActiveRecord::Base to `find`." \ 
      "Please pass the id of the object by calling `.id`"
  end

  column = columns_hash[primary_key]
  substitute = connection.substitute_at(column, bind_values.length)
  relation = where(table[primary_key].eq(substitute))
  relation.bind_values += [[column, id]]
  record = relation.take

  raise_record_not_found_exception!(id, 0, 1) unless record

  record
end

落穂拾い

上に載せたfind_oneメソッドの冒頭。

if ActiveRecord::Base === id
  id = id.id
  ActiveSupport::Deprecation.warn "You are passing an instance of ActiveRecord::Base to `find`." \ 
    "Please pass the id of the object by calling `.id`"
end

はじめて読んだ時この部分がwhut? でした。id = id.idとかちょっと何言ってるのかw
ですが、なるほどRails はどうも、こういう書き方もできるということなのです。

me = User.find_by_user_name! 'yachibit'
User.find me

ただし、この書き方はdeprecated なので、
"Please pass the id of the object by calling .id"
という具合に怒られます。