aasm 3.0.13 → 3.0.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,71 @@
1
+ class Gate < ActiveRecord::Base
2
+ include AASM
3
+
4
+ # Fake this column for testing purposes
5
+ attr_accessor :aasm_state
6
+
7
+ aasm do
8
+ state :opened
9
+ state :closed
10
+
11
+ event :view do
12
+ transitions :to => :read, :from => [:needs_attention]
13
+ end
14
+ end
15
+ end
16
+
17
+ class Reader < ActiveRecord::Base
18
+ def aasm_read_state
19
+ "fi"
20
+ end
21
+ include AASM
22
+ end
23
+
24
+ class Writer < ActiveRecord::Base
25
+ def aasm_write_state(state)
26
+ "fo"
27
+ end
28
+ include AASM
29
+ end
30
+
31
+ class Transient < ActiveRecord::Base
32
+ def aasm_write_state_without_persistence(state)
33
+ "fum"
34
+ end
35
+ include AASM
36
+ end
37
+
38
+ class Simple < ActiveRecord::Base
39
+ include AASM
40
+ aasm_column :status
41
+ aasm_state :unknown_scope
42
+ aasm_state :new
43
+ end
44
+
45
+ class SimpleNewDsl < ActiveRecord::Base
46
+ include AASM
47
+ aasm :column => :status
48
+ aasm do
49
+ state :unknown_scope
50
+ state :new
51
+ end
52
+ end
53
+
54
+ class Derivate < Simple
55
+ end
56
+
57
+ class DerivateNewDsl < SimpleNewDsl
58
+ end
59
+
60
+ class Thief < ActiveRecord::Base
61
+ if ActiveRecord::VERSION::MAJOR >= 3
62
+ self.table_name = 'thieves'
63
+ else
64
+ set_table_name "thieves"
65
+ end
66
+ include AASM
67
+ aasm_initial_state Proc.new { |thief| thief.skilled ? :rich : :jailed }
68
+ aasm_state :rich
69
+ aasm_state :jailed
70
+ attr_accessor :skilled, :aasm_state
71
+ end
@@ -76,91 +76,6 @@ class Argument
76
76
  end
77
77
  end
78
78
 
79
- class AuthMachine
80
- include AASM
81
-
82
- attr_accessor :activation_code, :activated_at, :deleted_at
83
-
84
- aasm do
85
- state :passive
86
- state :pending, :initial => true, :enter => :make_activation_code
87
- state :active, :enter => :do_activate
88
- state :suspended
89
- state :deleted, :enter => :do_delete, :exit => :do_undelete
90
- state :waiting
91
-
92
- event :register do
93
- transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| u.can_register? }
94
- end
95
-
96
- event :activate do
97
- transitions :from => :pending, :to => :active
98
- end
99
-
100
- event :suspend do
101
- transitions :from => [:passive, :pending, :active], :to => :suspended
102
- end
103
-
104
- event :delete do
105
- transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
106
- end
107
-
108
- # a dummy event that can never happen
109
- event :unpassify do
110
- transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
111
- end
112
-
113
- event :unsuspend do
114
- transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| u.has_activated? }
115
- transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| u.has_activation_code? }
116
- transitions :from => :suspended, :to => :passive
117
- end
118
-
119
- event :wait do
120
- transitions :from => :suspended, :to => :waiting, :guard => :if_polite?
121
- end
122
- end
123
-
124
- def initialize
125
- # the AR backend uses a before_validate_on_create :aasm_ensure_initial_state
126
- # lets do something similar here for testing purposes.
127
- aasm_enter_initial_state
128
- end
129
-
130
- def make_activation_code
131
- @activation_code = 'moo'
132
- end
133
-
134
- def do_activate
135
- @activated_at = Time.now
136
- @activation_code = nil
137
- end
138
-
139
- def do_delete
140
- @deleted_at = Time.now
141
- end
142
-
143
- def do_undelete
144
- @deleted_at = false
145
- end
146
-
147
- def can_register?
148
- true
149
- end
150
-
151
- def has_activated?
152
- !!@activated_at
153
- end
154
-
155
- def has_activation_code?
156
- !!@activation_code
157
- end
158
-
159
- def if_polite?(phrase = nil)
160
- phrase == :please
161
- end
162
- end
163
-
164
79
  class ThisNameBetterNotBeInUse
