simple_state_machine 0.5.3 → 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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/Changelog.rdoc +23 -0
  3. data/README.rdoc +154 -100
  4. data/lib/simple_state_machine/active_record.rb +2 -62
  5. data/lib/simple_state_machine/decorator/active_record.rb +68 -0
  6. data/lib/simple_state_machine/decorator/default.rb +91 -0
  7. data/lib/simple_state_machine/railtie.rb +1 -1
  8. data/lib/simple_state_machine/simple_state_machine.rb +8 -251
  9. data/lib/simple_state_machine/state_machine.rb +88 -0
  10. data/lib/simple_state_machine/state_machine_definition.rb +72 -0
  11. data/lib/simple_state_machine/tools/graphviz.rb +21 -0
  12. data/lib/simple_state_machine/tools/inspector.rb +44 -0
  13. data/lib/simple_state_machine/transition.rb +40 -0
  14. data/lib/simple_state_machine/version.rb +1 -1
  15. data/lib/simple_state_machine.rb +13 -3
  16. data/lib/tasks/graphviz.rake +31 -0
  17. metadata +37 -150
  18. data/.gitignore +0 -1
  19. data/.rspec +0 -3
  20. data/Gemfile +0 -4
  21. data/Rakefile +0 -28
  22. data/autotest/discover.rb +0 -1
  23. data/examples/conversation.rb +0 -33
  24. data/examples/lamp.rb +0 -21
  25. data/examples/relationship.rb +0 -87
  26. data/examples/traffic_light.rb +0 -17
  27. data/examples/user.rb +0 -37
  28. data/lib/tasks/graphiz.rake +0 -13
  29. data/rails/graphiz.rake +0 -16
  30. data/simple_state_machine.gemspec +0 -31
  31. data/spec/active_record_spec.rb +0 -223
  32. data/spec/decorator_spec.rb +0 -195
  33. data/spec/examples_spec.rb +0 -60
  34. data/spec/mountable_spec.rb +0 -24
  35. data/spec/simple_state_machine_spec.rb +0 -129
  36. data/spec/spec_helper.rb +0 -7
  37. data/spec/state_machine_definition_spec.rb +0 -89
  38. data/spec/state_machine_spec.rb +0 -26
