Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Skip all validations on state machine method

I am trying to figure out how to skip validations on a specific instance of an ActiveRecord object when my reservation model transitions on state machine via a rake task. I would like to skip all validations whenever reservation.close! is called. Would hope to call something like reservation.close!(:validate => false). FYI we are using https://github.com/pluginaweek/state_machine for state machine.

Here is a sample of my reservation model.

class Reservation < ActiveRecord::Base
    validates :ride_id, :start_at, :end_at, presence: true
    validate  :proper_custom_price?, allow_nil: true, on: :update
    validate  :dates_valid?
    validate  :dates_make_sense?

    scope :needs_close_transition, lambda { where("end_at < ?", Time.now).where("state" => ["requested", "negotiating", "approved"]) }

    state_machine :initial => 'requested' do

      all_prebooked = ["requested", "negotiating", "approved"]

      event :close do
        transition :from => all_prebooked,
          :to   => "precanceled"
        end

        before_transition :on => [:close] do |reservation|
          reservation.cancel_reason = :admin
        end
     end
end

Here is a sample of my rake task.

namespace :reservation do

  task :close => :environment do
    puts "== close"

    Reservation.needs_close_transition.each do |reservation|
      puts "===> #{reservation.id}"

      begin
        reservation.close!(:validate => false)
      rescue Exception => e
        Airbrake.notify(e, error_message: e.message) if defined?(Airbrake)
      end
    end
end
like image 757
Eric Baril Avatar asked Aug 21 '14 19:08

Eric Baril


2 Answers

I had the same problem, however I did not want to alter my current validations, so I checked the state machine code (version 1.2.0) and I found another solution, for your specific case it would be something like:

reservation.close!(false)
reservation.save(validate: false)

That will trigger all callbacks that you have defined in your state machine, and well this solution is working well for me.

like image 187
Johan Tique Avatar answered Nov 07 '22 06:11

Johan Tique


When using the state_machine gem, the state attribute is updated before the validations are run, so you can add an unless condition to the validation that tests the current state:

validates :start_at, :end_at, presence: true, unless: Proc.new {state == 'closed'}

If you want more complex logic, pass a method name symbox to unless instead of a proc:

validates :start_at, :end_at, presence: true, unless: :requires_validation?

def requires_validation?
  # complex logic to determine if the record should be validated
end
like image 4
infused Avatar answered Nov 07 '22 06:11

infused