null ain't false

Jared Carroll

This one come up today at the shop.

Say you have the following table:

users (id, admin)

And its got a couple rows in it

(1, 1)
(2, 0)
(3, 1)
(4, NULL)

Now we want to find all non-admin users.

class User < ActiveRecord::Base
end

User.find :all,
          :conditions => 'admin = false'

What rows will that return?

(2, 0)

It doesn’t include:

(4, NULL)

because in the database world NULL != false. So we have to rewrite it like:

User.find :all,
          :conditions => 'admin = false or admin is null'

That or admin is null part sucks. How can we avoid it?

How about in the users migration we write:

t.column :admin, :boolean, :default => false

That way all rows will default to false and we can avoid the or admin is null. But do we want logic like that in our database?

Let’s put it in the model:

class User < ActiveRecord::Base

  def before_create
    if admin.nil?
      self.admin = false
    end
  end

  # or 'hiding' the conditional

  def before_create
    self.admin ||= false
  end

end

That’s a pretty lame callback right there.

I say put it in the migration. Databases are designed for ‘default’ values and its less Ruby code I have to write.