aasm 4.0.8 → 4.5.2
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.
- checksums.yaml +4 -4
- data/.travis.yml +7 -2
- data/CHANGELOG.md +43 -2
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +6 -3
- data/LICENSE +1 -1
- data/PLANNED_CHANGES.md +8 -4
- data/README.md +171 -14
- data/aasm.gemspec +1 -5
- data/gemfiles/rails_3.2.gemfile +3 -2
- data/gemfiles/rails_4.0.gemfile +2 -6
- data/gemfiles/rails_4.0_mongo_mapper.gemfile +14 -0
- data/gemfiles/rails_4.1.gemfile +2 -6
- data/gemfiles/rails_4.1_mongo_mapper.gemfile +14 -0
- data/gemfiles/rails_4.2.gemfile +1 -5
- data/gemfiles/rails_4.2_mongo_mapper.gemfile +14 -0
- data/gemfiles/rails_4.2_mongoid_5.gemfile +12 -0
- data/lib/aasm/aasm.rb +69 -36
- data/lib/aasm/base.rb +51 -21
- data/lib/aasm/core/event.rb +7 -6
- data/lib/aasm/core/state.rb +11 -8
- data/lib/aasm/core/transition.rb +7 -5
- data/lib/aasm/errors.rb +15 -1
- data/lib/aasm/instance_base.rb +17 -13
- data/lib/aasm/localizer.rb +1 -1
- data/lib/aasm/persistence/active_record_persistence.rb +63 -73
- data/lib/aasm/persistence/base.rb +55 -20
- data/lib/aasm/persistence/mongo_mapper_persistence.rb +157 -0
- data/lib/aasm/persistence/mongoid_persistence.rb +16 -41
- data/lib/aasm/persistence/plain_persistence.rb +8 -7
- data/lib/aasm/persistence/sequel_persistence.rb +12 -9
- data/lib/aasm/persistence.rb +2 -0
- data/lib/aasm/rspec/allow_event.rb +22 -0
- data/lib/aasm/rspec/allow_transition_to.rb +22 -0
- data/lib/aasm/rspec/have_state.rb +22 -0
- data/lib/aasm/rspec/transition_from.rb +32 -0
- data/lib/aasm/rspec.rb +5 -0
- data/lib/aasm/state_machine.rb +20 -6
- data/lib/aasm/version.rb +1 -1
- data/spec/database.rb +27 -1
- data/spec/models/active_record/basic_active_record_two_state_machines_example.rb +25 -0
- data/spec/models/active_record/complex_active_record_example.rb +33 -0
- data/spec/models/active_record/derivate_new_dsl.rb +7 -0
- data/spec/models/active_record/false_state.rb +35 -0
- data/spec/models/active_record/gate.rb +39 -0
- data/spec/models/active_record/localizer_test_model.rb +34 -0
- data/spec/models/active_record/no_direct_assignment.rb +21 -0
- data/spec/models/active_record/no_scope.rb +21 -0
- data/spec/models/active_record/persisted_state.rb +12 -0
- data/spec/models/active_record/provided_and_persisted_state.rb +24 -0
- data/spec/models/active_record/reader.rb +7 -0
- data/spec/models/active_record/readme_job.rb +21 -0
- data/spec/models/active_record/simple_new_dsl.rb +17 -0
- data/spec/models/active_record/thief.rb +29 -0
- data/spec/models/active_record/transient.rb +6 -0
- data/spec/models/active_record/with_enum.rb +39 -0
- data/spec/models/active_record/with_false_enum.rb +31 -0
- data/spec/models/active_record/with_true_enum.rb +39 -0
- data/spec/models/active_record/writer.rb +6 -0
- data/spec/models/basic_two_state_machines_example.rb +25 -0
- data/spec/models/callbacks/basic.rb +3 -0
- data/spec/models/callbacks/basic_multiple.rb +75 -0
- data/spec/models/callbacks/guard_within_block_multiple.rb +66 -0
- data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +65 -0
- data/spec/models/callbacks/private_method_multiple.rb +44 -0
- data/spec/models/callbacks/with_args.rb +9 -9
- data/spec/models/callbacks/with_args_multiple.rb +61 -0
- data/spec/models/callbacks/{with_state_args.rb → with_state_arg.rb} +1 -1
- data/spec/models/callbacks/with_state_arg_multiple.rb +26 -0
- data/spec/models/complex_example.rb +222 -0
- data/spec/models/conversation.rb +47 -1
- data/spec/models/default_state.rb +12 -0
- data/spec/models/foo.rb +57 -0
- data/spec/models/foo_callback_multiple.rb +45 -0
- data/spec/models/guardian.rb +10 -0
- data/spec/models/guardian_multiple.rb +48 -0
- data/spec/models/initial_state_proc.rb +31 -0
- data/spec/models/invalid_persistor.rb +15 -0
- data/spec/models/mongo_mapper/complex_mongo_mapper_example.rb +37 -0
- data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +21 -0
- data/spec/models/mongo_mapper/simple_mongo_mapper.rb +23 -0
- data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +25 -0
- data/spec/models/mongoid/complex_mongoid_example.rb +37 -0
- data/spec/models/mongoid/no_scope_mongoid.rb +11 -0
- data/spec/models/mongoid/simple_mongoid.rb +12 -0
- data/spec/models/mongoid/simple_new_dsl_mongoid.rb +14 -1
- data/spec/models/no_initial_state.rb +25 -0
- data/spec/models/parametrised_event.rb +1 -1
- data/spec/models/parametrised_event_multiple.rb +29 -0
- data/spec/models/provided_state.rb +24 -0
- data/spec/models/sequel/complex_sequel_example.rb +45 -0
- data/spec/models/sequel/sequel_multiple.rb +25 -0
- data/spec/models/sequel/sequel_simple.rb +25 -0
- data/spec/models/silencer.rb +5 -0
- data/spec/models/simple_example.rb +15 -0
- data/spec/models/simple_multiple_example.rb +30 -0
- data/spec/models/state_machine_with_failed_event.rb +12 -0
- data/spec/models/sub_class.rb +7 -0
- data/spec/models/sub_class_with_more_states.rb +18 -0
- data/spec/models/super_class.rb +46 -0
- data/spec/models/transactor.rb +27 -0
- data/spec/models/valid_state_name.rb +23 -0
- data/spec/models/validator.rb +47 -0
- data/spec/spec_helper.rb +4 -4
- data/spec/unit/api_spec.rb +6 -1
- data/spec/unit/basic_two_state_machines_example_spec.rb +10 -0
- data/spec/unit/callback_multiple_spec.rb +295 -0
- data/spec/unit/callbacks_spec.rb +3 -2
- data/spec/unit/complex_example_spec.rb +2 -2
- data/spec/unit/complex_multiple_example_spec.rb +99 -0
- data/spec/unit/edge_cases_spec.rb +16 -0
- data/spec/unit/event_multiple_spec.rb +73 -0
- data/spec/unit/event_naming_spec.rb +2 -15
- data/spec/unit/event_spec.rb +11 -6
- data/spec/unit/guard_multiple_spec.rb +60 -0
- data/spec/unit/guard_spec.rb +12 -0
- data/spec/unit/initial_state_multiple_spec.rb +15 -0
- data/spec/unit/initial_state_spec.rb +3 -18
- data/spec/unit/inspection_multiple_spec.rb +201 -0
- data/spec/unit/inspection_spec.rb +12 -7
- data/spec/unit/localizer_spec.rb +0 -38
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +573 -0
- data/spec/unit/persistence/active_record_persistence_spec.rb +70 -13
- data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +146 -0
- data/spec/unit/persistence/mongo_mapper_persistence_spec.rb +93 -0
- data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +127 -0
- data/spec/unit/persistence/mongoid_persistence_spec.rb +79 -0
- data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +153 -0
- data/spec/unit/persistence/sequel_persistence_spec.rb +20 -23
- data/spec/unit/readme_spec.rb +42 -0
- data/spec/unit/reloading_spec.rb +1 -1
- data/spec/unit/rspec_matcher_spec.rb +79 -0
- data/spec/unit/simple_example_spec.rb +26 -42
- data/spec/unit/simple_multiple_example_spec.rb +63 -0
- data/spec/unit/state_spec.rb +3 -1
- data/spec/unit/subclassing_multiple_spec.rb +39 -0
- data/spec/unit/subclassing_spec.rb +10 -10
- data/spec/unit/transition_spec.rb +39 -25
- metadata +152 -23
- data/spec/models/active_record/api.rb +0 -75
- data/spec/models/argument.rb +0 -11
- data/spec/models/auth_machine.rb +0 -88
- data/spec/models/bar.rb +0 -15
- data/spec/models/father.rb +0 -21
- data/spec/models/persistence.rb +0 -164
- data/spec/models/son.rb +0 -3
- data/spec/unit/persistence/mongoid_persistance_spec.rb +0 -152
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'on initialization' do
|
|
4
|
+
let(:auth) {ComplexExampleMultiple.new}
|
|
5
|
+
|
|
6
|
+
it 'should be in the initial state' do
|
|
7
|
+
expect(auth.aasm(:left).current_state).to eq(:pending)
|
|
8
|
+
expect(auth.aasm(:right).current_state).to eq(:pending)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'should have an activation code' do
|
|
12
|
+
expect(auth.has_activation_code?).to be_truthy
|
|
13
|
+
expect(auth.activation_code).to eq '12'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe 'when being unsuspended' do
|
|
18
|
+
let(:auth) {ComplexExampleMultiple.new}
|
|
19
|
+
|
|
20
|
+
it 'should be able to unsuspend' do
|
|
21
|
+
auth.left_activate!
|
|
22
|
+
auth.left_suspend!
|
|
23
|
+
expect(auth.may_left_unsuspend?).to be true
|
|
24
|
+
|
|
25
|
+
auth.right_activate!
|
|
26
|
+
auth.right_suspend!
|
|
27
|
+
expect(auth.may_right_unsuspend?).to be true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'should not be able to unsuspend into active' do
|
|
31
|
+
auth.left_suspend!
|
|
32
|
+
expect(auth.may_left_unsuspend?(:active)).not_to be true
|
|
33
|
+
|
|
34
|
+
auth.right_activate!
|
|
35
|
+
auth.right_suspend!
|
|
36
|
+
expect(auth.may_right_unsuspend?(:active)).to be true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'should be able to wait into waiting if polite' do
|
|
40
|
+
auth.left_suspend!
|
|
41
|
+
expect(auth.may_left_wait?(:waiting, :please)).to be true
|
|
42
|
+
auth.left_wait!(nil, :please)
|
|
43
|
+
|
|
44
|
+
auth.right_suspend!
|
|
45
|
+
expect(auth.may_right_wait?(:waiting)).to be false
|
|
46
|
+
auth.right_wait!(nil, :please)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'should not be able to be unsuspended into active if not polite' do
|
|
50
|
+
auth.left_suspend!
|
|
51
|
+
expect(auth.may_left_wait?(:waiting)).not_to be true
|
|
52
|
+
expect(auth.may_left_wait?(:waiting, :rude)).not_to be true
|
|
53
|
+
expect {auth.left_wait!(nil, :rude)}.to raise_error(AASM::InvalidTransition)
|
|
54
|
+
expect {auth.left_wait!}.to raise_error(AASM::InvalidTransition)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'should not be able to be unpassified' do
|
|
58
|
+
auth.left_activate!
|
|
59
|
+
auth.left_suspend!
|
|
60
|
+
auth.left_unsuspend!
|
|
61
|
+
|
|
62
|
+
expect(auth.may_left_unpassify?).not_to be true
|
|
63
|
+
expect {auth.left_unpassify!}.to raise_error(AASM::InvalidTransition)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'should be active if previously activated' do
|
|
67
|
+
auth.left_activate!
|
|
68
|
+
auth.left_suspend!
|
|
69
|
+
auth.left_unsuspend!
|
|
70
|
+
|
|
71
|
+
expect(auth.aasm(:left).current_state).to eq(:active)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'should be pending if not previously activated, but an activation code is present' do
|
|
75
|
+
auth.left_suspend!
|
|
76
|
+
auth.left_unsuspend!
|
|
77
|
+
|
|
78
|
+
expect(auth.aasm(:left).current_state).to eq(:pending)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'should be passive if not previously activated and there is no activation code' do
|
|
82
|
+
auth.activation_code = nil
|
|
83
|
+
auth.left_suspend!
|
|
84
|
+
auth.left_unsuspend!
|
|
85
|
+
|
|
86
|
+
expect(auth.aasm(:left).current_state).to eq(:passive)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "should be able to fire known events" do
|
|
90
|
+
expect(auth.aasm(:left).may_fire_event?(:left_activate)).to be true
|
|
91
|
+
expect(auth.aasm(:right).may_fire_event?(:right_activate)).to be true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "should not be able to fire unknown events" do
|
|
95
|
+
expect(auth.aasm(:left).may_fire_event?(:unknown)).to be false
|
|
96
|
+
expect(auth.aasm(:right).may_fire_event?(:unknown)).to be false
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "edge cases" do
|
|
4
|
+
describe "for classes with multiple state machines" do
|
|
5
|
+
it "allows accessing a multiple state machine class without state machine name" do
|
|
6
|
+
# it's like starting to define a new state machine within the
|
|
7
|
+
# requested class
|
|
8
|
+
expect(SimpleMultipleExample.aasm.states.map(&:name)).to be_empty
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "do not know yet" do
|
|
12
|
+
example = ComplexExampleMultiple.new
|
|
13
|
+
expect { example.aasm.states.inspect }.to raise_error(AASM::UnknownStateMachineError)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'current event' do
|
|
4
|
+
let(:pe) {ParametrisedEventMultiple.new}
|
|
5
|
+
|
|
6
|
+
it 'if no event has been triggered' do
|
|
7
|
+
expect(pe.aasm(:left).current_event).to be_nil
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'if a event has been triggered' do
|
|
11
|
+
pe.wakeup
|
|
12
|
+
expect(pe.aasm(:left).current_event).to eql :wakeup
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'if no event has been triggered' do
|
|
16
|
+
pe.wakeup!
|
|
17
|
+
expect(pe.aasm(:left).current_event).to eql :wakeup!
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe 'parametrised events' do
|
|
22
|
+
let(:pe) {ParametrisedEventMultiple.new}
|
|
23
|
+
|
|
24
|
+
it 'should transition to specified next state (sleeping to showering)' do
|
|
25
|
+
pe.wakeup!(:showering)
|
|
26
|
+
expect(pe.aasm(:left).current_state).to eq(:showering)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'should transition to specified next state (sleeping to working)' do
|
|
30
|
+
pe.wakeup!(:working)
|
|
31
|
+
expect(pe.aasm(:left).current_state).to eq(:working)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'should transition to default (first or showering) state' do
|
|
35
|
+
pe.wakeup!
|
|
36
|
+
expect(pe.aasm(:left).current_state).to eq(:showering)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'should transition to default state when :after transition invoked' do
|
|
40
|
+
pe.dress!(nil, 'purple', 'dressy')
|
|
41
|
+
expect(pe.aasm(:left).current_state).to eq(:working)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'should call :after transition method with args' do
|
|
45
|
+
pe.wakeup!(:showering)
|
|
46
|
+
expect(pe).to receive(:wear_clothes).with('blue', 'jeans')
|
|
47
|
+
pe.dress!(:working, 'blue', 'jeans')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'should call :after transition proc' do
|
|
51
|
+
pe.wakeup!(:showering)
|
|
52
|
+
expect(pe).to receive(:wear_clothes).with('purple', 'slacks')
|
|
53
|
+
pe.dress!(:dating, 'purple', 'slacks')
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'should call :after transition with an array of methods' do
|
|
57
|
+
pe.wakeup!(:showering)
|
|
58
|
+
expect(pe).to receive(:condition_hair)
|
|
59
|
+
expect(pe).to receive(:fix_hair)
|
|
60
|
+
pe.dress!(:prettying_up)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe 'event firing without persistence' do
|
|
65
|
+
it 'should attempt to persist if aasm_write_state is defined' do
|
|
66
|
+
foo = Foo.new
|
|
67
|
+
def foo.aasm_write_state; end
|
|
68
|
+
expect(foo).to be_open
|
|
69
|
+
|
|
70
|
+
expect(foo).to receive(:aasm_write_state_without_persistence)
|
|
71
|
+
foo.close
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -1,22 +1,9 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
|
-
class SimpleStateMachine
|
|
4
|
-
include AASM
|
|
5
|
-
|
|
6
|
-
aasm do
|
|
7
|
-
state :init, :initial => true
|
|
8
|
-
state :failed
|
|
9
|
-
|
|
10
|
-
event :failed do
|
|
11
|
-
transitions :from => :init, :to => :failed
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
3
|
describe "event naming" do
|
|
17
|
-
let(:state_machine) {
|
|
4
|
+
let(:state_machine) { StateMachineWithFailedEvent.new }
|
|
18
5
|
|
|
19
|
-
it "allows an event of failed without blowing the stack" do
|
|
6
|
+
it "allows an event of failed without blowing the stack aka stack level too deep" do
|
|
20
7
|
state_machine.failed
|
|
21
8
|
|
|
22
9
|
expect { state_machine.failed }.to raise_error(AASM::InvalidTransition)
|
data/spec/unit/event_spec.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe 'adding an event' do
|
|
4
|
+
let(:state_machine) { AASM::StateMachine.new(:name) }
|
|
4
5
|
let(:event) do
|
|
5
|
-
AASM::Core::Event.new(:close_order, {:success => :success_callback}) do
|
|
6
|
+
AASM::Core::Event.new(:close_order, state_machine, {:success => :success_callback}) do
|
|
6
7
|
before :before_callback
|
|
7
8
|
after :after_callback
|
|
8
9
|
transitions :to => :closed, :from => [:open, :received]
|
|
@@ -35,8 +36,9 @@ describe 'adding an event' do
|
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
describe 'transition inspection' do
|
|
39
|
+
let(:state_machine) { AASM::StateMachine.new(:name) }
|
|
38
40
|
let(:event) do
|
|
39
|
-
AASM::Core::Event.new(:run) do
|
|
41
|
+
AASM::Core::Event.new(:run, state_machine) do
|
|
40
42
|
transitions :to => :running, :from => :sleeping
|
|
41
43
|
end
|
|
42
44
|
end
|
|
@@ -59,8 +61,9 @@ describe 'transition inspection' do
|
|
|
59
61
|
end
|
|
60
62
|
|
|
61
63
|
describe 'transition inspection without from' do
|
|
64
|
+
let(:state_machine) { AASM::StateMachine.new(:name) }
|
|
62
65
|
let(:event) do
|
|
63
|
-
AASM::Core::Event.new(:run) do
|
|
66
|
+
AASM::Core::Event.new(:run, state_machine) do
|
|
64
67
|
transitions :to => :running
|
|
65
68
|
end
|
|
66
69
|
end
|
|
@@ -76,15 +79,17 @@ describe 'transition inspection without from' do
|
|
|
76
79
|
end
|
|
77
80
|
|
|
78
81
|
describe 'firing an event' do
|
|
82
|
+
let(:state_machine) { AASM::StateMachine.new(:name) }
|
|
83
|
+
|
|
79
84
|
it 'should return nil if the transitions are empty' do
|
|
80
85
|
obj = double('object', :aasm => double('aasm', :current_state => 'open'))
|
|
81
86
|
|
|
82
|
-
event = AASM::Core::Event.new(:event)
|
|
87
|
+
event = AASM::Core::Event.new(:event, state_machine)
|
|
83
88
|
expect(event.fire(obj)).to be_nil
|
|
84
89
|
end
|
|
85
90
|
|
|
86
91
|
it 'should return the state of the first matching transition it finds' do
|
|
87
|
-
event = AASM::Core::Event.new(:event) do
|
|
92
|
+
event = AASM::Core::Event.new(:event, state_machine) do
|
|
88
93
|
transitions :to => :closed, :from => [:open, :received]
|
|
89
94
|
end
|
|
90
95
|
|
|
@@ -94,7 +99,7 @@ describe 'firing an event' do
|
|
|
94
99
|
end
|
|
95
100
|
|
|
96
101
|
it 'should call the guard with the params passed in' do
|
|
97
|
-
event = AASM::Core::Event.new(:event) do
|
|
102
|
+
event = AASM::Core::Event.new(:event, state_machine) do
|
|
98
103
|
transitions :to => :closed, :from => [:open, :received], :guard => :guard_fn
|
|
99
104
|
end
|
|
100
105
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "per-transition guards" do
|
|
4
|
+
let(:guardian) { GuardianMultiple.new }
|
|
5
|
+
|
|
6
|
+
it "allows the transition if the guard succeeds" do
|
|
7
|
+
expect { guardian.use_one_guard_that_succeeds! }.to_not raise_error
|
|
8
|
+
expect(guardian).to be_beta
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "stops the transition if the guard fails" do
|
|
12
|
+
expect { guardian.use_one_guard_that_fails! }.to raise_error(AASM::InvalidTransition)
|
|
13
|
+
expect(guardian).to be_alpha
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "allows the transition if all guards succeeds" do
|
|
17
|
+
expect { guardian.use_guards_that_succeed! }.to_not raise_error
|
|
18
|
+
expect(guardian).to be_beta
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "stops the transition if the first guard fails" do
|
|
22
|
+
expect { guardian.use_guards_where_the_first_fails! }.to raise_error(AASM::InvalidTransition)
|
|
23
|
+
expect(guardian).to be_alpha
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "stops the transition if the second guard fails" do
|
|
27
|
+
expect { guardian.use_guards_where_the_second_fails! }.to raise_error(AASM::InvalidTransition)
|
|
28
|
+
expect(guardian).to be_alpha
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "event guards" do
|
|
33
|
+
let(:guardian) { GuardianMultiple.new }
|
|
34
|
+
|
|
35
|
+
it "allows the transition if the event guards succeed" do
|
|
36
|
+
expect { guardian.use_event_guards_that_succeed! }.to_not raise_error
|
|
37
|
+
expect(guardian).to be_beta
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "allows the transition if the event and transition guards succeed" do
|
|
41
|
+
expect { guardian.use_event_and_transition_guards_that_succeed! }.to_not raise_error
|
|
42
|
+
expect(guardian).to be_beta
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "stops the transition if the first event guard fails" do
|
|
46
|
+
expect { guardian.use_event_guards_where_the_first_fails! }.to raise_error(AASM::InvalidTransition)
|
|
47
|
+
expect(guardian).to be_alpha
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "stops the transition if the second event guard fails" do
|
|
51
|
+
expect { guardian.use_event_guards_where_the_second_fails! }.to raise_error(AASM::InvalidTransition)
|
|
52
|
+
expect(guardian).to be_alpha
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "stops the transition if the transition guard fails" do
|
|
56
|
+
expect { guardian.use_event_and_transition_guards_where_third_fails! }.to raise_error(AASM::InvalidTransition)
|
|
57
|
+
expect(guardian).to be_alpha
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
data/spec/unit/guard_spec.rb
CHANGED
|
@@ -27,6 +27,18 @@ describe "per-transition guards" do
|
|
|
27
27
|
expect { guardian.use_guards_where_the_second_fails! }.to raise_error(AASM::InvalidTransition)
|
|
28
28
|
expect(guardian).to be_alpha
|
|
29
29
|
end
|
|
30
|
+
|
|
31
|
+
describe "with params" do
|
|
32
|
+
it "using a Proc" do
|
|
33
|
+
expect(guardian).to receive(:inner_guard).with({:flag => true}).and_return(true)
|
|
34
|
+
guardian.use_proc_guard_with_params(:flag => true)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "using a lambda" do
|
|
38
|
+
expect(guardian).to receive(:inner_guard).with({:flag => true}).and_return(true)
|
|
39
|
+
guardian.use_lambda_guard_with_params(:flag => true)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
30
42
|
end
|
|
31
43
|
|
|
32
44
|
describe "event guards" do
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'initial states' do
|
|
4
|
+
it 'should use the first state defined if no initial state is given' do
|
|
5
|
+
expect(NoInitialStateMultiple.new.aasm(:left).current_state).to eq(:read)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'should determine initial state from the Proc results' do
|
|
9
|
+
balance = InitialStateProcMultiple::RICH - 1
|
|
10
|
+
expect(InitialStateProcMultiple.new(balance).aasm(:left).current_state).to eq(:selling_bad_mortgages)
|
|
11
|
+
|
|
12
|
+
balance = InitialStateProcMultiple::RICH + 1
|
|
13
|
+
expect(InitialStateProcMultiple.new(balance).aasm(:left).current_state).to eq(:retired)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -1,27 +1,12 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
|
-
class Banker
|
|
4
|
-
include AASM
|
|
5
|
-
aasm do
|
|
6
|
-
state :retired
|
|
7
|
-
state :selling_bad_mortgages
|
|
8
|
-
initial_state Proc.new { |banker| banker.rich? ? :retired : :selling_bad_mortgages }
|
|
9
|
-
end
|
|
10
|
-
RICH = 1_000_000
|
|
11
|
-
attr_accessor :balance
|
|
12
|
-
def initialize(balance = 0); self.balance = balance; end
|
|
13
|
-
def rich?; self.balance >= RICH; end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
3
|
describe 'initial states' do
|
|
17
|
-
let(:bar) {Bar.new}
|
|
18
|
-
|
|
19
4
|
it 'should use the first state defined if no initial state is given' do
|
|
20
|
-
expect(
|
|
5
|
+
expect(NoInitialState.new.aasm.current_state).to eq(:read)
|
|
21
6
|
end
|
|
22
7
|
|
|
23
8
|
it 'should determine initial state from the Proc results' do
|
|
24
|
-
expect(
|
|
25
|
-
expect(
|
|
9
|
+
expect(InitialStateProc.new(InitialStateProc::RICH - 1).aasm.current_state).to eq(:selling_bad_mortgages)
|
|
10
|
+
expect(InitialStateProc.new(InitialStateProc::RICH + 1).aasm.current_state).to eq(:retired)
|
|
26
11
|
end
|
|
27
12
|
end
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'inspection for common cases' do
|
|
4
|
+
it 'should support the new DSL' do
|
|
5
|
+
# 1st state machine
|
|
6
|
+
expect(FooMultiple.aasm(:left)).to respond_to(:states)
|
|
7
|
+
expect(FooMultiple.aasm(:left).states.size).to eql 3
|
|
8
|
+
expect(FooMultiple.aasm(:left).states).to include(:open)
|
|
9
|
+
expect(FooMultiple.aasm(:left).states).to include(:closed)
|
|
10
|
+
expect(FooMultiple.aasm(:left).states).to include(:final)
|
|
11
|
+
|
|
12
|
+
expect(FooMultiple.aasm(:left)).to respond_to(:initial_state)
|
|
13
|
+
expect(FooMultiple.aasm(:left).initial_state).to eq(:open)
|
|
14
|
+
|
|
15
|
+
expect(FooMultiple.aasm(:left)).to respond_to(:events)
|
|
16
|
+
expect(FooMultiple.aasm(:left).events.size).to eql 2
|
|
17
|
+
expect(FooMultiple.aasm(:left).events).to include(:close)
|
|
18
|
+
expect(FooMultiple.aasm(:left).events).to include(:null)
|
|
19
|
+
|
|
20
|
+
# 2nd state machine
|
|
21
|
+
expect(FooMultiple.aasm(:right)).to respond_to(:states)
|
|
22
|
+
expect(FooMultiple.aasm(:right).states.size).to eql 3
|
|
23
|
+
expect(FooMultiple.aasm(:right).states).to include(:green)
|
|
24
|
+
expect(FooMultiple.aasm(:right).states).to include(:yellow)
|
|
25
|
+
expect(FooMultiple.aasm(:right).states).to include(:red)
|
|
26
|
+
|
|
27
|
+
expect(FooMultiple.aasm(:right)).to respond_to(:initial_state)
|
|
28
|
+
expect(FooMultiple.aasm(:right).initial_state).to eq(:green)
|
|
29
|
+
|
|
30
|
+
expect(FooMultiple.aasm(:right)).to respond_to(:events)
|
|
31
|
+
expect(FooMultiple.aasm(:right).events.size).to eql 3
|
|
32
|
+
expect(FooMultiple.aasm(:right).events).to include(:green)
|
|
33
|
+
expect(FooMultiple.aasm(:right).events).to include(:yellow)
|
|
34
|
+
expect(FooMultiple.aasm(:right).events).to include(:red)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
context "instance level inspection" do
|
|
38
|
+
let(:foo) { FooMultiple.new }
|
|
39
|
+
let(:two) { FooTwoMultiple.new }
|
|
40
|
+
|
|
41
|
+
it "delivers all states" do
|
|
42
|
+
# 1st state machine
|
|
43
|
+
states = foo.aasm(:left).states
|
|
44
|
+
expect(states.size).to eql 3
|
|
45
|
+
expect(states).to include(:open)
|
|
46
|
+
expect(states).to include(:closed)
|
|
47
|
+
expect(states).to include(:final)
|
|
48
|
+
|
|
49
|
+
states = foo.aasm(:left).states(:permitted => true)
|
|
50
|
+
expect(states.size).to eql 1
|
|
51
|
+
expect(states).to include(:closed)
|
|
52
|
+
expect(states).not_to include(:open)
|
|
53
|
+
expect(states).not_to include(:final)
|
|
54
|
+
|
|
55
|
+
foo.close
|
|
56
|
+
expect(foo.aasm(:left).states(:permitted => true)).to be_empty
|
|
57
|
+
|
|
58
|
+
# 2nd state machine
|
|
59
|
+
states = foo.aasm(:right).states
|
|
60
|
+
expect(states.size).to eql 3
|
|
61
|
+
expect(states).to include(:green)
|
|
62
|
+
expect(states).to include(:yellow)
|
|
63
|
+
expect(states).to include(:red)
|
|
64
|
+
|
|
65
|
+
states = foo.aasm(:right).states(:permitted => true)
|
|
66
|
+
expect(states.size).to eql 1
|
|
67
|
+
expect(states).to include(:yellow)
|
|
68
|
+
expect(states).not_to include(:green)
|
|
69
|
+
expect(states).not_to include(:red)
|
|
70
|
+
|
|
71
|
+
foo.yellow
|
|
72
|
+
states = foo.aasm(:right).states(:permitted => true)
|
|
73
|
+
expect(states.size).to eql 2
|
|
74
|
+
expect(states).to include(:red)
|
|
75
|
+
expect(states).to include(:green)
|
|
76
|
+
expect(states).not_to include(:yellow)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "delivers all states for subclasses" do
|
|
80
|
+
# 1st state machine
|
|
81
|
+
states = two.aasm(:left).states
|
|
82
|
+
expect(states.size).to eql 4
|
|
83
|
+
expect(states).to include(:open)
|
|
84
|
+
expect(states).to include(:closed)
|
|
85
|
+
expect(states).to include(:final)
|
|
86
|
+
expect(states).to include(:foo)
|
|
87
|
+
|
|
88
|
+
states = two.aasm(:left).states(:permitted => true)
|
|
89
|
+
expect(states.size).to eql 1
|
|
90
|
+
expect(states).to include(:closed)
|
|
91
|
+
expect(states).not_to include(:open)
|
|
92
|
+
|
|
93
|
+
two.close
|
|
94
|
+
expect(two.aasm(:left).states(:permitted => true)).to be_empty
|
|
95
|
+
|
|
96
|
+
# 2nd state machine
|
|
97
|
+
states = two.aasm(:right).states
|
|
98
|
+
expect(states.size).to eql 4
|
|
99
|
+
expect(states).to include(:green)
|
|
100
|
+
expect(states).to include(:yellow)
|
|
101
|
+
expect(states).to include(:red)
|
|
102
|
+
expect(states).to include(:bar)
|
|
103
|
+
|
|
104
|
+
states = two.aasm(:right).states(:permitted => true)
|
|
105
|
+
expect(states.size).to eql 1
|
|
106
|
+
expect(states).to include(:yellow)
|
|
107
|
+
expect(states).not_to include(:red)
|
|
108
|
+
expect(states).not_to include(:green)
|
|
109
|
+
expect(states).not_to include(:bar)
|
|
110
|
+
|
|
111
|
+
two.yellow
|
|
112
|
+
states = two.aasm(:right).states(:permitted => true)
|
|
113
|
+
expect(states.size).to eql 2
|
|
114
|
+
expect(states).to include(:green)
|
|
115
|
+
expect(states).to include(:red)
|
|
116
|
+
expect(states).not_to include(:yellow)
|
|
117
|
+
expect(states).not_to include(:bar)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "delivers all events" do
|
|
121
|
+
# 1st state machine
|
|
122
|
+
events = foo.aasm(:left).events
|
|
123
|
+
expect(events.size).to eql 2
|
|
124
|
+
expect(events).to include(:close)
|
|
125
|
+
expect(events).to include(:null)
|
|
126
|
+
|
|
127
|
+
foo.close
|
|
128
|
+
expect(foo.aasm(:left).events).to be_empty
|
|
129
|
+
|
|
130
|
+
# 2nd state machine
|
|
131
|
+
events = foo.aasm(:right).events
|
|
132
|
+
expect(events.size).to eql 1
|
|
133
|
+
expect(events).to include(:yellow)
|
|
134
|
+
expect(events).not_to include(:green)
|
|
135
|
+
expect(events).not_to include(:red)
|
|
136
|
+
|
|
137
|
+
foo.yellow
|
|
138
|
+
events = foo.aasm(:right).events
|
|
139
|
+
expect(events.size).to eql 2
|
|
140
|
+
expect(events).to include(:green)
|
|
141
|
+
expect(events).to include(:red)
|
|
142
|
+
expect(events).not_to include(:yellow)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it 'should list states in the order they have been defined' do
|
|
147
|
+
expect(ConversationMultiple.aasm(:left).states).to eq([
|
|
148
|
+
:needs_attention, :read, :closed, :awaiting_response, :junk
|
|
149
|
+
])
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
describe "special cases" do
|
|
154
|
+
it "should support valid as state name" do
|
|
155
|
+
expect(ValidStateNameMultiple.aasm(:left).states).to include(:invalid)
|
|
156
|
+
expect(ValidStateNameMultiple.aasm(:left).states).to include(:valid)
|
|
157
|
+
|
|
158
|
+
argument = ValidStateNameMultiple.new
|
|
159
|
+
expect(argument.invalid?).to be_truthy
|
|
160
|
+
expect(argument.aasm(:left).current_state).to eq(:invalid)
|
|
161
|
+
|
|
162
|
+
argument.valid!
|
|
163
|
+
expect(argument.valid?).to be_truthy
|
|
164
|
+
expect(argument.aasm(:left).current_state).to eq(:valid)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
describe 'aasm.states_for_select' do
|
|
169
|
+
it "should return a select friendly array of states" do
|
|
170
|
+
expect(FooMultiple.aasm(:left)).to respond_to(:states_for_select)
|
|
171
|
+
expect(FooMultiple.aasm(:left).states_for_select).to eq(
|
|
172
|
+
[['Open', 'open'], ['Closed', 'closed'], ['Final', 'final']]
|
|
173
|
+
)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
describe 'aasm.from_states_for_state' do
|
|
178
|
+
it "should return all from states for a state" do
|
|
179
|
+
expect(ComplexExampleMultiple.aasm(:left)).to respond_to(:from_states_for_state)
|
|
180
|
+
froms = ComplexExampleMultiple.aasm(:left).from_states_for_state(:active)
|
|
181
|
+
[:pending, :passive, :suspended].each {|from| expect(froms).to include(from)}
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it "should return from states for a state for a particular transition only" do
|
|
185
|
+
froms = ComplexExampleMultiple.aasm(:left).from_states_for_state(:active, :transition => :left_unsuspend)
|
|
186
|
+
[:suspended].each {|from| expect(froms).to include(from)}
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
describe 'permitted events' do
|
|
191
|
+
let(:foo) {FooMultiple.new}
|
|
192
|
+
|
|
193
|
+
it 'work' do
|
|
194
|
+
expect(foo.aasm(:left).events(:permitted => true)).to include(:close)
|
|
195
|
+
expect(foo.aasm(:left).events(:permitted => true)).not_to include(:null)
|
|
196
|
+
|
|
197
|
+
expect(foo.aasm(:right).events(:permitted => true)).to include(:yellow)
|
|
198
|
+
expect(foo.aasm(:right).events(:permitted => true)).not_to include(:green)
|
|
199
|
+
expect(foo.aasm(:right).events(:permitted => true)).not_to include(:red)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
@@ -62,11 +62,11 @@ describe 'inspection for common cases' do
|
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
describe "special cases" do
|
|
65
|
-
it "should support valid
|
|
66
|
-
expect(
|
|
67
|
-
expect(
|
|
65
|
+
it "should support valid as state name" do
|
|
66
|
+
expect(ValidStateName.aasm.states).to include(:invalid)
|
|
67
|
+
expect(ValidStateName.aasm.states).to include(:valid)
|
|
68
68
|
|
|
69
|
-
argument =
|
|
69
|
+
argument = ValidStateName.new
|
|
70
70
|
expect(argument.invalid?).to be_truthy
|
|
71
71
|
expect(argument.aasm.current_state).to eq(:invalid)
|
|
72
72
|
|
|
@@ -85,13 +85,13 @@ end
|
|
|
85
85
|
|
|
86
86
|
describe 'aasm.from_states_for_state' do
|
|
87
87
|
it "should return all from states for a state" do
|
|
88
|
-
expect(
|
|
89
|
-
froms =
|
|
88
|
+
expect(ComplexExample.aasm).to respond_to(:from_states_for_state)
|
|
89
|
+
froms = ComplexExample.aasm.from_states_for_state(:active)
|
|
90
90
|
[:pending, :passive, :suspended].each {|from| expect(froms).to include(from)}
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
it "should return from states for a state for a particular transition only" do
|
|
94
|
-
froms =
|
|
94
|
+
froms = ComplexExample.aasm.from_states_for_state(:active, :transition => :unsuspend)
|
|
95
95
|
[:suspended].each {|from| expect(froms).to include(from)}
|
|
96
96
|
end
|
|
97
97
|
end
|
|
@@ -103,4 +103,9 @@ describe 'permitted events' do
|
|
|
103
103
|
expect(foo.aasm.events(:permitted => true)).to include(:close)
|
|
104
104
|
expect(foo.aasm.events(:permitted => true)).not_to include(:null)
|
|
105
105
|
end
|
|
106
|
+
|
|
107
|
+
it 'should not include events in the reject option' do
|
|
108
|
+
expect(foo.aasm.events(:permitted => true, reject: :close)).not_to include(:close)
|
|
109
|
+
expect(foo.aasm.events(:permitted => true, reject: [:close])).not_to include(:close)
|
|
110
|
+
end
|
|
106
111
|
end
|