State Machine, Model Validations and RSpec

rspec, ruby, ruby-on-rails, state

Solution

It looks like you're running into a caveat noted in the documentation:

One important caveat here is that, due to a constraint in ActiveModel's validation framework, custom validators will not work as expected when defined to run in multiple states. For example:

 class Vehicle
   include ActiveModel::Validations

   state_machine do
     ...
     state :first_gear, :second_gear do
       validate :speed_is_legal
     end
   end
 end

In this case, the :speed_is_legal validation will only get run for the :second_gear state. To avoid this, you can define your custom validation like so:

 class Vehicle
   include ActiveModel::Validations

   state_machine do
     ...
     state :first_gear, :second_gear do
       validate {|vehicle| vehicle.speed_is_legal}
     end
   end
 end

Problem

Here's my current class definition and spec: ``` class Event < ActiveRecord::Base # ... state_machine :initial => :not_started do event :game_started do transition :not_started => :in_progress end event :game_ended do transition :in_progress => :final end event :game_postponed do transition [:not_started, :in_progress] => :postponed end state :not_started, :in_progress, :postponed do validate :end_time_before_final end end def end_time_before_final return if end_time.blank? errors.add :end_time, "must be nil until event is final" if end_time.present? end end describe Event do context 'not started, in progress or postponed' do describe '.end_time_before_final' do ['not_started', 'in_progress', 'postponed'].each do |state| it 'should not allow end_time to be present' do event = Event.new(state: state, end_time: Time.now.utc) event.valid? event.errors[:end_time].size.should == 1 event.errors[:end_time].should == ['must be nil until event is final'] end end end end end ``` When I run the spec, I get two failures and one success. I have no idea why. For two of the states, the `return if end_time.blank?` statement in the `end_time_before_final` method evaluates to true when it should be false each time. 'postponed' is the only state that seems to pass. Any idea as to what might be happening here?

Original source