クラスメソッドとインスタンスメソッドは違う(当然)
属性名の一覧を取得するときによく使うattributes
について、モデルに対して呼ぶときと、モデルのインスタンスに対して呼ぶときとでは違う値が返る。
いやそりゃクラスメソッドとインスタンスメソッドは違うものなんだから当たり前なんですけど、大体のケースで同じ値を返すので意外と気づきにくかったりする。ということは、稀にそれが原因でよくわからないエラーを踏んで爆発したりするのでメモ。
具体的には
モデルの属性はRailsが起動したときにロードされ、インスタンスの属性はそれが取得されたときにロードされる。
実例
違いがわかりやすいようにattribute_names
を使って実験。
[1] pry(main)> User.attribute_names
=> ["id", "name", "email", "created_at", "updated_at"]
[2] pry(main)> u = User.first
User Load (0.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User:0x007ffd6f881fe8 id: 1, name: "Taro", email: "foo@bar.com", created_at: Sat, 16 Jul 2016 11:06:18 UTC +00:00, updated_at: Sat, 16 Jul 2016 11:06:18 UTC +00:00>
[3] pry(main)> u.attribute_names
=> ["id", "name", "email", "created_at", "updated_at"]
見た感じでは一緒。ここでmigrateしてtableにcolumnを追加。
$ bundle exec rake db:migrate
== 20160719032723 AddClassificationToUsers: migrating =========================
-- add_column(:users, :classification, :integer, {:default=>0})
-> 0.0048s
== 20160719032723 AddClassificationToUsers: migrated (0.0050s) ================
User
のレコードを再度取得してインスタンスを生成。
[4] pry(main)> User.attribute_names
=> ["id", "name", "email", "created_at", "updated_at"]
[5] pry(main)> u = User.first
User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User:0x007ffd6a208248 id: 1, name: "Taro", email: "foo@bar.com", created_at: Sat, 16 Jul 2016 11:06:18 UTC +00:00, updated_at: Sat, 16 Jul 2016 11:06:18 UTC +00:00>
[6] pry(main)> u.attribute_names
=> ["id", "name", "email", "created_at", "updated_at", "classification"]
モデルに対する実行結果と、インスタンスに対する実行結果に違いが出た。
モデルについては、Railsが再起動されていないため属性に変化がない。
一方でインスタンスについては、取得時にtableのデータをロードしてくるため、その際に新たな属性を認識する(値がNULL
のときは認識しないが、今回はdefault値を設定している)。
[7] pry(main)> reload!
Reloading...
=> true
[8] pry(main)> User.attribute_names
=> ["id", "name", "email", "created_at", "updated_at", "classification"]
Railsを再起動すると、モデルでも属性を認識する。