queued_state_machine 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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,5 @@
1
+ module QueuedStateMachine
2
+ class InvalidTransition < StandardError
3
+
4
+ end
5
+ end
@@ -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: []