issue tracker
forum
source
rubyforge
rubydoc


validations

by kind

class Event < Doodle
  has Date
end

Doodle uses kind_of? to test the value, so kind can be set to any module (e.g. Enumerable), class or superclass that makes sense for your application.

If you now try to initialize Event#date with something that isn't a Date:

event = Event.new(:date => "Hello")

you'll get a Doodle::ValidationError exception:

# ~> -:9: Event.date must be a kind of Date - got String("Hello") (Doodle::ValidationError)

attribute validations using must

To specify a validation on an attribute, use must inside the attribute's definition block:

class Event < Doodle
  has :start_date, :kind => Date do
    must "be >= today" do |value|
      value >= Date.today
    end
  end
end

event = Event :start_date => Date.today
event.start_date = Date.parse('2001-01-01')
# ~> -:14: Event.start_date must be >= today - got Date(#<Date: 4903821/2,0,2299161>) (Doodle::ValidationError)

The must block should return true if the value is valid, false otherwise. A failed validation will raise a Doodle::ValidationError exception.

Attribute validations happen before the instance variable is changed.

instance level validations

You can also specify validations for the object as a whole:

class Event < Doodle
  has :start_date, :kind => Date do
    must "be >= today" do |value|
      value >= Date.today
    end
  end
  has :end_date, :kind => Date do
    default { start_date }
  end

  must "have end_date >= start_date" do
    end_date >= start_date
  end
end

event = Event :start_date => Date.today
event.end_date = Date.parse('2001-01-01')
# ~> -:21: Event must have end_date >= start_date (Doodle::ValidationError)

Note that you don't need to project the value into the block but you can if you like - its value is the object instance itself.

Object level validations occur after all instance variables have been set.

deferring validation with doodle.update

Sometimes you need to update attributes in a way that would temporarily cause the object state to be invalid.

The following class definition has two object level validations defined with must:

class Dude < Doodle
  # the attribute has an ~attribute~ validation, i.e. it must be a
  # String
  has :name, :kind => String
  has :cool, :default => false
  # whereas this is an ~object~ level validation
  must "be cool if name contains 'Dude'" do
    !(name =~ /Dude/ && !cool)
  end
  must "not be cool if name does not contains 'Dude'" do
    !(cool && name !~ /Dude/)
  end
end

# ~> -:2: uninitialized constant Doodle (NameError)

If you try to simply update an attribute in such a way as to cause the instance to be invalid, Doodle will throw an exception:

dude = Dude("The Dude", true)
dude.name = "Bozo"
# ~> -:7: Dude must not be cool if name does not contains 'Dude' (Doodle::ValidationError)

Using object.doodle.update, you can temporarily set attributes to values which would invalidate the object:

dude.doodle.update do
  name "Bozo"
  name "The Dude"
end
dude # => #<Dude:0x5840bc @name="The Dude", @cool=true>

dude.doodle.update do
  cool false
  name "Bozo"
end
dude # => #<Dude:0x5840bc @name="Bozo", @cool=false>

Values set in the block will override values set in the argument list:

dude.doodle.update :cool => false do
  cool true
  name "The Dude"
end
dude # => #<Dude:0x5840bc @name="The Dude", @cool=true>

Of course, if the object is invalid at the end of the block, Doodle will raise an exception:

dude.doodle.update do
  name "Bozo"
  cool true
end
# ~> -:7: Dude must not be cool if name does not contains 'Dude' (Doodle::ValidationError)

You can't escape individual attribute validations:

res = Doodle::Utils.try {
  dude.doodle.update do
    name 123
    name "Dude"
  end
}
res  # => #<Doodle::ValidationError: Dude.name must be a kind of String - got Fixnum(123)>
# the attribute is still valid, even after capturing the exception
dude # => #<Dude:0x5840bc @name="The Dude", @cool=true>

i.e. Doodle will not allow you to set an individual attribute to an invalid value for that attribute, even temporarily.

However if you capture the exception, update will not prevent you from invalidating the whole object:

res = Doodle::Utils.try {
  dude.doodle.update do
    name "Jeff"
  end
}
res  # => #<Doodle::ValidationError: Dude must not be cool if name does not contains 'Dude'>
dude # => #<Dude:0x5840bc @name="Jeff", @cool=true>

validating yaml data

See validating data loaded from a yaml source.

contents
examples
extras