LoginSignup
0
1

More than 5 years have passed since last update.

RailsのmodelにMongoMapper::Documentを使用してdefaultを設定した時にはまったこと

Posted at

またまた業務中にはまった点についてのメモ。

以下の方法で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の併用は難しい。。。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1