queued_state_machine 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/queued_state_machine/invalid_transition.rb +5 -0
- data/lib/queued_state_machine.rb +151 -0
- metadata +46 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ee5afccef7f058cfd32f2c50641359447a77287c
|
4
|
+
data.tar.gz: 4125d5ef26e850d7854c3116f96e599cc1e1d0a0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3102ab88418edb28b591044971e0d26b7ae3afc6fb1bb60479d1659f62af425a90932cf343d95767ed93e06ffaaf5912675a95ea930485c7af3ec97c43c9fc49
|
7
|
+
data.tar.gz: f33a5a56f2c9aec627c8a967b478d03007b0347a57c529d4ac4cecaae5fa1b8f4891567efc02dd872772880db5d0e1fb9f00d6c8fca34b7d24c0e05f3bf07e95
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'queued_state_machine/invalid_transition'
|
2
|
+
|
3
|
+
module QueuedStateMachine
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
attr_reader :initial_state
|
12
|
+
|
13
|
+
def inherited(subclass)
|
14
|
+
subclass.instance_variable_set("@states", instance_variable_get("@states").dup)
|
15
|
+
subclass.instance_variable_set("@initial_state", instance_variable_get("@initial_state"))
|
16
|
+
subclass.instance_variable_set("@transitions", instance_variable_get("@transitions").dup)
|
17
|
+
subclass.instance_variable_set("@callbacks", instance_variable_get("@callbacks").dup)
|
18
|
+
end
|
19
|
+
|
20
|
+
def states
|
21
|
+
@states ||= []
|
22
|
+
end
|
23
|
+
|
24
|
+
def transitions
|
25
|
+
@transitions ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
def callbacks
|
29
|
+
@callbacks ||= []
|
30
|
+
end
|
31
|
+
|
32
|
+
def state(name, initial: false)
|
33
|
+
unless transitions.empty? && callbacks.empty?
|
34
|
+
raise 'Error: all states must be defined before any transitions of callbacks'
|
35
|
+
end
|
36
|
+
name = clean_state(name)
|
37
|
+
self.initial_state = name if initial
|
38
|
+
states << name
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def transition(from: nil, to: nil)
|
43
|
+
if block_given?
|
44
|
+
raise "Error: transitions do not accept blocks, use 'on_transition'"
|
45
|
+
end
|
46
|
+
from = clean_state_list(from)
|
47
|
+
to = clean_state_list(to)
|
48
|
+
missing_state = (from + to).find { |state| !states.include?(state) }
|
49
|
+
if missing_state
|
50
|
+
raise "Error: state #{missing_state} not defined"
|
51
|
+
end
|
52
|
+
transitions << {from: from, to: to}
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def on_transition(from: nil, to: nil, &block)
|
57
|
+
unless block_given?
|
58
|
+
'Error: no callback defined'
|
59
|
+
end
|
60
|
+
from = clean_state_list(from)
|
61
|
+
to = clean_state_list(to)
|
62
|
+
missing_state = (from + to).find { |state| !states.include?(state) }
|
63
|
+
if missing_state
|
64
|
+
raise "Error: state #{missing_state} not defined"
|
65
|
+
end
|
66
|
+
callbacks << {from: from, to: to, callback: block}
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def clean_state(name)
|
71
|
+
name.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
def clean_state_list(arg)
|
75
|
+
case
|
76
|
+
when arg == nil
|
77
|
+
states
|
78
|
+
when arg.respond_to?(:to_a)
|
79
|
+
list = arg
|
80
|
+
list.map! {|name| clean_state(name) }
|
81
|
+
list
|
82
|
+
else
|
83
|
+
name = arg
|
84
|
+
[clean_state(name)]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def initial_state
|
89
|
+
raise 'Error: no initial state set' unless @initial_state
|
90
|
+
@initial_state
|
91
|
+
end
|
92
|
+
|
93
|
+
def initial_state=(name)
|
94
|
+
raise 'Error: initial state already set' if @initial_state
|
95
|
+
@initial_state = name
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
def initialize
|
101
|
+
super
|
102
|
+
@state = self.class.initial_state
|
103
|
+
@pending_transitions = []
|
104
|
+
end
|
105
|
+
|
106
|
+
def to(state, quiet: false)
|
107
|
+
from = @state
|
108
|
+
to = self.class.clean_state(state)
|
109
|
+
@pending_transitions << {from: from, to: to, quiet: quiet}
|
110
|
+
return if @pending_transitions.length > 1
|
111
|
+
loop do
|
112
|
+
next_transition = @pending_transitions.first
|
113
|
+
break unless next_transition
|
114
|
+
@state = next_transition[:to]
|
115
|
+
unless next_transition[:quiet]
|
116
|
+
process_transition(next_transition[:from], next_transition[:to])
|
117
|
+
end
|
118
|
+
@pending_transitions.shift
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def quietly_to(state)
|
123
|
+
to(state, quiet: true)
|
124
|
+
end
|
125
|
+
|
126
|
+
def at
|
127
|
+
@state
|
128
|
+
end
|
129
|
+
|
130
|
+
def at?(arg)
|
131
|
+
list = self.class.clean_state_list(arg)
|
132
|
+
raise if list == nil
|
133
|
+
list.include?(@state)
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def process_transition(from, to)
|
139
|
+
valid_transitions = self.class.transitions.select do |transition|
|
140
|
+
transition[:from].include?(from) && transition[:to].include?(to)
|
141
|
+
end
|
142
|
+
if valid_transitions.empty?
|
143
|
+
raise InvalidTransition, "Error: no valid transition from '#{from}' to '#{to}'"
|
144
|
+
end
|
145
|
+
valid_callbacks = self.class.callbacks.select do |transition|
|
146
|
+
transition[:from].include?(from) && transition[:to].include?(to)
|
147
|
+
end
|
148
|
+
valid_callbacks.each { |callback| instance_eval(&callback[:callback]) }
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: queued_state_machine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rob Fors
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-02-25 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A state machine that supports queuing any callbacks that are triggered
|
14
|
+
during a callback.
|
15
|
+
email: mail@robfors.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/queued_state_machine.rb
|
21
|
+
- lib/queued_state_machine/invalid_transition.rb
|
22
|
+
homepage: https://github.com/robfors/queued_state_machine
|
23
|
+
licenses:
|
24
|
+
- MIT
|
25
|
+
metadata: {}
|
26
|
+
post_install_message:
|
27
|
+
rdoc_options: []
|
28
|
+
require_paths:
|
29
|
+
- lib
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
requirements: []
|
41
|
+
rubyforge_project:
|
42
|
+
rubygems_version: 2.4.8
|
43
|
+
signing_key:
|
44
|
+
specification_version: 4
|
45
|
+
summary: A state machine supporting reentry.
|
46
|
+
test_files: []
|