No release in over a year
sequel-state-machine hooks together the excellent Ruby Sequel ORM to the state-machines library, with auditing and other tools.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

 Project Readme

Gem Version Status

sequel-state-machine

sequel-state-machines is a pair of plugins that supercharge your usage of the Sequel ORM combined with the state_machines Gem.

The functionality it adds is:

  • Automatic audit logging of all state transitions, plus adhoc audit logging.
  • Helpers to isolate processing in a transaction with row locking.
  • Validation methods to ensure the state machine status column value is included in the state machine specification.
  • Accessors to keep track of when particular transitions happened (uses the audit log).
  • RSpec helpers to test that a transition does or does not happen.
  • Supports multiple state machines on the same model!

Usage

State machines have a main model and an audit log model. The audit log model requires a particular schema, as shown below:

class FundingTransaction < Sequel::Model(:funding_transactions)
  plugin :state_machine
  one_to_many :audit_logs, class: "FundingTransaction::AuditLog", order: Sequel.desc(:at)

  state_machine :status, initial: :created do
    state :created,
      :collecting,
      :cleared,
      :needs_review,
      :canceled

    event :collect_funds do
      transition created: :collecting
      transition collecting: :cleared, if: :funds_cleared?
    end

    event :cancel do
      transition [:created, :needs_review] => :canceled
    end
    event :put_into_review do
      transition (any - :needs_review) => :needs_review
    end
    event :remove_from_review do
      transition needs_review: :created
    end

    after_transition(&:commit_audit_log)
    after_failure(&:commit_audit_log)
  end

  timestamp_accessors(
    [
      [{to: "collecting"}, :funds_collecting_at],
      [{to: "cleared"}, :funds_cleared_at],
      [{to: "needs_review"}, :put_into_review_at],
      [{to: "canceled"}, :canceled_at],
    ],
  )
end

class FundingTransaction::AuditLog < Sequel::Model(:funding_transaction_audit_logs)
  plugin :state_machine_audit_log
  many_to_one :funding_transaction, class: "FundingTransaction"
end

# Table: funding_transaction_audit_logs
# ---------------------------------------------------------------------------------------------------------------------------------------
# Columns:
#  id                     | integer                  | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
#  at                     | timestamp with time zone | NOT NULL
#  event                  | text                     | NOT NULL
#  to_state               | text                     | NOT NULL
#  from_state             | text                     | NOT NULL
#  reason                 | text                     | NOT NULL DEFAULT ''::text
#  messages               | jsonb                    | DEFAULT '[]'::jsonb
#  funding_transaction_id | integer                  | NOT NULL
#  actor_id               | integer                  |

Then you can use it as below:

o = FundingTransaction.create
o.audit('New member', reason: 'fraud_detector')
o.process(:put_into_review)

# Someone reviews it
o.audit('Looks good')
o.process(:remove_from_review)

o.process(:collect_funds)
# expect(o).to transition_on(:collect_funds).to('collecting')

o.audit_logs
# {event: 'put_into_review', from_state: 'created', to_state: 'in_review', reason: 'fraud_detector'}
# {event: 'remove_from_review', from_state: 'in_review', to_state: 'created'}
# {event: 'collect_funds', from_state: 'created', to_state: 'collecting'}