26 Feb 2013, 15:57

Validating data with Mongoid

Share

I’ve been working with Mongoid, which is an object-document-mapper for MongoDB written in Ruby.

Mongo organizes data into collections of documents, just as relational databases such as SQL organize data into tables of records. Reading and writing of documents is done via named classes, one for each collection.

The named class for each collection includes Mongoid::Document to get the database interface methods such as .where, .new, and .save. It also defines the data fields and any custom data handlers.

One very useful feature is the availability of automatic validators which check the format and integrity of your data before allowing it to be saved. There’s a myriad of options, and they are not very well explained in the documentation.

Since the data validators are shared with Active Model, I decided to look for some help there and found this pretty good description of what kind of validation could be done. Unfortunately, it wasn’t clear anywhere how to actually use the validators once they’re defined.

After a bit of hair-pulling, I discovered it’s actually quite simple.

Let’s define a minimal class Iqscore. (The name of the collection will be iqscores; this is a weird behavior of Mongoid whereby class names must be singular and Mongoid will pluralize them for you when naming the collection.)

require "mongoid"

class Iqscore

  include Mongoid::Document

  field :kid, :type => String
  field :iq,  :type => Integer

  validates :kid, :presence => true, :uniqueness => true
  validates :iq, :numericality => true

end

Mongoid provides a valid? method on Iqscore objects. Valid? tells us whether or not the criteria in the validates declarations are met.

1.9.3p385 :008 > x = Iqscore.new({kid: "George", iq: 70})
 => #<Iqscore _id: 512c72d5352420234d000003, kid: "George", iq: 70>
1.9.3p385 :009 > x.valid?
 => true
1.9.3p385 :010 > y = Iqscore.new({kid: "Bill", iq: "unknown"})
 => #<Iqscore _id: 512c736e352420234d000004, kid: "Bill", iq: 0>
1.9.3p385 :011 > y.valid?
 => false

If it doesn’t validate, we can see what’s wrong by looking at the errors property. In this case it tells us that iq is not a number (and it should be). Note that the message “is not a number” is in an array, as it’s possible for there to be multiple messages for a single field.

1.9.3p385 :012 > y.errors
 => #<ActiveModel::Errors:0x98a31cc @base=#<Iqscore _id: 512c736e352420234d000004, kid: "Bill", iq: 0>, @messages={:iq=>["is not a number"]}>

The valid? method is called automatically before any save operation (e.g. save or create), and if it returns false, then the save is not done. Both save and create return true or false to indicate whether the save was done or not.

1.9.3p385 :013 > x.save
 => true
1.9.3p385 :014 > y.save
 => false

At this point George is in our database, but Bill isn’t.