@@ -1,223 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- require "rubygems"
4
- require "bundler"
5
- Bundler.require
6
- #Bundler.setup(:test)#, :activerecord)
7
- require 'active_record'
8
- require 'examples/user'
9
-
10
- ActiveRecord::Base.logger = Logger.new "test.log"
11
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3",
12
- :database => ":memory:")
13
-
14
- def setup_db
15
- ActiveRecord::Schema.define(:version => 1) do
16
- create_table :users do |t|
17
- t.column :id, :integer
18
- t.column :name, :string
19
- t.column :state, :string
20
- t.column :activation_code, :string
21
- t.column :created_at, :datetime
22
- t.column :updated_at, :datetime
23
- end
24
- create_table :tickets do |t|
25
- t.column :id, :integer
26
- t.column :ssm_state, :string
27
- end
28
- end
29
- end
30
-
31
- def teardown_db
32
- ActiveRecord::Base.connection.tables.each do |table|
33
- ActiveRecord::Base.connection.drop_table(table)
34
- end
35
- end
36
-
37
- class Ticket < ActiveRecord::Base
38
- extend SimpleStateMachine::ActiveRecord
39
-
40
- state_machine_definition.state_method = :ssm_state
41
-
42
- def after_initialize
43
- self.ssm_state ||= 'open'
44
- end
45
-
46
- event :close, :open => :closed
47
- end
48
-
49
- describe ActiveRecord do
50
-
51
- before do
52
- setup_db
53
- end
54
-
55
- after do
56
- teardown_db
57
- end
58
-
59
- it "has a default state" do
60
- User.new.should be_new
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
- user.invite_and_save.should == true
69
- User.find(user.id).should be_invited
70
- User.find(user.id).activation_code.should_not be_nil
71
- end
72
-
73
- it "persist transitions even when state is attr_protected" do
74
- user_class = Class.new(User)
75
- user_class.instance_eval { attr_protected :state }
76
- user = user_class.create!(:name => 'name', :state => 'x')
77
- user.should be_new
78
- user.invite_and_save
79
- user.reload.should be_invited
80
- end
81
-
82
- it "persists transitions when using send and a symbol" do
83
- user = User.create!(:name => 'name')
84
- user.send(:invite_and_save).should == true
85
- User.find(user.id).should be_invited
86
- User.find(user.id).activation_code.should_not be_nil
87
- end
88
-
89
- it "raises an error if an invalid state_transition is called" do
90
- user = User.create!(:name => 'name')
91
- expect {
92
- user.confirm_invitation_and_save 'abc'
93
- }.to raise_error(SimpleStateMachine::IllegalStateTransitionError,
94
- "You cannot 'confirm_invitation' when state is 'new'")
95
- end
96
-
97
- it "returns false and keeps state if record is invalid" do
98
- user = User.new
99
- user.should be_new
100
- user.should_not be_valid
101
- user.invite_and_save.should == false
102
- user.should be_new
103
- end
104
-
105
- it "returns false, keeps state and keeps errors if event adds errors" do
106
- user = User.create!(:name => 'name')
107
- user.invite_and_save!
108
- user.should be_invited
109
- user.confirm_invitation_and_save('x').should == false
110
- user.should be_invited
111
- user.errors.entries.should == [['activation_code', 'is invalid']]
112
- end
113
-
114
- end
115
-
116
- describe "event_and_save!" do
117
-
118
- it "persists transitions" do
119
- user = User.create!(:name => 'name')
120
- user.invite_and_save!.should == true
121
- User.find(user.id).should be_invited
122
- User.find(user.id).activation_code.should_not be_nil
123
- end
124
-
125
- it "persist transitions even when state is attr_protected" do
126
- user_class = Class.new(User)
127
- user_class.instance_eval { attr_protected :state }
128
- user = user_class.create!(:name => 'name', :state => 'x')
129
- user.should be_new
130
- user.invite_and_save!
131
- user.reload.should be_invited
132
- end
133
-
134
- it "raises an error if an invalid state_transition is called" do
135
- user = User.create!(:name => 'name')
136
- expect {
137
- user.confirm_invitation_and_save! 'abc'
138
- }.to raise_error(SimpleStateMachine::IllegalStateTransitionError,
139
- "You cannot 'confirm_invitation' when state is 'new'")
140
- end
141
-
142
- it "raises a RecordInvalid and keeps state if record is invalid" do
143
- user = User.new
144
- user.should be_new
145
- user.should_not be_valid
146
- expect {
147
- user.invite_and_save!
148
- }.to raise_error(ActiveRecord::RecordInvalid,
149
- "Validation failed: Name can't be blank")
150
- user.should be_new
151
- end
152
-
153
- it "raises a RecordInvalid and keeps state if event adds errors" do
154
- user = User.create!(:name => 'name')
155
- user.invite_and_save!
156
- user.should be_invited
157
- expect {
158
- user.confirm_invitation_and_save!('x')
159
- }.to raise_error(ActiveRecord::RecordInvalid,
160
- "Validation failed: Activation code is invalid")
161
- user.should be_invited
162
- end
163
-
164
- end
165
-
166
- describe "event" do
167
- it "persists transitions" do
168
- user = User.create!(:name => 'name')
169
- user.invite.should == true
170
- User.find(user.id).should be_invited
171
- User.find(user.id).activation_code.should_not be_nil
172
- end
173
-
174
- it "returns false, keeps state and keeps errors if event adds errors" do
175
- user = User.create!(:name => 'name')
176
- user.invite_and_save!
177
- user.should be_invited
178
- user.confirm_invitation('x').should == false
179
- user.should be_invited
180
- user.errors.entries.should == [['activation_code', 'is invalid']]
181
- end
182
-
183
- end
184
-
185
- describe "event!" do
186
-
187
- it "persists transitions" do
188
- user = User.create!(:name => 'name')
189
- user.invite!.should == true
190
- User.find(user.id).should be_invited
191
- User.find(user.id).activation_code.should_not be_nil
192
- end
193
-
194
- it "raises a RecordInvalid and keeps state if record is invalid" do
195
- user = User.new
196
- user.should be_new
197
- user.should_not be_valid
198
- expect { user.invite! }.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Name can't be blank")
199
- user.should be_new
200
- end
201
-
202
- end
203
-
204
- describe 'custom state method' do
205
-
206
- it "persists transitions" do
207
- ticket = Ticket.create!
208
- ticket.should be_open
209
- ticket.close!.should == true
210
- ticket.should be_closed
211
- end
212
-
213
- it "persists transitions with !" do
214
- ticket = Ticket.create!
215
- ticket.should be_open
216
- ticket.close!
217
- ticket.should be_closed
218
- end
219
-
220
- end
221
-
222
- end
223
-
@@ -1,195 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- describe SimpleStateMachine::Decorator 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 = SimpleStateMachine::Decorator.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
- @instance.state_machine.should be_an(SimpleStateMachine::StateMachine)
22
- end
23
-
24
- it "defines a state getter method" do
25
- @instance.should respond_to(:state)
26
- end
27
-
28
- it "defines a state setter method" do
29
- @instance.should respond_to(:state=)
30
- end
31
- end
32
-
33
- describe "#decorate" do
34
- it "defines state_helper_methods for both states" do
35
- @instance.state1?.should == true
36
- @instance.state2?.should == false
37
- end
38
-
39
- it "defines an event method" do
40
- @instance.should 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 = SimpleStateMachine::Decorator.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
- @instance.state_machine.should be_an(SimpleStateMachine::StateMachine)
68
- end
69
-
70
- it "defines a state getter method" do
71
- @instance.should respond_to(:state)
72
- end
73
-
74
- it "defines a state setter method" do
75
- @instance.should respond_to(:state=)
76
- end
77
- end
78
-
79
- describe "#decorate" do
80
- it "does not overwrite predefined state_helper_methods" do
81
- @instance.state1?.should == "state1"
82
- @instance.state2?.should == "state2"
83
- end
84
-
85
- it "does not overwrite predefined event method" do
86
- @instance.event.should == "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 = SimpleStateMachine::Decorator.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
- @instance.state_machine.should be_an(SimpleStateMachine::StateMachine)
115
- end
116
-
117
- it "defines a state getter method" do
118
- @instance.should respond_to(:state)
119
- end
120
-
121
- it "defines a state setter method" do
122
- @instance.should respond_to(:state=)
123
- end
124
- end
125
-
126
- describe "#decorate" do
127
- it "does not overwrite predefined protected state_helper_methods" do
128
- @instance.send(:state1?).should == "state1"
129
- @instance.send(:state2?).should == "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
- @instance.event.should == "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 = SimpleStateMachine::Decorator.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
- @instance.state_machine.should be_an(SimpleStateMachine::StateMachine)
167
- end
168
-
169
- it "defines a state getter method" do
170
- @instance.should respond_to(:state)
171
- end
172
-
173
- it "defines a state setter method" do
174
- @instance.should respond_to(:state=)
175
- end
176
- end
177
-
178
- describe "#decorate" do
179
- it "does not overwrite predefined private state_helper_methods" do
180
- @instance.send(:state1?).should == "state1"
181
- @instance.send(:state2?).should == "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
- @instance.event.should == "predefined method"
191
- end
192
- end
193
- end
194
-
195
- end
@@ -1,60 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- describe "Examples" do
4
- describe "TrafficLight" do
5
- it "changes to the next state" do
6
- tl = TrafficLight.new
7
- tl.should be_green
8
- tl.change_state
9
- tl.should be_orange
10
- tl.change_state
11
- tl.should be_red
12
- tl.change_state
13
- tl.should be_green
14
- end
15
- end
16
-
17
- describe "Lamp" do
18
- it "changes between :on and :off" do
19
- lamp = Lamp.new
20
- lamp.should be_off
21
- lamp.push_button1
22
- lamp.should be_on
23
- lamp.push_button2
24
- lamp.should be_off
25
- lamp.push_button2
26
- lamp.should be_on
27
- lamp.push_button1
28
- lamp.should be_off
29
- end
30
- end
31
-
32
- describe "Conversation" do
33
- it "is :unread by default" do
34
- conversation = Conversation.new
35
- conversation.should be_unread
36
- end
37
-
38
- it "changes to read on view" do
39
- conversation = Conversation.new
40
- conversation.view
41
- conversation.should be_read
42
- end
43
-
44
- it "changes to closed on close" do
45
- conversation = Conversation.new
46
- conversation.close
47
- conversation.should 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
- conversation.should be_closed
55
- end
56
-
57
- end
58
-
59
- end
60
-
@@ -1,24 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- describe "Mountable" do
4
- before do
5
- mountable_class = Class.new(SimpleStateMachine::StateMachineDefinition) do
6
- def initialize(subject)
7
- self.lazy_decorator = lambda { SimpleStateMachine::Decorator.new(subject) }
8
- add_transition(:event, :state1, :state2)
9
- end
10
- end
11
- klass = Class.new do
12
- extend SimpleStateMachine::Mountable
13
- self.state_machine_definition = mountable_class.new self
14
- end
15
- @instance = klass.new
16
- @instance.state = 'state1'
17
- end
18
-
19
- it "has state_helper methods" do
20
- @instance.should be_state1
21
- @instance.should_not be_state2
22
- end
23
-
24
- end
@@ -1,129 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- require 'cgi'
3
-
4
- describe SimpleStateMachine do
5
-
6
- it "has an error that extends RuntimeError" do
7
- SimpleStateMachine::IllegalStateTransitionError.superclass.should == RuntimeError
8
- end
9
-
10
- describe ".event" do
11
-
12
- before do
13
- @klass = Class.new do
14
- extend SimpleStateMachine
15
- def initialize(state = 'state1')
16
- @state = state
17
- end
18
- end
19
- end
20
-
21
- it "changes state if event has multiple transitions" do
22
- klass = Class.new(@klass)
23
- klass.instance_eval do
24
- event :event, :state1 => :state2, :state2 => :state3
25
- end
26
- example = klass.new
27
- example.should be_state1
28
- example.event
29
- example.should be_state2
30
- example.event
31
- example.should be_state3
32
- end
33
-
34
- it "changes state if event has multiple froms" do
35
- klass = Class.new(@klass)
36
- klass.instance_eval do
37
- event :event, [:state1, :state2] => :state3
38
- end
39
- example = klass.new
40
- example.event
41
- example.should be_state3
42
- example = klass.new 'state2'
43
- example.should be_state2
44
- example.event
45
- example.should be_state3
46
- end
47
-
48
- it "changes state if event has all as from" do
49
- klass = Class.new(@klass)
50
- klass.instance_eval do
51
- event :other_event, :state1 => :state2
52
- event :event, :all => :state3
53
- end
54
- example = klass.new
55
- example.event
56
- example.should be_state3
57
- example = klass.new 'state2'
58
- example.should be_state2
59
- example.event
60
- example.should be_state3
61
- end
62
-
63
- it "changes state when state is a symbol instead of a string" do
64
- klass = Class.new(@klass)
65
- klass.instance_eval do
66
- event :event, :state1 => :state2
67
- end
68
- example = klass.new :state1
69
- example.state.should == :state1
70
- example.send(:event)
71
- example.should be_state2
72
- end
73
-
74
- it "changes state to error_state when error can be caught" do
75
- class_with_error = Class.new(@klass)
76
- class_with_error.instance_eval do
77
- define_method :raise_error do
78
- raise RuntimeError.new
79
- end
80
- event :raise_error, :state1 => :state2, RuntimeError => :failed
81
- end
82
- example = class_with_error.new
83
- example.should be_state1
84
- example.raise_error
85
- example.should be_failed
86
- end
87
-
88
- it "raises an error if an invalid state_transition is called" do
89
- klass = Class.new(@klass)
90
- klass.instance_eval do
91
- event :event, :state1 => :state2
92
- event :event2, :state2 => :state3
93
- end
94
- example = klass.new
95
- lambda { example.event2 }.should raise_error(SimpleStateMachine::IllegalStateTransitionError, "You cannot 'event2' when state is 'state1'")
96
- end
97
-
98
- it "returns what the decorated method returns" do
99
- klass = Class.new(@klass)
100
- klass.instance_eval do
101
- event :event1, :state1 => :state2
102
- define_method :event2 do
103
- 'event2'
104
- end
105
- event :event2, :state2 => :state3
106
- end
107
- example = klass.new
108
- example.event1.should == nil
109
- example.event2.should == 'event2'
110
- end
111
-
112
- it "calls existing methods" do
113
- klass = Class.new(@klass)
114
- klass.instance_eval do
115
- attr_accessor :event_called
116
- define_method :event do
117
- @event_called = true
118
- end
119
- event :event, :state1 => :state2
120
- end
121
- example = klass.new
122
- example.event
123
- example.event_called.should == true
124
- end
125
-
126
- end
127
-
128
- end
129
-
data/spec/spec_helper.rb DELETED
@@ -1,7 +0,0 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
- require 'simple_state_machine'
4
- require 'examples/conversation'
5
- require 'examples/lamp'
6
- require 'examples/traffic_light'
7
-