経緯
こんなcase文があるとします。
前提として、case文では渡されたオブジェクトとwhenに書かれているオブジェクトを ===
で評価します。
class Checker
def check(object)
case object
when Book
puts "これはBookです"
else
puts "わかりません"
end
end
end
今回使用するBookクラスはこんな感じだとします。
# == Schema Information
#
# Table name: books
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
class Book < ApplicationRecord
end
そして、次のコマンドをrails consoleで実行していたとします。
僕はpryを使っています。
$ rails console
checker = Checker.new
book = Book.first
checker.check(book)
# これはBookです
そのあと、consoleでゴニョゴニョしていると突然先ほどの処理が期待通りに動かなくなりました。
checker = Checker.new
checker.check(book)
# わかりません
checker.check(book)
# わかりません
checker.check(book)
# わかりません
なんでやねん!
原因
原因は至ってシンプルで、consoleにいるときにファイルを編集した際に リロードしてしまったために、Bookクラスの#object_idが変わってしまった ためでした。
調査
こんな調査をしました。
まず、Bookクラスが読み込まれたらputsでわかるようにします。
class Book
puts "Bookを読み込みました"
end
rails consoleを実行すると、まずBookクラスが読み込まれるので、putsが出力されます。
$ rails console
# Bookを読み込みました
次に、先ほどの処理を実行します。この時、
checker = Checker.new
book = Book.first
checker.check(book)
# これはBookです
# object_idは同じ
book.class.object_id
=> 70296714581660
Book.object_id
=> 70296714581660
# 同一のオブジェクト
book.class == Book
=> true
しかし、この流れでリロードを実行すると、状況が変わります。
reload!
# Reloading...
=> true
# checker.rbを変更したからもう一回読み込むと想定
checker = Checker.new
checker.check(book)
# Bookを読み込みました
# わかりません
# object_idが違う
book.class.object_id
=> 70296714581660
Book.object_id
=> 70296942109640
# 同一のオブジェクトではなくなっている
book.class == Book
=> false
当たり前と言えば当たり前ですが、reload!を実行すると、その後Bookを呼んだタイミングで読み込まれるので仕込んだ Bookを読み込みました
が出力されます。そうすると、rubyの#object_idが変わってしまうために、見かけ上では一緒でも、同一のオブジェクトとして認識されないということでした。