Why does false invalidate validates_presence_of?

Ok steps to reproduce this:

prompt> rails test_app
prompt> cd test_app
prompt> script/generate model event_service published:boolean

then go into the migration and add not null and default published to false:

class CreateEventServices < ActiveRecord::Migration
  def self.up
    create_table :event_services do |t|
      t.boolean :published, :null => false, :default => false
      t.timestamps
    end
  end

  def self.down
    drop_table :event_services
  end
end

now migrate your changes and run your tests:

prompt>rake db:migrate
prompt>rake

You should get no errors at this time. Now edit the model so that you validate_presence_of published:

class EventService < ActiveRecord::Base
  validates_presence_of :published
end

Now edit the unit test event_service_test.rb:

require 'test_helper'

class EventServiceTest < ActiveSupport::TestCase
  test "the truth" do
    e = EventService.new
    e.published = false
    assert e.valid?
  end
end

and run rake:

prompt>rake

You will get an error in the test. Now set e.published to true and rerun the test. IT WORKS! I think this probably has something to do with the field being boolean but I can't figure it out. Is this a bug in rails? or am I doing something wrong?


See the API docs...

If you want to validate the presence of a boolean field (where the real values are true and false), you will want to use validates_inclusion_of :field_name, :in => [true, false].


validates_inclusion_of :your_field, :in => [true, false]

no longer works for some versions after 1.3.0 of the shoulda matchers when you are testing that only Boolean values are accepted by your model.

Instead, you should do something like this:

it { should allow_value(true).for(:your_field) }  
it { should allow_value(false).for(:your_field) }
it { should_not allow_value(nil).for(:your_field) }

You can see the discussion here.

There was a partial fix for this that now warns if you are trying to do this here