またまた業務中にはまった点についてのメモ。
以下の方法でUser#token == nilのdocumentを取得しようとしたが、何故か取得したtokenがnilではない。
pry(main)> User.where(token: nil).all
[
[0] #<User:0x007fa1a2e966f0> {
"_id" => BSON::ObjectId('594915634cfad1a4ac0001ac'),
"email" => "foo@example.com"
"created_at" => Tue, 20 Jun 2017 12:30:27 UTC +00:00,
"token" => "4ruqkwkmdf63upayxhxj6as6iblyhawt",
"updated_at" => Wed, 28 Jun 2017 08:59:42 UTC +00:00,
"valid" => false
}
]
元々User#token fieldは途中から追加したfieldのため、tokenがnilのdocumentは確実に存在するはず。
また、他のcollectionで試してみたら普通に取得することができる。
先輩にこの事象について相談し、何度かこのコードを実行しているうちに、先輩がtokenの値が毎回変化していることに気がついた。
User modelの定義を見に行ったら、以下のようにdefaultが設定されていた。
key :token, String,
default: -> {
SecureRandom.base64(15)
}
このコード自体は自分が追加したものなので、見覚えはあった。
もしかしたらRailsのmodelを介してdocumentを取得したら、Userに設定されているdefaultが動作して、 dbにはちゃんとnilが格納されているdocumentであっても、modelのオブジェクトとして取得した瞬間にtokenに値がセットされているのではないか?という仮説を先輩が立てた。
一瞬言っている意味がわからなかったが、すぐに意味を理解することができた。この発言の前にinitializeとdefaultの挙動について少し話をしていたからだ。
そこで、modelを介さないコードで試してみることになった。
User.collection.find
のように.Model.collectionを使用するとmongodbのドライバ経由でdbにアクセスできるらしい。
これで想定していたオブジェクトを取得することができる。
pry(main)> User.collection.find(token: nil).to_a
[
[0] #<User:0x007fa1a2e966f0> {
"_id" => BSON::ObjectId('594915634cfad1a4ac0001ac'),
"email" => "foo@example.com"
"created_at" => Tue, 20 Jun 2017 12:30:27 UTC +00:00,
"token" => nil,
"updated_at" => Wed, 28 Jun 2017 08:59:42 UTC +00:00,
"valid" => false
}
]
よし、問題は解決したので進めることができる!と思ったのだが、またすぐにはまってしまう。
やりたいことはUser#tokenをいじることだったのだが、以下のように書いたらエラーがでる。
pry(main)> User.collection.find(token: nil).each do |u|
pry(main)* puts u.token
pry(main)* end
NoMethodError: undefined method `token' for #<BSON::OrderedHash:0x007fa1a6ae7708>
from (pry):11:in `block in <main>'
吐かれているエラーログからUser.collection.find
で取れるオブジェクトはBSON::OrderedHash
だということがわかっているのでググってみたら、以下のURLがヒットした。
MongoDB + Ruby. How to access document properties?
この記事によるとどうやらBSON::OrderedHash
は、m.token
ではなくm['token']
のように書く必要があるとのこと。
以下のように書いたらちゃんと取得することができた。
pry(main)> User.collection.find(token: nil).each do |u|
pry(main)* puts u['token']
pry(main)* end
MongoとRailsの併用は難しい。。。