{"id":13463325,"url":"https://github.com/gocardless/statesman","last_synced_at":"2025-05-11T13:58:52.600Z","repository":{"id":687001,"uuid":"12614368","full_name":"gocardless/statesman","owner":"gocardless","description":"A statesmanlike state machine library.","archived":false,"fork":false,"pushed_at":"2025-02-06T16:01:51.000Z","size":962,"stargazers_count":1812,"open_issues_count":34,"forks_count":160,"subscribers_count":121,"default_branch":"master","last_synced_at":"2025-05-11T13:58:43.992Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://gocardless.com/blog/statesman/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gocardless.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2013-09-05T09:42:17.000Z","updated_at":"2025-05-04T15:06:10.000Z","dependencies_parsed_at":"2024-04-22T07:53:43.615Z","dependency_job_id":"9982a542-9225-4674-b622-7ce55989da0f","html_url":"https://github.com/gocardless/statesman","commit_stats":{"total_commits":600,"total_committers":85,"mean_commits":"7.0588235294117645","dds":0.76,"last_synced_commit":"455a21dd74bb7d3b7555de0dd66e2ece9461c22d"},"previous_names":[],"tags_count":60,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fstatesman","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fstatesman/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fstatesman/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fstatesman/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gocardless","download_url":"https://codeload.github.com/gocardless/statesman/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253576262,"owners_count":21930169,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T13:00:50.987Z","updated_at":"2025-05-11T13:58:52.571Z","avatar_url":"https://github.com/gocardless.png","language":"Ruby","readme":"\u003c!-- markdownlint-disable no-inline-html first-line-heading --\u003e\n\u003cp align=\"center\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/110275/106792848-96e4ee80-664e-11eb-8fd1-16ff24b41eb2.png\" alt=\"Statesman\" width=\"512\"\u003e\u003c/p\u003e\n\u003c!-- markdownlint-enable no-inline-html first-line-heading --\u003e\n\nA statesmanlike state machine library.\n\nFor our policy on compatibility with Ruby and Rails versions, see [COMPATIBILITY.md](docs/COMPATIBILITY.md).\n\n[![Gem Version](https://badge.fury.io/rb/statesman.svg)](http://badge.fury.io/rb/statesman)\n[![CircleCI](https://circleci.com/gh/gocardless/statesman.svg?style=shield)](https://circleci.com/gh/gocardless/statesman)\n[![Code Climate](https://codeclimate.com/github/gocardless/statesman.svg)](https://codeclimate.com/github/gocardless/statesman)\n[![Gitter](https://badges.gitter.im/join.svg)](https://gitter.im/gocardless/statesman?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![SemVer](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=statesman\u0026package-manager=bundler\u0026version-scheme=semver\u0026previous-version=11.0.0\u0026new-version=12.0.0)](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=statesman\u0026package-manager=bundler\u0026version-scheme=semver\u0026previous-version=11.0.0\u0026new-version=12.0.0)\n\nStatesman is an opinionated state machine library designed to provide a robust\naudit trail and data integrity. It decouples the state machine logic from the\nunderlying model and allows for easy composition with one or more model classes.\n\nAs such, the design of statesman is a little different from other state machine\nlibraries:\n\n- State behaviour is defined in a separate, \"state machine\" class, rather than\nadded directly onto a model. State machines are then instantiated with the model\nto which they should apply.\n- State transitions are also modelled as a class, which can optionally be\npersisted to the database for a full audit history. This audit history can\ninclude JSON metadata set during a transition.\n- Database indices are used to offer database-level transaction duplication\nprotection.\n\n## Installation\n\nTo get started, just add Statesman to your `Gemfile`, and then run `bundle`:\n\n```ruby\ngem 'statesman', '~\u003e 12.0.0'\n```\n\n## Usage\n\nFirst, create a state machine based on `Statesman::Machine`:\n\n```ruby\nclass OrderStateMachine\n  include Statesman::Machine\n\n  state :pending, initial: true\n  state :checking_out\n  state :purchased\n  state :shipped\n  state :cancelled\n  state :failed\n  state :refunded\n\n  transition from: :pending,      to: [:checking_out, :cancelled]\n  transition from: :checking_out, to: [:purchased, :cancelled]\n  transition from: :purchased,    to: [:shipped, :failed]\n  transition from: :shipped,      to: :refunded\n\n  guard_transition(to: :checking_out) do |order|\n    order.products_in_stock?\n  end\n\n  before_transition(from: :checking_out, to: :cancelled) do |order, transition|\n    order.reallocate_stock\n  end\n\n  before_transition(to: :purchased) do |order, transition|\n    PaymentService.new(order).submit\n  end\n\n  after_transition(to: :purchased) do |order, transition|\n    MailerService.order_confirmation(order).deliver\n  end\nend\n```\n\nThen, link it to your model:\n\n```ruby\nclass Order \u003c ActiveRecord::Base\n  has_many :order_transitions, autosave: false\n\n  include Statesman::Adapters::ActiveRecordQueries[\n    transition_class: OrderTransition,\n    initial_state: :pending\n  ]\n\n  def state_machine\n    @state_machine ||= OrderStateMachine.new(self, transition_class: OrderTransition)\n  end\nend\n```\n\nNext, you'll need to create a further model to represent state transitions:\n\n```ruby\nclass OrderTransition \u003c ActiveRecord::Base\n  include Statesman::Adapters::ActiveRecordTransition\n\n  validates :to_state, inclusion: { in: OrderStateMachine.states }\n\n  belongs_to :order, inverse_of: :order_transitions\nend\n```\n\nNow, you can start working with your state machine:\n\n```ruby\nOrder.first.state_machine.current_state # =\u003e \"pending\"\nOrder.first.state_machine.allowed_transitions # =\u003e [\"checking_out\", \"cancelled\"]\nOrder.first.state_machine.can_transition_to?(:cancelled) # =\u003e true/false\nOrder.first.state_machine.transition_to(:cancelled, optional: :metadata) # =\u003e true/false\nOrder.first.state_machine.transition_to!(:cancelled) # =\u003e true/exception\nOrder.first.state_machine.last_transition # =\u003e transition model or nil\nOrder.first.state_machine.last_transition_to(:pending) # =\u003e transition model or nil\n\nOrder.in_state(:cancelled) # =\u003e [#\u003cOrder id: \"123\"\u003e]\nOrder.not_in_state(:checking_out) # =\u003e [#\u003cOrder id: \"123\"\u003e]\n```\n\nIf you'd like, you can also define a template for a generic state machine, then alter classes which extend it as required:\n\n```ruby\nmodule Template\n  def define_states\n    state :a, initial: true\n    state :b\n    state :c\n  end\n\n  def define_transitions\n    transition from: :a, to: :b\n    transition from: :b, to: :c\n    transition from: :c, to: :a\n  end\nend\n\nclass Circular\n  include Statesman::Machine\n  extend Template\n\n  define_states\n  define_transitions\nend\n\nclass Linear\n  include Statesman::Machine\n  extend Template\n\n  define_states\n  define_transitions\n\n  remove_transitions from: :c, to: :a\nend\n\nclass Shorter\n  include Statesman::Machine\n  extend Template\n\n  define_states\n  define_transitions\n\n  remove_state :c\nend\n```\n\n## Persistence\n\nBy default Statesman stores transition history in memory only. It can be\npersisted by configuring Statesman to use a different adapter. For example,\nfor ActiveRecord within Rails:\n\n`config/initializers/statesman.rb`:\n\n```ruby\nStatesman.configure do\n  storage_adapter(Statesman::Adapters::ActiveRecord)\nend\n```\n\nGenerate the transition model:\n\n```bash\nrails g statesman:active_record_transition Order OrderTransition\n```\n\nYour transition class should\n`include Statesman::Adapters::ActiveRecordTransition` if you're using the\nActiveRecord adapter.\n\nIf you're using the ActiveRecord adapter and decide not to include the default\n`updated_at` column in your transition table, you'll need to configure the\n`updated_timestamp_column` option on the transition class, setting it to another column\nname (e.g. `:updated_on`) or `nil`.\n\nAnd add an association from the parent model:\n\n`app/models/order.rb`:\n\n```ruby\nclass Order \u003c ActiveRecord::Base\n  has_many :transitions, class_name: \"OrderTransition\", autosave: false\n\n  # Initialize the state machine\n  def state_machine\n    @state_machine ||= OrderStateMachine.new(self, transition_class: OrderTransition,\n                                                   association_name: :transitions)\n  end\n\n  # Optionally delegate some methods\n\n  delegate :can_transition_to?,\n           :current_state, :history, :last_transition, :last_transition_to,\n           :transition_to!, :transition_to, :in_state?, to: :state_machine\nend\n```\n\n### Using PostgreSQL JSON column\n\nBy default, Statesman uses `serialize` to store the metadata in JSON format.\nIt is also possible to use the PostgreSQL JSON column if you are using Rails 4\nor 5. To do that\n\n- Change `metadata` column type in the transition model migration to `json` or `jsonb`\n\n  ```ruby\n  # Before\n  t.text :metadata, default: \"{}\"\n  # After (Rails 4)\n  t.json :metadata, default: \"{}\"\n  # After (Rails 5)\n  t.json :metadata, default: {}\n  ```\n\n- Remove the `include Statesman::Adapters::ActiveRecordTransition` statement from\n  your transition model. (If you want to customise your transition class's \"updated\n  timestamp column\", as described above, you should define a\n  `.updated_timestamp_column` method on your class and return the name of the column\n  as a symbol, or `nil` if you don't want to record an updated timestamp on\n  transitions.)\n\n## Configuration\n\n### `storage_adapter`\n\n```ruby\nStatesman.configure do\n  storage_adapter(Statesman::Adapters::ActiveRecord)\nend\n```\n\nStatesman defaults to storing transitions in memory. If you're using rails, you\ncan instead configure it to persist transitions to the database by using the\nActiveRecord adapter.\n\nStatesman will fallback to memory unless you specify a transition_class when instantiating your state machine. This allows you to only persist transitions on certain state machines in your app.\n\n### `initial_transition`\n\n```ruby\ndef state_machine\n  @state_machine ||= OrderStateMachine.new(self, transition_class: OrderTransition,\n                                                 association_name: :transitions,\n                                                 initial_transition: true)\nend\n```\n\nBy default Statesman does not record a transition to the initial state of the state machine.\n\nYou can configure Statesman to record a transition to the initial state, this will allow you to:\n- Keep an accurate record of the initial state even if configuration changes\n- Keep a record of how long the state machine spent in the initial state\n- Utilise a transition hook for the transition to the initial state\n\n## Class methods\n\n### `Machine.state`\n\n```ruby\nMachine.state(:some_state, initial: true)\nMachine.state(:another_state)\n```\n\nDefine a new state and optionally mark as the initial state.\n\n### `Machine.transition`\n\n```ruby\nMachine.transition(from: :some_state, to: :another_state)\n```\n\nDefine a transition rule. Both method parameters are required, `to` can also be\nan array of states (`.transition(from: :some_state, to: [:another_state, :some_other_state])`).\n\n### `Machine.guard_transition`\n\n```ruby\nMachine.guard_transition(from: :some_state, to: :another_state) do |object|\n  object.some_boolean?\nend\n```\n\nDefine a guard. `to` and `from` parameters are optional, a nil parameter means\nguard all transitions. The passed block should evaluate to a boolean and must\nbe idempotent as it could be called many times. The guard will pass when it\nevaluates to a truthy value and fail when it evaluates to a falsey value (`nil` or `false`).\n\n### `Machine.before_transition`\n\n```ruby\nMachine.before_transition(from: :some_state, to: :another_state) do |object|\n  object.side_effect\nend\n```\n\nDefine a callback to run before a transition. `to` and `from` parameters are\noptional, a nil parameter means run before all transitions. This callback can\nhave side-effects as it will only be run once immediately before the transition.\n\n### `Machine.after_transition`\n\n```ruby\nMachine.after_transition(from: :some_state, to: :another_state) do |object, transition|\n  object.side_effect\nend\n```\n\nDefine a callback to run after a successful transition. `to` and `from`\nparameters are optional, a nil parameter means run after all transitions. The\nmodel object and transition object are passed as arguments to the callback.\nThis callback can have side-effects as it will only be run once immediately\nafter the transition.\n\nIf you specify `after_commit: true`, the callback will be executed once the\ntransition has been committed to the database.\n\n### `Machine.after_transition_failure`\n\n```ruby\nMachine.after_transition_failure(from: :some_state, to: :another_state) do |object, exception|\n  Logger.info(\"transition to #{exception.to} failed for #{object.id}\")\nend\n```\n\nDefine a callback to run if `Statesman::TransitionFailedError` is raised\nduring the execution of transition callbacks. `to` and `from`\nparameters are optional, a nil parameter means run after all transitions.\nThe model object, and exception are passed as arguments to the callback.\nThis is executed outside of the transaction wrapping other callbacks.\nIf using `transition!` the exception is re-raised after these callbacks are\nexecuted.\n\n### `Machine.after_guard_failure`\n\n```ruby\nMachine.after_guard_failure(from: :some_state, to: :another_state) do |object, exception|\n  Logger.info(\"guard failed during transition to #{exception.to} for #{object.id}\")\nend\n```\n\nDefine a callback to run if `Statesman::GuardFailedError` is raised\nduring the execution of guard callbacks. `to` and `from`\nparameters are optional, a nil parameter means run after all transitions.\nThe model object, and exception are passed as arguments to the callback.\nThis is executed outside of the transaction wrapping other callbacks.\nIf using `transition!` the exception is re-raised after these callbacks are\nexecuted.\n\n### `Machine.new`\n\n```ruby\nmy_machine = Machine.new(my_model, transition_class: MyTransitionModel)\n```\n\nInitialize a new state machine instance. `my_model` is required. If using the\nActiveRecord adapter `my_model` should have a `has_many` association with\n`MyTransitionModel`.\n\n### `Machine.retry_conflicts`\n\n```ruby\nMachine.retry_conflicts { instance.transition_to(:new_state) }\n```\n\nAutomatically retry the given block if a `TransitionConflictError` is raised.\nIf you know you want to retry a transition if it fails due to a race condition\ncall it from within this block. Takes an (optional) argument for the maximum\nnumber of retry attempts (defaults to 1).\n\n### `Machine.states`\n\nReturns an array of all possible state names as strings.\n\n### `Machine.successors`\n\nReturns a hash of states and the states it is valid for them to transition to.\n\n```ruby\nMachine.successors\n\n{\n  \"pending\" =\u003e [\"checking_out\", \"cancelled\"],\n  \"checking_out\" =\u003e [\"purchased\", \"cancelled\"],\n  \"purchased\" =\u003e [\"shipped\", \"failed\"],\n  \"shipped\" =\u003e [\"refunded\"]\n}\n```\n\n## Instance methods\n\n### `Machine#current_state`\n\nReturns the current state based on existing transition objects.\n\nTakes an optional keyword argument to force a reload of data from the\ndatabase.\ne.g `current_state(force_reload: true)`\n\n### `Machine#in_state?(:state_1, :state_2, ...)`\n\nReturns true if the machine is in any of the given states.\n\n### `Machine#history`\n\nReturns a sorted array of all transition objects.\n\n### `Machine#last_transition`\n\nReturns the most recent transition object.\n\n### `Machine#last_transition_to(:state)`\n\nReturns the most recent transition object to a given state.\n\n### `Machine#allowed_transitions`\n\nReturns an array of states you can `transition_to` from current state.\n\n### `Machine#can_transition_to?(:state)`\n\nReturns true if the current state can transition to the passed state and all\napplicable guards pass.\n\n### `Machine#transition_to!(:state)`\n\nTransition to the passed state, returning `true` on success. Raises\n`Statesman::GuardFailedError` or `Statesman::TransitionFailedError` on failure.\n\n### `Machine#transition_to(:state)`\n\nTransition to the passed state, returning `true` on success. Swallows all\nStatesman exceptions and returns false on failure. (NB. if your guard or\ncallback code throws an exception, it will not be caught.)\n\n## Errors\n\n### Initialization errors\n\nThese errors are raised when the Machine and/or Model is initialized. A simple spec like\n\n```ruby\nexpect { OrderStateMachine.new(Order.new, transition_class: OrderTransition) }.to_not raise_error\n```\n\nwill expose these errors as part of your test suite\n\n#### InvalidStateError\n\nRaised if:\n\n- Attempting to define a transition without a `to` state.\n- Attempting to define a transition with a non-existent state.\n- Attempting to define multiple states as `initial`.\n\n#### InvalidTransitionError\n\nRaised if:\n\n- Attempting to define a callback `from` a state that has no valid transitions (A terminal state).\n- Attempting to define a callback `to` the `initial` state if that state has no transitions to it.\n- Attempting to define a callback with `from` and `to` where any of the pairs have no transition between them.\n\n#### InvalidCallbackError\n\nRaised if:\n\n- Attempting to define a callback without a block.\n\n#### UnserializedMetadataError\n\nRaised if:\n\n- ActiveRecord is configured to not serialize the `metadata` attribute into\n  to Database column backing it. See the `Using PostgreSQL JSON column` section.\n\n#### IncompatibleSerializationError\n\nRaised if:\n\n- There is a mismatch between the column type of the `metadata` in the\n  Database and the model. See the `Using PostgreSQL JSON column` section.\n\n#### MissingTransitionAssociation\n\nRaised if:\n\n- The model that `Statesman::Adapters::ActiveRecordQueries` is included in\n  does not have a `has_many` association to the `transition_class`.\n\n### Runtime errors\n\nThese errors are raised by `transition_to!`. Using `transition_to` will\nsuppress `GuardFailedError` and `TransitionFailedError` and return `false` instead.\n\n#### GuardFailedError\n\nRaised if:\n\n- A guard callback between `from` and `to` state returned a falsey value.\n\n#### TransitionFailedError\n\nRaised if:\n\n- A transition is attempted but `current_state -\u003e new_state` is not a valid pair.\n\n#### TransitionConflictError\n\nRaised if:\n\n- A database conflict affecting the `sort_key` or `most_recent` columns occurs\n  when attempting a transition.\n  Retried automatically if it occurs wrapped in `retry_conflicts`.\n\n## Model scopes\n\nA mixin is provided for the ActiveRecord adapter which adds scopes to easily\nfind all models currently in (or not in) a given state. Include it into your\nmodel and passing in `transition_class` and `initial_state` as options.\n\nIn 4.1.2 and below, these two options had to be defined as methods on the model,\nbut 5.0.0 and above allow this style of configuration as well.\nThe old method pollutes the model with extra class methods, and is deprecated,\nto be removed in 6.0.0.\n\n```ruby\nclass Order \u003c ActiveRecord::Base\n  has_many :order_transitions, autosave: false\n  include Statesman::Adapters::ActiveRecordQueries[\n    transition_class: OrderTransition,\n    initial_state: OrderStateMachine.initial_state\n  ]\nend\n```\n\nIf the transition class-name differs from the association name, you will also\nneed to pass `transition_name` as an option:\n\n```ruby\nclass Order \u003c ActiveRecord::Base\n  has_many :transitions, class_name: \"OrderTransition\", autosave: false\n  include Statesman::Adapters::ActiveRecordQueries[\n    transition_class: OrderTransition,\n    initial_state: OrderStateMachine.initial_state,\n    transition_name: :transitions\n  ]\nend\n```\n\n### `Model.in_state(:state_1, :state_2, etc)`\n\nReturns all models currently in any of the supplied states.\n\n### `Model.not_in_state(:state_1, :state_2, etc)`\n\nReturns all models not currently in any of the supplied states.\n\n### `Model.most_recent_transition_join`\n\nThis joins the model to its most recent transition whatever that may be.\nWe expose this method to ease use of ActiveRecord's `or` e.g\n\n```ruby\nModel.in_state(:state_1).or(\n  Model.most_recent_transition_join.where(model_field: 123)\n)\n```\n\n## Frequently Asked Questions\n\n### Storing the state on the model object\n\nIf you wish to store the model state on the model directly, you can keep it up\nto date using an `after_transition` hook.\nCombine it with the `after_commit` option to ensure the model state will only be\nsaved once the transition has made it irreversibly to the database:\n\n```ruby\nafter_transition(after_commit: true) do |model, transition|\n  model.state = transition.to_state\n  model.save!\nend\n```\n\nYou could also use a calculated column or view in your database.\n\n### Accessing metadata from the last transition\n\nGiven a field `foo` that was stored in the metadata, you can access it like so:\n\n```ruby\nmodel_instance.state_machine.last_transition.metadata[\"foo\"]\n```\n\n### Events\n\nUsed to using a state machine with \"events\"? Support for events is provided by\nthe [statesman-events](https://github.com/gocardless/statesman-events) gem. Once\nthat's included in your Gemfile you can include event functionality in your\nstate machine as follows:\n\n```ruby\nclass OrderStateMachine\n  include Statesman::Machine\n  include Statesman::Events\n\n  ...\nend\n```\n\n### Deleting records\n\nIf you need to delete the Parent model regularly you will need to change\neither the association deletion behaviour or add a `DELETE CASCADE` condition\nto foreign key in your database.\n\nE.g\n\n```ruby\nhas_many :order_transitions, autosave: false, dependent: :destroy\n```\n\nor when migrating the transition model\n\n```ruby\nadd_foreign_key :order_transitions, :orders, on_delete: :cascade\n```\n\n## Testing Statesman Implementations\n\nThis answer was abstracted from [this issue](https://github.com/gocardless/statesman/issues/77).\n\nAt GoCardless we focus on testing that:\n\n- guards correctly prevent / allow transitions\n- callbacks execute when expected and perform the expected actions\n\n### Testing Guards\n\nGuards can be tested by asserting that `transition_to!` does or does not raise a `Statesman::GuardFailedError`:\n\n```ruby\ndescribe \"guards\" do\n  it \"cannot transition from state foo to state bar\" do\n    expect { some_model.transition_to!(:bar) }.to raise_error(Statesman::GuardFailedError)\n  end\n\n  it \"can transition from state foo to state baz\" do\n    expect { some_model.transition_to!(:baz) }.to_not raise_error\n  end\nend\n```\n\n### Testing Callbacks\n\nCallbacks are tested by asserting that the action they perform occurs:\n\n```ruby\ndescribe \"some callback\" do\n  it \"adds one to the count property on the model\" do\n    expect { some_model.transition_to!(:some_state) }.\n      to change { some_model.reload.count }.\n      by(1)\n  end\nend\n```\n\n## Compatibility with type checkers\n\nIncluding ActiveRecordQueries to your model can cause issues with type checkers\nsuch as Sorbet, this is because this technically is using a dynamic include,\nwhich is not supported by Sorbet.\n\nTo avoid these issues you can instead include the TypeSafeActiveRecordQueries\nmodule and pass in configuration.\n\n```ruby\nclass Order \u003c ActiveRecord::Base\n  has_many :order_transitions, autosave: false\n\n  include Statesman::Adapters::TypeSafeActiveRecordQueries\n\n  configure_state_machine transition_class: OrderTransition,\n                          initial_state: :pending\n\n  def state_machine\n    @state_machine ||= OrderStateMachine.new(self, transition_class: OrderTransition)\n  end\nend\n```\n\n# Third-party extensions\n\n[statesman-sequel](https://github.com/badosu/statesman-sequel) - An adapter to make Statesman work with [Sequel](https://github.com/jeremyevans/sequel)\n\n---\n\nGoCardless ♥ open source. If you do too, come [join us](https://gocardless.com/about/careers/).\n","funding_links":[],"categories":["Rails Plugins","Web 后端","Ruby","State Machines","Gems","Libraries"],"sub_categories":["State Machines","Ruby"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgocardless%2Fstatesman","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgocardless%2Fstatesman","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgocardless%2Fstatesman/lists"}