scramble cadenza

技術ネタのガラクタ置き場

ActiveRecord::Base.connection#table_exists? と ActiveRecord::Base#table_exists? の違い

イントロ

#

似ているメソッドで、微妙に挙動が異なり、ちょっとハマったのでメモ

違い

引数、結果をキャッシュするかどうかが違う

引数

  • ActiveRecord::Base.connection#table_exists?
    • 引数を一つ取る
ActiveRecord::Base.connection.table_exists?("users")
#=> 引数で受け取ったテーブルが存在していれば true, なかったら false
  • ActiveRecord::Base.table_exists?
    • 引数を取らない
class User < ActiveRecord::Base
end

user = User.first
user.class.table_exists?
#=> モデルに対応したテーブルが存在していれば true, なかったら false

キャッシュするかどうか

  • ActiveRecord::Base.connection#table_exists?
    • キャッシュしない
    • 毎回 database とやり取りして、テーブルが存在するかどうかを確認するメソッド
ActiveRecord::Base.connection.table_exists?('users')
#=> true

ActiveRecord::Base.connection.drop_table('users')

ActiveRecord::Base.connection.table_exists?('users')
#=> false
  • ActiveRecord::Base.table_exists?
    • 一度呼びだされたらキャッシュされる
      • キャッシュ先は ActiveRecord::Base.connection
user = User.first

user.class.table_exists?
#=> true

ActiveRecord::Base.connection.drop_table('users')

user.class.table_exists?
# => true
# DROP TABLE `users` したのに true が返る!

ActiveRecord::Base.connection.schema_cache.table_exists?('users')
# => true
#
# * `ActiveRecord::ConnectionAdapters::SchemaCache` によってメソッド呼び出し結果が保存されている。
# * `ActiveRecord::ConnectionAdapters::SchemaCache` インスタンスは `ActiveRecord::Base.connection` のインスタンスが保持している

結論

上にも書いたとおり、引数と、メソッド呼び出しの結果をキャッシュするかどうかが違う。

  • ActiveRecord::Base.table_exists? は通常 ActiveRecord::Base 継承したモデル(例だとUser)が対象となるので、引数を取らない
  • また、table の存在判定は、一般的には変更されにくい値であるので、一度 table_exits? を呼び出したらキャッシュされる

と覚えれば良い。

私は動的テーブルを作ったり消したりしていてハマってしまいました。
ActiveRecord を使って、頻繁にテーブルを作成したり削除したりする場合は注意が必要。かも。

# 表記に迷ったのですが ActiveRecord::Base.connection の戻り値は、ActiveRecord::ConnectionAdapters::XxxAdapterインスタンスが返るので、 ActiveRecord::Base.connection#table_exists? という表記は正確ではありません。可読性を考えて敢えてそのままにしてます。(rails 触ったことある人に馴染み深そうな表記のほうがわかりやすいのでは、と考えてのこと)