0.01
No release in over a year
state machine for enums
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

 Project Readme

Enum machine

Enum machine is a library for defining enums and setting state machines for attributes in ActiveRecord models and plain Ruby classes.

You can visualize the state machine with enum_machine-contrib

Why not state_machines/aasm?

The aasm and state_machines gems suggest calling special methods to change the state. In practice, this can lead to errors. In enum_machine, the state is changed by updating the corresponding field, and the validation of the ability to change from one state to another is done in the after_validation callback. This allows the state of a model to be changed consistently with the usual save!. In addition aasm/state_machines add many autogenerated methods to the model class and instances. This makes it much more difficult to search by the project. Pollutes the method's space. Adds a bottleneck in method naming because you have to remember these methods in your code.

Performance comparison (see test/performance.rb)

Gem Method
enum_machine order.state.forming? 894921.3 i/s
state_machines order.forming? 189901.8 i/s - 4.71x slower
aasm order.forming? 127073.7 i/s - 7.04x slower
enum_machine order.state.can_closed? 473150.4 i/s
aasm order.may_to_closed? 24459.1 i/s - 19.34x slower
state_machines order.can_to_closed? 12136.8 i/s - 38.98x slower
enum_machine Order::STATE.values 6353820.4 i/s
aasm Order.aasm(:state).states.map(&:name) 131390.5 i/s - 48.36x slower
state_machines Order.state_machines[:state].states.map(&:value) 108449.7 i/s - 58.59x slower
enum_machine order.state = "forming" and order.valid? 13873.4 i/s
state_machines order.state_event = "to_forming" and order.valid? 6173.6 i/s - 2.25x slower
aasm order.to_forming 3095.9 i/s - 4.48x slower

Installation

Add to your Gemfile:

gem 'enum_machine'

Usage

Enums

# With ActiveRecord
class Product < ActiveRecord::Base
  enum_machine :color, %w(red green)
end

# Or with plain class
class Product
  include EnumMachine[color: { enum: %w[red green] }]
end

Product::COLOR.values # => ["red", "green"]
Product::COLOR::RED # => "red"
Product::COLOR::RED__GREEN # => ["red", "green"]

product = Product.new
product.color # => nil
product.color = 'red'
product.color.red? # => true

Aliases

class Product < ActiveRecord::Base
  enum_machine :state, %w[created approved published] do
    aliases(
      'forming' => %w[created approved],
    )
end

Product::STATE.forming # => %w[created approved]

product = Product.new(state: 'created')
product.state.forming? # => true

Transitions

class Product < ActiveRecord::Base
  enum_machine :color, %w[red green blue]
  enum_machine :state, %w[created approved cancelled activated] do
    transitions(
      nil                    => 'red',
      'created'              => [nil, 'approved'],
      %w[cancelled approved] => 'activated',
      'activated'            => %w[created cancelled],
    )

    # Will be executed in `before_save` callback
    before_transition 'created' => 'approved' do |product|
      product.color = 'green' if product.color.red?
    end

    # Will be executed in `after_save` callback
    after_transition %w[created] => %w[approved] do |product|
      product.color = 'red'
    end

    after_transition any => 'cancelled' do |product|
      product.cancelled_at = Time.zone.now
    end
  end
end

product = Product.create(state: 'created')
product.state.possible_transitions # => [nil, "approved"]
product.state.can_activated? # => false
product.state.to_activated! # => EnumMachine::Error: transition "created" => "activated" not defined in enum_machine
product.state.to_approved! # => true; equal to `product.update!(state: 'approved')`

I18n

ru.yml

ru:
  enums:
    product:
      color:
        red: Красный
        green: Зеленый
# ActiveRecord
class Product < ActiveRecord::Base
  enum_machine :color, %w(red green)
end

# Plain class
class Product
  # `i18n_scope` option must be explicitly set to use methods below
  include EnumMachine[color: { enum: %w[red green], i18n_scope: 'product' }]
end

Product::COLOR.human_name_for('red') # => 'Красный'
Product::COLOR.values_for_form # => [["Красный", "red"], ["Зеленый", "green"]]

product = Product.new(color: 'red')
product.color.human_name # => 'Красный'

I18n scope can be changed with i18n_scope option:

# For AciveRecord
class Product < ActiveRecord::Base
  enum_machine :color, %w(red green), i18n_scope: 'users.product'
end

# For plain class
class Product
  include EnumMachine[color: { enum: %w[red green], i18n_scope: 'users.product' }]
end

ru.yml

ru:
  enums:
    users:
      product:
        color:
          red: Красный
          green: Зеленый

License

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