validate vs. validateS

Published on: January 15, 2014

I stumbled across this validation gotcha a while back, but that was before I had this awesome blog ;) I think this is a pretty big one, hope this help someone!

::ActiveModel::Validations treats validates and validate differently. validates is used for normal validations presence, length, and the like. validate is used for custom validation methods validate_name_starts_with_a, or whatever crazy method you come up with. These methods are clearly useful and help keep data clean.

That’s all well and good, except for one tiny thing:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  #lang: ruby
  require 'active_record'

  class Foo
    include ::ActiveModel::Validations

    attr_accessor :bar
    validate :bar, presence: true
    # Note the validate without an s!
  end


  require 'rspec'
  require 'rspec/autorun'

  describe 'Foo' do
    it 'fails to actually validate' do
      foo = Foo.new
      foo.valid?.should be_false
    end
  end

That test fails.

Go ahead, copy that into a new file and run it for yourself. I'll wait. Yep, it fails. There’s no value set for bar and yet foo.valid? still returns true. This is a problem.

So what’s going on here? I asked stackOverflow, and it turns out there’s a totally reasonable explanation. validate is written to look for a custom validation method, this time one called bar. It just so happens there is a bar method, set from the attr_accessor :bar line. That bar method returns doesn’t return false, nor does it put any error messages on the main object’s body. Therefore validate interprets the call to bar as a success and doesn’t invalidate the object.

This explanation also means there’s no way for the code to “fail loudly” - to alert us that we haven’t purposefully defined a custom method when we use validate. So I'd suggest grepping through your codebase for validate : and making very sure that’s what you actually want.

So remember folks, validates is for Rails validators (and custom validator classes ending with Validator if that’s what you're into), and validate is for your custom validator methods. Don’t make a typo!


comments powered by Disqus