No commit activity in last 3 years
No release in over 3 years
A state machine plugin on top of Cequel
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 0

Runtime

>= 0
 Project Readme

CequelStatefulEnum

cequel_stateful_enum is a simple state machine gem built on top of Cequel's built-in enum column. This gem is totally based on stateful_enum gem for ActiveRecord. Huge thanks to Akira Matsuda! This gem in not depends on Rails.

Installation

Add this line to your app's Gemfile:

gem 'cequel_stateful_enum'

And bundle.

Usage

The cequel_stateful_enum gem extends Cequel's column definition to take a block with a similar DSL to the state_machine gem.

Example:

class Bug
  include Cequel::Record

  column :status, :enum, values: { unassigned: 0, assigned: 1, resolved: 2, closed: 3 } do
    event :assign do
      transition :unassigned => :assigned
    end

    event :resolve do
      before do
        self.resolved_at = Time.now
      end

      transition [:unassigned, :assigned] => :resolved
    end

    event :close do
      transition all - [:closed] => :closed

      after :notify_author_about_status

      after do
        BugCache.remove_from_open_cache(self)
      end
    end
  end

  # ...

end

Defining the States

Just call the Cequel's column method with :enum type. The only difference from the original method is that our column call takes a block.

Defining the Events

You can declare events through event method inside of an column block. Then cequel_stateful_enum defines the following methods per each event:

An instance method to fire the event

@bug.assign # does nothing and returns false if a valid transition for the current state is not defined

An instance method with ! to fire the event

@bug.assign! # raises if a valid transition for the current state is not defined

A predicate method that returns if the event is fireable

@bug.can_assign? # returns if the `assign` event can be called on this bug or not and all `if` and `unless` conditions are met

Defining the Transitions

You can define state transitions through transition method inside of an event block.

There are a few important details to note regarding this feature:

  • The transition method takes a Hash each key of which is state "from" transitions to the Hash value.
  • The "from" states and the "to" states should both be given in Symbols.
  • The "from" state can be multiple states, in which case the key can be given as an Array of states, as shown in the usage example.
  • The "from" state can be all that means all defined states.

:if and :unless Condition

The transition method takes an :if and/or :unless option as a Proc or Symbol.

Example:

event :assign do
  transition :unassigned => :assigned, if: -> { assigned_to.any? }, unless: :blocked?
end

Saving the model

When firing the event model will be saved by default. To prevent saving use save: false attribute:

@bug.assign(save: false) # => true

If model can't be saved (for example, it is in invalid state) and there was no save: false parameter passed, event will return false, but field will be changed and before callbacks will be fired. Can method does not check is the model valid:

@bug.subject = '' # Invalid subject
@bug.can_resolve? # => true
@bug.resolve # => false
@bug.updated_at # Not saved
@bug.status # => :resolved
@bug.resolved_at # Setted by after callback

Danger mode

If you should control the bang and non-bang behavour with variable, you can use danger: value parameter, where value is true or false. Parameter is accepted on both event and can methods:

@bug.assign(danger: true) # Same as @bug.assign!
@bug.can_assign?(danger: true) # Will raise error if can't

Event Hooks

You can define before and after event hooks inside of an event block as shown in the example above. Symbols and Proc objects are supported.

Contributing

Pull requests are welcome on GitHub at https://github.com/Xanders/cequel_stateful_enum.

License

The gem is available as open source under the terms of the MIT License.