alexrevin-aasm_numerical 2.3.1
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/.document +5 -0
- data/.gitignore +11 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +149 -0
- data/Rakefile +27 -0
- data/lib/alexrevin-aasm_numerical.rb +10 -0
- data/lib/alexrevin-aasm_numerical/aasm.rb +222 -0
- data/lib/alexrevin-aasm_numerical/event.rb +127 -0
- data/lib/alexrevin-aasm_numerical/localizer.rb +36 -0
- data/lib/alexrevin-aasm_numerical/persistence.rb +14 -0
- data/lib/alexrevin-aasm_numerical/persistence/active_record_persistence.rb +257 -0
- data/lib/alexrevin-aasm_numerical/state.rb +53 -0
- data/lib/alexrevin-aasm_numerical/state_machine.rb +31 -0
- data/lib/alexrevin-aasm_numerical/state_transition.rb +46 -0
- data/lib/alexrevin-aasm_numerical/supporting_classes.rb +6 -0
- data/lib/alexrevin-aasm_numerical/version.rb +3 -0
- data/spec/database.yml +3 -0
- data/spec/en.yml +10 -0
- data/spec/functional/conversation.rb +49 -0
- data/spec/functional/conversation_spec.rb +8 -0
- data/spec/schema.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/unit/aasm_spec.rb +462 -0
- data/spec/unit/active_record_persistence_spec.rb +246 -0
- data/spec/unit/before_after_callbacks_spec.rb +79 -0
- data/spec/unit/event_spec.rb +140 -0
- data/spec/unit/localizer_spec.rb +51 -0
- data/spec/unit/state_spec.rb +85 -0
- data/spec/unit/state_transition_spec.rb +163 -0
- data/test/functional/auth_machine_test.rb +148 -0
- data/test/models/process.rb +18 -0
- data/test/test_helper.rb +43 -0
- data/test/unit/aasm_test.rb +0 -0
- data/test/unit/event_test.rb +54 -0
- data/test/unit/state_machine_test.rb +37 -0
- data/test/unit/state_test.rb +69 -0
- data/test/unit/state_transition_test.rb +75 -0
- metadata +254 -0
@@ -0,0 +1,246 @@
|
|
1
|
+
begin
|
2
|
+
require 'rubygems'
|
3
|
+
require 'active_record'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
load_schema
|
7
|
+
|
8
|
+
ActiveRecord::Base.logger = Logger.new(STDERR)
|
9
|
+
|
10
|
+
class Gate < ActiveRecord::Base
|
11
|
+
include AASM
|
12
|
+
|
13
|
+
# Fake this column for testing purposes
|
14
|
+
attr_accessor :aasm_state
|
15
|
+
|
16
|
+
aasm_state :opened
|
17
|
+
aasm_state :closed
|
18
|
+
|
19
|
+
aasm_event :view do
|
20
|
+
transitions :to => :read, :from => [:needs_attention]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Reader < ActiveRecord::Base
|
25
|
+
def aasm_read_state
|
26
|
+
"fi"
|
27
|
+
end
|
28
|
+
include AASM
|
29
|
+
end
|
30
|
+
|
31
|
+
class Writer < ActiveRecord::Base
|
32
|
+
def aasm_write_state(state)
|
33
|
+
"fo"
|
34
|
+
end
|
35
|
+
include AASM
|
36
|
+
end
|
37
|
+
|
38
|
+
class Transient < ActiveRecord::Base
|
39
|
+
def aasm_write_state_without_persistence(state)
|
40
|
+
"fum"
|
41
|
+
end
|
42
|
+
include AASM
|
43
|
+
end
|
44
|
+
|
45
|
+
class Simple < ActiveRecord::Base
|
46
|
+
include AASM
|
47
|
+
aasm_column :status
|
48
|
+
end
|
49
|
+
|
50
|
+
class Derivate < Simple
|
51
|
+
end
|
52
|
+
|
53
|
+
class Thief < ActiveRecord::Base
|
54
|
+
set_table_name "thieves"
|
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
|
+
shared_examples_for "aasm model" 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 Gate, "class methods" do
|
72
|
+
before(:each) do
|
73
|
+
@klass = Gate
|
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 Reader, "class methods" do
|
88
|
+
before(:each) do
|
89
|
+
@klass = Reader
|
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 Writer, "class methods" do
|
104
|
+
before(:each) do
|
105
|
+
@klass = Writer
|
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 Transient, "class methods" do
|
120
|
+
before(:each) do
|
121
|
+
@klass = Transient
|
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 Gate, "instance methods" do
|
136
|
+
|
137
|
+
it "should respond to aasm read state when not previously defined" do
|
138
|
+
Gate.new.should respond_to(:aasm_read_state)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should respond to aasm write state when not previously defined" do
|
142
|
+
Gate.new.should respond_to(:aasm_write_state)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should respond to aasm write state without persistence when not previously defined" do
|
146
|
+
Gate.new.should respond_to(:aasm_write_state_without_persistence)
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should return the initial state when new and the aasm field is nil" do
|
150
|
+
Gate.new.aasm_current_state.should == :opened
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should return the aasm column when new and the aasm field is not nil" do
|
154
|
+
foo = Gate.new
|
155
|
+
foo.aasm_state = "closed"
|
156
|
+
foo.aasm_current_state.should == :closed
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should return the aasm column when not new and the aasm_column is not nil" do
|
160
|
+
foo = Gate.new
|
161
|
+
foo.stub!(:new_record?).and_return(false)
|
162
|
+
foo.aasm_state = "state"
|
163
|
+
foo.aasm_current_state.should == :state
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should allow a nil state" do
|
167
|
+
foo = Gate.new
|
168
|
+
foo.stub!(:new_record?).and_return(false)
|
169
|
+
foo.aasm_state = nil
|
170
|
+
foo.aasm_current_state.should be_nil
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should have aasm_ensure_initial_state" do
|
174
|
+
foo = Gate.new
|
175
|
+
foo.send :aasm_ensure_initial_state
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should call aasm_ensure_initial_state on validation before create" do
|
179
|
+
foo = Gate.new
|
180
|
+
foo.should_receive(:aasm_ensure_initial_state).and_return(true)
|
181
|
+
foo.valid?
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should call aasm_ensure_initial_state on validation before create" do
|
185
|
+
foo = Gate.new
|
186
|
+
foo.stub!(:new_record?).and_return(false)
|
187
|
+
foo.should_not_receive(:aasm_ensure_initial_state)
|
188
|
+
foo.valid?
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
describe 'Derivates' do
|
194
|
+
it "should have the same states as it's parent" do
|
195
|
+
Derivate.aasm_states.should == Simple.aasm_states
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should have the same events as it's parent" do
|
199
|
+
Derivate.aasm_events.should == Simple.aasm_events
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should have the same column as it's parent" do
|
203
|
+
Derivate.aasm_column.should == :status
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe AASM::Persistence::ActiveRecordPersistence::NamedScopeMethods do
|
208
|
+
|
209
|
+
context "Does not already respond_to? the scope name" do
|
210
|
+
it "should add a scope" do
|
211
|
+
Simple.should_not respond_to(:unknown_scope)
|
212
|
+
Simple.aasm_state :unknown_scope
|
213
|
+
Simple.should respond_to(:unknown_scope)
|
214
|
+
Simple.unknown_scope.class.should == ActiveRecord::Relation
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
context "Already respond_to? the scope name" do
|
219
|
+
it "should not add a scope" do
|
220
|
+
Simple.aasm_state :new
|
221
|
+
Simple.should respond_to(:new)
|
222
|
+
Simple.new.class.should == Simple
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe 'Thieves' do
|
228
|
+
|
229
|
+
it 'should be rich if they\'re skilled' do
|
230
|
+
Thief.new(:skilled => true).aasm_current_state.should == :rich
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'should be jailed if they\'re unskilled' do
|
234
|
+
Thief.new(:skilled => false).aasm_current_state.should == :jailed
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# TODO: figure out how to test ActiveRecord reload! without a database
|
239
|
+
|
240
|
+
rescue LoadError => e
|
241
|
+
if e.message == "no such file to load -- active_record"
|
242
|
+
puts "You must install active record to run this spec. Install with sudo gem install activerecord"
|
243
|
+
else
|
244
|
+
raise
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.expand_path(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,140 @@
|
|
1
|
+
require File.expand_path(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
|
+
|
52
|
+
|
53
|
+
it 'should call the guard with the params passed in' do
|
54
|
+
event = AASM::SupportingClasses::Event.new(:event) do
|
55
|
+
transitions :to => :closed, :from => [:open, :received], :guard => :guard_fn
|
56
|
+
end
|
57
|
+
|
58
|
+
obj = mock('object')
|
59
|
+
obj.stub!(:aasm_current_state).and_return(:open)
|
60
|
+
obj.should_receive(:guard_fn).with('arg1', 'arg2').and_return(true)
|
61
|
+
|
62
|
+
event.fire(obj, nil, 'arg1', 'arg2').should == :closed
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
describe AASM::SupportingClasses::Event, 'when executing the success callback' do
|
68
|
+
class ThisNameBetterNotBeInUse
|
69
|
+
include AASM
|
70
|
+
|
71
|
+
aasm_state :initial
|
72
|
+
aasm_state :symbol
|
73
|
+
aasm_state :string
|
74
|
+
aasm_state :array
|
75
|
+
aasm_state :proc
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should send the success callback if it's a symbol" do
|
79
|
+
ThisNameBetterNotBeInUse.instance_eval {
|
80
|
+
aasm_event :with_symbol, :success => :symbol_success_callback do
|
81
|
+
transitions :to => :symbol, :from => [:initial]
|
82
|
+
end
|
83
|
+
}
|
84
|
+
|
85
|
+
model = ThisNameBetterNotBeInUse.new
|
86
|
+
model.should_receive(:symbol_success_callback)
|
87
|
+
model.with_symbol!
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should send the success callback if it's a string" do
|
91
|
+
ThisNameBetterNotBeInUse.instance_eval {
|
92
|
+
aasm_event :with_string, :success => 'string_success_callback' do
|
93
|
+
transitions :to => :string, :from => [:initial]
|
94
|
+
end
|
95
|
+
}
|
96
|
+
|
97
|
+
model = ThisNameBetterNotBeInUse.new
|
98
|
+
model.should_receive(:string_success_callback)
|
99
|
+
model.with_string!
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should call each success callback if passed an array of strings and/or symbols" do
|
103
|
+
ThisNameBetterNotBeInUse.instance_eval {
|
104
|
+
aasm_event :with_array, :success => [:success_callback1, 'success_callback2'] do
|
105
|
+
transitions :to => :array, :from => [:initial]
|
106
|
+
end
|
107
|
+
}
|
108
|
+
|
109
|
+
model = ThisNameBetterNotBeInUse.new
|
110
|
+
model.should_receive(:success_callback1)
|
111
|
+
model.should_receive(:success_callback2)
|
112
|
+
model.with_array!
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should call each success callback if passed an array of strings and/or symbols and/or procs" do
|
116
|
+
ThisNameBetterNotBeInUse.instance_eval {
|
117
|
+
aasm_event :with_array_including_procs, :success => [:success_callback1, 'success_callback2', lambda { |obj| obj.proc_success_callback }] do
|
118
|
+
transitions :to => :array, :from => [:initial]
|
119
|
+
end
|
120
|
+
}
|
121
|
+
|
122
|
+
model = ThisNameBetterNotBeInUse.new
|
123
|
+
model.should_receive(:success_callback1)
|
124
|
+
model.should_receive(:success_callback2)
|
125
|
+
model.should_receive(:proc_success_callback)
|
126
|
+
model.with_array_including_procs!
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should call the success callback if it's a proc" do
|
130
|
+
ThisNameBetterNotBeInUse.instance_eval {
|
131
|
+
aasm_event :with_proc, :success => lambda { |obj| obj.proc_success_callback } do
|
132
|
+
transitions :to => :proc, :from => [:initial]
|
133
|
+
end
|
134
|
+
}
|
135
|
+
|
136
|
+
model = ThisNameBetterNotBeInUse.new
|
137
|
+
model.should_receive(:proc_success_callback)
|
138
|
+
model.with_proc!
|
139
|
+
end
|
140
|
+
end
|