165
80
  include AASM
166
81
 
@@ -41,7 +41,7 @@ end
41
41
 
42
42
 
43
43
  describe AASM, '- aasm_states_for_select' do
44
- it "should return a select friendly array of states in the form of [['Friendly name', 'state_name']]" do
44
+ it "should return a select friendly array of states" do
45
45
  Foo.aasm_states_for_select.should == [['Open', 'open'], ['Closed', 'closed']]
46
46
  end
47
47
  end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'callbacks for the new DSL' do
4
+ before(:each) do
5
+ @callback = CallbackNewDsl.new
6
+ end
7
+
8
+ it "should get close callbacks" do
9
+ @callback.should_receive(:before).once.ordered
10
+ @callback.should_receive(:before_exit_open).once.ordered # these should be before the state changes
11
+ @callback.should_receive(:before_enter_closed).once.ordered
12
+ @callback.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
13
+ @callback.should_receive(:after_exit_open).once.ordered # these should be after the state changes
14
+ @callback.should_receive(:after_enter_closed).once.ordered
15
+ @callback.should_receive(:after).once.ordered
16
+
17
+ @callback.close!
18
+ end
19
+
20
+ it "should get open callbacks" do
21
+ @callback.close!
22
+
23
+ @callback.should_receive(:before).once.ordered
24
+ @callback.should_receive(:before_exit_closed).once.ordered # these should be before the state changes
25
+ @callback.should_receive(:before_enter_open).once.ordered
26
+ @callback.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
27
+ @callback.should_receive(:after_exit_closed).once.ordered # these should be after the state changes
28
+ @callback.should_receive(:after_enter_open).once.ordered
29
+ @callback.should_receive(:after).once.ordered
30
+
31
+ @callback.open!
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'callbacks for the old DSL' do
4
+ before(:each) do
5
+ @callback = CallbackOldDsl.new
6
+ end
7
+
8
+ it "should get close callbacks" do
9
+ @callback.should_receive(:before).once.ordered
10
+ @callback.should_receive(:before_exit_open).once.ordered # these should be before the state changes
11
+ @callback.should_receive(:before_enter_closed).once.ordered
12
+ @callback.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
13
+ @callback.should_receive(:after_exit_open).once.ordered # these should be after the state changes
14
+ @callback.should_receive(:after_enter_closed).once.ordered
15
+ @callback.should_receive(:after).once.ordered
16
+
17
+ @callback.close!
18
+ end
19
+
20
+ it "should get open callbacks" do
21
+ @callback.close!
22
+
23
+ @callback.should_receive(:before).once.ordered
24
+ @callback.should_receive(:before_exit_closed).once.ordered # these should be before the state changes
25
+ @callback.should_receive(:before_enter_open).once.ordered
26
+ @callback.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
27
+ @callback.should_receive(:after_exit_closed).once.ordered # these should be after the state changes
28
+ @callback.should_receive(:after_enter_open).once.ordered
29
+ @callback.should_receive(:after).once.ordered
30
+
31
+ @callback.open!
32
+ end
33
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'AuthMachine on initialization' do
4
+ let(:auth) {AuthMachine.new}
5
+
6
+ it 'should be in the pending state' do
7
+ auth.aasm_current_state.should == :pending
8
+ end
9
+
10
+ it 'should have an activation code' do
11
+ auth.has_activation_code?.should be_true
12
+ auth.activation_code.should_not be_nil
13
+ end
14
+ end
15
+
16
+ describe 'AuthMachine when being unsuspended' do
17
+ let(:auth) {AuthMachine.new}
18
+
19
+ it 'should be able to be unsuspended' do
20
+ auth.activate!
21
+ auth.suspend!
22
+ auth.may_unsuspend?.should be_true
23
+ end
24
+
25
+ it 'should not be able to be unsuspended into active' do
26
+ auth.suspend!
27
+ auth.may_unsuspend?(:active).should_not be_true
28
+ end
29
+
30
+ it 'should be able to be unsuspended into active if polite' do
31
+ auth.suspend!
32
+ auth.may_wait?(:waiting, :please).should be_true
33
+ auth.wait!(nil, :please)
34
+ end
35
+
36
+ it 'should not be able to be unsuspended into active if not polite' do
37
+ auth.suspend!
38
+ auth.may_wait?(:waiting).should_not be_true
39
+ auth.may_wait?(:waiting, :rude).should_not be_true
40
+ lambda {auth.wait!(nil, :rude)}.should raise_error(AASM::InvalidTransition)
41
+ lambda {auth.wait!}.should raise_error(AASM::InvalidTransition)
42
+ end
43
+
44
+ it 'should not be able to be unpassified' do
45
+ auth.activate!
46
+ auth.suspend!
47
+ auth.unsuspend!
48
+
49
+ auth.may_unpassify?.should_not be_true
50
+ lambda {auth.unpassify!}.should raise_error(AASM::InvalidTransition)
51
+ end
52
+
53
+ it 'should be active if previously activated' do
54
+ auth.activate!
55
+ auth.suspend!
56
+ auth.unsuspend!
57
+
58
+ auth.aasm_current_state.should == :active
59
+ end
60
+
61
+ it 'should be pending if not previously activated, but an activation code is present' do
62
+ auth.suspend!
63
+ auth.unsuspend!
64
+
65
+ auth.aasm_current_state.should == :pending
66
+ end
67
+
68
+ it 'should be passive if not previously activated and there is no activation code' do
69
+ auth.activation_code = nil
70
+ auth.suspend!
71
+ auth.unsuspend!
72
+
73
+ auth.aasm_current_state.should == :passive
74
+ end
75
+ end
@@ -1,25 +1,21 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
1
+ require 'spec_helper'
2
2
 
