aasm 3.0.13 → 3.0.14

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.
@@ -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