simple_state_machine 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,250 +0,0 @@
1
- require 'spec_helper'
2
- require 'logger'
3
-
4
- if defined? ActiveRecord
5
- ActiveRecord::Base.logger = Logger.new "test.log"
6
- ActiveRecord::Base.establish_connection('adapter' => RUBY_PLATFORM == 'java' ? 'jdbcsqlite3' : 'sqlite3', 'database' => ':memory:')
7
-
8
- def setup_db
9
- ActiveRecord::Schema.define(:version => 1) do
10
- create_table :users do |t|
11
- t.column :name, :string
12
- t.column :state, :string
13
- t.column :activation_code, :string
14
- t.column :created_at, :datetime
15
- t.column :updated_at, :datetime
16
- end
17
- create_table :tickets do |t|
18
- t.column :ssm_state, :string
19
- end
20
- end
21
- end
22
-
23
- def teardown_db
24
- ActiveRecord::Base.connection.tables.each do |table|
25
- ActiveRecord::Base.connection.drop_table(table)
26
- end
27
- end
28
-
29
- class Ticket < ActiveRecord::Base
30
- extend SimpleStateMachine::ActiveRecord
31
-
32
- state_machine_definition.state_method = :ssm_state
33
-
34
- after_initialize :after_initialize
35
- def after_initialize
36
- self.ssm_state ||= 'open'
37
- end
38
-
39
- event :close, :open => :closed
40
- end
41
-
42
- describe ActiveRecord do
43
-
44
- before do
45
- setup_db
46
- end
47
-
48
- after do
49
- teardown_db
50
- end
51
-
52
- it "has a default state" do
53
- expect(User.new).to be_new
54
- end
55
-
56
- it "persists transitions when using send and a symbol" do
57
- user = User.create!(:name => 'name')
58
- expect(user.send(:invite_and_save)).to eq(true)
59
- expect(User.find(user.id)).to be_invited
60
- expect(User.find(user.id).activation_code).not_to be_nil
61
- end
62
-
63
- # TODO needs nesting/grouping, seems to have some duplication
64
-
65
- describe "event_and_save" do
66
- it "persists transitions" do
67
- user = User.create!(:name => 'name')
68
- expect(user.invite_and_save).to eq(true)
69
- expect(User.find(user.id)).to be_invited
70
- expect(User.find(user.id).activation_code).not_to be_nil
71
- end
72
-
73
- it "persists transitions when using send and a symbol" do
74
- user = User.create!(:name => 'name')
75
- expect(user.send(:invite_and_save)).to eq(true)
76
- expect(User.find(user.id)).to be_invited
77
- expect(User.find(user.id).activation_code).not_to be_nil
78
- end
79
-
80
- it "raises an error if an invalid state_transition is called" do
81
- user = User.create!(:name => 'name')
82
- expect {
83
- user.confirm_invitation_and_save 'abc'
84
- }.to raise_error(SimpleStateMachine::IllegalStateTransitionError,
85
- "You cannot 'confirm_invitation' when state is 'new'")
86
- end
87
-
88
- it "returns false and keeps state if record is invalid" do
89
- user = User.new
90
- expect(user).to be_new
91
- expect(user).not_to be_valid
92
- expect(user.invite_and_save).to eq(false)
93
- expect(user).to be_new
94
- end
95
-
96
- it "returns false, keeps state and keeps errors if event adds errors" do
97
- user = User.create!(:name => 'name')
98
- user.invite_and_save!
99
- expect(user).to be_invited
100
- expect(user.confirm_invitation_and_save('x')).to eq(false)
101
- expect(user).to be_invited
102
- expect(Array(user.errors[:activation_code])).to eq(['is invalid'])
103
- end
104
-
105
- it "rollsback if an exception is raised" do
106
- user_class = Class.new(User)
107
- user_class.instance_eval do
108
- define_method :invite_without_managed_state do
109
- User.create!(:name => 'name2') #this shouldn't be persisted
110
- User.create! #this should raise an error
111
- end
112
- end
113
- expect(user_class.count).to eq(0)
114
- user = user_class.create!(:name => 'name')
115
- expect {
116
- user.transaction { user.invite_and_save }
117
- }.to raise_error(ActiveRecord::RecordInvalid,
118
- "Validation failed: Name can't be blank")
119
- expect(user_class.count).to eq(1)
120
- expect(user_class.first.name).to eq('name')
121
- expect(user_class.first).to be_new
122
- end
123
-
124
- it "raises an error if an invalid state_transition is called" do
125
- user = User.create!(:name => 'name')
126
- expect {
127
- user.confirm_invitation_and_save! 'abc'
128
- }.to raise_error(SimpleStateMachine::IllegalStateTransitionError,
129
- "You cannot 'confirm_invitation' when state is 'new'")
130
- end
131
- end
132
-
133
- describe "event_and_save!" do
134
-
135
- it "persists transitions" do
136
- user = User.create!(:name => 'name')
137
- expect(user.invite_and_save!).to eq(true)
138
- expect(User.find(user.id)).to be_invited
139
- expect(User.find(user.id).activation_code).not_to be_nil
140
- end
141
-
142
- it "raises an error if an invalid state_transition is called" do
143
- user = User.create!(:name => 'name')
144
- expect {
145
- user.confirm_invitation_and_save! 'abc'
146
- }.to raise_error(SimpleStateMachine::IllegalStateTransitionError,
147
- "You cannot 'confirm_invitation' when state is 'new'")
148
- end
149
-
150
- it "raises a RecordInvalid and keeps state if record is invalid" do
151
- user = User.new
152
- expect(user).to be_new
153
- expect(user).not_to be_valid
154
- expect {
155
- user.invite_and_save!
156
- }.to raise_error(ActiveRecord::RecordInvalid,
157
- "Validation failed: Name can't be blank")
158
- expect(user).to be_new
159
- end
160
-
161
- it "raises a RecordInvalid and keeps state if event adds errors" do
162
- user = User.create!(:name => 'name')
163
- user.invite_and_save!
164
- expect(user).to be_invited
165
- expect {
166
- user.confirm_invitation_and_save!('x')
167
- }.to raise_error(ActiveRecord::RecordInvalid,
168
- "Validation failed: Activation code is invalid")
169
- expect(user).to be_invited
170
- end
171
-
172
- it "rollsback if an exception is raised" do
173
- user_class = Class.new(User)
174
- user_class.instance_eval do
175
- define_method :invite_without_managed_state do
176
- User.create!(:name => 'name2') #this shouldn't be persisted
177
- User.create! #this should raise an error
178
- end
179
- end
180
- expect(user_class.count).to eq(0)
181
- user = user_class.create!(:name => 'name')
182
- expect {
183
- user.transaction { user.invite_and_save! }
184
- }.to raise_error(ActiveRecord::RecordInvalid,
185
- "Validation failed: Name can't be blank")
186
- expect(user_class.count).to eq(1)
187
- expect(user_class.first.name).to eq('name')
188
- expect(user_class.first).to be_new
189
- end
190
-
191
- end
192
-
193
- describe "event" do
194
- it "persists transitions" do
195
- user = User.create!(:name => 'name')
196
- expect(user.invite).to eq(true)
197
- expect(User.find(user.id)).to be_invited
198
- expect(User.find(user.id).activation_code).not_to be_nil
199
- end
200
-
201
- it "returns false, keeps state and keeps errors if event adds errors" do
202
- user = User.create!(:name => 'name')
203
- user.invite_and_save!
204
- expect(user).to be_invited
205
- expect(user.confirm_invitation('x')).to eq(false)
206
- expect(user).to be_invited
207
- expect(Array(user.errors[:activation_code])).to eq(['is invalid'])
208
- end
209
-
210
- end
211
-
212
- describe "event!" do
213
-
214
- it "persists transitions" do
215
- user = User.create!(:name => 'name')
216
- expect(user.invite!).to eq(true)
217
- expect(User.find(user.id)).to be_invited
218
- expect(User.find(user.id).activation_code).not_to be_nil
219
- end
220
-
221
- it "raises a RecordInvalid and keeps state if record is invalid" do
222
- user = User.new
223
- expect(user).to be_new
224
- expect(user).not_to be_valid
225
- expect { user.invite! }.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Name can't be blank")
226
- expect(user).to be_new
227
- end
228
-
229
- end
230
-
231
- describe 'custom state method' do
232
-
233
- it "persists transitions" do
234
- ticket = Ticket.create!
235
- expect(ticket.ssm_state).to eq('open')
236
- expect(ticket.close!).to eq(true)
237
- expect(ticket.ssm_state).to eq('closed')
238
- end
239
-
240
- it "persists transitions with !" do
241
- ticket = Ticket.create!
242
- expect(ticket.ssm_state).to eq('open')
243
- ticket.close!
244
- expect(ticket.ssm_state).to eq('closed')
245
- end
246
-
247
- end
248
-
249
- end
250
- end
@@ -1,195 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe SimpleStateMachine::Decorator::Default do
4
-
5
- context "given a class" do
6
- before do
7
- klass = Class.new do
8
- # TODO remove the need for defining this method
9
- def self.state_machine_definition
10
- @state_machine_definition ||= SimpleStateMachine::StateMachineDefinition.new
11
- end
12
- end
13
- decorator = described_class.new klass
14
- decorator.decorate SimpleStateMachine::Transition.new(:event, :state1, :state2)
15
- @instance = klass.new
16
- @instance.state = 'state1'
17
- end
18
-
19
- describe "#initialize" do
20
- it "defines a state_machine method" do
21
- expect(@instance.state_machine).to be_an(SimpleStateMachine::StateMachine)
22
- end
23
-
24
- it "defines a state getter method" do
25
- expect(@instance).to respond_to(:state)
26
- end
27
-
28
- it "defines a state setter method" do
29
- expect(@instance).to respond_to(:state=)
30
- end
31
- end
32
-
33
- describe "#decorate" do
34
- it "defines state_helper_methods for both states" do
35
- expect(@instance.state1?).to eq(true)
36
- expect(@instance.state2?).to eq(false)
37
- end
38
-
39
- it "defines an event method" do
40
- expect(@instance).to respond_to(:event)
41
- end
42
- end
43
- end
44
-
45
- context "given a class with predefined public methods" do
46
- before do
47
- klass = Class.new do
48
- # TODO remove the need for defining this method
49
- def self.state_machine_definition
50
- @state_machine_definition ||= SimpleStateMachine::StateMachineDefinition.new
51
- end
52
- # predefined methods
53
- def state1?() "state1" end
54
- def state2?() "state2" end
55
- def event() "predefined method" end
56
- end
57
- transition = SimpleStateMachine::Transition.new(:event, :state1, :state2)
58
- decorator = described_class.new klass
59
- decorator.decorate transition
60
- klass.state_machine_definition.transitions << transition
61
- @instance = klass.new
62
- @instance.state = 'state1'
63
- end
64
-
65
- describe "#initialize" do
66
- it "defines a state_machine method" do
67
- expect(@instance.state_machine).to be_an(SimpleStateMachine::StateMachine)
68
- end
69
-
70
- it "defines a state getter method" do
71
- expect(@instance).to respond_to(:state)
72
- end
73
-
74
- it "defines a state setter method" do
75
- expect(@instance).to respond_to(:state=)
76
- end
77
- end
78
-
79
- describe "#decorate" do
80
- it "does not overwrite predefined state_helper_methods" do
81
- expect(@instance.state1?).to eq("state1")
82
- expect(@instance.state2?).to eq("state2")
83
- end
84
-
85
- it "does not overwrite predefined event method" do
86
- expect(@instance.event).to eq("predefined method")
87
- end
88
- end
89
- end
90
-
91
- context "given a class with predefined protected methods" do
92
- before do
93
- klass = Class.new do
94
- # TODO remove the need for defining this method
95
- def self.state_machine_definition
96
- @state_machine_definition ||= SimpleStateMachine::StateMachineDefinition.new
97
- end
98
- # predefined methods
99
- protected
100
- def state1?() "state1" end
101
- def state2?() "state2" end
102
- def event() "predefined method" end
103
- end
104
- transition = SimpleStateMachine::Transition.new(:event, :state1, :state2)
105
- decorator = described_class.new klass
106
- decorator.decorate transition
107
- klass.state_machine_definition.transitions << transition
108
- @instance = klass.new
109
- @instance.state = 'state1'
110
- end
111
-
112
- describe "#initialize" do
113
- it "defines a state_machine method" do
114
- expect(@instance.state_machine).to be_an(SimpleStateMachine::StateMachine)
115
- end
116
-
117
- it "defines a state getter method" do
118
- expect(@instance).to respond_to(:state)
119
- end
120
-
121
- it "defines a state setter method" do
122
- expect(@instance).to respond_to(:state=)
123
- end
124
- end
125
-
126
- describe "#decorate" do
127
- it "does not overwrite predefined protected state_helper_methods" do
128
- expect(@instance.send(:state1?)).to eq("state1")
129
- expect(@instance.send(:state2?)).to eq("state2")
130
- end
131
-
132
- it "keeps predefined protected state_helper_methods protected" do
133
- expect { @instance.state1? }.to raise_error(NoMethodError)
134
- expect { @instance.state2? }.to raise_error(NoMethodError)
135
- end
136
-
137
- it "does not overwrite predefined protected event method" do
138
- expect(@instance.event).to eq("predefined method")
139
- end
140
- end
141
- end
142
-
143
- context "given a class with predefined private methods" do
144
- before do
145
- klass = Class.new do
146
- # TODO the need for defining this method
147
- def self.state_machine_definition
148
- @state_machine_definition ||= SimpleStateMachine::StateMachineDefinition.new
149
- end
150
- # predefined methods
151
- private
152
- def state1?() "state1" end
153
- def state2?() "state2" end
154
- def event() "predefined method" end
155
- end
156
- transition = SimpleStateMachine::Transition.new(:event, :state1, :state2)
157
- decorator = described_class.new klass
158
- decorator.decorate transition
159
- klass.state_machine_definition.transitions << transition
160
- @instance = klass.new
161
- @instance.state = 'state1'
162
- end
163
-
164
- describe "#initialize" do
165
- it "defines a state_machine method" do
166
- expect(@instance.state_machine).to be_an(SimpleStateMachine::StateMachine)
167
- end
168
-
169
- it "defines a state getter method" do
170
- expect(@instance).to respond_to(:state)
171
- end
172
-
173
- it "defines a state setter method" do
174
- expect(@instance).to respond_to(:state=)
175
- end
176
- end
177
-
178
- describe "#decorate" do
179
- it "does not overwrite predefined private state_helper_methods" do
180
- expect(@instance.send(:state1?)).to eq("state1")
181
- expect(@instance.send(:state2?)).to eq("state2")
182
- end
183
-
184
- it "keeps predefined private state_helper_methods private" do
185
- expect { @instance.state1? }.to raise_error(NoMethodError)
186
- expect { @instance.state2? }.to raise_error(NoMethodError)
187
- end
188
-
189
- it "does not overwrite predefined protected event method" do
190
- expect(@instance.event).to eq("predefined method")
191
- end
192
- end
193
- end
194
-
195
- end
@@ -1,60 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "Examples" do
4
- describe "TrafficLight" do
5
- it "changes to the next state" do
6
- tl = TrafficLight.new
7
- expect(tl).to be_green
8
- tl.change_state
9
- expect(tl).to be_orange
10
- tl.change_state
11
- expect(tl).to be_red
12
- tl.change_state
13
- expect(tl).to be_green
14
- end
15
- end
16
-
17
- describe "Lamp" do
18
- it "changes between :on and :off" do
19
- lamp = Lamp.new
20
- expect(lamp).to be_off
21
- lamp.push_button1
22
- expect(lamp).to be_on
23
- lamp.push_button2
24
- expect(lamp).to be_off
25
- lamp.push_button2
26
- expect(lamp).to be_on
27
- lamp.push_button1
28
- expect(lamp).to be_off
29
- end
30
- end
31
-
32
- describe "Conversation" do
33
- it "is :unread by default" do
34
- conversation = Conversation.new
35
- expect(conversation).to be_unread
36
- end
37
-
38
- it "changes to read on view" do
39
- conversation = Conversation.new
40
- conversation.view
41
- expect(conversation).to be_read
42
- end
43
-
44
- it "changes to closed on close" do
45
- conversation = Conversation.new
46
- conversation.close
47
- expect(conversation).to be_closed
48
- end
49
-
50
- it "changes to closed on close if :read" do
51
- conversation = Conversation.new
52
- conversation.view
53
- conversation.close
54
- expect(conversation).to be_closed
55
- end
56
-
57
- end
58
-
59
- end
60
-
@@ -1,36 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe SimpleStateMachine::Mountable do
4
- class MountableExample < SimpleStateMachine::StateMachineDefinition
5
- event(:event, :state1 => :state2)
6
-
7
- def decorator_class
8
- SimpleStateMachine::Decorator::Default
9
- end
10
- end
11
-
12
- let(:klass) do
13
- Class.new do
14
- attr_accessor :event_called
15
- extend SimpleStateMachine::Mountable
16
- mount_state_machine MountableExample
17
- def event_without_managed_state
18
- @event_called = true
19
- end
20
- end
21
- end
22
- subject do
23
- klass.new.tap{|i| i.state = 'state1' }
24
- end
25
-
26
- it "has state_helper methods" do
27
- expect(subject).to be_state1
28
- expect(subject).not_to be_state2
29
- end
30
-
31
- it "calls existing methods" do
32
- subject.event
33
- expect(subject).to be_state2
34
- expect(subject.event_called).to eq(true)
35
- end
36
- end
@@ -1,165 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe SimpleStateMachine do
4
-
5
- it "has an error that extends RuntimeError" do
6
- expect(SimpleStateMachine::IllegalStateTransitionError.superclass).to eq(RuntimeError)
7
- end
8
-
9
- let(:klass) do
10
- Class.new do
11
- extend SimpleStateMachine
12
- def initialize(state = 'state1')
13
- @state = state
14
- end
15
- end
16
- end
17
-
18
- describe ".event" do
19
-
20
- it "returns what the decorated method returns" do
21
- klass.instance_eval do
22
- event :event1, :state1 => :state2
23
- define_method :event2 do
24
- 'event2'
25
- end
26
- event :event2, :state2 => :state3
27
- end
28
- subject = klass.new
29
- expect(subject.event1).to eq(nil)
30
- expect(subject.event2).to eq('event2')
31
- end
32
-
33
- it "calls existing methods" do
34
- klass.instance_eval do
35
- attr_accessor :event_called
36
- define_method :event do
37
- @event_called = true
38
- end
39
- event :event, :state1 => :state2
40
- end
41
- subject = klass.new
42
- subject.event
43
- expect(subject.event_called).to eq(true)
44
- end
45
-
46
- context "given an event has multiple transitions" do
47
- before do
48
- klass.instance_eval do
49
- event :event, :state1 => :state2, :state2 => :state3
50
- end
51
- end
52
-
53
- it "changes state for all transitions" do
54
- subject = klass.new
55
- expect(subject).to be_state1
56
- subject.event
57
- expect(subject).to be_state2
58
- subject.event
59
- expect(subject).to be_state3
60
- end
61
- end
62
-
63
- context "given an event has multiple from states" do
64
- before do
65
- klass.instance_eval do
66
- event :event, [:state1, :state2] => :state3
67
- end
68
- end
69
-
70
- it "changes state for all from states" do
71
- subject = klass.new
72
- subject.event
73
- expect(subject).to be_state3
74
- subject = klass.new 'state2'
75
- expect(subject).to be_state2
76
- subject.event
77
- expect(subject).to be_state3
78
- end
79
- end
80
-
81
- context "given an event has :all as from state" do
82
- before do
83
- klass.instance_eval do
84
- event :other_event, :state1 => :state2
85
- event :event, :all => :state3
86
- end
87
- end
88
-
89
- it "changes state from all states" do
90
- subject = klass.new
91
- subject.event
92
- expect(subject).to be_state3
93
- subject = klass.new 'state2'
94
- expect(subject).to be_state2
95
- subject.event
96
- expect(subject).to be_state3
97
- end
98
- end
99
-
100
- context "given state is a symbol instead of a string" do
101
- before do
102
- klass.instance_eval do
103
- event :event, :state1 => :state2
104
- end
105
- end
106
-
107
- it "changes state" do
108
- subject = klass.new :state1
109
- expect(subject.state).to eq(:state1)
110
- subject.send(:event)
111
- expect(subject).to be_state2
112
- end
113
- end
114
-
115
- context "given an RuntimeError begin state" do
116
- it "changes state to error_state when error can be caught" do
117
- class_with_error = Class.new(klass)
118
- class_with_error.instance_eval do
119
- define_method :raise_error do
120
- raise RuntimeError.new
121
- end
122
- event :raise_error, :state1 => :state2, RuntimeError => :failed
123
- end
124
- subject = class_with_error.new
125
- expect(subject).to be_state1
126
- subject.raise_error
127
- expect(subject).to be_failed
128
- end
129
-
130
- it "changes state to error_state when error superclass can be caught" do
131
- error_subclass = Class.new(RuntimeError)
132
- class_with_error = Class.new(klass)
133
- class_with_error.instance_eval do
134
- define_method :raise_error do
135
- raise error_subclass.new
136
- end
137
- event :raise_error, :state1 => :state2, RuntimeError => :failed
138
- end
139
- subject = class_with_error.new
140
- expect(subject).to be_state1
141
- subject.raise_error
142
- expect(subject).to be_failed
143
- end
144
- end
145
-
146
- context "given an invalid state_transition is called" do
147
- before do
148
- klass.instance_eval do
149
- event :event, :state1 => :state2
150
- event :event2, :state2 => :state3
151
- end
152
- end
153
-
154
- it "raises an IllegalStateTransitionError" do
155
- subject = klass.new
156
- expect { subject.event2 }.to raise_error(
157
- SimpleStateMachine::IllegalStateTransitionError,
158
- "You cannot 'event2' when state is 'state1'")
159
- end
160
- end
161
-
162
- end
163
-
164
- end
165
-