3
- describe AASM::SupportingClasses::Event do
4
- before(:each) do
5
- @name = :close_order
6
- @success = :success_callback
7
- end
3
+ describe 'adding an event' do
8
4
 
9
5
  def new_event
10
- @event = AASM::SupportingClasses::Event.new(@name, {:success => @success}) do
6
+ @event = AASM::SupportingClasses::Event.new(:close_order, {:success => :success_callback}) do
11
7
  transitions :to => :closed, :from => [:open, :received]
12
8
  end
13
9
  end
14
10
 
15
11
  it 'should set the name' do
16
12
  new_event
17
- @event.name.should == @name
13
+ @event.name.should == :close_order
18
14
  end
19
15
 
20
16
  it 'should set the success option' do
21
17
  new_event
22
- @event.success.should == @success
18
+ @event.success.should == :success_callback
23
19
  end
24
20
 
25
21
  it 'should create StateTransitions' do
@@ -29,7 +25,31 @@ describe AASM::SupportingClasses::Event do
29
25
  end
30
26
  end
31
27
 
32
- describe AASM::SupportingClasses::Event, 'when firing an event' do
28
+ describe 'transition inspection' do
29
+ before do
30
+ @event = AASM::SupportingClasses::Event.new(:run) do
31
+ transitions :to => :running, :from => :sleeping
32
+ end
33
+ end
34
+
35
+ it 'should support inspecting transitions from states' do
36
+ @event.transitions_from_state(:sleeping).map(&:to).should == [:running]
37
+ @event.transitions_from_state?(:sleeping).should be_true
38
+
39
+ @event.transitions_from_state(:cleaning).map(&:to).should == []
40
+ @event.transitions_from_state?(:cleaning).should be_false
41
+ end
42
+
43
+ it 'should support inspecting transitions to states' do
44
+ @event.transitions_to_state(:running).map(&:from).should == [:sleeping]
45
+ @event.transitions_to_state?(:running).should be_true
46
+
47
+ @event.transitions_to_state(:cleaning).map(&:to).should == []
48
+ @event.transitions_to_state?(:cleaning).should be_false
49
+ end
50
+ end
51
+
52
+ describe 'firing an event' do
33
53
  it 'should return nil if the transitions are empty' do
34
54
  obj = mock('object')
35
55
  obj.stub!(:aasm_current_state)
@@ -49,7 +69,6 @@ describe AASM::SupportingClasses::Event, 'when firing an event' do
49
69
  event.fire(obj).should == :closed
50
70
  end
51
71
 
52
-
53
72
  it 'should call the guard with the params passed in' do
54
73
  event = AASM::SupportingClasses::Event.new(:event) do
55
74
  transitions :to => :closed, :from => [:open, :received], :guard => :guard_fn
