All Projects → edisonywh → Gearbox

edisonywh / Gearbox

Licence: mit
⚙️ Gearbox is a functional state machine with an easy-to-use API, inspired by both Fsm and Machinery

Programming Languages

elixir
2628 projects

Projects that are alternatives of or similar to Gearbox

Finite
UI as finite-state machine
Stars: ✭ 99 (-29.79%)
Mutual labels:  state-machine
Zproc
Process on steroids
Stars: ✭ 112 (-20.57%)
Mutual labels:  state-machine
Qpcpp
QP/C++ real-time embedded framework/RTOS for embedded systems based on active objects (actors) and hierarchical state machines
Stars: ✭ 124 (-12.06%)
Mutual labels:  state-machine
Library
Collection of papers in the field of distributed systems, game theory, cryptography, cryptoeconomics, zero knowledge
Stars: ✭ 100 (-29.08%)
Mutual labels:  state-machine
Ws Machine
WS-Machine is a websocket finite state machine for client websocket connections (Go)
Stars: ✭ 110 (-21.99%)
Mutual labels:  state-machine
Core
UI-Router Core: Framework agnostic, State-based routing for JavaScript Single Page Apps
Stars: ✭ 112 (-20.57%)
Mutual labels:  state-machine
Makina
A simple hierarchical state machine compiler that generates C.
Stars: ✭ 93 (-34.04%)
Mutual labels:  state-machine
Squirrel
squirrel-foundation is a State Machine library, which provided a lightweight, easy use, type safe and programmable state machine implementation for Java.
Stars: ✭ 1,789 (+1168.79%)
Mutual labels:  state-machine
When Ts
When: recombinant design pattern for state machines based on gene expression with a temporal model
Stars: ✭ 112 (-20.57%)
Mutual labels:  state-machine
Workflowserver
Workflow Server is a ready-to-use Workflow Engine-based application that you can deploy into your infrastructure. It can be integrated with NodeJS, PHP, Ruby, .NET, or Java applications via a REST API. Workflow Server is a key component for managing the lifecycle of business objects within your enterprise.
Stars: ✭ 124 (-12.06%)
Mutual labels:  state-machine
Hsm
Finite state machine library based on the boost hana meta programming library. It follows the principles of the boost msm and boost sml libraries, but tries to reduce own complex meta programming code to a minimum.
Stars: ✭ 106 (-24.82%)
Mutual labels:  state-machine
Jungle
An embedded key-value store library specialized for building state machine and log store
Stars: ✭ 110 (-21.99%)
Mutual labels:  state-machine
Afsm
C++14 Finite State Machine library
Stars: ✭ 113 (-19.86%)
Mutual labels:  state-machine
Statemachine
Statemachine in PHP 5.6 / PHP 7
Stars: ✭ 99 (-29.79%)
Mutual labels:  state-machine
Smudge
A domain-specific language for state machines.
Stars: ✭ 126 (-10.64%)
Mutual labels:  state-machine
Workflow Swift
A Swift and Kotlin library for making composable state machines, and UIs driven by those state machines.
Stars: ✭ 92 (-34.75%)
Mutual labels:  state-machine
Rafcon
RAFCON (RMC advanced flow control) uses hierarchical state machines, featuring concurrent state execution, to represent robot programs. It ships with a graphical user interface supporting the creation of state machines and contains IDE like debugging mechanisms. Alternatively, state machines can programmatically be generated using RAFCON's API.
Stars: ✭ 112 (-20.57%)
Mutual labels:  state-machine
Hfsm2
High-Performance Hierarchical Finite State Machine Framework
Stars: ✭ 134 (-4.96%)
Mutual labels:  state-machine
Smacc
An Event-Driven, Asynchronous, Behavioral State Machine Library for real-time ROS (Robotic Operating System) applications written in C++
Stars: ✭ 129 (-8.51%)
Mutual labels:  state-machine
Kdstatemachineeditor
A framework for creating Qt State Machine metacode using a graphical user interface
Stars: ✭ 121 (-14.18%)
Mutual labels:  state-machine

Gearbox

Gearbox is a functional state machine with an easy-to-use API, inspired by both Fsm and Machinery.

Gearbox does not run in a process, so there's no potential for a GenServer bottleneck. This way there's also less overhead as you won't need to setup a supervision tree/manage your state machine processes.

Note: Gearbox is heavily inspired by Machinery, and also took inspiration from Fsm.

