simple_state_machine 0.6.1 → 0.6.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.
@@ -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
-