Module: Statesman::Machine

Defined in:
lib/statesman/machine.rb

Overview

The main module, that should be extended in to state machine classes.

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



12
13
14
15
# File 'lib/statesman/machine.rb', line 12

def self.included(base)
  base.extend(ClassMethods)
  base.send(:attr_reader, :object)
end

.retry_conflicts(max_retries = 1) ⇒ Object

Retry any transitions that fail due to a TransitionConflictError



18
19
20
21
22
23
24
25
26
27
# File 'lib/statesman/machine.rb', line 18

def self.retry_conflicts(max_retries = 1)
  retry_attempt = 0

  begin
    yield
  rescue TransitionConflictError
    retry_attempt += 1
    retry_attempt <= max_retries ? retry : raise
  end
end

Instance Method Details

#allowed_transitions(metadata = {}) ⇒ Object



278
279
280
281
282
# File 'lib/statesman/machine.rb', line 278

def allowed_transitions( = {})
  successors_for(current_state).select do |state|
    can_transition_to?(state, )
  end
end

#can_transition_to?(new_state, metadata = {}) ⇒ Boolean

Returns:

  • (Boolean)


292
293
294
295
296
297
298
299
# File 'lib/statesman/machine.rb', line 292

def can_transition_to?(new_state,  = {})
  validate_transition(from: current_state,
                      to: new_state,
                      metadata: )
  true
rescue TransitionFailedError, GuardFailedError
  false
end

#current_state(force_reload: false) ⇒ Object



269
270
271
272
# File 'lib/statesman/machine.rb', line 269

def current_state(force_reload: false)
  last_action = last_transition(force_reload: force_reload)
  last_action ? last_action.to_state : self.class.initial_state
end

#execute(phase, initial_state, new_state, transition) ⇒ Object



329
330
331
332
# File 'lib/statesman/machine.rb', line 329

def execute(phase, initial_state, new_state, transition)
  callbacks = callbacks_for(phase, from: initial_state, to: new_state)
  callbacks.each { |cb| cb.call(@object, transition) }
end

#execute_on_failure(phase, initial_state, new_state, exception) ⇒ Object



324
325
326
327
# File 'lib/statesman/machine.rb', line 324

def execute_on_failure(phase, initial_state, new_state, exception)
  callbacks = callbacks_for(phase, from: initial_state, to: new_state)
  callbacks.each { |cb| cb.call(@object, exception) }
end

#historyObject



301
302
303
# File 'lib/statesman/machine.rb', line 301

def history
  @storage_adapter.history
end

#in_state?(*states) ⇒ Boolean

Returns:

  • (Boolean)


274
275
276
# File 'lib/statesman/machine.rb', line 274

def in_state?(*states)
  states.flatten.any? { |state| current_state == state.to_s }
end

#initialize(object, options = { transition_class: Statesman::Adapters::MemoryTransition, initial_transition: false, }) ⇒ Object



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/statesman/machine.rb', line 249

def initialize(object,
               options = {
                 transition_class: Statesman::Adapters::MemoryTransition,
                 initial_transition: false,
               })
  @object = object
  @transition_class = options[:transition_class]
  @storage_adapter = adapter_class(@transition_class).new(
    @transition_class, object, self, options
  )

  if options[:initial_transition]
    if history.empty? && self.class.initial_state
      @storage_adapter.create(nil, self.class.initial_state)
    end
  end

  send(:after_initialize) if respond_to? :after_initialize
end

#last_transition(force_reload: false) ⇒ Object



284
285
286
# File 'lib/statesman/machine.rb', line 284

def last_transition(force_reload: false)
  @storage_adapter.last(force_reload: force_reload)
end

#last_transition_to(state) ⇒ Object



288
289
290
# File 'lib/statesman/machine.rb', line 288

def last_transition_to(state)
  history.reverse.find { |transition| transition.to_state.to_sym == state.to_sym }
end

#resetObject



340
341
342
# File 'lib/statesman/machine.rb', line 340

def reset
  @storage_adapter.reset
end

#transition_to(new_state, metadata = {}) ⇒ Object



334
335
336
337
338
# File 'lib/statesman/machine.rb', line 334

def transition_to(new_state,  = {})
  transition_to!(new_state, )
rescue TransitionFailedError, GuardFailedError
  false
end

#transition_to!(new_state, metadata = {}) ⇒ Object



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/statesman/machine.rb', line 305

def transition_to!(new_state,  = {})
  initial_state = current_state
  new_state = new_state.to_s

  validate_transition(from: initial_state,
                      to: new_state,
                      metadata: )

  @storage_adapter.create(initial_state, new_state, )

  true
rescue TransitionFailedError => e
  execute_on_failure(:after_transition_failure, initial_state, new_state, e)
  raise
rescue GuardFailedError => e
  execute_on_failure(:after_guard_failure, initial_state, new_state, e)
  raise
end