Gearbox is very similar to Machinery in term of the API usage, however it differs in the ways below:

  • Gearbox does not use a GenServer as a backing process. Since GenServer can be a potential bottleneck in a system, for that reason I think it's best to leave process management to users of the library.
  • No before/after callbacks. Callback allow you to add side effects, but side effects violate Single Responsibility Principle, and that can bring surprises to your codebase (e.g: "How come everytime this transition happens, X happens?"). Gearbox nudges you to keep domain-logic callbacks close to your contexts/domain events. Gearbox still ships with a guard_transition/3 callback, as that is intrinsic to state machines.
  • Gearbox does not ship with a Phoenix Dashboard view. A really cool and great concept, but more often than not it is not needed and the added dependency can prove more trouble than worth.

For a more detailed documentation, checkout the Gearbox's HexDoc.

Installation

Get the latest version from Hex

def deps do
  [
    {:gearbox, "~> 0.3.1"}
  ]
end

Usage

Gearbox's main API is Gearbox.transition/3. There's a bang! variant available too.

Example

Gearbox.transitions(%Order{}, PaymentMachine, "paid")

  • First Argument - an Elixir map, can be a struct or a non-struct.
  • Second Argument - a State Machine, read on to find out how to create a state machine.
  • Third Argument - the desired next state.

Here's how to create a state machine:

defmodule PaymentMachine do
  use Gearbox,
    field: :status, # used to retrieve the state of the given struct. Defaults to `:state`
    states: ~w(pending_payment paid refunded), # list of finite states in the state machine
    initial: "pending_payment", # initial state of the struct, if struct has `nil` state to begin with. Defaults to the first item of `:states`
    transitions: %{
      "pending_payment" => "paid",
      "paid" => "refunded",
    } # a map of possible transitions from `current_state` to `next_state`. `*` wildcard is allowed to indicate any states.
end

Rationale

Gearbox operates on the philosophy that it acts purely as a functional state machine, wherein it does not care where your state is store (e.g: Ecto, GenServer), all Gearbox does is to help you ensure state transitions happen the way you expect it to.

In most cases like for example Order, it is very likely that you don't need a process for that. Just get the record out of the database, run it through Gearbox machine, then persist it back to database.

In some rare cases where you need to have a stateful state machine, for example a traffic light that has an internal timer to shift from red (30s) -> green (30s) -> yellow (5s) -> red, you are better off to use an Agent/GenServer where you have better control over backpressuring/ business logics.

As of now, Gearbox does not provide a way to create events/actions in a state machine. This is because Gearbox is not a domain/context wrapper, events and actions that can trigger a state change should reside closer to your contexts, therefore I urge users to group these events as domain events (contexts), rather than state machine events.

Gearbox previously shipped with before_transition/3 and after_transition/3 in 0.1.0, but after some discussions I have decided to take a deliberate decision to remove callbacks. This is because callbacks by nature, allow you to add side effects, but side effects violate Single Responsibility Principle, and callbacks can often bring unintended surprises to your codebase (e.g: "How come everytime this transition happens, X happens?").

Therefore, Gearbox nudges you to keep domain/business-logic callbacks close to your contexts/domain events. Gearbox still ships with a guard_transition/3 callback, as that is intrinsic to state machines.

Features

Below lists a couple of features that Gearbox currently have.

State Transitions

The core of Gearbox. Allows you to transition a state from one to another (managed by your own machine).

defmodule Commerce do
  def pay(user, order) do
    # ...
    # Your payment logic
    {:ok, updated_order} = Gearbox.transition(order, PaymentMachine, "paid")
    # ...
  end
end

There's also a bang! variant of transition, Gearbox.transition!/3, so you can rewrite your code to like so:

defmodule Commerce do
  def pay(user, order) do
    # ...
    # Your payment logic
    order
    |> Gearbox.transition!(PaymentMachine, "paid")
    |> Repo.insert!
  end
end

Guard Transitions

Guard transitions enforces a condition to be passed before a transitions is committed.

A transition is halted if the function returns {:halt, reason}, it continues otherwise. The reason giving in {:halt, reason} will then propagate up to Gearbox.transition/3 as {:error, reason}.

# You can add condition check on both `from` and `to` states.
def guard_transition(struct, _from, _to) do
  case :rand.uniform() do
    val when val >= 0.5 ->
      # You can return anything
    _ ->
      {:halt, "You have been snapped."}
  end
end

Note that guard transitions will only be run if transition is valid.

Ecto Support

If your project uses Ecto, you can use the Gearbox.Ecto module to create a changeset based on the outcome of the transition. The original struct will not be modified.
A successful transition will return a changeset with the change applied, and an unsuccessful one will return a changeset with the appropriate error message.

# similar to the above example
def pay(user, order) do
  with {:ok, changeset} <- Gearbox.Ecto.transition_changeset(order, PaymentMachine, "paid")
  do
    Repo.update(changeset)
  else
    {:error, changeset} ->
      # handle error here
  end
end

Contributions

Contributions are very welcomed, but please first open an issue so we can align and discuss before any development begins.

License

View License

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].