very_tiny_state_machine 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 40cb0d3afb2f9c64b8eb616492132d2c45d424f0
4
+ data.tar.gz: 7ccefb4f23c017273a13c5b696bdbf10b2978db6
5
+ SHA512:
6
+ metadata.gz: 1c95e4e56f4c9da12c9bf07cf007060e885f7201b646ea0c5d604a949d51f27b8684ac7f202bcdaf9507afa808f17b6264e6e45d1db524089a24a43a44e28e05
7
+ data.tar.gz: 26e7aeba3f817d853f9f6bce6a4695f6a76a0f00c728b4b1a8a7d2e3f3d3932f6f3cc7254ae43afea8a0c5b60eeebc1a4ffe8f53fdf382208aa19b9b3ecf096f
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ gem "rspec", "~> 3.2.0"
5
+ gem "rdoc", "~> 3.12"
6
+ gem "bundler", "~> 1.0"
7
+ gem "jeweler", "~> 2.0.1"
8
+ gem 'simplecov', '~> 0.10'
9
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2016 WeTransfer
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,51 @@
1
+ # very_tiny_state_machine
2
+
3
+ For when the others are not tiny enough.
4
+
5
+ The entire state machine lives in a separate variable, and does not pollute the class or the module of the caller.
6
+ The state machine has the ability to dispatch callbacks when states are switched, and the callbacks
7
+ are dispatched to the given object as messages.
8
+
9
+ @automaton = VeryTinyStateMachine.new(:initialized, self)
10
+ @automaton.permit_state :processing, :closing, :closed
11
+ @automaton.permit_transition :initialized => :processing, :processing => :closing
12
+ @automaton.permit_transition :closing => :closed
13
+
14
+ # Then, lower down the code
15
+ @automaton.transition! :processing
16
+
17
+ The object supplied as the optional second argument will receive messages when states are switched around,
18
+ in the following order (using the state machine from the previous example):
19
+
20
+ # self.leaving_initialized_state
21
+ # self.entering_processing_state
22
+ # self.transitioning_from_initialized_to_processing_state
23
+ # ..the state variable is switched here
24
+ # self.after_transitioning_from_initialized_to_processing_state
25
+ # self.after_leaving_initialized_state
26
+ # self.after_entering_processing_state
27
+
28
+ You can see in which state the machine is in:
29
+
30
+ @automaton.in_state?(:processing) #=> true
31
+ @automaton.in_state?(:initialized) #=> false
32
+
33
+ and state machine has your back if you want to do something invalid:
34
+
35
+ @automaton.transition :initialized # Will raise TinyStateMachine::InvalidFlow
36
+ @automaton.transition :something_odd # Will raise TinyStateMachine::UnknownState
37
+
38
+ ## Contributing to very_tiny_state_machine
39
+
40
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
41
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
42
+ * Fork the project.
43
+ * Start a feature/bugfix branch.
44
+ * Commit and push until you are happy with your contribution.
45
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
46
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
47
+
48
+ ## Copyright
49
+
50
+ Copyright (c) 2016 WeTransfer. See LICENSE.txt for further details.
51
+
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+ require_relative 'lib/very_tiny_state_machine'
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
17
+ gem.version = VeryTinyStateMachine::VERSION
18
+ gem.name = "very_tiny_state_machine"
19
+ gem.homepage = "http://github.com/WeTransfer/very_tiny_state_machine"
20
+ gem.license = "MIT"
21
+ gem.description = %Q{You wouldn't beleive how tiny it is}
22
+ gem.summary = %Q{A minuscule state machine for storing state of interesting objects}
23
+ gem.email = "me@julik.nl"
24
+ gem.authors = ["Julik Tarkhanov"]
25
+ # dependencies defined in Gemfile
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rspec/core'
30
+ require 'rspec/core/rake_task'
31
+ RSpec::Core::RakeTask.new(:spec) do |spec|
32
+ spec.pattern = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ desc "Code coverage detail"
36
+ task :simplecov do
37
+ ENV['COVERAGE'] = "true"
38
+ Rake::Task['spec'].execute
39
+ end
40
+
41
+ task :default => :spec
42
+
43
+ require 'rdoc/task'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "very_tiny_state_machine #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
@@ -0,0 +1,215 @@
1
+ require 'thread'
2
+ require 'set'
3
+
4
+ # A mini state machine object that can be used to track a state flow.
5
+ #
6
+ # The entire state machine lives in a separate variable, and does not pollute the
7
+ # class or the module of the caller.
8
+ # The state machine has the ability to dispatch callbacks when states are switched, the callbacks
9
+ # are dispatched to the given object.
10
+ #
11
+ # @automaton = VeryTinyStateMachine.new(:initialized, self)
12
+ # @automaton.permit_state :processing, :closing, :closed
13
+ # @automaton.permit_transition :initialized => :processing, :processing => :closing
14
+ # @automaton.permit_transition :closing => :closed
15
+ #
16
+ # # Then, lower down the code
17
+ # @automaton.transition! :processing
18
+ #
19
+ # # This switches the internal state of the machine, and dispatches the following method
20
+ # # calls on the object given as the second argument to the constructor, in the following order:
21
+ #
22
+ # # self.leaving_initialized_state
23
+ # # self.entering_processing_state
24
+ # # self.transitioning_from_initialized_to_processing_state
25
+ # # ..the state variable is switched here
26
+ # # self.after_transitioning_from_initialized_to_processing_state
27
+ # # self.after_leaving_initialized_state
28
+ # # self.after_entering_processing_state
29
+ #
30
+ # @automaton.transition :initialized # Will raise TinyStateMachine::InvalidFlow
31
+ # @automaton.transition :something_odd # Will raise TinyStateMachine::UnknownState
32
+ #
33
+ # @automaton.in_state?(:processing) #=> true
34
+ # @automaton.in_state?(:initialized) #=> false
35
+ class VeryTinyStateMachine
36
+ VERSION = '1.0.0'
37
+
38
+ InvalidFlow = Class.new(StandardError) # Gets raised when an impossible transition gets requested
39
+ UnknownState = Class.new(StandardError) # Gets raised when an unknown state gets requested
40
+
41
+ # Initialize a new TinyStateMachine, with the initial state and the object that will receive callbacks.
42
+ #
43
+ # @param initial_state[#to_sym] the initial state of the state machine
44
+ # @param object_handling_callbacks[#send, #respond_to?] the callback handler that will receive transition notifications
45
+ def initialize(initial_state, object_handling_callbacks = nil)
46
+ @mutex = Mutex.new
47
+ @state = initial_state.to_sym
48
+ @flow = [@state]
49
+ @permitted_states = Set.new([initial_state])
50
+ @permitted_transitions = Set.new
51
+ @callbacks_via = object_handling_callbacks
52
+ end
53
+
54
+ # Permit a single state or multiple states
55
+ #
56
+ # @param states [Array] states to permit
57
+ # @return [Set] the Set of states added to permitted states as the result of the call
58
+ def permit_state(*states)
59
+ states_to_permit = Set.new(states.map(&:to_sym))
60
+ will_be_added = states_to_permit - @permitted_states
61
+ @permitted_states += states_to_permit
62
+ will_be_added
63
+ end
64
+
65
+ # Permit a transition from one state to another. If you need to add multiple transitions
66
+ # from the same state, just call the method multiple times:
67
+ #
68
+ # @machine.permit_transition :initialized => :failed, :running => :closed
69
+ # @machine.permit_transition :initialized => :running
70
+ #
71
+ # @param from_to_hash[Hash] the transitions to allow
72
+ # @return [Array] the list of states added to permitted states
73
+ def permit_transition(from_to_hash)
74
+ transitions_to_permit = Set.new
75
+ from_to_hash.each_pair do | from_state, to_state |
76
+ raise UnknownState, from_state unless @permitted_states.include?(from_state.to_sym)
77
+ raise UnknownState, to_state unless @permitted_states.include?(to_state.to_sym)
78
+ transitions_to_permit << {from_state.to_sym => to_state.to_sym}
79
+ end
80
+ additions = transitions_to_permit - @permitted_transitions
81
+ @permitted_transitions += transitions_to_permit
82
+ additions
83
+ end
84
+
85
+ # Tells whether the state is known to this state machine
86
+ #
87
+ # @param state[Symbol,String] the state to check for
88
+ # @return [Boolean] whether the state is known
89
+ def known?(state)
90
+ @permitted_states.include?(state.to_sym)
91
+ end
92
+
93
+ # Tells whether a transition is permitted to the given state.
94
+ #
95
+ # @param to_state[Symbol,String] state to transition to
96
+ # @return [Boolean] whether the state can be transitioned to
97
+ def may_transition_to?(to_state)
98
+ to_state = to_state.to_sym
99
+ transition = {@state => to_state.to_sym}
100
+ @permitted_states.include?(to_state) && @permitted_transitions.include?(transition)
101
+ end
102
+
103
+ # Tells whether the state machine is in a given state at the moment
104
+ #
105
+ # @param requisite_state [Symbol,String] name of the state to check for
106
+ # @return [Boolean] whether the machine is in that state currently
107
+ def in_state?(requisite_state)
108
+ @state == requisite_state.to_sym
109
+ end
110
+
111
+ # Ensure the machine is in a given state, and if it isn't raise an InvalidFlow
112
+ #
113
+ # @param requisite_state[#to_sym] the state to verify
114
+ # @raise InvalidFlow
115
+ # @return [TrueClass] true if the machine is in the requisite state
116
+ def expect!(requisite_state)
117
+ unless requisite_state.to_sym == @state
118
+ raise InvalidFlow, "Must be in #{requisite_state.inspect} state, but was in #{@state.inspect}"
119
+ end
120
+ true
121
+ end
122
+
123
+ # Transition to a given state. Will raise an InvalidFlow exception if the transition is impossible.
124
+ # Additionally, if you want to transition to a state that is already activated, an InvalidFlow will
125
+ # be raised if you did not permit this transition explicitly. If you want to transition to a state OR
126
+ # stay in it if it is already active use {TinyStateMachine#transition_or_maintain!}
127
+ #
128
+ #
129
+ # During transitions the before callbacks will be called on the @callbacks_via instance variable. If you are
130
+ # transitioning from "initialized" to "processing" for instance, the following callbacks will be dispatched:
131
+ #
132
+ # * leaving_initialized_state
133
+ # * entering_processing_state
134
+ # * transitioning_from_initialized_to_processing_state
135
+ # ..the state variable is switched here
136
+ # * after_transitioning_from_initialized_to_processing_state
137
+ # * after_leaving_initialized_state
138
+ # * after_entering_processing_state
139
+ #
140
+ # The return value of the callbacks does not matter.
141
+ #
142
+ # @param new_state[#to_sym] the state to transition to.
143
+ # @return [Symbol] the state that the machine has just left
144
+ # @raise InvalidFlow
145
+ def transition!(new_state)
146
+ new_state = new_state.to_sym
147
+
148
+ raise UnknownState, new_state.inspect unless known?(new_state)
149
+ if may_transition_to?(new_state)
150
+ @mutex.synchronize do
151
+ dispatch_callbacks_before_transition(new_state)
152
+ previous = @state
153
+ @state = new_state.to_sym
154
+ @flow << new_state.to_sym
155
+ dispatch_callbacks_after_transition(previous)
156
+ previous
157
+ end
158
+ else
159
+ raise InvalidFlow,
160
+ "Cannot change states from #{@state} to #{new_state} (flow so far: #{@flow.join(' > ')})"
161
+ end
162
+ end
163
+
164
+ # Transition to a given state. If the machine already is in that state, do nothing.
165
+ # If the transition has to happen (the requested state is different than the current)
166
+ # transition! will be called instead.
167
+ #
168
+ # @see TinyStateMachine#transition!
169
+ # @param new_state[Symbol,String] the state to transition to.
170
+ # @raise InvalidFlow
171
+ # @return [void]
172
+ def transition_or_maintain!(new_state)
173
+ return if in_state?(new_state)
174
+ transition! new_state
175
+ end
176
+
177
+ # Returns the flow of the transitions the machine went through so far
178
+ #
179
+ # @return [Array] the array of states
180
+ def flow_so_far
181
+ @flow.dup
182
+ end
183
+
184
+ private
185
+
186
+ def dispatch_callbacks_after_transition(from)
187
+ to = @state
188
+ if @callbacks_via.respond_to?("after_transitioning_from_#{from}_to_#{to}_state", also_protected_and_private=true)
189
+ @callbacks_via.send("after_transitioning_from_#{from}_to_#{to}_state")
190
+ end
191
+
192
+ if @callbacks_via.respond_to?("after_leaving_#{from}_state", also_protected_and_private=true)
193
+ @callbacks_via.send("after_leaving_#{from}_state")
194
+ end
195
+
196
+ if @callbacks_via.respond_to?("after_entering_#{to}_state", also_protected_and_private=true)
197
+ @callbacks_via.send("after_entering_#{to}_state")
198
+ end
199
+ end
200
+
201
+ def dispatch_callbacks_before_transition(to)
202
+ from = @state
203
+ if @callbacks_via.respond_to?("leaving_#{from}_state", also_protected_and_private=true)
204
+ @callbacks_via.send("leaving_#{from}_state")
205
+ end
206
+
207
+ if @callbacks_via.respond_to?("entering_#{to}_state", also_protected_and_private=true)
208
+ @callbacks_via.send("entering_#{to}_state")
209
+ end
210
+
211
+ if @callbacks_via.respond_to?("transitioning_from_#{from}_to_#{to}", also_protected_and_private=true)
212
+ @callbacks_via.send("transitioning_from_#{from}_to_#{to}")
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ require 'rspec'
5
+ require 'simplecov'
6
+ SimpleCov.start
7
+
8
+ require 'very_tiny_state_machine'
9
+
10
+ RSpec.configure { |config| config.order = 'random' }
@@ -0,0 +1,205 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe VeryTinyStateMachine do
4
+ describe '#initialize' do
5
+ it 'creates a state machine and sets its initial state' do
6
+ machine = described_class.new(:started)
7
+ expect(machine).to be_known(:started)
8
+ expect(machine).to be_in_state(:started)
9
+ end
10
+
11
+ it 'accepts a second argument' do
12
+ acceptor = double('Callbacks')
13
+ machine = described_class.new(:started, acceptor)
14
+ end
15
+ end
16
+
17
+ describe '#permit_state' do
18
+ it 'makes the state known to the state machine' do
19
+ machine = described_class.new(:started)
20
+
21
+ first_result_of_permission = machine.permit_state :closed
22
+ second_result_of_permission = machine.permit_state :closed, :open
23
+
24
+ expect(first_result_of_permission).to eq(Set.new([:closed]))
25
+ expect(second_result_of_permission).to eq(Set.new([:open]))
26
+
27
+ expect(machine).to be_known(:started)
28
+ expect(machine).to be_known(:closed)
29
+ expect(machine).to be_known(:open)
30
+ end
31
+
32
+ it 'does not permit transitions to the newly added state by default' do
33
+ machine = described_class.new(:started)
34
+ machine.permit_state :running
35
+
36
+ expect(machine).not_to be_may_transition_to(:running)
37
+
38
+ expect {
39
+ machine.transition! :running
40
+ }.to raise_error(described_class::InvalidFlow, /Cannot change states from started to running/)
41
+ end
42
+ end
43
+
44
+ describe '#permit_transition' do
45
+ it 'raises on an unknown state specified as source' do
46
+ machine = described_class.new(:started)
47
+ expect {
48
+ machine.permit_transition :unknown => :started
49
+ }.to raise_error(VeryTinyStateMachine::UnknownState)
50
+ end
51
+
52
+ it 'raises on an unknwon state specified as destination' do
53
+ machine = described_class.new(:started)
54
+ expect {
55
+ machine.permit_transition :started => :unknown
56
+ }.to raise_error(VeryTinyStateMachine::UnknownState)
57
+ end
58
+
59
+ it 'returns a Set of transitions permitted after the call' do
60
+ machine = described_class.new(:started)
61
+ machine.permit_state :running
62
+
63
+ result = machine.permit_transition :started => :running
64
+
65
+ expect(result).to be_kind_of(Set)
66
+ expect(result).to eq(Set.new([{:started => :running}]))
67
+
68
+ adding_second_time = machine.permit_transition :started => :running
69
+ expect(adding_second_time).to be_kind_of(Set)
70
+ expect(adding_second_time).to be_empty
71
+ end
72
+
73
+ it 'is able to perform the transition after it has been defined' do
74
+ machine = described_class.new(:started)
75
+ machine.permit_state :running
76
+ machine.permit_transition :started => :running
77
+ machine.transition! :running
78
+ end
79
+
80
+ it 'allows the transition from a state to itself only explicitly' do
81
+ machine = described_class.new(:started)
82
+ expect {
83
+ machine.transition! :started
84
+ }.to raise_error(described_class::InvalidFlow)
85
+
86
+ machine.permit_transition :started => :started
87
+ machine.transition! :started
88
+ expect(machine.flow_so_far).to eq([:started, :started])
89
+ end
90
+ end
91
+
92
+ describe '#flow_so_far' do
93
+ it 'records the flow' do
94
+ machine = described_class.new(:started)
95
+ machine.permit_state :running, :stopped
96
+ machine.permit_transition :started => :running, :running => :stopped, :stopped => :started
97
+
98
+ machine.transition! :running
99
+ machine.transition! :stopped
100
+ machine.transition! :started
101
+
102
+ flow = machine.flow_so_far
103
+ expect(flow).to eq([:started, :running, :stopped, :started])
104
+
105
+ flow << nil
106
+ expect(flow).not_to eq(machine.flow_so_far), "The flow returned should not link to the mutable array in the machine"
107
+ end
108
+ end
109
+
110
+ describe '#transition!' do
111
+ it 'returns the previous state the object was in' do
112
+ machine = described_class.new(:started)
113
+ machine.permit_state :running
114
+ machine.permit_transition :started => :running
115
+ transitioned_from = machine.transition! :running
116
+ expect(transitioned_from).to eq(:started)
117
+ end
118
+
119
+ it 'sends all of the callbacks if the object responds to them' do
120
+ fake_acceptor = double('Callback handler')
121
+ allow(fake_acceptor).to receive(:respond_to?) {|method_name, honor_private_and_public|
122
+ expect(honor_private_and_public).to eq(true)
123
+ true
124
+ }
125
+
126
+ machine = described_class.new(:started, fake_acceptor)
127
+ machine.permit_state :running, :stopped
128
+ machine.permit_transition :started => :running, :running => :stopped, :stopped => :started
129
+
130
+ expect(fake_acceptor).to receive(:leaving_started_state)
131
+ expect(fake_acceptor).to receive(:entering_running_state)
132
+ expect(fake_acceptor).to receive(:transitioning_from_started_to_running)
133
+ expect(fake_acceptor).to receive(:after_transitioning_from_started_to_running_state)
134
+ expect(fake_acceptor).to receive(:after_leaving_started_state)
135
+ expect(fake_acceptor).to receive(:after_entering_running_state)
136
+
137
+ machine.transition! :running
138
+ end
139
+
140
+ it 'dispatches callbacks to private methods as well' do
141
+ acceptor = Class.new do
142
+ def called?
143
+ @entered_state
144
+ end
145
+
146
+ private
147
+ def entering_running_state
148
+ @entered_state = true
149
+ end
150
+ end.new
151
+
152
+ machine = described_class.new(:started, acceptor)
153
+ machine.permit_state :running, :stopped
154
+ machine.permit_transition :started => :running, :running => :stopped, :stopped => :started
155
+
156
+ machine.transition! :running
157
+ expect(acceptor).to be_called
158
+ end
159
+
160
+ it 'does not send the messages to an acceptor that does not respond to those messages' do
161
+ fake_acceptor = double('Callback handler')
162
+ allow(fake_acceptor).to receive(:respond_to?) {|method_name, honor_private_and_public|
163
+ expect(honor_private_and_public).to eq(true)
164
+ false
165
+ }
166
+
167
+ machine = described_class.new(:started, fake_acceptor)
168
+ machine.permit_state :running, :stopped
169
+ machine.permit_transition :started => :running, :running => :stopped, :stopped => :started
170
+
171
+ machine.transition! :running
172
+ end
173
+ end
174
+
175
+ describe '#transition_or_maintain!' do
176
+ it 'does not perform any transitions if the object is already in the requisite state' do
177
+ machine = described_class.new(:perfect)
178
+ machine.transition_or_maintain! :perfect
179
+ expect(machine.flow_so_far).to eq([:perfect])
180
+ end
181
+
182
+ it 'does perform a transition if the object is not in the requisite state' do
183
+ machine = described_class.new(:perfect)
184
+ machine.permit_state :perfect, :improving
185
+ machine.permit_transition :perfect => :improving, :improving => :perfect
186
+
187
+ machine.transition_or_maintain! :improving
188
+ expect(machine.flow_so_far).to eq([:perfect, :improving])
189
+ end
190
+ end
191
+
192
+ describe '#expect!' do
193
+ it 'returns true when the machine is in the requisite state' do
194
+ machine = described_class.new(:started)
195
+ expect(machine.expect!(:started)).to eq(true)
196
+ end
197
+
198
+ it 'raises an exception if the machine is not in that state' do
199
+ machine = described_class.new(:started)
200
+ expect {
201
+ machine.expect!(:running)
202
+ }.to raise_error(VeryTinyStateMachine::InvalidFlow, 'Must be in :running state, but was in :started')
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,62 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: very_tiny_state_machine 1.0.0 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "very_tiny_state_machine"
9
+ s.version = "1.0.0"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
13
+ s.authors = ["Julik Tarkhanov"]
14
+ s.date = "2016-01-14"
15
+ s.description = "You wouldn't beleive how tiny it is"
16
+ s.email = "me@julik.nl"
17
+ s.extra_rdoc_files = [
18
+ "LICENSE.txt",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".rspec",
24
+ "Gemfile",
25
+ "LICENSE.txt",
26
+ "README.md",
27
+ "Rakefile",
28
+ "lib/very_tiny_state_machine.rb",
29
+ "spec/spec_helper.rb",
30
+ "spec/very_tiny_state_machine_spec.rb",
31
+ "very_tiny_state_machine.gemspec"
32
+ ]
33
+ s.homepage = "http://github.com/WeTransfer/very_tiny_state_machine"
34
+ s.licenses = ["MIT"]
35
+ s.rubygems_version = "2.2.2"
36
+ s.summary = "A minuscule state machine for storing state of interesting objects"
37
+
38
+ if s.respond_to? :specification_version then
39
+ s.specification_version = 4
40
+
41
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
42
+ s.add_development_dependency(%q<rspec>, ["~> 3.2.0"])
43
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
44
+ s.add_development_dependency(%q<bundler>, ["~> 1.0"])
45
+ s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
46
+ s.add_development_dependency(%q<simplecov>, ["~> 0.10"])
47
+ else
48
+ s.add_dependency(%q<rspec>, ["~> 3.2.0"])
49
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
50
+ s.add_dependency(%q<bundler>, ["~> 1.0"])
51
+ s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
52
+ s.add_dependency(%q<simplecov>, ["~> 0.10"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<rspec>, ["~> 3.2.0"])
56
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
57
+ s.add_dependency(%q<bundler>, ["~> 1.0"])
58
+ s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
59
+ s.add_dependency(%q<simplecov>, ["~> 0.10"])
60
+ end
61
+ end
62
+
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: very_tiny_state_machine
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Julik Tarkhanov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rdoc
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.12'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.12'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: jeweler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.0.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.0.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.10'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.10'
83
+ description: You wouldn't beleive how tiny it is
84
+ email: me@julik.nl
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files:
88
+ - LICENSE.txt
89
+ - README.md
90
+ files:
91
+ - ".document"
92
+ - ".rspec"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/very_tiny_state_machine.rb
98
+ - spec/spec_helper.rb
99
+ - spec/very_tiny_state_machine_spec.rb
100
+ - very_tiny_state_machine.gemspec
101
+ homepage: http://github.com/WeTransfer/very_tiny_state_machine
102
+ licenses:
103
+ - MIT
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.2.2
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: A minuscule state machine for storing state of interesting objects
125
+ test_files: []