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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 98120d72e903183f217fc3f3b3ab4e4ef41f3aa8
4
- data.tar.gz: 1c02dc9ec7c5830b9701a86e23593aa9fd1810df
3
+ metadata.gz: de2abc1192e14cd5e30017a8e650f25821def00a
4
+ data.tar.gz: a3ee59402a6bf2780a345873fbfdd605570a7c3a
5
5
  SHA512:
6
- metadata.gz: 9348c67eb908105f981aea17d1998d6ed535e5bb613b790e6558169481615610a3ac71b0c648f0db8f842f7549aed870208d6056edb4f21ec65f23308f4e649e
7
- data.tar.gz: ccee6da7323d35f86f36c55a6d905866ebe544f574ad3d981e2fa3637c6d20d7b403b352527334363e2611868fb573de7760d59a9c16c5d205c3482fa0a7a441
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.9.2"
4
- - "1.9.3"
5
- - "2.2.3"
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
- [![Gem Version](https://badge.fury.io/rb/simplestate.svg)](https://badge.fury.io/rb/simplestate) [![Build Status](https://travis-ci.org/dpneumo/simplestate.svg?branch=master)](https://travis-ci.org/dpneumo/simplestate)
2
- [![Code Climate](https://codeclimate.com/github/dpneumo/simplestate/badges/gpa.svg)](https://codeclimate.com/github/dpneumo/simplestate)
3
-
4
- # Simplestate
5
- ````ruby
6
- class Button < StateHolder
7
- def initialize(opts={})
8
- super
9
- end
10
- end
11
-
12
- class Off < State
13
- def press; transition_to(On); end
14
- private
15
- def enter
16
- # send_message_to_log("entering off state")
17
- # turn_light_off
18
- end
19
-
20
- def exit
21
- # send_message_to_log("leaving off state")
22
- end
23
- end
24
-
25
- class On < State
26
- def press; transition_to(Off); end
27
- private
28
- def enter
29
- # send_message_to_log("entering on state")
30
- # turn_light_on
31
- end
32
-
33
- def exit
34
- # send_message_to_log("leaving on state")
35
- end
36
- end
37
-
38
- button = Button.new(start_in: Off)
39
- button.press #=> current_state: On
40
- button.press #=> current_state: Off
41
- ````
42
- # Description
43
- 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.
44
-
45
- 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 machine. A State instance stores a reference to the state holder and a __#transition_to__ method which simply calls the state holder's __#transition_to__.
46
-
47
- StateHolder and State are not expected to be used directly. Rather, they are intended to be inherited from. The child state holder should provide methods not specific to its current state. A child state should provide methods specific to that state. 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.
48
-
49
- For convenience StateHolder provides instance methods, __#current_state__ and __#set_new_state__, to provide easy access to the underlying methods, __\_\_getobj\_\___ and __\_\_setobj\_\___, of SimpleDelegator. A method to retrieve the history of state transitions from the state holder, __#state_history__, is also provided. Since the transition history could grow quite large that history is limited to the last 5 transitions by default. The history size limit may be changed via __#hx_size_limit__. That limit may be changed at any time. Changes to the limit will take effect at the next state transition.
50
-
51
- 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.
52
-
53
- 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.
54
-
55
-
56
- ## Installation
57
-
58
- Add this line to your application's Gemfile:
59
-
60
- ```ruby
61
- gem 'simplestate'
62
- ```
63
-
64
- And then execute:
65
-
66
- $ bundle
67
-
68
- Or install it yourself as:
69
-
70
- $ gem install simplestate
71
-
72
- ## Usage
73
-
74
- #### State Holder
75
- Inherit from StateHolder to create the class of the object that will hold states. You *must* call super if you provide an initialize method in your holder class:
76
-
77
- ```ruby
78
- class Button < StateHolder
79
- attr_reader :color
80
- def initialize(opts={})
81
- @color = opts.fetch :color
82
- super
83
- end
84
-
85
- # button methods here
86
- end
87
- ```
88
-
89
- Creation of a StateHolder instance *must* specify the initial state class. StateHolder expects to receive the initial state class in an opts hash at creation:
90
-
91
- ```ruby
92
- button = Button.new(initial_state_class: Off)
93
- ```
94
- or use this alternate syntax:
95
-
96
- ```ruby
97
- button = Button.new(start_in: Off)
98
- ```
99
- 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.
100
-
101
- ```ruby
102
- button = Button.new(start_in: Off, color: 'Red')
103
- ```
104
-
105
- #### States
106
-
107
- Inherit from State to create a class to provide specific state behaviors:
108
-
109
- ```ruby
110
- class Off < State
111
- def press
112
- transition_to(On)
113
- holder.messages << "#{holder.name} is on"
114
- end
115
-
116
- def name
117
- 'Off'
118
- end
119
-
120
- private
121
- def enter
122
- holder.messages << "Entered the Off state"
123
- end
124
-
125
- def exit
126
- holder.messages << "Exited the Off state"
127
- end
128
- end
129
- ```
130
- 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. __#enter__ and __#exit__ will always be called appropriately during state transitions.
131
-
132
- A state has access to methods on the state holder via __#holder__:
133
-
134
- ```ruby
135
- holder.a_special_holder_method
136
- ```
137
-
138
- #### version 1.0.0
139
- 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__).
140
-
141
- ```ruby
142
- class Button < StateHolder
143
- ...
144
- def prior_state
145
- state_history.last.class
146
- end
147
- end
148
-
149
- # Then in tests for example:
150
- def setup
151
- @button = Button.new(start_in: Off, color: 'Red', hz_size_limit: 3)
152
- end
153
-
154
- def test_a_button_returns_its_last_prior_state
155
- @button.press # Curr state: On, Prior state: Off
156
- @button.press # Curr state: Off, Prior state: On
157
- assert_equal On, @button.prior_state
158
- end
159
- ```
160
-
161
- Please note that the State instance method, __#previous_state_class__, has been removed in this release.
162
-
163
- #### version 0.3.0
164
- 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.
165
-
166
- #### usage example
167
- The button module (test/dummys/button.rb) provides an example of the usage of Simplestate. Tests of this are provided in simplestate_test.rb.
168
-
169
- ## Alternatives
170
-
171
- 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.
172
-
173
- ## Development
174
-
175
- 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.
176
-
177
- 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.
178
-
179
- To install this gem onto your local machine, run `bundle exec rake install`.
180
-
181
- ## Contributing
182
-
183
- 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.
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
+ [![Gem Version](https://badge.fury.io/rb/simplestate.svg)](https://badge.fury.io/rb/simplestate) [![Build Status](https://travis-ci.org/dpneumo/simplestate.svg?branch=master)](https://travis-ci.org/dpneumo/simplestate)
2
+ [![Code Climate](https://codeclimate.com/github/dpneumo/simplestate/badges/gpa.svg)](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
@@ -1,19 +1,36 @@
1
- class State
2
- attr_reader :holder
3
- def initialize(holder)
4
- @holder = holder
5
- end
6
-
7
- private
8
- def transition_to(new_state_class)
9
- holder.transition_to(new_state_class)
10
- end
11
-
12
- def enter
13
- raise NotImplementedError, "need to define #enter."
14
- end
15
-
16
- def exit
17
- raise NotImplementedError, "need to define #exit."
18
- end
19
- end
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 :beginning, :state_history
3
- attr_accessor :hx_size_limit
4
- def initialize(opts={})
5
- @beginning = initial_state_class(opts)
6
- @hx_size_limit = opts.fetch :hx_size_limit, 5
7
- @state_history = []
8
- super(NilState.new(nil))
9
- transition_to beginning
10
- end
11
-
12
- def transition_to(new_state_class)
13
- update_state_history
14
- current_state.send(:exit)
15
- set_new_state(new_state_class)
16
- current_state.send(:enter)
17
- end
18
-
19
- def current_state
20
- __getobj__
21
- end
22
-
23
- def set_new_state(new_state_class)
24
- self.current_state = new_state_class.new(self)
25
- end
26
-
27
- private
28
- def current_state=(state)
29
- __setobj__(state)
30
- end
31
-
32
- def update_state_history
33
- @state_history << current_state
34
- @state_history = @state_history.last(hx_size_limit)
35
- end
36
-
37
- def initial_state_class(opts)
38
- isc = opts.fetch :start_in,
39
- (opts.fetch :initial_state_class, nil)
40
- isc ? isc : raise(ArgumentError, "initial state class is missing.")
41
- end
42
- end
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
@@ -1,3 +1,3 @@
1
- module Simplestate
2
- VERSION = "1.0.6"
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/state"
5
- require "simplestate/nil_state"
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.9.2 or greater...
18
- spec.required_ruby_version = '>= 1.9.2'
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
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: 1.0.6
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-02-25 00:00:00.000000000 Z
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/nil_state.rb
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.9.2
95
+ version: 2.1.0
93
96
  required_rubygems_version: !ruby/object:Gem::Requirement
94
97
  requirements:
95
98
  - - ">="
@@ -1,11 +0,0 @@
1
- class NilState < State
2
- def initialize(holder)
3
- end
4
-
5
- private
6
- def enter
7
- end
8
-
9
- def exit
10
- end
11
- end