simplestate 1.0.6 → 2.0.2
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 +4 -4
- data/.gitattributes +17 -17
- data/.travis.yml +7 -7
- data/CHANGE_NOTES.md +47 -0
- data/README.md +185 -188
- data/button_irb_session.txt +8 -0
- data/lib/simplestate/null_state.rb +33 -0
- data/lib/simplestate/state.rb +36 -19
- data/lib/simplestate/state_history.rb +18 -0
- data/lib/simplestate/state_holder.rb +49 -42
- data/lib/simplestate/version.rb +3 -3
- data/lib/simplestate.rb +7 -5
- data/simplestate.gemspec +41 -41
- metadata +13 -10
- data/lib/simplestate/nil_state.rb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: de2abc1192e14cd5e30017a8e650f25821def00a
|
|
4
|
+
data.tar.gz: a3ee59402a6bf2780a345873fbfdd605570a7c3a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d59c31bfabcd9379ea482c780516093b2fe42d261ae96054d6683d98d5325e397d72332e4b4084591332628e38999542e8aa7dfeae5b29e32f55a704b605e0f
|
|
7
|
+
data.tar.gz: 9fb22a378838a4240f3145356fc12d54667a0d9fa30d9f8d420cc181a021288782024c03e411cffa41a4610f4eb7e42f54e886bd8cdabe30bf6268eaaf154c58
|
data/.gitattributes
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
# Auto detect text files and perform LF normalization
|
|
2
|
-
* text=auto
|
|
3
|
-
|
|
4
|
-
# Custom for Visual Studio
|
|
5
|
-
*.cs diff=csharp
|
|
6
|
-
|
|
7
|
-
# Standard to msysgit
|
|
8
|
-
*.doc diff=astextplain
|
|
9
|
-
*.DOC diff=astextplain
|
|
10
|
-
*.docx diff=astextplain
|
|
11
|
-
*.DOCX diff=astextplain
|
|
12
|
-
*.dot diff=astextplain
|
|
13
|
-
*.DOT diff=astextplain
|
|
14
|
-
*.pdf diff=astextplain
|
|
15
|
-
*.PDF diff=astextplain
|
|
16
|
-
*.rtf diff=astextplain
|
|
17
|
-
*.RTF diff=astextplain
|
|
1
|
+
# Auto detect text files and perform LF normalization
|
|
2
|
+
* text=auto
|
|
3
|
+
|
|
4
|
+
# Custom for Visual Studio
|
|
5
|
+
*.cs diff=csharp
|
|
6
|
+
|
|
7
|
+
# Standard to msysgit
|
|
8
|
+
*.doc diff=astextplain
|
|
9
|
+
*.DOC diff=astextplain
|
|
10
|
+
*.docx diff=astextplain
|
|
11
|
+
*.DOCX diff=astextplain
|
|
12
|
+
*.dot diff=astextplain
|
|
13
|
+
*.DOT diff=astextplain
|
|
14
|
+
*.pdf diff=astextplain
|
|
15
|
+
*.PDF diff=astextplain
|
|
16
|
+
*.rtf diff=astextplain
|
|
17
|
+
*.RTF diff=astextplain
|
data/.travis.yml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
language: ruby
|
|
2
|
-
rvm:
|
|
3
|
-
- "1.
|
|
4
|
-
- "
|
|
5
|
-
- "2.
|
|
6
|
-
- rbx
|
|
7
|
-
before_install: gem install bundler -v 1.11.2
|
|
1
|
+
language: ruby
|
|
2
|
+
rvm:
|
|
3
|
+
- "2.1.0"
|
|
4
|
+
- "2.2.3"
|
|
5
|
+
- "2.3.1"
|
|
6
|
+
- rbx
|
|
7
|
+
before_install: gem install bundler -v 1.11.2
|
data/CHANGE_NOTES.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#### SimpleState version 2.0.0
|
|
2
|
+
Ruby version requirement has changed to: __>= 2.1.0__ . (Ruby 2.0.0 can be used with some minor modification: Default values must be provided for all keyword arguments.)
|
|
3
|
+
|
|
4
|
+
State:
|
|
5
|
+
States are represented as 'singleton' instances of their respective classes and are referenced by their symbolized names.
|
|
6
|
+
|
|
7
|
+
StateHolder:
|
|
8
|
+
The alternative syntax 'start\_in' to specify the initial state to the state holder is no longer supported.
|
|
9
|
+
The setter __#hx\_size\_limit=__ is no longer supported.
|
|
10
|
+
The method __#set\_current\_state__ is no longer supported. This bypassed the transition logic and could lead to an inaccurate state history.
|
|
11
|
+
__#start__ is provided to put the state\_holder in it's initial state. This must be called after creation of the stateholder and the associated states. The stateholder will not respond to events until this is called.
|
|
12
|
+
|
|
13
|
+
StateHistory:
|
|
14
|
+
This has been added to separate the responsibility for management of state history from StateHolder.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
#### SimpleState version 1.0.0
|
|
19
|
+
A state holder tracks the history of state transitions in an array accessed via __#state_history__. The array size defaults to 5. The last item in the array will be the most recent previous state instance. The size may be set at holder creation in the opts hash (:hx\_size\_limit). The history size limit has a getter and a setter defined as well. (__#hx\_size\_limit=__ and __#hx\_size\_limit__).
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
class Button < StateHolder
|
|
23
|
+
...
|
|
24
|
+
def prior_state
|
|
25
|
+
state_history.last.class
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Then in tests for example:
|
|
30
|
+
def setup
|
|
31
|
+
@button = Button.new(start_in: Off, color: 'Red', hz_size_limit: 3)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_a_button_returns_its_last_prior_state
|
|
35
|
+
@button.press # Curr state: On, Prior state: Off
|
|
36
|
+
@button.press # Curr state: Off, Prior state: On
|
|
37
|
+
assert_equal On, @button.prior_state
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Please note that the State instance method, __#previous\_state\_class__, has been removed in this release.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
#### SimpleState version 0.3.0
|
|
46
|
+
The 0.3.0 version contained a serious code smell: A state was expected to know about the history of state transitions. However, a state should know only the states to which it may transition and it's holder to support triggering those transitions. Knowlege of the transition history belongs with the state holder, if it is tracked at all.
|
|
47
|
+
|
data/README.md
CHANGED
|
@@ -1,188 +1,185 @@
|
|
|
1
|
-
[](https://badge.fury.io/rb/simplestate) [](https://travis-ci.org/dpneumo/simplestate)
|
|
2
|
-
[](https://codeclimate.com/github/dpneumo/simplestate)
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
####
|
|
164
|
-
The
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
## License
|
|
187
|
-
|
|
188
|
-
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
|
1
|
+
[](https://badge.fury.io/rb/simplestate) [](https://travis-ci.org/dpneumo/simplestate)
|
|
2
|
+
[](https://codeclimate.com/github/dpneumo/simplestate)
|
|
3
|
+
|
|
4
|
+
## Ruby Version requirement: >= 2.1.0
|
|
5
|
+
|
|
6
|
+
# Simplestate
|
|
7
|
+
```ruby
|
|
8
|
+
class Button < StateHolder
|
|
9
|
+
attr_reader :name
|
|
10
|
+
def initialize(initial_state: :Off, state_history: StateHistory.new, opts: {})
|
|
11
|
+
@name = opts.fetch :name
|
|
12
|
+
super
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def transition_to(state)
|
|
16
|
+
super(state)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def prior_state
|
|
20
|
+
history[-2] || :NullState
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Off < State
|
|
25
|
+
def press
|
|
26
|
+
transition_to(:On)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
def enter
|
|
31
|
+
buzz
|
|
32
|
+
# turn_light_off
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def exit
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def buzz(sec: 0.5)
|
|
39
|
+
# sound buzzer for sec seconds
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class On < State
|
|
44
|
+
def press
|
|
45
|
+
transition_to(:Off)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
def enter
|
|
50
|
+
flash(3)
|
|
51
|
+
# turn_button_light_on
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def exit
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def flash(n)
|
|
58
|
+
# n.times { turn_button_light_on_then_off }
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
button = Button.new(initial_state: :Off)
|
|
63
|
+
on_state = On.new(holder: button)
|
|
64
|
+
off_state = Off.new(holder: button)
|
|
65
|
+
|
|
66
|
+
button.start #=> current_state: Off
|
|
67
|
+
button.press #=> current_state: On
|
|
68
|
+
button.press #=> current_state: Off
|
|
69
|
+
```
|
|
70
|
+
# Description
|
|
71
|
+
Simplestate arose out of a desire for a very low ceremony mechanism to implement a state machine. SimpleDelegator (delegate.rb) is used to implement this. Because SimpleDelegator supports dynamically swapping the object to which methods are delegated, it provides a good base for Simplestate.
|
|
72
|
+
|
|
73
|
+
The current version (2.0.0) is a rewrite of SimpleState. State logic is provided via instances of the appropriate subclasses of State. A state is referenced via a symbol created by symbolizing the state's name. The State class maintains a list of state instances available for transitions in a hash keyed on the state symbols. This is an incomplete variant of the singleton pattern. It solved some issues I was having with the previous version of SimpleState, but please use some care. For example, there is no logic to actually prevent creation of more than one instance of a given State subclass. Doing this would likely create bugs that will eventually bite you. :-(
|
|
74
|
+
|
|
75
|
+
The StateHolder class provides the required functionality for a basic state machine: methods to set the initial state and to transition to a new state. To complement this a State class is provided to serve as ancestor to the states of the state holder. A State instance stores a reference to the state holder and a private __#transition\_to__ method which simply calls the state holder’s __#transition\_to__.
|
|
76
|
+
|
|
77
|
+
StateHolder and State are not expected to be used directly. Rather, they are intended to be the ancestors of the actual state holder and states. A state should provide only methods that are unique to it. Methods that are not specific to a state should be provided by the state holder. The public methods of a child state act as receivers of event messages via delegation from the state holder. Such events may cause effects that are managed by the current state and may also cause transition to a new state. State change logic is expected to be held within the current state. Two private methods, __#enter__ and __#exit__, *must* be provided by each state. These are called by the state holder __#transition_to__ method at the appropriate points in the state life cycle. Neither __#enter__ nor __#exit__ nor any other private method of a state are intended to be called by a user of the state holder. To avoid potentially stepping on the user's code __#enter__ and __#exit__ are called with __\_\_send\_\___.
|
|
78
|
+
|
|
79
|
+
For convenience StateHolder provides instance methods, __#current_state__, __#history__, and __#hx_size_limit__ to provide easy access to the underlying method, __\_\_getobj\_\___ of SimpleDelegator, and to the read methods of the StateHistory container. The StateHistory container holds a list of the most recent state transitions from the state holder. Because the transition history could grow quite large that history is limited to the last 10 transitions by default. The history size limit may be changed via __#hx_size_limit__ at creation of the state holder. That limit may NOT be changed later
|
|
80
|
+
|
|
81
|
+
Simplestate does not provide a DSL for specifying the events, states and allowed state transitions. That logic must be specified within each state. Neither does Simplestate provide any mechanism for serialization. There is no "magic" here. It is just a couple of PORO's. As such, it is very easy to see and to reason about what is happening within Simplestate. It should not be too difficult to add serialization support to Simplestate.
|
|
82
|
+
|
|
83
|
+
As an aside, I have looked into providing the Simplestate functionality via a module. However, I found that the SimpleDelegator class provides delegation via a mechanism that makes a module based Simplestate implementation very difficult to achieve. The complexity of that implementation seemed to be not worth the effort. I think the [StatePattern](https://github.com/dcadenas/state_pattern) gem by Daniel Cadenas, the inspiration for Simplestate, ran into just this problem. I chose to avoid that issue by relying solely on inheritance.
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
## Installation
|
|
87
|
+
|
|
88
|
+
Add this line to your application's Gemfile:
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
gem 'simplestate'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
And then execute:
|
|
95
|
+
|
|
96
|
+
$ bundle install
|
|
97
|
+
|
|
98
|
+
Or install it yourself as:
|
|
99
|
+
|
|
100
|
+
$ gem install simplestate
|
|
101
|
+
|
|
102
|
+
## Usage
|
|
103
|
+
|
|
104
|
+
#### State Holder
|
|
105
|
+
Inherit from StateHolder to create the class of the object that will hold states. You may provide a default initial_state. You *must* call super if you provide an initialize method in your holder class:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
class Button < StateHolder
|
|
109
|
+
attr_reader :color
|
|
110
|
+
def initialize( initial_state: :Off, opts: {} )
|
|
111
|
+
@color = opts.fetch :color
|
|
112
|
+
super
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# button methods here
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Creation of a StateHolder instance *must* specify the initial state. StateHolder expects to receive the initial state as a symbol based on the state name. The name case is preserved in creating the symbol.:
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
button = Button.new( initial_state: :Off )
|
|
123
|
+
```
|
|
124
|
+
If you want to set additional attributes at creation of a new button, do so within the opts hash when new is called. Set the attribute from the opts hash in initialize.
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
button = Button.new( initial_state: :Off,
|
|
128
|
+
opts: {color: 'Red'} )
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### States
|
|
132
|
+
|
|
133
|
+
Inherit from State to create a class to provide specific state behaviors:
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
class Off < State
|
|
137
|
+
def press
|
|
138
|
+
transition_to(:On)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def name
|
|
142
|
+
'Off'
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
private
|
|
146
|
+
def enter
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def exit
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
```
|
|
153
|
+
The subclassed state must provide *private* __#enter__ and __#exit__ methods. Any other state methods intended to be available via a method call on the state holder must be public. The private methods, __#enter__ and __#exit__, will always be called appropriately during state transitions.
|
|
154
|
+
|
|
155
|
+
A state has access to methods on the state holder via __#holder__:
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
holder.specialmethod
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
However, since a state should have minimal knowledge about the inner workings of it's holder, it is best to limit calls to holder to __#transition\_to__.
|
|
162
|
+
|
|
163
|
+
#### Usage Example
|
|
164
|
+
The button module (test/dummys/button.rb) provides an example of the usage of Simplestate. Tests of this are provided in button\_test.rb.
|
|
165
|
+
|
|
166
|
+
## Alternatives
|
|
167
|
+
|
|
168
|
+
If a DSL is desired, complex state functionality is required, events may arrive asynchronously from multiple sources, or state machine functionality must be provided via inclusion of a module rather than via inheritance then Simplestate is probably not appropriate. Consider looking at the [Statemachine](https://github.com/pluginaweek/state\_machine), [AASM](https://github.com/aasm/aasm) or [Workflow](https://github.com/geekq/workflow) gems. [The Ruby Toolbox](https://www.ruby-toolbox.com/categories/state\_machines.html) provides links to several other statemachine implementations.
|
|
169
|
+
|
|
170
|
+
## Development
|
|
171
|
+
|
|
172
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
173
|
+
|
|
174
|
+
StateHolderInterfaceTest and StateInterfaceTest are provided to verify that your state holder and state instances respond to the minimum required method calls for each. Simply include these in the tests of your decendents of StateHolder and State and in any dummies of these classes you may use in your tests.
|
|
175
|
+
|
|
176
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
177
|
+
|
|
178
|
+
## Contributing
|
|
179
|
+
|
|
180
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/dpneumo/simplestate. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
|
185
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
[loco@bear simplestate]$ irb
|
|
2
|
+
irb(main):001:0> require_relative 'test/dummys/button' => true
|
|
3
|
+
irb(main):002:0> launcher = Runner.new => #<Runner:0x007ff16b8de4d8 ...
|
|
4
|
+
irb(main):003:0> launcher.launch_sequence_begin => true
|
|
5
|
+
irb(main):004:0> launcher.launch_is_go => true
|
|
6
|
+
irb(main):005:0> launcher.launch => true
|
|
7
|
+
irb(main):006:0> launcher.launch_button.state_history.list => [:NullState, :Off, :On, :Off]
|
|
8
|
+
irb(main):007:0> launcher.launch_button.current_state.name => "Off"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
class NullState
|
|
2
|
+
# Simplifies call via super to SimpleDelegator during StateHolder#new
|
|
3
|
+
# Avoid chicken and egg problem by mimicing rather than inheriting from State
|
|
4
|
+
|
|
5
|
+
attr_reader :holder
|
|
6
|
+
def initialize(holder: nil, opts: {})
|
|
7
|
+
@holder = holder
|
|
8
|
+
State.list[self.symbol]= self
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.list
|
|
12
|
+
{}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def name
|
|
16
|
+
'NullState'
|
|
17
|
+
end
|
|
18
|
+
alias :to_s :name
|
|
19
|
+
|
|
20
|
+
def symbol
|
|
21
|
+
:NullState
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
def transition_to(state)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def enter
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def exit
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/simplestate/state.rb
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
|
-
class State
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
class State
|
|
2
|
+
@list = {}
|
|
3
|
+
|
|
4
|
+
def self.list
|
|
5
|
+
@list
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
attr_reader :holder
|
|
10
|
+
def initialize(holder:, opts: {})
|
|
11
|
+
@holder = holder
|
|
12
|
+
State.list[self.symbol]= self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def name
|
|
16
|
+
'State'
|
|
17
|
+
end
|
|
18
|
+
alias :to_s :name
|
|
19
|
+
|
|
20
|
+
def symbol
|
|
21
|
+
name.to_sym
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
def transition_to(state)
|
|
26
|
+
holder.transition_to(state)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def enter
|
|
30
|
+
raise NotImplementedError, "#enter was called on an instance of State either directly or via super."
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def exit
|
|
34
|
+
raise NotImplementedError, "#exit was called on an instance of State either directly or via super."
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class StateHistory
|
|
2
|
+
attr_reader :hx_size_limit
|
|
3
|
+
|
|
4
|
+
def initialize(hx_size_limit: 10)
|
|
5
|
+
@hx_size_limit = hx_size_limit
|
|
6
|
+
@container = []
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def <<(state_symbol)
|
|
10
|
+
@container << state_symbol
|
|
11
|
+
@container = @container.last(hx_size_limit)
|
|
12
|
+
self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def list
|
|
16
|
+
@container
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -1,42 +1,49 @@
|
|
|
1
|
-
class StateHolder < SimpleDelegator
|
|
2
|
-
attr_reader :
|
|
3
|
-
|
|
4
|
-
def initialize(opts
|
|
5
|
-
@
|
|
6
|
-
@
|
|
7
|
-
|
|
8
|
-
super(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
1
|
+
class StateHolder < SimpleDelegator
|
|
2
|
+
attr_reader :initial_state, :state_history
|
|
3
|
+
|
|
4
|
+
def initialize(initial_state:, state_history: StateHistory.new, opts: {})
|
|
5
|
+
@initial_state = initial_state
|
|
6
|
+
@state_history = state_history
|
|
7
|
+
|
|
8
|
+
super(NullState.new)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def start
|
|
12
|
+
state_history << current_state.symbol
|
|
13
|
+
transition_to(initial_state)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def transition_to(state)
|
|
17
|
+
leave_old_state
|
|
18
|
+
enter_new_state(state)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Convenience methods
|
|
22
|
+
def current_state
|
|
23
|
+
__getobj__
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def history
|
|
27
|
+
state_history.list
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def hx_size_limit
|
|
31
|
+
state_history.hx_size_limit
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
def leave_old_state
|
|
36
|
+
current_state.__send__(:exit)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def enter_new_state(state)
|
|
40
|
+
self.current_state = state
|
|
41
|
+
state_history << current_state.symbol
|
|
42
|
+
current_state.__send__(:enter)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def current_state=(state)
|
|
46
|
+
state_obj = State.list[state]
|
|
47
|
+
__setobj__(state_obj)
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/simplestate/version.rb
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
module Simplestate
|
|
2
|
-
VERSION = "
|
|
3
|
-
end
|
|
1
|
+
module Simplestate
|
|
2
|
+
VERSION = "2.0.2"
|
|
3
|
+
end
|
data/lib/simplestate.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
require "delegate"
|
|
2
|
-
require "simplestate/version"
|
|
3
|
-
require "simplestate/state_holder"
|
|
4
|
-
require "simplestate/
|
|
5
|
-
require "simplestate/
|
|
1
|
+
require "delegate"
|
|
2
|
+
require "simplestate/version"
|
|
3
|
+
require "simplestate/state_holder"
|
|
4
|
+
require "simplestate/state_history"
|
|
5
|
+
require "simplestate/state"
|
|
6
|
+
require "simplestate/null_state"
|
|
7
|
+
|
data/simplestate.gemspec
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
-
require 'simplestate/version'
|
|
5
|
-
|
|
6
|
-
Gem::Specification.new do |spec|
|
|
7
|
-
spec.name = "simplestate"
|
|
8
|
-
spec.version = Simplestate::VERSION
|
|
9
|
-
spec.authors = ["Mitchell C Kuppinger"]
|
|
10
|
-
spec.email = ["dpneumo@gmail.com"]
|
|
11
|
-
|
|
12
|
-
spec.summary = %q{A SimpleDelegator based implementation of the State Pattern}
|
|
13
|
-
spec.description = %q{Provides a base State class and a subclassed SimpleDelegator modified to provide state pattern support.}
|
|
14
|
-
spec.homepage = "https://github.com/dpneumo/simplestate"
|
|
15
|
-
spec.license = "MIT"
|
|
16
|
-
|
|
17
|
-
# This gem will work with 1.
|
|
18
|
-
spec.required_ruby_version = '>= 1.
|
|
19
|
-
|
|
20
|
-
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
|
21
|
-
# delete this section to allow pushing this gem to any host.
|
|
22
|
-
if spec.respond_to?(:metadata)
|
|
23
|
-
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
|
24
|
-
else
|
|
25
|
-
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
29
|
-
spec.bindir = "exe"
|
|
30
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
31
|
-
spec.require_paths = ["lib"]
|
|
32
|
-
|
|
33
|
-
spec.add_development_dependency "bundler", "
|
|
34
|
-
spec.add_development_dependency "rake", "
|
|
35
|
-
spec.add_development_dependency "minitest", "
|
|
36
|
-
|
|
37
|
-
# Uncomment these to make pry available in testing
|
|
38
|
-
# Also make necessary changes in test_helper.rb
|
|
39
|
-
#spec.add_development_dependency "pry"
|
|
40
|
-
#spec.add_development_dependency "pry-byebug"
|
|
41
|
-
end
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'simplestate/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "simplestate"
|
|
8
|
+
spec.version = Simplestate::VERSION
|
|
9
|
+
spec.authors = ["Mitchell C Kuppinger"]
|
|
10
|
+
spec.email = ["dpneumo@gmail.com"]
|
|
11
|
+
|
|
12
|
+
spec.summary = %q{A SimpleDelegator based implementation of the State Pattern}
|
|
13
|
+
spec.description = %q{Provides a base State class and a subclassed SimpleDelegator modified to provide state pattern support.}
|
|
14
|
+
spec.homepage = "https://github.com/dpneumo/simplestate"
|
|
15
|
+
spec.license = "MIT"
|
|
16
|
+
|
|
17
|
+
# This gem will work with 2.1.0 or greater...
|
|
18
|
+
spec.required_ruby_version = '>= 2.1.0'
|
|
19
|
+
|
|
20
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
|
21
|
+
# delete this section to allow pushing this gem to any host.
|
|
22
|
+
if spec.respond_to?(:metadata)
|
|
23
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
|
24
|
+
else
|
|
25
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
29
|
+
spec.bindir = "exe"
|
|
30
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
31
|
+
spec.require_paths = ["lib"]
|
|
32
|
+
|
|
33
|
+
spec.add_development_dependency "bundler", ">= 1.11"
|
|
34
|
+
spec.add_development_dependency "rake", ">= 10.0"
|
|
35
|
+
spec.add_development_dependency "minitest", ">= 5.0"
|
|
36
|
+
|
|
37
|
+
# Uncomment these to make pry available in testing
|
|
38
|
+
# Also make necessary changes in test_helper.rb
|
|
39
|
+
#spec.add_development_dependency "pry"
|
|
40
|
+
#spec.add_development_dependency "pry-byebug"
|
|
41
|
+
end
|
metadata
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: simplestate
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mitchell C Kuppinger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
11
|
+
date: 2016-09-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: '1.11'
|
|
20
20
|
type: :development
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- - "
|
|
24
|
+
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '1.11'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rake
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: '10.0'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '10.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: minitest
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- - "
|
|
45
|
+
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
47
|
version: '5.0'
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- - "
|
|
52
|
+
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '5.0'
|
|
55
55
|
description: Provides a base State class and a subclassed SimpleDelegator modified
|
|
@@ -63,6 +63,7 @@ files:
|
|
|
63
63
|
- ".gitattributes"
|
|
64
64
|
- ".gitignore"
|
|
65
65
|
- ".travis.yml"
|
|
66
|
+
- CHANGE_NOTES.md
|
|
66
67
|
- CODE_OF_CONDUCT.md
|
|
67
68
|
- Gemfile
|
|
68
69
|
- LICENSE.txt
|
|
@@ -70,9 +71,11 @@ files:
|
|
|
70
71
|
- Rakefile
|
|
71
72
|
- bin/console
|
|
72
73
|
- bin/setup
|
|
74
|
+
- button_irb_session.txt
|
|
73
75
|
- lib/simplestate.rb
|
|
74
|
-
- lib/simplestate/
|
|
76
|
+
- lib/simplestate/null_state.rb
|
|
75
77
|
- lib/simplestate/state.rb
|
|
78
|
+
- lib/simplestate/state_history.rb
|
|
76
79
|
- lib/simplestate/state_holder.rb
|
|
77
80
|
- lib/simplestate/version.rb
|
|
78
81
|
- simplestate.gemspec
|
|
@@ -89,7 +92,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
89
92
|
requirements:
|
|
90
93
|
- - ">="
|
|
91
94
|
- !ruby/object:Gem::Version
|
|
92
|
-
version: 1.
|
|
95
|
+
version: 2.1.0
|
|
93
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
97
|
requirements:
|
|
95
98
|
- - ">="
|