Paranoid state machine

In one of our ruby project, we are using AASM gem.

This gem lets you build a finite state machine for Ruby objects. We usually use it with ActiveRecord to persist the “state” of an object. This gem also provides simple API methods to switch the states of the object. The biggest advantage of this is that it prevents moving to invalid states, and thereby builds confidence that there can’t be any invalid transitions.

However, I found out in our rails app this wasn’t the case. There are quite a lot of people who contribute to our project, and most common way of updating any record in rails is to use update_attributes!(column_name: value). Calling this method makes a direct call to the DB and therefore, developers are prone to accidentally change the “state” of the object to an invalid one. update_attribute!(status: invalid_status).

Therefore we added another validator to prevent invalid transition state. The AASM. This custom validator finds out if the new value that’s being saved in the “state” column, is one of the valid transition values.

Here is the code in case you want to add it in your project.

# frozen_string_literal: true
module StateMachine
  extend ActiveSupport::Concern
included do
    include AASM
    validate :valid_state_transition
  end
def valid_state_transition
    AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
      status_column = AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.column
next unless persisted? && changes.key?(status_column.to_s)
      status_value = send(status_column)
      can_transition = aasm(state_machine_name).states(permitted: true).map(&:name).include?(status_value.to_sym)
      errors.add(status_column, 'Invalid transition.') unless can_transition
    end
  end
end
Mastodon