aasm 2.1.1 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,254 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'aasm')
2
+
3
+ begin
4
+ require 'rubygems'
5
+ require 'active_record'
6
+
7
+ # A dummy class for mocking the activerecord connection class
8
+ class Connection
9
+ end
10
+
11
+ class FooBar < ActiveRecord::Base
12
+ include AASM
13
+
14
+ # Fake this column for testing purposes
15
+ attr_accessor :aasm_state
16
+
17
+ aasm_state :open
18
+ aasm_state :closed
19
+
20
+ aasm_event :view do
21
+ transitions :to => :read, :from => [:needs_attention]
22
+ end
23
+ end
24
+
25
+ class Fi < ActiveRecord::Base
26
+ def aasm_read_state
27
+ "fi"
28
+ end
29
+ include AASM
30
+ end
31
+
32
+ class Fo < ActiveRecord::Base
33
+ def aasm_write_state(state)
34
+ "fo"
35
+ end
36
+ include AASM
37
+ end
38
+
39
+ class Fum < ActiveRecord::Base
40
+ def aasm_write_state_without_persistence(state)
41
+ "fum"
42
+ end
43
+ include AASM
44
+ end
45
+
46
+ class June < ActiveRecord::Base
47
+ include AASM
48
+ aasm_column :status
49
+ end
50
+
51
+ class Beaver < June
52
+ end
53
+
54
+ class Thief < ActiveRecord::Base
55
+ include AASM
56
+ aasm_initial_state Proc.new { |thief| thief.skilled ? :rich : :jailed }
57
+ aasm_state :rich
58
+ aasm_state :jailed
59
+ attr_accessor :skilled, :aasm_state
60
+ end
61
+
62
+ describe "aasm model", :shared => true do
63
+ it "should include AASM::Persistence::ActiveRecordPersistence" do
64
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence)
65
+ end
66
+ it "should include AASM::Persistence::ActiveRecordPersistence::InstanceMethods" do
67
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
68
+ end
69
+ end
70
+
71
+ describe FooBar, "class methods" do
72
+ before(:each) do
73
+ @klass = FooBar
74
+ end
75
+ it_should_behave_like "aasm model"
76
+ it "should include AASM::Persistence::ActiveRecordPersistence::ReadState" do
77
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
78
+ end
79
+ it "should include AASM::Persistence::ActiveRecordPersistence::WriteState" do
80
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
81
+ end
82
+ it "should include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
83
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
84
+ end
85
+ end
86
+
87
+ describe Fi, "class methods" do
88
+ before(:each) do
89
+ @klass = Fi
90
+ end
91
+ it_should_behave_like "aasm model"
92
+ it "should not include AASM::Persistence::ActiveRecordPersistence::ReadState" do
93
+ @klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
94
+ end
95
+ it "should include AASM::Persistence::ActiveRecordPersistence::WriteState" do
96
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
97
+ end
98
+ it "should include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
99
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
100
+ end
101
+ end
102
+
103
+ describe Fo, "class methods" do
104
+ before(:each) do
105
+ @klass = Fo
106
+ end
107
+ it_should_behave_like "aasm model"
108
+ it "should include AASM::Persistence::ActiveRecordPersistence::ReadState" do
109
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
110
+ end
111
+ it "should not include AASM::Persistence::ActiveRecordPersistence::WriteState" do
112
+ @klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
113
+ end
114
+ it "should include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
115
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
116
+ end
117
+ end
118
+
119
+ describe Fum, "class methods" do
120
+ before(:each) do
121
+ @klass = Fum
122
+ end
123
+ it_should_behave_like "aasm model"
124
+ it "should include AASM::Persistence::ActiveRecordPersistence::ReadState" do
125
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
126
+ end
127
+ it "should include AASM::Persistence::ActiveRecordPersistence::WriteState" do
128
+ @klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
129
+ end
130
+ it "should not include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
131
+ @klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
132
+ end
133
+ end
134
+
135
+ describe FooBar, "instance methods" do
136
+ before(:each) do
137
+ connection = mock(Connection, :columns => [])
138
+ FooBar.stub!(:connection).and_return(connection)
139
+ end
140
+
141
+ it "should respond to aasm read state when not previously defined" do
142
+ FooBar.new.should respond_to(:aasm_read_state)
143
+ end
144
+
145
+ it "should respond to aasm write state when not previously defined" do
146
+ FooBar.new.should respond_to(:aasm_write_state)
147
+ end
148
+
149
+ it "should respond to aasm write state without persistence when not previously defined" do
150
+ FooBar.new.should respond_to(:aasm_write_state_without_persistence)
151
+ end
152
+
153
+ it "should return the initial state when new and the aasm field is nil" do
154
+ FooBar.new.aasm_current_state.should == :open
155
+ end
156
+
157
+ it "should return the aasm column when new and the aasm field is not nil" do
158
+ foo = FooBar.new
159
+ foo.aasm_state = "closed"
160
+ foo.aasm_current_state.should == :closed
161
+ end
162
+
163
+ it "should return the aasm column when not new and the aasm_column is not nil" do
164
+ foo = FooBar.new
165
+ foo.stub!(:new_record?).and_return(false)
166
+ foo.aasm_state = "state"
167
+ foo.aasm_current_state.should == :state
168
+ end
169
+
170
+ it "should allow a nil state" do
171
+ foo = FooBar.new
172
+ foo.stub!(:new_record?).and_return(false)
173
+ foo.aasm_state = nil
174
+ foo.aasm_current_state.should be_nil
175
+ end
176
+
177
+ it "should have aasm_ensure_initial_state" do
178
+ foo = FooBar.new
179
+ foo.send :aasm_ensure_initial_state
180
+ end
181
+
182
+ it "should call aasm_ensure_initial_state on validation before create" do
183
+ foo = FooBar.new
184
+ foo.should_receive(:aasm_ensure_initial_state).and_return(true)
185
+ foo.valid?
186
+ end
187
+
188
+ it "should call aasm_ensure_initial_state on validation before create" do
189
+ foo = FooBar.new
190
+ foo.stub!(:new_record?).and_return(false)
191
+ foo.should_not_receive(:aasm_ensure_initial_state)
192
+ foo.valid?
193
+ end
194
+
195
+ end
196
+
197
+ describe 'Beavers' do
198
+ it "should have the same states as it's parent" do
199
+ Beaver.aasm_states.should == June.aasm_states
200
+ end
201
+
202
+ it "should have the same events as it's parent" do
203
+ Beaver.aasm_events.should == June.aasm_events
204
+ end
205
+
206
+ it "should have the same column as it's parent" do
207
+ Beaver.aasm_column.should == :status
208
+ end
209
+ end
210
+
211
+ describe AASM::Persistence::ActiveRecordPersistence::NamedScopeMethods do
212
+ class NamedScopeExample < ActiveRecord::Base
213
+ include AASM
214
+ end
215
+
216
+ context "Does not already respond_to? the scope name" do
217
+ it "should add a named_scope" do
218
+ NamedScopeExample.should_receive(:named_scope)
219
+ NamedScopeExample.aasm_state :unknown_scope
220
+ end
221
+ end
222
+
223
+ context "Already respond_to? the scope name" do
224
+ it "should not add a named_scope" do
225
+ NamedScopeExample.should_not_receive(:named_scope)
226
+ NamedScopeExample.aasm_state :new
227
+ end
228
+ end
229
+ end
230
+
231
+ describe 'Thieves' do
232
+ before(:each) do
233
+ connection = mock(Connection, :columns => [])
234
+ Thief.stub!(:connection).and_return(connection)
235
+ end
236
+
237
+ it 'should be rich if they\'re skilled' do
238
+ Thief.new(:skilled => true).aasm_current_state.should == :rich
239
+ end
240
+
241
+ it 'should be jailed if they\'re unskilled' do
242
+ Thief.new(:skilled => false).aasm_current_state.should == :jailed
243
+ end
244
+ end
245
+
246
+ # TODO: figure out how to test ActiveRecord reload! without a database
247
+
248
+ rescue LoadError => e
249
+ if e.message == "no such file to load -- active_record"
250
+ puts "You must install active record to run this spec. Install with sudo gem install activerecord"
251
+ else
252
+ raise
253
+ end
254
+ end
@@ -0,0 +1,79 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ class Foo2
4
+ include AASM
5
+ aasm_initial_state :open
6
+ aasm_state :open,
7
+ :before_enter => :before_enter_open,
8
+ :before_exit => :before_exit_open,
9
+ :after_enter => :after_enter_open,
10
+ :after_exit => :after_exit_open
11
+ aasm_state :closed,
12
+ :before_enter => :before_enter_closed,
13
+ :before_exit => :before_exit_closed,
14
+ :after_enter => :after_enter_closed,
15
+ :after_exit => :after_exit_closed
16
+
17
+ aasm_event :close, :before => :before, :after => :after do
18
+ transitions :to => :closed, :from => [:open]
19
+ end
20
+
21
+ aasm_event :open, :before => :before, :after => :after do
22
+ transitions :to => :open, :from => :closed
23
+ end
24
+
25
+ def before_enter_open
26
+ end
27
+ def before_exit_open
28
+ end
29
+ def after_enter_open
30
+ end
31
+ def after_exit_open
32
+ end
33
+
34
+ def before_enter_closed
35
+ end
36
+ def before_exit_closed
37
+ end
38
+ def after_enter_closed
39
+ end
40
+ def after_exit_closed
41
+ end
42
+
43
+ def before
44
+ end
45
+ def after
46
+ end
47
+ end
48
+
49
+ describe Foo2, '- new callbacks' do
50
+ before(:each) do
51
+ @foo = Foo2.new
52
+ end
53
+
54
+ it "should get close callbacks" do
55
+ @foo.should_receive(:before).once.ordered
56
+ @foo.should_receive(:before_exit_open).once.ordered # these should be before the state changes
57
+ @foo.should_receive(:before_enter_closed).once.ordered
58
+ @foo.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
59
+ @foo.should_receive(:after_exit_open).once.ordered # these should be after the state changes
60
+ @foo.should_receive(:after_enter_closed).once.ordered
61
+ @foo.should_receive(:after).once.ordered
62
+
63
+ @foo.close!
64
+ end
65
+
66
+ it "should get open callbacks" do
67
+ @foo.close!
68
+
69
+ @foo.should_receive(:before).once.ordered
70
+ @foo.should_receive(:before_exit_closed).once.ordered # these should be before the state changes
71
+ @foo.should_receive(:before_enter_open).once.ordered
72
+ @foo.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
73
+ @foo.should_receive(:after_exit_closed).once.ordered # these should be after the state changes
74
+ @foo.should_receive(:after_enter_open).once.ordered
75
+ @foo.should_receive(:after).once.ordered
76
+
77
+ @foo.open!
78
+ end
79
+ end
@@ -0,0 +1,126 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe AASM::SupportingClasses::Event do
4
+ before(:each) do
5
+ @name = :close_order
6
+ @success = :success_callback
7
+ end
8
+
9
+ def new_event
10
+ @event = AASM::SupportingClasses::Event.new(@name, {:success => @success}) do
11
+ transitions :to => :closed, :from => [:open, :received]
12
+ end
13
+ end
14
+
15
+ it 'should set the name' do
16
+ new_event
17
+ @event.name.should == @name
18
+ end
19
+
20
+ it 'should set the success option' do
21
+ new_event
22
+ @event.success.should == @success
23
+ end
24
+
25
+ it 'should create StateTransitions' do
26
+ AASM::SupportingClasses::StateTransition.should_receive(:new).with({:to => :closed, :from => :open})
27
+ AASM::SupportingClasses::StateTransition.should_receive(:new).with({:to => :closed, :from => :received})
28
+ new_event
29
+ end
30
+ end
31
+
32
+ describe AASM::SupportingClasses::Event, 'when firing an event' do
33
+ it 'should raise an AASM::InvalidTransition error if the transitions are empty' do
34
+ obj = mock('object')
35
+ obj.stub!(:aasm_current_state)
36
+
37
+ event = AASM::SupportingClasses::Event.new(:event)
38
+ lambda { event.fire(obj) }.should raise_error(AASM::InvalidTransition)
39
+ end
40
+
41
+ it 'should return the state of the first matching transition it finds' do
42
+ event = AASM::SupportingClasses::Event.new(:event) do
43
+ transitions :to => :closed, :from => [:open, :received]
44
+ end
45
+
46
+ obj = mock('object')
47
+ obj.stub!(:aasm_current_state).and_return(:open)
48
+
49
+ event.fire(obj).should == :closed
50
+ end
51
+ end
52
+
53
+ describe AASM::SupportingClasses::Event, 'when executing the success callback' do
54
+ class ThisNameBetterNotBeInUse
55
+ include AASM
56
+
57
+ aasm_state :initial
58
+ aasm_state :symbol
59
+ aasm_state :string
60
+ aasm_state :array
61
+ aasm_state :proc
62
+ end
63
+
64
+ it "should send the success callback if it's a symbol" do
65
+ ThisNameBetterNotBeInUse.instance_eval {
66
+ aasm_event :with_symbol, :success => :symbol_success_callback do
67
+ transitions :to => :symbol, :from => [:initial]
68
+ end
69
+ }
70
+
71
+ model = ThisNameBetterNotBeInUse.new
72
+ model.should_receive(:symbol_success_callback)
73
+ model.with_symbol!
74
+ end
75
+
76
+ it "should send the success callback if it's a string" do
77
+ ThisNameBetterNotBeInUse.instance_eval {
78
+ aasm_event :with_string, :success => 'string_success_callback' do
79
+ transitions :to => :string, :from => [:initial]
80
+ end
81
+ }
82
+
83
+ model = ThisNameBetterNotBeInUse.new
84
+ model.should_receive(:string_success_callback)
85
+ model.with_string!
86
+ end
87
+
88
+ it "should call each success callback if passed an array of strings and/or symbols" do
89
+ ThisNameBetterNotBeInUse.instance_eval {
90
+ aasm_event :with_array, :success => [:success_callback1, 'success_callback2'] do
91
+ transitions :to => :array, :from => [:initial]
92
+ end
93
+ }
94
+
95
+ model = ThisNameBetterNotBeInUse.new
96
+ model.should_receive(:success_callback1)
97
+ model.should_receive(:success_callback2)
98
+ model.with_array!
99
+ end
100
+
101
+ it "should call each success callback if passed an array of strings and/or symbols and/or procs" do
102
+ ThisNameBetterNotBeInUse.instance_eval {
103
+ aasm_event :with_array_including_procs, :success => [:success_callback1, 'success_callback2', lambda { |obj| obj.proc_success_callback }] do
104
+ transitions :to => :array, :from => [:initial]
105
+ end
106
+ }
107
+
108
+ model = ThisNameBetterNotBeInUse.new
109
+ model.should_receive(:success_callback1)
110
+ model.should_receive(:success_callback2)
111
+ model.should_receive(:proc_success_callback)
112
+ model.with_array_including_procs!
113
+ end
114
+
115
+ it "should call the success callback if it's a proc" do
116
+ ThisNameBetterNotBeInUse.instance_eval {
117
+ aasm_event :with_proc, :success => lambda { |obj| obj.proc_success_callback } do
118
+ transitions :to => :proc, :from => [:initial]
119
+ end
120
+ }
121
+
122
+ model = ThisNameBetterNotBeInUse.new
123
+ model.should_receive(:proc_success_callback)
124
+ model.with_proc!
125
+ end
126
+ end