こんにちは @sonots です。別に旬でもなんでもないのはわかっていますが、mongoid 2.x を 3.x にバージョンアップする作業をしたので、ポイントなどをまとめておこうと思います。
参考にしたもの
まずは参考にしたもの。重要だと実感した順。
- mongoid の CHANGELOG
- 利用してた 2.x 系バージョンの mongoid のコード
- 利用する 3.x 系バージョンの mongoid のコード
- ぐーぐる
いつもなら「1. ぐーぐる」なんですが、こういう作業の場合はあまり参考になりませんね。CHANGELOG に変更箇所が載っていると非常に捗りました。CHANGELOG に載ってなくて、コードを直接読まないといけないことも多々ありましたが・・・
というわけで、さっそく。
index の書き方が変わった
class Band include Mongoid::Document index([[:field1, Mongo::ASCENDING], [:field2, Mongo::DESCENDING]]) end
のように書いていた index の書き方が
class Band include Mongoid::Document index({field1: 1, field2: -1}) end
のように変わりました。
Mongo gem に依存しなくなった
Mongo gem を使わなくなったため、Mongo ネームスペースのものが使えなくなっています。
例えば、先ほどでてきた Mongo::ASCENDING は使えなくなったので、mongo 上で直接 index を貼る時と同様に 1 と指定するようになりました。Mongo::DESCENDING は -1 になります、
ほかにも Mongo::Collection が Moped::Collection に変わったりしています。
BSON が Moped::BSON に変わった
BSON::ObjectId が Moped::BSON::ObjectId に変わりました。
BSON::InvalidObjectId エラーが発生しなくなった
Mongoid::Document#find メソッドの引数に _id の型に一致しないオブジェクトを指定すると、BSON::InvalidObjectId エラーが発生していましたが、単に nil が返るようになりました。
補足: find の引数に一致しない要素を持つ Array/Hash を指定した場合は [] が返ります。
collection_name の指定方法が変わった
mongo の collection 名を明示的に指定したい場合は、
class Band include Mongoid::Document self.collection_name = 'collection_name' end
と書いていましたが、これが
class Band include Mongoid::Document store_in collection: 'collection_name' end
のように変わりました。
mongoid.yml の書き方が変わった
development: raise_not_found_error: false logger: false host: localhost port: 27017 database: mongo_development
のように書いていましたが、
development: options: raise_not_found_error: false sessions: default: hosts: - localhost:27017 database: mongo_development
のように変わりました。リファレンス を見ると、replica や sharding ができるようになって、hosts を Array で指定するようになったようですね。ホストは port 番号もあわせて指定しなければいけなくなりました。
また、以前は指定できていた mongoid のログ機能をオフにする設定 logger: false が消えました。代わりに
Mongoid.logger = nil
とすることで logger をオフにできるようです。
_id フィールドの型を指定する方法が変わった
_id フィールドはデフォルトで BSON::ObjectId (Moped::BSON::ObjectId) 型になりますが、これを Interger や String 型にしたい場合の指定方法が変わりました。
class Band include Mongoid::Document identity type: Interger # field :_id end
のように書いていましたが、identity マクロが消えました。
class Band include Mongoid::Document field :_id, type: Interger end
のように上書きするようになりました。
同時に任意のフィールドを識別子(id)に指定する key マクロも消えました。
class Band include Mongoid::Document field :name, type: Integer key :name end
のように書いていましたが、
class Band include Mongoid::Document field :name, type: Integer field :_id, type: Integer, default: ->{ name } end
のように変わりました。CHANGELOG によると、default には proc が指定でき、ほかのすべての field がセットされた"後" に適用されるようです。
セットされる"前"に適用したい場合は、以下のように :pre_processed を true にしろとのことです。
class Band include Mongoid::Document field :_id, type: String, pre_processed: true, default: ->{ BSON::ObjectId.new.to_s } end
create(:_id => 1) ができなくなった
これにだいぶハマっていたのですが・・・
上述のように :_id フィールドの型を Integer に変えて直接指定しているコードがあったのですが、create(:_id => 1) としても id に値が設定されず nil のままになります。sensitive_fields という機構が入り、_id および _type に直接値を指定できなくなりました。
mongoid.yml に
develpment: options: protect_sensitive_fields: false
と指定、または
Mongoid.protect_sensitive_fields = false
とした後に mongoid model をロードすることで、以前と同様に _id および _type に値を直接代入するこができるようになります。
GridFS サポートがなくなった
Mongoid に mongodb の GridFS engine サポートの機能を追加する carrierwave-mongoid を使用していたのですが、現在 rubygems.org にあるものは mongoid 2.x にしか対応していません。
github に mongoid-3.0 ブランチがあったのでそれに移行しようとしていたのですが、こちらの Issue によるとそもそも mongoid 3.x が GridFS をサポートしていないとのことで、GridFS が使えないことが発覚しました。
使わないように別実装に置き換えて対応しました。
まとめ
けっこう大変でしたが、なんとか1日で移行実装を終わらせることができました。と言っても、今日の段階ではテストが通ったというだけなので、また何か追加が必要になるかも。追記します!
protect_sensitive_fields: false の件は、どハマりしていて、create メソッドにあてる monkey patch 書いたり dirty なことをしようとしていたのですが、@shyouhei さんに助けて頂き、clean な解決に辿りつくことができました。大変お世話になっております。謝辞。
(追記) Mongoid2 から Mongoid3 にアップデートするためのオフィシャルな Upgrading ページも用意されていました。親切!