Class: HasStateMachine::State

Inherits:
String
  • Object
show all
Extended by:
ActiveModel::Callbacks, ActiveModel::Model
Includes:
ActiveModel::Validations
Defined in:
lib/has_state_machine/state.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(object, transient_values = {}) ⇒ State

Initializes the HasStateMachine::State instance.

Examples:

state = Workflow::Post::Draft.new(post) #=> "draft"


35
36
37
38
39
40
41
42
43
# File 'lib/has_state_machine/state.rb', line 35

def initialize(object, transient_values = {})
  @object = object

  transient_values.to_h.slice(*transients).each do |transient, value|
    instance_variable_set(:"@#{transient}", value)
  end

  super(state)
end

Instance Attribute Details

#objectObject (readonly)

Returns the value of attribute object.



11
12
13
# File 'lib/has_state_machine/state.rb', line 11

def object
  @object
end

#stateObject (readonly)

Returns the value of attribute state.



11
12
13
# File 'lib/has_state_machine/state.rb', line 11

def state
  @state
end

Class Method Details

.possible_transitionsObject



152
153
154
# File 'lib/has_state_machine/state.rb', line 152

def possible_transitions
  @possible_transitions || []
end

.stateObject



156
157
158
# File 'lib/has_state_machine/state.rb', line 156

def state
  to_s.demodulize.underscore
end

.state_options(transitions_to: [], transactional: false, transients: []) ⇒ Object

Set the options for the HasStateMachine::State classes to define the possible states the current state can transition to and whether or not transitioning to the state should be performed within a transaction.



172
173
174
175
176
177
178
179
180
181
182
# File 'lib/has_state_machine/state.rb', line 172

def state_options(transitions_to: [], transactional: false, transients: [])
  @possible_transitions = transitions_to.map(&:to_s)
  @transactional = transactional
  @transients = transients.map(&:to_sym)

  transients.each do |transient_name|
    define_method(transient_name) do
      instance_variable_get(:"@#{transient_name}")
    end
  end
end

.transactional?Boolean

Returns:

  • (Boolean)


160
161
162
# File 'lib/has_state_machine/state.rb', line 160

def transactional?
  @transactional || false
end

.transientsObject



164
165
166
# File 'lib/has_state_machine/state.rb', line 164

def transients
  @transients || []
end

Instance Method Details

#can_transition?(desired_state) ⇒ Boolean

Determines if the given desired state exists in the predetermined list of allowed transitions.

Parameters:

  • desired_state (String, Symbol)

    the state to check if the object can transition to

Returns:

  • (Boolean)

    whether or not the object can transition to the desired state



50
51
52
# File 'lib/has_state_machine/state.rb', line 50

def can_transition?(desired_state)
  possible_transitions.include? desired_state.to_s
end

#perform_transactional_transition!Object

Makes the actual transition from one state to the next and runs the before and after transition callbacks in a transaction to allow for roll backs. The after_transition_commit callbacks run outside the transaction and only when it commits (not on rollback).



104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/has_state_machine/state.rb', line 104

def perform_transactional_transition!
  run_callbacks :transition_commit do
    ActiveRecord::Base.transaction(requires_new: true, joinable: false) do
      run_callbacks :transition do
        rollback_transition unless object.update("#{object.state_attribute}": state)
      end
    end

    @previous_state = previous_state

    object.reload.public_send(object.state_attribute) == state
  end
end

#perform_transition!Object

Makes the actual transition from one state to the next and runs the before and after transition callbacks. The after_transition_commit callbacks run after the update completes and only when it succeeds.



91
92
93
94
95
96
97
# File 'lib/has_state_machine/state.rb', line 91

def perform_transition!
  run_callbacks :transition_commit do
    run_callbacks :transition do
      object.update("#{object.state_attribute}": state)
    end
  end
end

#possible_transitionsObject

possible_transitions - Retrieves the next available transitions for a given state. transactional? - Determines whether or not the transition should happen with a transactional block. state - The underscored name of the state transients - Specified list of optional transient attributes on this state



28
# File 'lib/has_state_machine/state.rb', line 28

delegate :possible_transitions, :transactional?, :state, :transients, to: "self.class"

#transitionObject

Defines the before_transition and after_transition callbacks for use on a HasStateMachine::State instance.



16
# File 'lib/has_state_machine/state.rb', line 16

define_model_callbacks :transition, only: %i[before after]

#transition_commitObject

Defines the after_transition_commit callback. It runs only after a transition has committed.



21
# File 'lib/has_state_machine/state.rb', line 21

define_model_callbacks :transition_commit, only: %i[after]

#transition_to(desired_state, **options) ⇒ Boolean

Checks to see if the desired state is valid and then gives responsibility to the desired state’s instance to make the transition.

Parameters:

  • desired_state (String)

    the state to transition to

  • options (Hash)

    a hash of additional options for transitioning the object

Returns:

  • (Boolean)

    whether or not the transition took place



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/has_state_machine/state.rb', line 64

def transition_to(desired_state, **options)
  transitioned = false
  options = options.transform_keys(&:to_sym)
  desired_state_instance = state_instance(desired_state, options)

  with_transition_options(options) do
    return false unless valid_transition?(desired_state_instance)

    transitioned = if desired_state_instance.transactional?
      desired_state_instance.perform_transactional_transition!
    else
      desired_state_instance.perform_transition!
    end
  end

  transitioned
ensure
  (desired_state_instance&.errors || []).each do |error|
    object.errors.add(error.attribute, error.type)
  end
end