aasm 2.1.1 → 2.1.3

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,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