Low commit activity in last 3 years
No release in over a year
State Machine + Active Job
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
 Dependencies

Development

Runtime

>= 4.2, < 6
 Project Readme

State Machine Job

Gem Version Build Status

Enqueue jobs on state machine transitions and change state according to job result.

Installation

Add this line to your application's Gemfile:

gem 'state_machine_job'

Usage

Include the StateMachineJob mixin in your job and provide a perform_with_result method instead of the normal perform method:

class SomeJob < ApplicationJob
  include StateMachineJob

  def perform_with_result(record, payload)
    # do something
    :ok
  end
end

The record parameter is a reference to the object the state machine will be defined on.

Now you can wire up the job in a state machine using the StateMachineJob::Macro:

state_machine :initial => 'idle' do
  extend StateMachineJob::Macro

  state 'idle'
  state 'running'
  state 'done'
  state 'failed'

  event :run do
    transition 'idle' => "running"
  end

  job SomeJob do
    on_enter 'running'

    result :ok => 'done'
    result :error => 'failed'
  end
end

When the state attribute changes to 'running' (either by the run event or by manually updateing the attribute), SomeJob will automatically be enqueued. If perform_with_result returns :ok, the state machine transitions to the 'done' state. You can specify as many results as you want.

Note that any exception raised by perform_with_result leads to a state machine transition as if the result had been :error. The exception is not rescued, though. If perform_with_result raises an exception and the record is invalid, previous attribute values will be restored before invoking the transition. That way the transition to the error state can be persisted by rolling back the changes that led to the records invalidity during job execution.

Passing custom Payload

You can specify further options to pass to the perform_with_result method using the payload method:

job SomeJob do
  on_enter 'running'

  payload do |record|
    {:some_attribute => record.some_attribute}
  end

  result :ok => 'done'
  result :error => 'failed'
end

perform_with_result is now called with the given hash of options as the second parameter.

Changing to States with Conditions

One job result can lead to different states based on a conditional. When the job finishes with the given result, the state machine transitions to the first state whose conditional evaluates to true.

job SomeJob do
  on_enter 'running'

  result :ok, :state => 'special', :if => lambda { |record| record.some_condition? }
  result :ok, :state => 'other', :if => :other_condition?
  result :ok, :state => 'done'

  result :error => 'failed'
end

A conditional can either be a lambda optionally accepting the record as parameter or a symbol specifying a method to call on the record.

Retrying Jobs after a Delay

You can tell the state machine to retry a job based on its result:

job SomeJob do
  on_enter 'running'

  result :ok => 'done'
  result :pending, :retry_after => 2.minutes
  result :error => 'failed'
end

When perform_with_result returns the result :pending, the state machine will remain in the runnning state and enqueue a delayed job. This feature uses the Active Job set(wait: n) functionality.

Retrying Jobs Based on State

You can tell the state machine to retry a job if a transition to a certain state occures while a job is running:

event :run do
  transition 'idle' => 'running'
  transition 'running' => 'rerun_requested'
end

job SomeJob do
  on_enter 'running'

  result :ok, :state => 'done', :retry_if_state => 'rerun_requested'
  result :error => 'failed'
end

If the run event is invoked while the job is already running, you can transition to a state signaling that the job will need to run again once it has finished. In example, passing the :retry_if_state option causes the state machine to transition back to the running state once the job finishes with result :ok.

See also

CHANGELOG