aasm 3.0.16 → 3.0.17
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.
- data/.gitignore +1 -0
- data/.travis.yml +2 -1
- data/API +34 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +1 -1
- data/HOWTO +12 -0
- data/README.md +57 -4
- data/aasm.gemspec +2 -0
- data/lib/aasm.rb +5 -4
- data/lib/aasm/aasm.rb +50 -75
- data/lib/aasm/base.rb +22 -18
- data/lib/aasm/event.rb +130 -0
- data/lib/aasm/instance_base.rb +87 -0
- data/lib/aasm/localizer.rb +54 -0
- data/lib/aasm/persistence.rb +22 -14
- data/lib/aasm/persistence/active_record_persistence.rb +38 -69
- data/lib/aasm/persistence/base.rb +42 -2
- data/lib/aasm/persistence/mongoid_persistence.rb +33 -64
- data/lib/aasm/state.rb +78 -0
- data/lib/aasm/state_machine.rb +2 -2
- data/lib/aasm/transition.rb +49 -0
- data/lib/aasm/version.rb +1 -1
- data/spec/models/active_record/api.rb +75 -0
- data/spec/models/auth_machine.rb +1 -1
- data/spec/models/bar.rb +15 -0
- data/spec/models/foo.rb +34 -0
- data/spec/models/mongoid/simple_mongoid.rb +10 -0
- data/spec/models/mongoid/{mongoid_models.rb → simple_new_dsl_mongoid.rb} +1 -12
- data/spec/models/persistence.rb +2 -1
- data/spec/models/this_name_better_not_be_in_use.rb +11 -0
- data/spec/schema.rb +1 -1
- data/spec/spec_helper.rb +8 -1
- data/spec/unit/api_spec.rb +72 -0
- data/spec/unit/callbacks_spec.rb +2 -2
- data/spec/unit/event_spec.rb +269 -0
- data/spec/unit/inspection_spec.rb +43 -5
- data/spec/unit/{supporting_classes/localizer_spec.rb → localizer_spec.rb} +2 -2
- data/spec/unit/memory_leak_spec.rb +12 -12
- data/spec/unit/persistence/active_record_persistence_spec.rb +0 -40
- data/spec/unit/persistence/mongoid_persistance_spec.rb +3 -2
- data/spec/unit/simple_example_spec.rb +6 -0
- data/spec/unit/{supporting_classes/state_spec.rb → state_spec.rb} +2 -2
- data/spec/unit/{supporting_classes/state_transition_spec.rb → transition_spec.rb} +18 -18
- metadata +127 -38
- data/lib/aasm/persistence/read_state.rb +0 -40
- data/lib/aasm/supporting_classes/event.rb +0 -146
- data/lib/aasm/supporting_classes/localizer.rb +0 -56
- data/lib/aasm/supporting_classes/state.rb +0 -80
- data/lib/aasm/supporting_classes/state_transition.rb +0 -51
- data/spec/spec_helpers/models_spec_helper.rb +0 -64
- data/spec/unit/supporting_classes/event_spec.rb +0 -203
data/spec/models/foo.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
class Foo
|
2
|
+
include AASM
|
3
|
+
aasm do
|
4
|
+
state :open, :initial => true, :exit => :exit
|
5
|
+
state :closed, :enter => :enter
|
6
|
+
|
7
|
+
event :close, :success => :success_callback do
|
8
|
+
transitions :from => [:open], :to => [:closed]
|
9
|
+
end
|
10
|
+
|
11
|
+
event :null do
|
12
|
+
transitions :from => [:open], :to => :closed, :guard => :always_false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def always_false
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def success_callback
|
21
|
+
end
|
22
|
+
|
23
|
+
def enter
|
24
|
+
end
|
25
|
+
def exit
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class FooTwo < Foo
|
30
|
+
include AASM
|
31
|
+
aasm do
|
32
|
+
state :foo
|
33
|
+
end
|
34
|
+
end
|
@@ -1,14 +1,3 @@
|
|
1
|
-
class SimpleMongoid
|
2
|
-
include Mongoid::Document
|
3
|
-
include AASM
|
4
|
-
|
5
|
-
field :status, type: String
|
6
|
-
|
7
|
-
aasm_column :status
|
8
|
-
aasm_state :unknown_scope
|
9
|
-
aasm_state :new
|
10
|
-
end
|
11
|
-
|
12
1
|
class SimpleNewDslMongoid
|
13
2
|
include Mongoid::Document
|
14
3
|
include AASM
|
@@ -20,4 +9,4 @@ class SimpleNewDslMongoid
|
|
20
9
|
state :unknown_scope
|
21
10
|
state :new
|
22
11
|
end
|
23
|
-
end
|
12
|
+
end
|
data/spec/models/persistence.rb
CHANGED
data/spec/schema.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
ActiveRecord::Schema.define(:version => 0) do
|
2
2
|
|
3
|
-
%w{gates readers writers transients simples simple_new_dsls thieves localizer_test_models}.each do |table_name|
|
3
|
+
%w{gates readers writers transients simples simple_new_dsls thieves localizer_test_models persisted_states provided_and_persisted_states}.each do |table_name|
|
4
4
|
create_table table_name, :force => true do |t|
|
5
5
|
t.string "aasm_state"
|
6
6
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,8 +5,12 @@ require 'aasm'
|
|
5
5
|
require 'rspec'
|
6
6
|
require 'rspec/autorun'
|
7
7
|
|
8
|
+
require 'coveralls'
|
9
|
+
Coveralls.wear!
|
10
|
+
|
8
11
|
# require 'ruby-debug'; Debugger.settings[:autoeval] = true; debugger; rubys_debugger = 'annoying'
|
9
12
|
# require 'ruby-debug/completion'
|
13
|
+
# require 'pry'
|
10
14
|
|
11
15
|
def load_schema
|
12
16
|
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
@@ -15,5 +19,8 @@ def load_schema
|
|
15
19
|
load(File.dirname(__FILE__) + "/schema.rb")
|
16
20
|
end
|
17
21
|
|
18
|
-
#
|
22
|
+
# custom spec helpers
|
19
23
|
Dir[File.dirname(__FILE__) + "/spec_helpers/**/*.rb"].sort.each { |f| require File.expand_path(f) }
|
24
|
+
|
25
|
+
# example model classes
|
26
|
+
Dir[File.dirname(__FILE__) + "/models/*.rb"].sort.each { |f| require File.expand_path(f) }
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'models/active_record/api.rb'
|
3
|
+
|
4
|
+
describe "reading the current state" do
|
5
|
+
it "uses the AASM default" do
|
6
|
+
DefaultState.new.aasm.current_state.should eql :alpha
|
7
|
+
end
|
8
|
+
|
9
|
+
it "uses the provided method" do
|
10
|
+
ProvidedState.new.aasm.current_state.should eql :beta
|
11
|
+
end
|
12
|
+
|
13
|
+
it "uses the persistence storage" do
|
14
|
+
PersistedState.new.aasm.current_state.should eql :alpha
|
15
|
+
end
|
16
|
+
|
17
|
+
it "uses the provided method even if persisted" do
|
18
|
+
ProvidedAndPersistedState.new.aasm.current_state.should eql :gamma
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "writing and persisting the current state" do
|
23
|
+
it "uses the AASM default" do
|
24
|
+
o = DefaultState.new
|
25
|
+
o.release!
|
26
|
+
o.persisted_store.should be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it "uses the provided method" do
|
30
|
+
o = ProvidedState.new
|
31
|
+
o.release!
|
32
|
+
o.persisted_store.should eql :beta
|
33
|
+
end
|
34
|
+
|
35
|
+
it "uses the persistence storage" do
|
36
|
+
o = PersistedState.new
|
37
|
+
o.release!
|
38
|
+
o.persisted_store.should be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "uses the provided method even if persisted" do
|
42
|
+
o = ProvidedAndPersistedState.new
|
43
|
+
o.release!
|
44
|
+
o.persisted_store.should eql :beta
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "writing the current state without persisting it" do
|
49
|
+
it "uses the AASM default" do
|
50
|
+
o = DefaultState.new
|
51
|
+
o.release
|
52
|
+
o.transient_store.should be_nil
|
53
|
+
end
|
54
|
+
|
55
|
+
it "uses the provided method" do
|
56
|
+
o = ProvidedState.new
|
57
|
+
o.release
|
58
|
+
o.transient_store.should eql :beta
|
59
|
+
end
|
60
|
+
|
61
|
+
it "uses the persistence storage" do
|
62
|
+
o = PersistedState.new
|
63
|
+
o.release
|
64
|
+
o.transient_store.should be_nil
|
65
|
+
end
|
66
|
+
|
67
|
+
it "uses the provided method even if persisted" do
|
68
|
+
o = ProvidedAndPersistedState.new
|
69
|
+
o.release
|
70
|
+
o.transient_store.should eql :beta
|
71
|
+
end
|
72
|
+
end
|
data/spec/unit/callbacks_spec.rb
CHANGED
@@ -85,7 +85,7 @@ describe 'event callbacks' do
|
|
85
85
|
end
|
86
86
|
|
87
87
|
it 'should not call it for failing bang fire' do
|
88
|
-
@foo.stub!(:
|
88
|
+
@foo.aasm.stub!(:set_current_state_with_persistence).and_return(false)
|
89
89
|
@foo.should_not_receive(:aasm_event_fired)
|
90
90
|
@foo.close!
|
91
91
|
end
|
@@ -108,7 +108,7 @@ describe 'event callbacks' do
|
|
108
108
|
end
|
109
109
|
|
110
110
|
it 'should not call it if persist fails for bang fire' do
|
111
|
-
@foo.stub!(:
|
111
|
+
@foo.aasm.stub!(:set_current_state_with_persistence).and_return(false)
|
112
112
|
@foo.should_receive(:aasm_event_failed)
|
113
113
|
@foo.close!
|
114
114
|
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'adding an event' do
|
4
|
+
let(:event) do
|
5
|
+
AASM::Event.new(:close_order, {:success => :success_callback}) do
|
6
|
+
before :before_callback
|
7
|
+
after :after_callback
|
8
|
+
transitions :to => :closed, :from => [:open, :received]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should set the name' do
|
13
|
+
event.name.should == :close_order
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should set the success callback' do
|
17
|
+
event.options[:success].should == :success_callback
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should set the after callback' do
|
21
|
+
event.options[:after].should == [:after_callback]
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should set the before callback' do
|
25
|
+
event.options[:before].should == [:before_callback]
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should create transitions' do
|
29
|
+
transitions = event.all_transitions
|
30
|
+
transitions[0].from.should == :open
|
31
|
+
transitions[0].to.should == :closed
|
32
|
+
transitions[1].from.should == :received
|
33
|
+
transitions[1].to.should == :closed
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'transition inspection' do
|
38
|
+
let(:event) do
|
39
|
+
AASM::Event.new(:run) do
|
40
|
+
transitions :to => :running, :from => :sleeping
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should support inspecting transitions from other states' do
|
45
|
+
event.transitions_from_state(:sleeping).map(&:to).should == [:running]
|
46
|
+
event.transitions_from_state?(:sleeping).should be_true
|
47
|
+
|
48
|
+
event.transitions_from_state(:cleaning).map(&:to).should == []
|
49
|
+
event.transitions_from_state?(:cleaning).should be_false
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should support inspecting transitions to other states' do
|
53
|
+
event.transitions_to_state(:running).map(&:from).should == [:sleeping]
|
54
|
+
event.transitions_to_state?(:running).should be_true
|
55
|
+
|
56
|
+
event.transitions_to_state(:cleaning).map(&:to).should == []
|
57
|
+
event.transitions_to_state?(:cleaning).should be_false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'firing an event' do
|
62
|
+
it 'should return nil if the transitions are empty' do
|
63
|
+
obj = mock('object')
|
64
|
+
obj.stub!(:aasm_current_state)
|
65
|
+
|
66
|
+
event = AASM::Event.new(:event)
|
67
|
+
event.fire(obj).should be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should return the state of the first matching transition it finds' do
|
71
|
+
event = AASM::Event.new(:event) do
|
72
|
+
transitions :to => :closed, :from => [:open, :received]
|
73
|
+
end
|
74
|
+
|
75
|
+
obj = mock('object')
|
76
|
+
obj.stub!(:aasm_current_state).and_return(:open)
|
77
|
+
|
78
|
+
event.fire(obj).should == :closed
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should call the guard with the params passed in' do
|
82
|
+
event = AASM::Event.new(:event) do
|
83
|
+
transitions :to => :closed, :from => [:open, :received], :guard => :guard_fn
|
84
|
+
end
|
85
|
+
|
86
|
+
obj = mock('object')
|
87
|
+
obj.stub!(:aasm_current_state).and_return(:open)
|
88
|
+
obj.should_receive(:guard_fn).with('arg1', 'arg2').and_return(true)
|
89
|
+
|
90
|
+
event.fire(obj, nil, 'arg1', 'arg2').should == :closed
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'should fire callbacks' do
|
96
|
+
describe 'success' do
|
97
|
+
it "if it's a symbol" do
|
98
|
+
ThisNameBetterNotBeInUse.instance_eval {
|
99
|
+
aasm_event :with_symbol, :success => :symbol_success_callback do
|
100
|
+
transitions :to => :symbol, :from => [:initial]
|
101
|
+
end
|
102
|
+
}
|
103
|
+
|
104
|
+
model = ThisNameBetterNotBeInUse.new
|
105
|
+
model.should_receive(:symbol_success_callback)
|
106
|
+
model.with_symbol!
|
107
|
+
end
|
108
|
+
|
109
|
+
it "if it's a string" do
|
110
|
+
ThisNameBetterNotBeInUse.instance_eval {
|
111
|
+
aasm_event :with_string, :success => 'string_success_callback' do
|
112
|
+
transitions :to => :string, :from => [:initial]
|
113
|
+
end
|
114
|
+
}
|
115
|
+
|
116
|
+
model = ThisNameBetterNotBeInUse.new
|
117
|
+
model.should_receive(:string_success_callback)
|
118
|
+
model.with_string!
|
119
|
+
end
|
120
|
+
|
121
|
+
it "if passed an array of strings and/or symbols" do
|
122
|
+
ThisNameBetterNotBeInUse.instance_eval {
|
123
|
+
aasm_event :with_array, :success => [:success_callback1, 'success_callback2'] do
|
124
|
+
transitions :to => :array, :from => [:initial]
|
125
|
+
end
|
126
|
+
}
|
127
|
+
|
128
|
+
model = ThisNameBetterNotBeInUse.new
|
129
|
+
model.should_receive(:success_callback1)
|
130
|
+
model.should_receive(:success_callback2)
|
131
|
+
model.with_array!
|
132
|
+
end
|
133
|
+
|
134
|
+
it "if passed an array of strings and/or symbols and/or procs" do
|
135
|
+
ThisNameBetterNotBeInUse.instance_eval {
|
136
|
+
aasm_event :with_array_including_procs, :success => [:success_callback1, 'success_callback2', lambda { proc_success_callback }] do
|
137
|
+
transitions :to => :array, :from => [:initial]
|
138
|
+
end
|
139
|
+
}
|
140
|
+
|
141
|
+
model = ThisNameBetterNotBeInUse.new
|
142
|
+
model.should_receive(:success_callback1)
|
143
|
+
model.should_receive(:success_callback2)
|
144
|
+
model.should_receive(:proc_success_callback)
|
145
|
+
model.with_array_including_procs!
|
146
|
+
end
|
147
|
+
|
148
|
+
it "if it's a proc" do
|
149
|
+
ThisNameBetterNotBeInUse.instance_eval {
|
150
|
+
aasm_event :with_proc, :success => lambda { proc_success_callback } do
|
151
|
+
transitions :to => :proc, :from => [:initial]
|
152
|
+
end
|
153
|
+
}
|
154
|
+
|
155
|
+
model = ThisNameBetterNotBeInUse.new
|
156
|
+
model.should_receive(:proc_success_callback)
|
157
|
+
model.with_proc!
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe 'after' do
|
162
|
+
it "if they set different ways" do
|
163
|
+
ThisNameBetterNotBeInUse.instance_eval do
|
164
|
+
aasm_event :with_afters, :after => :do_one_thing_after do
|
165
|
+
after do
|
166
|
+
do_another_thing_after_too
|
167
|
+
end
|
168
|
+
after do
|
169
|
+
do_third_thing_at_last
|
170
|
+
end
|
171
|
+
transitions :to => :proc, :from => [:initial]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
model = ThisNameBetterNotBeInUse.new
|
176
|
+
model.should_receive(:do_one_thing_after).once.ordered
|
177
|
+
model.should_receive(:do_another_thing_after_too).once.ordered
|
178
|
+
model.should_receive(:do_third_thing_at_last).once.ordered
|
179
|
+
model.with_afters!
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe 'before' do
|
184
|
+
it "if it's a proc" do
|
185
|
+
ThisNameBetterNotBeInUse.instance_eval do
|
186
|
+
aasm_event :before_as_proc do
|
187
|
+
before do
|
188
|
+
do_something_before
|
189
|
+
end
|
190
|
+
transitions :to => :proc, :from => [:initial]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
model = ThisNameBetterNotBeInUse.new
|
195
|
+
model.should_receive(:do_something_before).once
|
196
|
+
model.before_as_proc!
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'in right order' do
|
201
|
+
ThisNameBetterNotBeInUse.instance_eval do
|
202
|
+
aasm_event :in_right_order, :after => :do_something_after do
|
203
|
+
before do
|
204
|
+
do_something_before
|
205
|
+
end
|
206
|
+
transitions :to => :proc, :from => [:initial]
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
model = ThisNameBetterNotBeInUse.new
|
211
|
+
model.should_receive(:do_something_before).once.ordered
|
212
|
+
model.should_receive(:do_something_after).once.ordered
|
213
|
+
model.in_right_order!
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe 'parametrised events' do
|
218
|
+
let(:pe) {ParametrisedEvent.new}
|
219
|
+
|
220
|
+
it 'should transition to specified next state (sleeping to showering)' do
|
221
|
+
pe.wakeup!(:showering)
|
222
|
+
pe.aasm_current_state.should == :showering
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'should transition to specified next state (sleeping to working)' do
|
226
|
+
pe.wakeup!(:working)
|
227
|
+
pe.aasm_current_state.should == :working
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'should transition to default (first or showering) state' do
|
231
|
+
pe.wakeup!
|
232
|
+
pe.aasm_current_state.should == :showering
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'should transition to default state when on_transition invoked' do
|
236
|
+
pe.dress!(nil, 'purple', 'dressy')
|
237
|
+
pe.aasm_current_state.should == :working
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'should call on_transition method with args' do
|
241
|
+
pe.wakeup!(:showering)
|
242
|
+
pe.should_receive(:wear_clothes).with('blue', 'jeans')
|
243
|
+
pe.dress!(:working, 'blue', 'jeans')
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'should call on_transition proc' do
|
247
|
+
pe.wakeup!(:showering)
|
248
|
+
pe.should_receive(:wear_clothes).with('purple', 'slacks')
|
249
|
+
pe.dress!(:dating, 'purple', 'slacks')
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'should call on_transition with an array of methods' do
|
253
|
+
pe.wakeup!(:showering)
|
254
|
+
pe.should_receive(:condition_hair)
|
255
|
+
pe.should_receive(:fix_hair)
|
256
|
+
pe.dress!(:prettying_up)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe 'event firing without persistence' do
|
261
|
+
it 'should attempt to persist if aasm_write_state is defined' do
|
262
|
+
foo = Foo.new
|
263
|
+
def foo.aasm_write_state; end
|
264
|
+
foo.should be_open
|
265
|
+
|
266
|
+
foo.should_receive(:aasm_write_state_without_persistence)
|
267
|
+
foo.close
|
268
|
+
end
|
269
|
+
end
|