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



173
174
175
# File 'lib/has_state_machine/state.rb', line 173

def possible_transitions
  @possible_transitions || []
end

.stateObject



177
178
179
# File 'lib/has_state_machine/state.rb', line 177

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.



193
194
195
196
197
198
199
200
201
202
203
# File 'lib/has_state_machine/state.rb', line 193

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)


181
182
183
# File 'lib/has_state_machine/state.rb', line 181

def transactional?
  @transactional || false
end

.transientsObject



185
186
187
# File 'lib/has_state_machine/state.rb', line 185

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!Boolean

Same as #perform_transition!, but wrapped in a transaction so callbacks can roll the transition back.

Returns:

  • (Boolean)

    whether or not the transition succeeded



107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/has_state_machine/state.rb', line 107

def perform_transactional_transition! # rubocop:disable Naming/PredicateMethod -- public API
  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
  return false unless object.reload.public_send(object.state_attribute) == state

  enqueue_transition_commit_callbacks
  true
end

#perform_transition!Boolean

Makes the actual transition from one state to the next and runs the before and after transition callbacks.

Returns:

  • (Boolean)

    whether or not the transition succeeded



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

def perform_transition! # rubocop:disable Naming/PredicateMethod -- public API
  transitioned = run_callbacks :transition do
    object.update("#{object.state_attribute}": state)
  end
  return false unless transitioned

  @previous_state = previous_state
  enqueue_transition_commit_callbacks
  true
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, which runs once a successful transition is committed to the database.



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.symbolize_keys
  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