@@ -64,7 +83,7 @@ describe AASM::SupportingClasses::Event, 'when firing an event' do
64
83
 
65
84
  end
66
85
 
67
- describe AASM::SupportingClasses::Event, 'when executing the success callback' do
86
+ describe 'executing the success callback' do
68
87
 
69
88
  it "should send the success callback if it's a symbol" do
70
89
  ThisNameBetterNotBeInUse.instance_eval {
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'inspecting AASM' do
4
+ it 'should support listing all states in the order they have been defined' do
5
+ Conversation.aasm_states.should == [:needs_attention, :read, :closed, :awaiting_response, :junk]
6
+ end
7
+ end
@@ -0,0 +1,228 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+ require 'logger'
4
+ require 'spec_helper'
5
+
6
+ load_schema
7
+
8
+ # if you want to see the statements while running the spec enable the following line
9
+ # ActiveRecord::Base.logger = Logger.new(STDERR)
10
+
11
+ shared_examples_for "aasm model" do
12
+ it "should include persistence mixins" do
13
+ klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence)
14
+ klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
15
+ end
16
+ end
17
+
18
+ describe "class methods for classes without own read or write state" do
19
+ let(:klass) {Gate}
20
+ it_should_behave_like "aasm model"
21
+ it "should include all persistence mixins" do
22
+ klass.included_modules.should be_include(AASM::Persistence::ReadState)
23
+ klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
24
+ klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
25
+ end
26
+ end
27
+
28
+ describe "class methods for classes with own read state" do
29
+ let(:klass) {Reader}
30
+ it_should_behave_like "aasm model"
31
+ it "should include all persistence mixins but read state" do
32
+ klass.included_modules.should_not be_include(AASM::Persistence::ReadState)
33
+ klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
34
+ klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
35
+ end
36
+ end
37
+
38
+ describe "class methods for classes with own write state" do
39
+ let(:klass) {Writer}
40
+ it_should_behave_like "aasm model"
41
+ it "should include include all persistence mixins but write state" do
42
+ klass.included_modules.should be_include(AASM::Persistence::ReadState)
43
+ klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
44
+ klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
45
+ end
46
+ end
47
+
48
+ describe "class methods for classes without persistence" do
49
+ let(:klass) {Transient}
50
+ it_should_behave_like "aasm model"
51
+ it "should include all mixins but persistence" do
52
+ klass.included_modules.should be_include(AASM::Persistence::ReadState)
53
+ klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
54
+ klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
55
+ end
56
+ end
57
+
58
+ describe "instance methods" do
59
+ let(:gate) {Gate.new}
60
+
61
+ it "should respond to aasm states" do
62
+ gate.should respond_to(:aasm_read_state)
63
+ gate.should respond_to(:aasm_write_state)
64
+ gate.should respond_to(:aasm_write_state_without_persistence)
65
+ end
66
+
67
+ it "should return the initial state when new and the aasm field is nil" do
68
+ gate.aasm_current_state.should == :opened
69
+ end
70
+
71
+ it "should return the aasm column when new and the aasm field is not nil" do
72
+ gate.aasm_state = "closed"
73
+ gate.aasm_current_state.should == :closed
74
+ end
75
+
76
+ it "should return the aasm column when not new and the aasm_column is not nil" do
77
+ gate.stub!(:new_record?).and_return(false)
78
+ gate.aasm_state = "state"
79
+ gate.aasm_current_state.should == :state
80
+ end
81
+
82
+ it "should allow a nil state" do
83
+ gate.stub!(:new_record?).and_return(false)
84
+ gate.aasm_state = nil
85
+ gate.aasm_current_state.should be_nil
86
+ end
87
+
88
+ it "should have aasm_ensure_initial_state" do
89
+ gate.send :aasm_ensure_initial_state
90
+ end
91
+
92
+ it "should call aasm_ensure_initial_state on validation before create" do
93
+ gate.should_receive(:aasm_ensure_initial_state).and_return(true)
94
+ gate.valid?
95
+ end
96
+
97
+ it "should call aasm_ensure_initial_state on validation before create" do
98
+ gate.stub!(:new_record?).and_return(false)
99
+ gate.should_not_receive(:aasm_ensure_initial_state)
100
+ gate.valid?
101
+ end
102
+
103
+ end
104
+
105
+ describe 'subclasses' do
106
+ it "should have the same states as its parent class" do
107
+ Derivate.aasm_states.should == Simple.aasm_states
108
+ end
109
+
110
+ it "should have the same events as its parent class" do
111
+ Derivate.aasm_events.should == Simple.aasm_events
112
+ end
113
+
114
+ it "should have the same column as its parent class" do
115
+ Derivate.aasm_column.should == :status
116
+ end
117
+
118
+ it "should have the same column as its parent even for the new dsl" do
119
+ SimpleNewDsl.aasm_column.should == :status
120
+ DerivateNewDsl.aasm_column.should == :status
121
+ end
122
+ end
123
+
124
+ describe "named scopes with the old DSL" do
125
+
126
+ context "Does not already respond_to? the scope name" do
127
+ it "should add a scope" do
128
+ Simple.should respond_to(:unknown_scope)
129
+ Simple.unknown_scope.class.should == ActiveRecord::Relation
130
+ end
131
+ end
132
+
133
+ context "Already respond_to? the scope name" do
134
+ it "should not add a scope" do
135
+ Simple.should respond_to(:new)
136
+ Simple.new.class.should == Simple
137
+ end
138
+ end
139
+
140
+ end
141
+
142
+ describe "named scopes with the new DSL" do
143
+
144
+ context "Does not already respond_to? the scope name" do
145
+ it "should add a scope" do
146
+ SimpleNewDsl.should respond_to(:unknown_scope)
147
+ SimpleNewDsl.unknown_scope.class.should == ActiveRecord::Relation
148
+ end
149
+ end
150
+
151
+ context "Already respond_to? the scope name" do
152
+ it "should not add a scope" do
153
+ SimpleNewDsl.should respond_to(:new)
154
+ SimpleNewDsl.new.class.should == SimpleNewDsl
155
+ end
156
+ end
157
+
158
+ end
159
+
160
+ describe 'initial states' do
161
+
162
+ it 'should support conditions' do
163
+ Thief.new(:skilled => true).aasm_current_state.should == :rich
164
+ Thief.new(:skilled => false).aasm_current_state.should == :jailed
165
+ end
166
+ end
167
+
168
+ describe 'transitions with persistence' do
169
+
170
+ it 'should not store states for invalid models' do
171
+ validator = Validator.create(:name => 'name')
172
+ validator.should be_valid
173
+ validator.should be_sleeping
174
+
175
+ validator.name = nil
176
+ validator.should_not be_valid
177
+ validator.run!.should be_false
178
+ validator.should be_sleeping
179
+
180
+ validator.reload
181
+ validator.should_not be_running
182
+ validator.should be_sleeping
183
+
184
+ validator.name = 'another name'
185
+ validator.should be_valid
186
+ validator.run!.should be_true
187
+ validator.should be_running
188
+
189
+ validator.reload
190
+ validator.should be_running
191
+ validator.should_not be_sleeping
192
+ end
193
+
194
+ it 'should store states for invalid models if configured' do
195
+ persistor = InvalidPersistor.create(:name => 'name')
196
+ persistor.should be_valid
197
+ persistor.should be_sleeping
198
+
199
+ persistor.name = nil
200
+ persistor.should_not be_valid
201
+ persistor.run!.should be_true
202
+ persistor.should be_running
203
+
204
+ persistor = InvalidPersistor.find(persistor.id)
205
+ persistor.valid?
206
+ persistor.should be_valid
207
+ persistor.should be_running
208
+ persistor.should_not be_sleeping
209
+
210
+ persistor.reload
211
+ persistor.should be_running
212
+ persistor.should_not be_sleeping
213
+ end
214
+
215
+ describe 'transactions' do
216
+ it 'should rollback all changes' do
217
+ worker = Worker.create!(:name => 'worker', :status => 'sleeping')
218
+ transactor = Transactor.create!(:name => 'transactor', :worker => worker)
219
+ transactor.should be_sleeping
220
+ worker.status.should == 'sleeping'
221
+
222
+ lambda {transactor.run!}.should raise_error(StandardError, 'failed on purpose')
223
+ transactor.should be_running
224
+ worker.reload.status.should == 'sleeping'
225
+ end
226
+ end
227
+
228
+ end