state_design_pattern 0.0.1

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MzlmYTVlZmM1OTNmMjk4ODU4NTE2MmFlMzk1NDFhNTI1MjBhOTUzMg==
5
+ data.tar.gz: !binary |-
6
+ NGFmMWNkZTZiZjk4OGExOTMwYjlmMjQ2MmVhZmM0NGY5MjgwMGFlZA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MTlmZTQ2MjAwZmU0YTUxYTBlMWEwZGQ4NmU2ZjBiNzdiYmUzNzM1YmI1ZjFj
10
+ OGJlMWY5YjRkMGE3ZTJhZjMzZjk3NDUzZjNlOGU1ZjVlYmQ4MDZmNzAxYWU4
11
+ YTFhNGRkYjlkYTEwMTJmNzIxNDBkZGNkMjBlNDU5YWRjZGJiZDM=
12
+ data.tar.gz: !binary |-
13
+ MGM0OGNmYWQxNGUyNzUxMjk5MDI5NWEyYzkzNTRiMDNlZDljYjU2MTRiZWFh
14
+ OGRhMTNjN2ZhYzU4MTZhMmQ2MTM3YmEzYmMxNDBjNzY5NTU5YjhmZTc2NGQ4
15
+ YjllMTFlMDE3ODcwOWEwZTRjNjg2OGVlYjY5NjkwNmE1NWRhY2E=
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .ruby-gemset
3
+ .ruby-version
4
+ /coverage
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,39 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ state_design_pattern (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ coveralls (0.7.0)
10
+ multi_json (~> 1.3)
11
+ rest-client
12
+ simplecov (>= 0.7)
13
+ term-ansicolor
14
+ thor
15
+ docile (1.1.3)
16
+ mime-types (2.2)
17
+ minitest (5.3.4)
18
+ multi_json (1.10.1)
19
+ rake (10.3.2)
20
+ rest-client (1.6.7)
21
+ mime-types (>= 1.16)
22
+ simplecov (0.8.2)
23
+ docile (~> 1.1.0)
24
+ multi_json
25
+ simplecov-html (~> 0.8.0)
26
+ simplecov-html (0.8.0)
27
+ term-ansicolor (1.3.0)
28
+ tins (~> 1.0)
29
+ thor (0.19.1)
30
+ tins (1.3.0)
31
+
32
+ PLATFORMS
33
+ ruby
34
+
35
+ DEPENDENCIES
36
+ coveralls (~> 0.7)
37
+ minitest (~> 5.3)
38
+ rake (~> 10.3)
39
+ state_design_pattern!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Dwayne R. Crooks
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # state_design_pattern
2
+
3
+ [![Build Status](https://travis-ci.org/dwayne/state_design_pattern.svg?branch=master)](https://travis-ci.org/dwayne/state_design_pattern)
4
+ [![Coverage Status](https://coveralls.io/repos/dwayne/state_design_pattern/badge.png)](https://coveralls.io/r/dwayne/state_design_pattern)
5
+ [![Code Climate](https://codeclimate.com/github/dwayne/state_design_pattern.png)](https://codeclimate.com/github/dwayne/state_design_pattern)
6
+
7
+ An implementation of the
8
+ [State Design Pattern](http://sourcemaking.com/design_patterns/state) in Ruby.
9
+ The State Design Pattern allows an object to alter its behavior when its
10
+ internal state changes.
11
+
12
+ ## Example
13
+
14
+ Here's an example of the state design pattern in use. A light bulb is modeled.
15
+ It can be turned on and off. Every time it is turned on it uses 25% of its
16
+ energy. When it runs out of energy it cannot be turned on again.
17
+
18
+ ```ruby
19
+ class LightBulb < StateDesignPattern::StateMachine
20
+ def start_state
21
+ Off
22
+ end
23
+
24
+ def initial_context
25
+ Struct
26
+ .new(:energy, :times_on, :times_off)
27
+ .new(100, 0, 0)
28
+ end
29
+ end
30
+
31
+ class Switch < StateDesignPattern::BaseState
32
+ def_actions :turn_on, :turn_off
33
+ end
34
+
35
+ class On < Switch
36
+
37
+ def turn_on
38
+ state_machine.send_event(:already_turned_on)
39
+ end
40
+
41
+ def turn_off
42
+ state_machine.times_off += 1
43
+ state_machine.transition_to_state_and_send_event(Off, :turned_off)
44
+ end
45
+ end
46
+
47
+ class Off < Switch
48
+
49
+ def turn_on
50
+ if state_machine.energy >= 25
51
+ state_machine.energy -= 25
52
+ state_machine.times_on += 1
53
+ state_machine.transition_to_state_and_send_event(On, :turned_on)
54
+ else
55
+ state_machine.send_event(:out_of_energy)
56
+ end
57
+ end
58
+
59
+ def turn_off
60
+ state_machine.send_event(:already_turned_off)
61
+ end
62
+ end
63
+
64
+ class LightBulbObserver
65
+
66
+ def initialize
67
+ @events = []
68
+ end
69
+
70
+ def last_event
71
+ @events.last
72
+ end
73
+
74
+ def update(event)
75
+ @events << event
76
+ end
77
+ end
78
+
79
+ light_bulb = LightBulb.new
80
+ light_bulb_observer = LightBulbObserver.new
81
+
82
+ light_bulb.add_observer(light_bulb_observer)
83
+
84
+ light_bulb.current_state #=> Off
85
+
86
+ light_bulb.energy #=> 100
87
+ light_bulb.times_on #=> 0
88
+ light_bulb.times_off #=> 0
89
+
90
+ light_bulb.turn_on
91
+ light_bulb_observer.last_event[:name] #=> :turned_on
92
+
93
+ light_bulb.current_state #=> On
94
+
95
+ light_bulb.energy #=> 75
96
+ light_bulb.times_on #=> 1
97
+ light_bulb.times_off #=> 0
98
+
99
+ light_bulb.turn_off
100
+ light_bulb.turn_on
101
+
102
+ light_bulb.turn_off
103
+ light_bulb.turn_on
104
+
105
+ light_bulb.turn_off
106
+ light_bulb.turn_on
107
+
108
+ light_bulb.energy #=> 0
109
+ light_bulb.times_on #=> 4
110
+ light_bulb.times_off #=> 3
111
+
112
+ light_bulb.turn_off
113
+ light_bulb.turn_on
114
+
115
+ light_bulb_observer.last_event[:name] #=> :out_of_energy
116
+ ```
117
+
118
+ ## Testing
119
+
120
+ You can run:
121
+
122
+ - All specs: `bundle exec rake`, or
123
+ - A specific spec: `bundle exec ruby -Ilib -Ispec spec/path_to_spec_file.rb`
124
+
125
+ ## Contributing
126
+
127
+ If you'd like to contribute a feature or bugfix: Thanks! To make sure your
128
+ fix/feature has a high chance of being included, please read the following
129
+ guidelines:
130
+
131
+ 1. Post a [pull request](https://github.com/dwayne/state_design_pattern/compare/).
132
+ 2. Make sure there are tests! I will not accept any patch that is not tested.
133
+ It's a rare time when explicit tests aren't needed. If you have questions about
134
+ writing tests for state_pattern, please open a
135
+ [GitHub issue](https://github.com/dwayne/state_design_pattern/issues/new).
136
+
137
+ ## License
138
+
139
+ state_design_pattern is Copyright © 2014 Dwayne R. Crooks. It is free software,
140
+ and may be redistributed under the terms specified in the MIT-LICENSE file.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = 'spec/**/*_spec.rb'
6
+ t.libs.push 'spec'
7
+ end
8
+
9
+ task default: :test
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # See http://en.wikipedia.org/wiki/State_pattern#Java
4
+
5
+ require 'state_design_pattern'
6
+
7
+ class CaseChanger < StateDesignPattern::StateMachine
8
+ def start_state
9
+ Lowercase
10
+ end
11
+ end
12
+
13
+ class CaseChangerState < StateDesignPattern::BaseState
14
+ def_actions :write
15
+ end
16
+
17
+ class Lowercase < CaseChangerState
18
+ def write(name)
19
+ puts name.downcase
20
+ state_machine.transition_to_state(MultipleUppercase)
21
+ end
22
+ end
23
+
24
+ class MultipleUppercase < CaseChangerState
25
+ def write(name)
26
+ puts name.upcase
27
+ state_machine.transition_to_state(Lowercase) if incr_count > 1
28
+ end
29
+
30
+ private
31
+ def incr_count
32
+ @count = 0 unless @count
33
+ @count += 1
34
+ end
35
+ end
36
+
37
+ def main
38
+ changer = CaseChanger.new
39
+
40
+ ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'].each do |day|
41
+ changer.write(day)
42
+ end
43
+ end
44
+
45
+ main
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # See http://sourcemaking.com/design_patterns/state/java/1
4
+
5
+ require 'state_design_pattern'
6
+
7
+ class CeilingFanPullChain < StateDesignPattern::StateMachine
8
+ def start_state
9
+ Off
10
+ end
11
+ end
12
+
13
+ class CeilingFanState < StateDesignPattern::BaseState
14
+ def_actions :pull
15
+ end
16
+
17
+ class Off < CeilingFanState
18
+ def pull
19
+ puts "-> low speed"
20
+ state_machine.transition_to_state(Low)
21
+ end
22
+ end
23
+
24
+ class Low < CeilingFanState
25
+ def pull
26
+ puts "-> medium speed"
27
+ state_machine.transition_to_state(Medium)
28
+ end
29
+ end
30
+
31
+ class Medium < CeilingFanState
32
+ def pull
33
+ puts "-> high speed"
34
+ state_machine.transition_to_state(High)
35
+ end
36
+ end
37
+
38
+ class High < CeilingFanState
39
+ def pull
40
+ puts "-> turning off"
41
+ state_machine.transition_to_state(Off)
42
+ end
43
+ end
44
+
45
+ def main
46
+ chain = CeilingFanPullChain.new
47
+
48
+ loop do
49
+ print "Press "
50
+ gets.chomp
51
+ chain.pull
52
+ end
53
+ end
54
+
55
+ main
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # See http://en.wikipedia.org/wiki/State_pattern#Pseudocode
4
+
5
+ require 'state_design_pattern'
6
+
7
+ class Cursor < StateDesignPattern::StateMachine
8
+ def start_state
9
+ PenTool
10
+ end
11
+
12
+ def use_pen_tool
13
+ transition_to_state(PenTool)
14
+ end
15
+
16
+ def use_selection_tool
17
+ transition_to_state(SelectionTool)
18
+ end
19
+ end
20
+
21
+ class AbstractTool < StateDesignPattern::BaseState
22
+ def_actions :move_to, :mouse_down, :mouse_up
23
+ end
24
+
25
+ class PenTool < AbstractTool
26
+ def move_to(point)
27
+ puts "pen: moving to #{point}"
28
+ end
29
+
30
+ def mouse_down(point)
31
+ puts "pen: mouse down at #{point}"
32
+ end
33
+
34
+ def mouse_up(point)
35
+ puts "pen: mouse up at #{point}"
36
+ end
37
+ end
38
+
39
+ class SelectionTool < AbstractTool
40
+ def move_to(point)
41
+ if @mouse_button == :down
42
+ puts "selection: current selected rectangle is between #{@selection_start} and #{point}"
43
+ else
44
+ puts "selection: moving to #{point}"
45
+ end
46
+ end
47
+
48
+ def mouse_down(point)
49
+ @mouse_button = :down
50
+ @selection_start = point
51
+
52
+ puts "selection: mouse down at #{point}"
53
+ end
54
+
55
+ def mouse_up(point)
56
+ @mouse_button = :up
57
+
58
+ puts "selection: mouse up at #{point}"
59
+ end
60
+ end
61
+
62
+ class Point < Struct.new(:x, :y)
63
+ end
64
+
65
+ def main
66
+ cursor = Cursor.new
67
+
68
+ # Draw a line from (1, 1) to (5, 5)
69
+ cursor.use_pen_tool
70
+ cursor.move_to(Point.new(1, 1))
71
+ cursor.mouse_down(Point.new(1, 1))
72
+ cursor.move_to(Point.new(5, 5))
73
+ cursor.mouse_up(Point.new(5, 5))
74
+
75
+ # Select part of the line
76
+ cursor.use_selection_tool
77
+ cursor.move_to(Point.new(2, 2))
78
+ cursor.mouse_down(Point.new(2, 2))
79
+ cursor.move_to(Point.new(4, 4))
80
+ cursor.mouse_up(Point.new(4, 4))
81
+ end
82
+
83
+ main
data/examples/fsm.rb ADDED
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # See http://sourcemaking.com/design_patterns/state/java/6
4
+
5
+ require 'state_design_pattern'
6
+
7
+ class FSM < StateDesignPattern::StateMachine
8
+ def start_state
9
+ A
10
+ end
11
+ end
12
+
13
+ class FSMState < StateDesignPattern::BaseState
14
+ def_actions :on, :off, :ack
15
+ end
16
+
17
+ class A < FSMState
18
+ def on
19
+ puts "A + on = C"
20
+ state_machine.transition_to_state(C)
21
+ end
22
+
23
+ def off
24
+ puts "A + off = B"
25
+ state_machine.transition_to_state(B)
26
+ end
27
+
28
+ def ack
29
+ puts "A + ack = A"
30
+ state_machine.transition_to_state(A)
31
+ end
32
+ end
33
+
34
+ class B < FSMState
35
+ def on
36
+ puts "B + on = A"
37
+ state_machine.transition_to_state(A)
38
+ end
39
+
40
+ def off
41
+ puts "B + off = C"
42
+ state_machine.transition_to_state(C)
43
+ end
44
+ end
45
+
46
+ class C < FSMState
47
+ def on
48
+ puts "C + on = B"
49
+ state_machine.transition_to_state(B)
50
+ end
51
+ end
52
+
53
+ def main
54
+ fsm = FSM.new
55
+
56
+ [2, 1, 2, 1, 0, 2, 0, 0].each do |msg|
57
+ begin
58
+ fsm.on if msg == 0
59
+ fsm.off if msg == 1
60
+ fsm.ack if msg == 2
61
+ rescue StateDesignPattern::IllegalStateException
62
+ puts "error"
63
+ end
64
+ end
65
+ end
66
+
67
+ main
@@ -0,0 +1,29 @@
1
+ module StateDesignPattern
2
+
3
+ class BaseState
4
+
5
+ attr_reader :state_machine
6
+
7
+ def initialize(state_machine)
8
+ @state_machine = state_machine
9
+ end
10
+
11
+ def self.def_actions(*actions)
12
+ define_method(:actions) do
13
+ actions
14
+ end
15
+
16
+ actions.each do |action|
17
+ def_action(action)
18
+ end
19
+ end
20
+
21
+ def self.def_action(action_name)
22
+ define_method(action_name) do |*args|
23
+ raise IllegalStateException
24
+ end
25
+ end
26
+ end
27
+
28
+ class IllegalStateException < StandardError; end
29
+ end
@@ -0,0 +1,74 @@
1
+ require 'forwardable'
2
+ require 'observer'
3
+
4
+ module StateDesignPattern
5
+
6
+ class StateMachine
7
+ extend Forwardable
8
+ include Observable
9
+
10
+ def initialize
11
+ initialize_context
12
+ transition_to_start_state
13
+ setup_delegation
14
+ end
15
+
16
+ def initial_context
17
+ end
18
+
19
+ def start_state
20
+ raise NotImplementedError
21
+ end
22
+
23
+ def current_state
24
+ @state.class
25
+ end
26
+
27
+ def transition_to_state_and_send_event(state_class, name, message = {})
28
+ transition_to_state(state_class)
29
+ send_event(name, message)
30
+ end
31
+
32
+ def transition_to_state(state_class)
33
+ @state = state_class.new(self)
34
+ end
35
+
36
+ def send_event(name, message = {})
37
+ event = { name: name, source: self }.merge(message)
38
+
39
+ changed
40
+ notify_observers(event)
41
+
42
+ self
43
+ end
44
+
45
+ private
46
+
47
+ def initialize_context
48
+ @ctx = initial_context
49
+ end
50
+
51
+ def transition_to_start_state
52
+ transition_to_state(start_state)
53
+ end
54
+
55
+ def setup_delegation
56
+ setup_context_delegation
57
+ setup_action_delegation
58
+ end
59
+
60
+ def setup_context_delegation
61
+ if @ctx
62
+ methods = @ctx.members.map { |reader| [reader, "#{reader}=".to_sym] }.flatten
63
+ self.class.def_delegators :@ctx, *methods
64
+ end
65
+ end
66
+
67
+ def setup_action_delegation
68
+ if @state.respond_to?(:actions)
69
+ methods = @state.actions
70
+ self.class.def_delegators :@state, *methods
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,3 @@
1
+ module StateDesignPattern
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,2 @@
1
+ require 'state_design_pattern/state_machine'
2
+ require 'state_design_pattern/base_state'
@@ -0,0 +1,7 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require 'minitest/autorun'
5
+ require 'minitest/spec'
6
+
7
+ require 'state_design_pattern'
@@ -0,0 +1,314 @@
1
+ require 'spec_helper'
2
+
3
+ describe "the operation of the state design pattern with an example" do
4
+
5
+ TIMESTAMP = Time.now
6
+
7
+ class LightBulb < StateDesignPattern::StateMachine
8
+ def start_state
9
+ Off
10
+ end
11
+
12
+ def initial_context
13
+ Struct
14
+ .new(:energy, :times_on, :times_off)
15
+ .new(100, 0, 0)
16
+ end
17
+ end
18
+
19
+ class Switch < StateDesignPattern::BaseState
20
+ def_actions :turn_on, :turn_off, :toggle
21
+ end
22
+
23
+ class On < Switch
24
+
25
+ def turn_on
26
+ state_machine.send_event(:already_turned_on, when: TIMESTAMP)
27
+ end
28
+
29
+ def turn_off
30
+ state_machine.times_off += 1
31
+ state_machine.transition_to_state_and_send_event(Off, :turned_off, when: TIMESTAMP)
32
+ end
33
+ end
34
+
35
+ class Off < Switch
36
+
37
+ def turn_on
38
+ if state_machine.energy >= 25
39
+ state_machine.energy -= 25
40
+ state_machine.times_on += 1
41
+ state_machine.transition_to_state_and_send_event(On, :turned_on, when: TIMESTAMP)
42
+ else
43
+ state_machine.send_event(:out_of_energy, when: TIMESTAMP)
44
+ end
45
+ end
46
+
47
+ def turn_off
48
+ state_machine.send_event(:already_turned_off, when: TIMESTAMP)
49
+ end
50
+ end
51
+
52
+ class LightBulbObserver
53
+
54
+ def initialize
55
+ @events = []
56
+ end
57
+
58
+ def last_event
59
+ @events.last
60
+ end
61
+
62
+ def update(event)
63
+ @events << event
64
+ end
65
+ end
66
+
67
+ describe LightBulb do
68
+
69
+ let(:light_bulb) { LightBulb.new }
70
+ let(:light_bulb_observer) { LightBulbObserver.new }
71
+
72
+ before do
73
+ light_bulb.add_observer(light_bulb_observer)
74
+ end
75
+
76
+ describe "the initial state of the bulb" do
77
+ it "is off" do
78
+ light_bulb.current_state.must_equal Off
79
+ end
80
+
81
+ it "is full of energy" do
82
+ light_bulb.energy.must_equal 100
83
+ end
84
+
85
+ it "has been turned on 0 times" do
86
+ light_bulb.times_on.must_equal 0
87
+ end
88
+
89
+ it "has been turned off 0 times" do
90
+ light_bulb.times_off.must_equal 0
91
+ end
92
+ end
93
+
94
+ describe "the actions that can be performed on the light bulb" do
95
+
96
+ it "can be turned on" do
97
+ light_bulb.must_respond_to :turn_on
98
+ end
99
+
100
+ it "can be turned off" do
101
+ light_bulb.must_respond_to :turn_off
102
+ end
103
+
104
+ it "can be toggled" do
105
+ light_bulb.must_respond_to :toggle
106
+ end
107
+ end
108
+
109
+ describe "how the actions work" do
110
+
111
+ describe "in the off state" do
112
+
113
+ describe "#turn_on" do
114
+
115
+ before do
116
+ light_bulb.turn_on
117
+ end
118
+
119
+ it "turns on the bulb" do
120
+ light_bulb.current_state.must_equal On
121
+ end
122
+
123
+ it "causes the bulb to use 25% of its energy" do
124
+ light_bulb.energy.must_equal 75
125
+ end
126
+
127
+ it "has been turned on 1 time" do
128
+ light_bulb.times_on.must_equal 1
129
+ end
130
+
131
+ it "has been turned off 0 times" do
132
+ light_bulb.times_off.must_equal 0
133
+ end
134
+
135
+ it "sends a :turned_on event" do
136
+ event = light_bulb_observer.last_event
137
+
138
+ event[:name].must_equal :turned_on
139
+ event[:source].must_equal light_bulb
140
+ event[:when].must_equal TIMESTAMP
141
+ end
142
+ end
143
+
144
+ describe "#turn_off" do
145
+
146
+ before do
147
+ light_bulb.turn_off
148
+ end
149
+
150
+ it "keeps the bulb turned off" do
151
+ light_bulb.current_state.must_equal Off
152
+ end
153
+
154
+ it "doesn't use any energy" do
155
+ light_bulb.energy.must_equal 100
156
+ end
157
+
158
+ it "has been turned on 0 times" do
159
+ light_bulb.times_on.must_equal 0
160
+ end
161
+
162
+ it "has been turned off 0 times" do
163
+ light_bulb.times_off.must_equal 0
164
+ end
165
+
166
+ it "sends an :already_turned_off event" do
167
+ event = light_bulb_observer.last_event
168
+
169
+ event[:name].must_equal :already_turned_off
170
+ event[:source].must_equal light_bulb
171
+ event[:when].must_equal TIMESTAMP
172
+ end
173
+ end
174
+
175
+ describe "#toggle" do
176
+
177
+ it "doesn't work" do
178
+ proc { light_bulb.toggle }.must_raise StateDesignPattern::IllegalStateException
179
+ end
180
+ end
181
+ end
182
+
183
+ describe "in the on state" do
184
+
185
+ before do
186
+ light_bulb.turn_on
187
+ end
188
+
189
+ describe "#turn_on" do
190
+
191
+ before do
192
+ light_bulb.turn_on
193
+ end
194
+
195
+ it "keeps the bulb turned on" do
196
+ light_bulb.current_state.must_equal On
197
+ end
198
+
199
+ it "doesn't use any energy" do
200
+ light_bulb.energy.must_equal 75
201
+ end
202
+
203
+ it "has been turned on 1 time" do
204
+ light_bulb.times_on.must_equal 1
205
+ end
206
+
207
+ it "has been turned off 0 times" do
208
+ light_bulb.times_off.must_equal 0
209
+ end
210
+
211
+ it "sends an :already_turned_on event" do
212
+ event = light_bulb_observer.last_event
213
+
214
+ event[:name].must_equal :already_turned_on
215
+ event[:source].must_equal light_bulb
216
+ event[:when].must_equal TIMESTAMP
217
+ end
218
+ end
219
+
220
+ describe "#turn_off" do
221
+
222
+ before do
223
+ light_bulb.turn_off
224
+ end
225
+
226
+ it "turns off the bulb" do
227
+ light_bulb.current_state.must_equal Off
228
+ end
229
+
230
+ it "doesn't use any energy" do
231
+ light_bulb.energy.must_equal 75
232
+ end
233
+
234
+ it "has been turned on 1 time" do
235
+ light_bulb.times_on.must_equal 1
236
+ end
237
+
238
+ it "has been turned off 1 time" do
239
+ light_bulb.times_off.must_equal 1
240
+ end
241
+
242
+ it "sends a :turned_off event" do
243
+ event = light_bulb_observer.last_event
244
+
245
+ event[:name].must_equal :turned_off
246
+ event[:source].must_equal light_bulb
247
+ event[:when].must_equal TIMESTAMP
248
+ end
249
+ end
250
+
251
+ describe "#toggle" do
252
+
253
+ it "doesn't work" do
254
+ proc { light_bulb.toggle }.must_raise StateDesignPattern::IllegalStateException
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ describe "how the bulb works when it is out of energy" do
261
+
262
+ before do
263
+ # at 100%
264
+ light_bulb.turn_on
265
+ light_bulb.turn_off
266
+ # at 75%
267
+ light_bulb.turn_on
268
+ light_bulb.turn_off
269
+ # at 50%
270
+ light_bulb.turn_on
271
+ light_bulb.turn_off
272
+ # at 25%
273
+ light_bulb.turn_on
274
+ light_bulb.turn_off
275
+ # at 0%
276
+ end
277
+
278
+ it "is off" do
279
+ light_bulb.current_state.must_equal Off
280
+ end
281
+
282
+ it "is out of energy" do
283
+ light_bulb.energy.must_equal 0
284
+ end
285
+
286
+ it "has been turned on 4 times" do
287
+ light_bulb.times_on.must_equal 4
288
+ end
289
+
290
+ it "has been turned off 4 times" do
291
+ light_bulb.times_off.must_equal 4
292
+ end
293
+
294
+ describe "when you try to turn it on" do
295
+
296
+ before do
297
+ light_bulb.turn_on
298
+ end
299
+
300
+ it "remains off" do
301
+ light_bulb.current_state.must_equal Off
302
+ end
303
+
304
+ it "sends an :out_of_energy energy event" do
305
+ event = light_bulb_observer.last_event
306
+
307
+ event[:name].must_equal :out_of_energy
308
+ event[:source].must_equal light_bulb
309
+ event[:when].must_equal TIMESTAMP
310
+ end
311
+ end
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'state_design_pattern/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'state_design_pattern'
8
+ spec.version = StateDesignPattern::VERSION
9
+ spec.author = 'Dwayne R. Crooks'
10
+ spec.email = ['me@dwaynecrooks.com']
11
+ spec.summary = %q{An implementation of the State Design Pattern in Ruby.}
12
+ spec.description = %q{An implementation of the State Design Pattern in Ruby. The State Design Pattern allows an object to alter its behavior when its
13
+ internal state changes.}
14
+ spec.homepage = 'https://github.com/dwayne/state_design_pattern'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.required_ruby_version = '>= 1.9.3'
23
+
24
+ spec.add_development_dependency 'rake', '~> 10.3'
25
+ spec.add_development_dependency 'minitest', '~> 5.3'
26
+ spec.add_development_dependency 'coveralls', '~> 0.7'
27
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: state_design_pattern
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Dwayne R. Crooks
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '10.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '10.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '5.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '5.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: coveralls
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.7'
55
+ description: ! "An implementation of the State Design Pattern in Ruby. The State Design
56
+ Pattern allows an object to alter its behavior when its\n internal state changes."
57
+ email:
58
+ - me@dwaynecrooks.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - .gitignore
64
+ - .travis.yml
65
+ - Gemfile
66
+ - Gemfile.lock
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - examples/case_changer.rb
71
+ - examples/ceiling_fan.rb
72
+ - examples/cursor.rb
73
+ - examples/fsm.rb
74
+ - lib/state_design_pattern.rb
75
+ - lib/state_design_pattern/base_state.rb
76
+ - lib/state_design_pattern/state_machine.rb
77
+ - lib/state_design_pattern/version.rb
78
+ - spec/spec_helper.rb
79
+ - spec/state_design_pattern_spec.rb
80
+ - state_design_pattern.gemspec
81
+ homepage: https://github.com/dwayne/state_design_pattern
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 1.9.3
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.2.2
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: An implementation of the State Design Pattern in Ruby.
105
+ test_files:
106
+ - spec/spec_helper.rb
107
+ - spec/state_design_pattern_spec.rb