I recently went through an exercise of upgrading one of Artsy’s largest web projects to the current HEAD of Mongoid 4.x. This is going to be a major release with numerous changes and I wanted to flush out bugs before the final version of the ODM is released. All Mongoid changes currently live on master.
gem 'mongoid', github: 'mongoid/mongoid'
In the process I’ve worked on making a few gems compatible with Mongoid 4 and learned a couple of things that should help you make this process smooth for your own applications.
Moped::BSON::ObjectId
Moped’s BSON implementation has been removed in favor of the MongoDB bson gem 2.0 and higher. All Moped::BSON
references must change to BSON
. This is rather annoying and forces many libraries to have to fork behavior at runtime.
module Mongoid
def self.mongoid3?
::Mongoid.const_defined? :Observer # deprecated in Mongoid 4.x
end
def self.mongoid2?
::Mongoid.const_defined? :Contexts # deprecated in Mongoid 3.x
end
end
The mongoid2?
implementation is borrowed from mongoid_orderable and I wrote the mongoid3?
version by parsing the CHANGELOG - observers are deprecated in 4.0.
Now, instead of calling Moped::BSON::ObjectId.legal?(id)
, you have to do something like this:
if Mongoid.mongoid3?
Moped::BSON::ObjectId.legal? id
else
BSON::ObjectId.legal? id
end
Furthermore, you can no longer convert a string into a Moped::BSON::ObjectId(id)
, you must explicitly call from_string
:
if Mongoid.mongoid3?
Moped::BSON::ObjectId(id)
else
BSON::ObjectId.from_string(id)
end
Libraries should then adjust their dependencies on Mongoid and specify >= 3.0
, and maybe < 5.0
.
Testing Against Multiple Mongoid Versions
The mongoid-orderable gem has a neat system for testing against all versions of Mongoid with Travis CI. First, the .travis.yml file declares a test matrix that sets MONGOID_VERSION
. Note that Mongoid 3.x or newer doesn’t run with Ruby 1.8.x or 1.9.2.
```ruby .travis.yml rvm:
- 1.8.7
- 1.9.2
- 1.9.3
- ruby-head
env:
- MONGOID_VERSION=2
- MONGOID_VERSION=3
- MONGOID_VERSION=4
matrix: exclude: - rvm: 1.8.7 env: MONGOID_VERSION=3 - rvm: 1.8.7 env: MONGOID_VERSION=4 - rvm: 1.9.2 env: MONGOID_VERSION=3 - rvm: 1.9.2 env: MONGOID_VERSION=4
services: mongodb
The library's *Gemfile* locks a different version depending on the environment variable, defaulting to 3.x. You can also test against a very specific version, if you must.
```ruby Gemfile
source "http://rubygems.org"
gemspec
case version = ENV['MONGOID_VERSION'] || "~> 3.1"
when /4/
gem "mongoid", :github => 'mongoid/mongoid'
when /3/
gem "mongoid", "~> 3.1"
when /2/
gem "mongoid", "~> 2.8"
else
gem "mongoid", version
end
Upgraded Gems
I used the above method to make a few gems Mongoid 4.x compatible, via the following pull requests.
- mongoid-slug
- mongoid-scroll
- mongoid_orderable
- mongoid-history
- mongoid_collection_snapshot
- delayed_job_shallow_mongoid
Upgrading a Rails Project
If you’re using Rails, you’re in for upgrading both Mongoid 4.x and Rails to 4.x. This means you will suffer a lot of pain trying to find compatible versions of various interdependent gems. I suggest locking Rails, Mongoid and ActiveSupport to begin with.
``` ruby Gemfile gem ‘rails’, ‘4.0.1’ gem ‘activesupport’, ‘4.0.1’ gem ‘mongoid’, github: ‘mongoid/mongoid’
Bulk search & replace `Moped::BSON::ObjectId` references.
Calls to `inc`, `set` and `add_to_set` now take hashes, eg. `artist.inc(likes_count: 1)`.
If you're converting Mongoid objects to JSON and seeing data such as `{ "$oid" => "..." }` instead of an ID, monkey-patch `BSON::ObjectId.as_json`. See [this discussion thread](https://groups.google.com/forum/#!msg/mongoid/MaXFVw7D_4s/T3sl6Flg428J).
``` ruby config/initializers/bson/object_id.rb
module BSON
class ObjectId
def as_json(options = {})
to_s
end
end
end
If you’re using Warden (including via Devise) and/or rely on session cookies that may contain a user ID, add an implementation for the deprecated Moped::BSON::Document
. This will prevent all old cookies from causing a serialization error and logging all those users out.
``` ruby config/initializers/bson/ module Moped module BSON ObjectId = ::BSON::ObjectId
class Document < Hash
class << self
def deserialize(io, document = new)
__bson_load__(io, document)
end
def serialize(document, io = "")
document.__bson_dump__(io)
end
end
end end end ```
Updates
Please post your updates below and questions to the mongoid mailing list. I’ll update this post up until Mongoid 4.x ships.
Comments