aasm 4.11.1 → 4.12.0
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 +15 -16
- data/Appraisals +44 -0
- data/CHANGELOG.md +14 -0
- data/CONTRIBUTING.md +24 -0
- data/Gemfile +4 -21
- data/README.md +53 -32
- data/Rakefile +6 -1
- data/TESTING.md +25 -0
- data/aasm.gemspec +3 -0
- data/gemfiles/rails_3.2.gemfile +13 -0
- data/gemfiles/rails_4.0.gemfile +8 -9
- data/gemfiles/rails_4.2.gemfile +9 -9
- data/gemfiles/rails_4.2_mongoid_5.gemfile +5 -9
- data/gemfiles/rails_5.0.gemfile +7 -16
- data/lib/aasm/aasm.rb +9 -3
- data/lib/aasm/base.rb +3 -1
- data/lib/aasm/configuration.rb +4 -0
- data/lib/aasm/core/event.rb +17 -3
- data/lib/aasm/core/state.rb +7 -0
- data/lib/aasm/core/transition.rb +9 -0
- data/lib/aasm/persistence.rb +0 -3
- data/lib/aasm/persistence/active_record_persistence.rb +1 -1
- data/lib/aasm/persistence/mongoid_persistence.rb +48 -9
- data/lib/aasm/state_machine.rb +4 -2
- data/lib/aasm/state_machine_store.rb +5 -2
- data/lib/aasm/version.rb +1 -1
- data/lib/motion-aasm.rb +0 -1
- data/spec/generators/active_record_generator_spec.rb +42 -39
- data/spec/generators/mongoid_generator_spec.rb +4 -6
- data/spec/models/{invalid_persistor.rb → active_record/invalid_persistor.rb} +0 -2
- data/spec/models/{silent_persistor.rb → active_record/silent_persistor.rb} +0 -2
- data/spec/models/{transactor.rb → active_record/transactor.rb} +0 -2
- data/spec/models/{validator.rb → active_record/validator.rb} +0 -2
- data/spec/models/{worker.rb → active_record/worker.rb} +0 -0
- data/spec/models/callbacks/basic.rb +5 -2
- data/spec/models/guard_with_params.rb +1 -1
- data/spec/models/mongoid/invalid_persistor_mongoid.rb +39 -0
- data/spec/models/mongoid/silent_persistor_mongoid.rb +39 -0
- data/spec/models/mongoid/validator_mongoid.rb +100 -0
- data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +31 -0
- data/spec/models/parametrised_event.rb +7 -0
- data/spec/models/simple_multiple_example.rb +12 -0
- data/spec/models/sub_class.rb +34 -0
- data/spec/spec_helper.rb +0 -33
- data/spec/spec_helpers/active_record.rb +7 -0
- data/spec/spec_helpers/dynamoid.rb +33 -0
- data/spec/spec_helpers/mongoid.rb +7 -0
- data/spec/spec_helpers/redis.rb +7 -0
- data/spec/spec_helpers/remove_warnings.rb +1 -0
- data/spec/spec_helpers/sequel.rb +7 -0
- data/spec/unit/api_spec.rb +76 -73
- data/spec/unit/callbacks_spec.rb +5 -0
- data/spec/unit/event_spec.rb +12 -0
- data/spec/unit/guard_with_params_spec.rb +4 -0
- data/spec/unit/localizer_spec.rb +55 -53
- data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +14 -0
- data/spec/unit/override_warning_spec.rb +8 -0
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +452 -448
- data/spec/unit/persistence/active_record_persistence_spec.rb +523 -501
- data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +4 -9
- data/spec/unit/persistence/dynamoid_persistence_spec.rb +4 -9
- data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +83 -9
- data/spec/unit/persistence/mongoid_persistence_spec.rb +85 -9
- data/spec/unit/persistence/redis_persistence_spec.rb +3 -7
- data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +4 -9
- data/spec/unit/persistence/sequel_persistence_spec.rb +4 -9
- data/spec/unit/simple_multiple_example_spec.rb +28 -0
- data/spec/unit/subclassing_multiple_spec.rb +37 -2
- data/spec/unit/subclassing_spec.rb +17 -2
- metadata +66 -28
- data/gemfiles/rails_3.2_stable.gemfile +0 -15
- data/gemfiles/rails_4.0_mongo_mapper.gemfile +0 -16
- data/gemfiles/rails_4.2_mongo_mapper.gemfile +0 -17
- data/lib/aasm/persistence/mongo_mapper_persistence.rb +0 -163
- data/spec/models/mongo_mapper/complex_mongo_mapper_example.rb +0 -37
- data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +0 -21
- data/spec/models/mongo_mapper/simple_mongo_mapper.rb +0 -23
- data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +0 -25
- data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +0 -149
- data/spec/unit/persistence/mongo_mapper_persistence_spec.rb +0 -96
@@ -0,0 +1 @@
|
|
1
|
+
AASM::Configuration.hide_warnings = true
|
data/spec/unit/api_spec.rb
CHANGED
@@ -1,97 +1,100 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'models/default_state.rb'
|
3
|
-
require 'models/provided_state.rb'
|
4
|
-
require 'models/active_record/persisted_state.rb'
|
5
|
-
require 'models/active_record/provided_and_persisted_state.rb'
|
6
2
|
|
7
|
-
|
3
|
+
if defined?(ActiveRecord)
|
4
|
+
require 'models/default_state.rb'
|
5
|
+
require 'models/provided_state.rb'
|
6
|
+
require 'models/active_record/persisted_state.rb'
|
7
|
+
require 'models/active_record/provided_and_persisted_state.rb'
|
8
8
|
|
9
|
-
|
10
|
-
it "uses the AASM default" do
|
11
|
-
expect(DefaultState.new.aasm.current_state).to eql :alpha
|
12
|
-
end
|
13
|
-
|
14
|
-
it "uses the provided method" do
|
15
|
-
expect(ProvidedState.new.aasm.current_state).to eql :beta
|
16
|
-
end
|
9
|
+
load_schema
|
17
10
|
|
18
|
-
|
19
|
-
|
20
|
-
|
11
|
+
describe "reading the current state" do
|
12
|
+
it "uses the AASM default" do
|
13
|
+
expect(DefaultState.new.aasm.current_state).to eql :alpha
|
14
|
+
end
|
21
15
|
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
it "uses the provided method" do
|
17
|
+
expect(ProvidedState.new.aasm.current_state).to eql :beta
|
18
|
+
end
|
25
19
|
|
26
|
-
context "after dup" do
|
27
20
|
it "uses the persistence storage" do
|
28
|
-
|
29
|
-
|
30
|
-
copy.save!
|
21
|
+
expect(PersistedState.new.aasm.current_state).to eql :alpha
|
22
|
+
end
|
31
23
|
|
32
|
-
|
24
|
+
it "uses the provided method even if persisted" do
|
25
|
+
expect(ProvidedAndPersistedState.new.aasm.current_state).to eql :gamma
|
26
|
+
end
|
27
|
+
|
28
|
+
context "after dup" do
|
29
|
+
it "uses the persistence storage" do
|
30
|
+
source = PersistedState.create!
|
31
|
+
copy = source.dup
|
32
|
+
copy.save!
|
33
33
|
|
34
|
-
|
35
|
-
expect(source.aasm.current_state).to eql :alpha
|
34
|
+
copy.release!
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
expect(source2.aasm.current_state).to eql :alpha
|
36
|
+
expect(source.aasm_state).to eql 'alpha'
|
37
|
+
expect(source.aasm.current_state).to eql :alpha
|
40
38
|
|
41
|
-
|
42
|
-
|
39
|
+
source2 = PersistedState.find(source.id)
|
40
|
+
expect(source2.reload.aasm_state).to eql 'alpha'
|
41
|
+
expect(source2.aasm.current_state).to eql :alpha
|
42
|
+
|
43
|
+
expect(copy.aasm_state).to eql 'beta'
|
44
|
+
expect(copy.aasm.current_state).to eql :beta
|
45
|
+
end
|
43
46
|
end
|
44
47
|
end
|
45
|
-
end
|
46
48
|
|
47
|
-
describe "writing and persisting the current state" do
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
describe "writing and persisting the current state" do
|
50
|
+
it "uses the AASM default" do
|
51
|
+
o = DefaultState.new
|
52
|
+
o.release!
|
53
|
+
expect(o.persisted_store).to be_nil
|
54
|
+
end
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
it "uses the provided method" do
|
57
|
+
o = ProvidedState.new
|
58
|
+
o.release!
|
59
|
+
expect(o.persisted_store).to eql :beta
|
60
|
+
end
|
59
61
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
62
|
+
it "uses the persistence storage" do
|
63
|
+
o = PersistedState.new
|
64
|
+
o.release!
|
65
|
+
expect(o.persisted_store).to be_nil
|
66
|
+
end
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
it "uses the provided method even if persisted" do
|
69
|
+
o = ProvidedAndPersistedState.new
|
70
|
+
o.release!
|
71
|
+
expect(o.persisted_store).to eql :beta
|
72
|
+
end
|
70
73
|
end
|
71
|
-
end
|
72
74
|
|
73
|
-
describe "writing the current state without persisting it" do
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
describe "writing the current state without persisting it" do
|
76
|
+
it "uses the AASM default" do
|
77
|
+
o = DefaultState.new
|
78
|
+
o.release
|
79
|
+
expect(o.transient_store).to be_nil
|
80
|
+
end
|
79
81
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
82
|
+
it "uses the provided method" do
|
83
|
+
o = ProvidedState.new
|
84
|
+
o.release
|
85
|
+
expect(o.transient_store).to eql :beta
|
86
|
+
end
|
85
87
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
it "uses the persistence storage" do
|
89
|
+
o = PersistedState.new
|
90
|
+
o.release
|
91
|
+
expect(o.transient_store).to be_nil
|
92
|
+
end
|
91
93
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
94
|
+
it "uses the provided method even if persisted" do
|
95
|
+
o = ProvidedAndPersistedState.new
|
96
|
+
o.release
|
97
|
+
expect(o.transient_store).to eql :beta
|
98
|
+
end
|
96
99
|
end
|
97
100
|
end
|
data/spec/unit/callbacks_spec.rb
CHANGED
@@ -118,6 +118,7 @@ describe 'callbacks for the new DSL' do
|
|
118
118
|
expect(callback).to receive(:before_enter_closed).once.ordered
|
119
119
|
expect(callback).to receive(:enter_closed).once.ordered
|
120
120
|
expect(callback).to receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
|
121
|
+
expect(callback).to receive(:event_before_success).once.ordered
|
121
122
|
expect(callback).to receive(:success_transition).once.ordered.and_return(true) # these should be after the state changes
|
122
123
|
expect(callback).to receive(:after_exit_open).once.ordered
|
123
124
|
expect(callback).to receive(:after_enter_closed).once.ordered
|
@@ -143,6 +144,7 @@ describe 'callbacks for the new DSL' do
|
|
143
144
|
expect(callback).to_not receive(:before_enter_closed)
|
144
145
|
expect(callback).to_not receive(:enter_closed)
|
145
146
|
expect(callback).to_not receive(:aasm_write_state)
|
147
|
+
expect(callback).to_not receive(:event_before_success)
|
146
148
|
expect(callback).to_not receive(:success_transition)
|
147
149
|
expect(callback).to_not receive(:after_exit_open)
|
148
150
|
expect(callback).to_not receive(:after_enter_closed)
|
@@ -186,6 +188,7 @@ describe 'callbacks for the new DSL' do
|
|
186
188
|
expect(callback).to_not receive(:before_enter_closed)
|
187
189
|
expect(callback).to_not receive(:enter_closed)
|
188
190
|
expect(callback).to_not receive(:aasm_write_state)
|
191
|
+
expect(callback).to_not receive(:event_before_success)
|
189
192
|
expect(callback).to_not receive(:success_transition)
|
190
193
|
expect(callback).to_not receive(:after_exit_open)
|
191
194
|
expect(callback).to_not receive(:after_enter_closed)
|
@@ -217,6 +220,7 @@ describe 'callbacks for the new DSL' do
|
|
217
220
|
expect(callback).to receive(:after).once.ordered
|
218
221
|
|
219
222
|
expect(callback).to_not receive(:transitioning)
|
223
|
+
expect(callback).to_not receive(:event_before_success)
|
220
224
|
expect(callback).to_not receive(:success_transition)
|
221
225
|
expect(callback).to_not receive(:before_enter_closed)
|
222
226
|
expect(callback).to_not receive(:enter_closed)
|
@@ -240,6 +244,7 @@ describe 'callbacks for the new DSL' do
|
|
240
244
|
expect(callback).to_not receive(:before_enter_closed)
|
241
245
|
expect(callback).to_not receive(:enter_closed)
|
242
246
|
expect(callback).to_not receive(:aasm_write_state)
|
247
|
+
expect(callback).to_not receive(:event_before_success)
|
243
248
|
expect(callback).to_not receive(:success_transition)
|
244
249
|
expect(callback).to_not receive(:after_exit_open)
|
245
250
|
expect(callback).to_not receive(:after_enter_closed)
|
data/spec/unit/event_spec.rb
CHANGED
@@ -325,6 +325,12 @@ describe 'parametrised events' do
|
|
325
325
|
pe.dress!(:working, 'blue', 'jeans')
|
326
326
|
end
|
327
327
|
|
328
|
+
it 'should call :after transition method if arg is nil' do
|
329
|
+
dryer = nil
|
330
|
+
expect(pe).to receive(:wet_hair).with(dryer)
|
331
|
+
pe.shower!(dryer)
|
332
|
+
end
|
333
|
+
|
328
334
|
it 'should call :after transition proc' do
|
329
335
|
pe.wakeup!(:showering)
|
330
336
|
expect(pe).to receive(:wear_clothes).with('purple', 'slacks')
|
@@ -344,6 +350,12 @@ describe 'parametrised events' do
|
|
344
350
|
pe.dress!(:working, 'foundation', 'SPF')
|
345
351
|
end
|
346
352
|
|
353
|
+
it 'should call :success transition method if arg is nil' do
|
354
|
+
shirt_color = nil
|
355
|
+
expect(pe).to receive(:wear_clothes).with(shirt_color)
|
356
|
+
pe.shower!(shirt_color)
|
357
|
+
end
|
358
|
+
|
347
359
|
it 'should call :success transition proc' do
|
348
360
|
pe.wakeup!(:showering)
|
349
361
|
expect(pe).to receive(:wear_makeup).with('purple', 'slacks')
|
@@ -7,4 +7,8 @@ describe "guards with params" do
|
|
7
7
|
it "list permitted states" do
|
8
8
|
expect(guard.aasm.states({:permitted => true}, user).map(&:name)).to eql [:reviewed]
|
9
9
|
end
|
10
|
+
|
11
|
+
it "list no states if user is blank" do
|
12
|
+
expect(guard.aasm.states({:permitted => true}, nil).map(&:name)).to eql []
|
13
|
+
end
|
10
14
|
end
|
data/spec/unit/localizer_spec.rb
CHANGED
@@ -1,76 +1,78 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'active_record'
|
3
|
-
require 'i18n'
|
4
2
|
|
5
|
-
|
6
|
-
|
3
|
+
if defined?(ActiceRecord)
|
4
|
+
require 'i18n'
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
I18n.load_path << 'spec/en.yml'
|
11
|
-
I18n.default_locale = :en
|
12
|
-
I18n.reload!
|
13
|
-
end
|
14
|
-
|
15
|
-
after(:all) do
|
16
|
-
I18n.load_path.clear
|
17
|
-
end
|
18
|
-
|
19
|
-
let (:foo_opened) { LocalizerTestModel.new }
|
20
|
-
let (:foo_closed) { LocalizerTestModel.new.tap { |x| x.aasm_state = :closed } }
|
6
|
+
I18n.enforce_available_locales = false
|
7
|
+
load_schema
|
21
8
|
|
22
|
-
|
23
|
-
|
24
|
-
|
9
|
+
describe AASM::Localizer, "new style" do
|
10
|
+
before(:all) do
|
11
|
+
I18n.load_path << 'spec/en.yml'
|
12
|
+
I18n.default_locale = :en
|
13
|
+
I18n.reload!
|
25
14
|
end
|
26
15
|
|
27
|
-
|
28
|
-
|
16
|
+
after(:all) do
|
17
|
+
I18n.load_path.clear
|
29
18
|
end
|
30
|
-
end
|
31
19
|
|
32
|
-
|
33
|
-
|
34
|
-
expect(LocalizerTestModel.aasm.human_event_name(:close)).to eq("Let's close it!")
|
35
|
-
end
|
20
|
+
let (:foo_opened) { LocalizerTestModel.new }
|
21
|
+
let (:foo_closed) { LocalizerTestModel.new.tap { |x| x.aasm_state = :closed } }
|
36
22
|
|
37
|
-
|
38
|
-
|
23
|
+
context 'aasm.human_state' do
|
24
|
+
it 'should return translated state value' do
|
25
|
+
expect(foo_opened.aasm.human_state).to eq("It's open now!")
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should return humanized value if not localized' do
|
29
|
+
expect(foo_closed.aasm.human_state).to eq("Closed")
|
30
|
+
end
|
39
31
|
end
|
40
|
-
end
|
41
|
-
end
|
42
32
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
I18n.reload!
|
48
|
-
end
|
33
|
+
context 'aasm.human_event_name' do
|
34
|
+
it 'should return translated event name' do
|
35
|
+
expect(LocalizerTestModel.aasm.human_event_name(:close)).to eq("Let's close it!")
|
36
|
+
end
|
49
37
|
|
50
|
-
|
51
|
-
|
38
|
+
it 'should return humanized event name' do
|
39
|
+
expect(LocalizerTestModel.aasm.human_event_name(:open)).to eq("Open")
|
40
|
+
end
|
41
|
+
end
|
52
42
|
end
|
53
43
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
expect(foo_opened.aasm.human_state).to eq("It's open now!")
|
44
|
+
describe AASM::Localizer, "deprecated style" do
|
45
|
+
before(:all) do
|
46
|
+
I18n.load_path << 'spec/en_deprecated_style.yml'
|
47
|
+
I18n.default_locale = :en
|
48
|
+
I18n.reload!
|
60
49
|
end
|
61
50
|
|
62
|
-
|
63
|
-
|
51
|
+
after(:all) do
|
52
|
+
I18n.load_path.clear
|
64
53
|
end
|
65
|
-
end
|
66
54
|
|
67
|
-
|
68
|
-
|
69
|
-
|
55
|
+
let (:foo_opened) { LocalizerTestModel.new }
|
56
|
+
let (:foo_closed) { LocalizerTestModel.new.tap { |x| x.aasm_state = :closed } }
|
57
|
+
|
58
|
+
context 'aasm.human_state' do
|
59
|
+
it 'should return translated state value' do
|
60
|
+
expect(foo_opened.aasm.human_state).to eq("It's open now!")
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should return humanized value if not localized' do
|
64
|
+
expect(foo_closed.aasm.human_state).to eq("Closed")
|
65
|
+
end
|
70
66
|
end
|
71
67
|
|
72
|
-
|
73
|
-
|
68
|
+
context 'aasm.human_event_name' do
|
69
|
+
it 'should return translated event name' do
|
70
|
+
expect(LocalizerTestModel.aasm.human_event_name(:close)).to eq("Let's close it!")
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should return humanized event name' do
|
74
|
+
expect(LocalizerTestModel.aasm.human_event_name(:open)).to eq("Open")
|
75
|
+
end
|
74
76
|
end
|
75
77
|
end
|
76
78
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "multiple transitions that differ only by guard" do
|
4
|
+
let(:job) { MultipleTransitionsThatDifferOnlyByGuard.new }
|
5
|
+
|
6
|
+
it 'does not follow the first transition if its guard fails' do
|
7
|
+
expect{job.go}.not_to raise_error
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'executes the second transition\'s callbacks' do
|
11
|
+
job.go
|
12
|
+
expect(job.executed_second).to be_truthy
|
13
|
+
end
|
14
|
+
end
|
@@ -1,6 +1,14 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'warns when overrides a method' do
|
4
|
+
before do
|
5
|
+
AASM::Configuration.hide_warnings = false
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
AASM::Configuration.hide_warnings = true
|
10
|
+
end
|
11
|
+
|
4
12
|
module Clumsy
|
5
13
|
def self.included base
|
6
14
|
base.send :include, AASM
|
@@ -1,614 +1,618 @@
|
|
1
|
-
require 'active_record'
|
2
1
|
require 'spec_helper'
|
3
|
-
Dir[File.dirname(__FILE__) + "/../../models/active_record/*.rb"].sort.each do |f|
|
4
|
-
require File.expand_path(f)
|
5
|
-
end
|
6
|
-
|
7
|
-
load_schema
|
8
|
-
|
9
|
-
# if you want to see the statements while running the spec enable the following line
|
10
|
-
# require 'logger'
|
11
|
-
# ActiveRecord::Base.logger = Logger.new(STDERR)
|
12
2
|
|
13
|
-
|
14
|
-
let(:gate) {MultipleGate.new}
|
3
|
+
if defined?(ActiveRecord)
|
15
4
|
|
16
|
-
|
17
|
-
|
18
|
-
expect(gate).to respond_to(:aasm_write_state)
|
19
|
-
expect(gate).to respond_to(:aasm_write_state_without_persistence)
|
5
|
+
Dir[File.dirname(__FILE__) + "/../../models/active_record/*.rb"].sort.each do |f|
|
6
|
+
require File.expand_path(f)
|
20
7
|
end
|
21
8
|
|
22
|
-
|
23
|
-
subject { lambda{ gate.send(:aasm_column_looks_like_enum, :left) } }
|
9
|
+
load_schema
|
24
10
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
before :each do
|
29
|
-
allow(gate.class.aasm(:left)).to receive(:attribute_name).and_return(column_name.to_sym)
|
30
|
-
allow(gate.class).to receive(:columns_hash).and_return(columns_hash)
|
31
|
-
end
|
11
|
+
# if you want to see the statements while running the spec enable the following line
|
12
|
+
# require 'logger'
|
13
|
+
# ActiveRecord::Base.logger = Logger.new(STDERR)
|
32
14
|
|
33
|
-
|
34
|
-
|
15
|
+
describe "instance methods" do
|
16
|
+
let(:gate) {MultipleGate.new}
|
35
17
|
|
36
|
-
|
37
|
-
|
38
|
-
|
18
|
+
it "should respond to aasm persistence methods" do
|
19
|
+
expect(gate).to respond_to(:aasm_read_state)
|
20
|
+
expect(gate).to respond_to(:aasm_write_state)
|
21
|
+
expect(gate).to respond_to(:aasm_write_state_without_persistence)
|
39
22
|
end
|
40
23
|
|
41
|
-
|
42
|
-
|
24
|
+
describe "aasm_column_looks_like_enum" do
|
25
|
+
subject { lambda{ gate.send(:aasm_column_looks_like_enum, :left) } }
|
43
26
|
|
44
|
-
|
45
|
-
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
27
|
+
let(:column_name) { "value" }
|
28
|
+
let(:columns_hash) { Hash[column_name, column] }
|
49
29
|
|
50
|
-
|
51
|
-
|
30
|
+
before :each do
|
31
|
+
allow(gate.class.aasm(:left)).to receive(:attribute_name).and_return(column_name.to_sym)
|
32
|
+
allow(gate.class).to receive(:columns_hash).and_return(columns_hash)
|
33
|
+
end
|
52
34
|
|
53
|
-
|
54
|
-
|
55
|
-
end
|
35
|
+
context "when AASM column has integer type" do
|
36
|
+
let(:column) { double(Object, type: :integer) }
|
56
37
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
38
|
+
it "returns true" do
|
39
|
+
expect(subject.call).to be_truthy
|
40
|
+
end
|
41
|
+
end
|
61
42
|
|
62
|
-
|
63
|
-
|
64
|
-
let(:with_enum) { MultipleWithEnum.new }
|
43
|
+
context "when AASM column has string type" do
|
44
|
+
let(:column) { double(Object, type: :string) }
|
65
45
|
|
66
|
-
|
67
|
-
|
46
|
+
it "returns false" do
|
47
|
+
expect(subject.call).to be_falsey
|
48
|
+
end
|
68
49
|
end
|
69
50
|
end
|
70
51
|
|
71
|
-
|
72
|
-
|
52
|
+
describe "aasm_guess_enum_method" do
|
53
|
+
subject { lambda{ gate.send(:aasm_guess_enum_method, :left) } }
|
54
|
+
|
73
55
|
before :each do
|
74
|
-
allow(
|
56
|
+
allow(gate.class.aasm(:left)).to receive(:attribute_name).and_return(:value)
|
75
57
|
end
|
76
58
|
|
77
|
-
it "
|
78
|
-
expect(
|
59
|
+
it "pluralizes AASM column name" do
|
60
|
+
expect(subject.call).to eq :values
|
79
61
|
end
|
80
62
|
end
|
81
63
|
|
82
|
-
|
83
|
-
|
64
|
+
describe "aasm_enum" do
|
65
|
+
context "when AASM enum setting contains an explicit enum method name" do
|
66
|
+
let(:with_enum) { MultipleWithEnum.new }
|
84
67
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
context "when AASM enum setting is not enabled" do
|
91
|
-
before :each do
|
92
|
-
allow(MultipleGate.aasm(:left)).to receive(:attribute_name).and_return(:value)
|
68
|
+
it "returns whatever value was set in AASM config" do
|
69
|
+
expect(with_enum.send(:aasm_enum, :left)).to eq :test
|
70
|
+
end
|
93
71
|
end
|
94
72
|
|
95
|
-
context "when AASM
|
73
|
+
context "when AASM enum setting is simply set to true" do
|
74
|
+
let(:with_true_enum) { MultipleWithTrueEnum.new }
|
96
75
|
before :each do
|
97
|
-
allow(
|
76
|
+
allow(MultipleWithTrueEnum.aasm(:left)).to receive(:attribute_name).and_return(:value)
|
98
77
|
end
|
99
78
|
|
100
79
|
it "infers enum method name from pluralized column name" do
|
101
|
-
expect(
|
80
|
+
expect(with_true_enum.send(:aasm_enum, :left)).to eq :values
|
102
81
|
end
|
103
82
|
end
|
104
83
|
|
105
|
-
context "when AASM
|
84
|
+
context "when AASM enum setting is explicitly disabled" do
|
85
|
+
let(:with_false_enum) { MultipleWithFalseEnum.new }
|
86
|
+
|
87
|
+
it "returns nil" do
|
88
|
+
expect(with_false_enum.send(:aasm_enum, :left)).to be_nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "when AASM enum setting is not enabled" do
|
106
93
|
before :each do
|
107
|
-
allow(
|
108
|
-
|
94
|
+
allow(MultipleGate.aasm(:left)).to receive(:attribute_name).and_return(:value)
|
95
|
+
end
|
96
|
+
|
97
|
+
context "when AASM column looks like enum" do
|
98
|
+
before :each do
|
99
|
+
allow(gate).to receive(:aasm_column_looks_like_enum).with(:left).and_return(true)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "infers enum method name from pluralized column name" do
|
103
|
+
expect(gate.send(:aasm_enum, :left)).to eq :values
|
104
|
+
end
|
109
105
|
end
|
110
106
|
|
111
|
-
|
112
|
-
|
107
|
+
context "when AASM column doesn't look like enum'" do
|
108
|
+
before :each do
|
109
|
+
allow(gate).to receive(:aasm_column_looks_like_enum)
|
110
|
+
.and_return(false)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "returns nil, as we're not using enum" do
|
114
|
+
expect(gate.send(:aasm_enum, :left)).to be_nil
|
115
|
+
end
|
113
116
|
end
|
114
117
|
end
|
115
|
-
end
|
116
118
|
|
117
|
-
|
118
|
-
|
119
|
-
|
119
|
+
if ActiveRecord::VERSION::MAJOR >= 4 && ActiveRecord::VERSION::MINOR >= 1 # won't work with Rails <= 4.1
|
120
|
+
# Enum are introduced from Rails 4.1, therefore enum syntax will not work on Rails <= 4.1
|
121
|
+
context "when AASM enum setting is not enabled and aasm column not present" do
|
120
122
|
|
121
|
-
|
123
|
+
let(:multiple_with_enum_without_column) {MultipleWithEnumWithoutColumn.new}
|
122
124
|
|
123
|
-
|
124
|
-
|
125
|
+
it "should raise NoMethodError for transitions" do
|
126
|
+
expect{multiple_with_enum_without_column.send(:view, :left)}.to raise_error(NoMethodError, "undefined method 'status' for MultipleWithEnumWithoutColumn")
|
127
|
+
end
|
125
128
|
end
|
129
|
+
|
126
130
|
end
|
127
131
|
|
128
132
|
end
|
129
133
|
|
130
|
-
|
134
|
+
context "when AASM is configured to use enum" do
|
135
|
+
let(:state_sym) { :running }
|
136
|
+
let(:state_code) { 2 }
|
137
|
+
let(:enum_name) { :states }
|
138
|
+
let(:enum) { Hash[state_sym, state_code] }
|
131
139
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
let(:enum) { Hash[state_sym, state_code] }
|
137
|
-
|
138
|
-
before :each do
|
139
|
-
allow(gate).to receive(:aasm_enum).and_return(enum_name)
|
140
|
-
allow(gate).to receive(:aasm_write_attribute)
|
141
|
-
allow(gate).to receive(:write_attribute)
|
140
|
+
before :each do
|
141
|
+
allow(gate).to receive(:aasm_enum).and_return(enum_name)
|
142
|
+
allow(gate).to receive(:aasm_write_attribute)
|
143
|
+
allow(gate).to receive(:write_attribute)
|
142
144
|
|
143
|
-
|
144
|
-
|
145
|
+
allow(MultipleGate).to receive(enum_name).and_return(enum)
|
146
|
+
end
|
145
147
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
148
|
+
describe "aasm_write_state" do
|
149
|
+
context "when AASM is configured to skip validations on save" do
|
150
|
+
before :each do
|
151
|
+
allow(gate).to receive(:aasm_skipping_validations).and_return(true)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "passes state code instead of state symbol to update_all" do
|
155
|
+
# stub_chain does not allow us to give expectations on call
|
156
|
+
# parameters in the middle of the chain, so we need to use
|
157
|
+
# intermediate object instead.
|
158
|
+
obj = double(Object, update_all: 1)
|
159
|
+
allow(MultipleGate).to receive_message_chain(:unscoped, :where)
|
160
|
+
.and_return(obj)
|
161
|
+
|
162
|
+
gate.aasm_write_state state_sym, :left
|
163
|
+
|
164
|
+
expect(obj).to have_received(:update_all)
|
165
|
+
.with(Hash[gate.class.aasm(:left).attribute_name, state_code])
|
166
|
+
end
|
150
167
|
end
|
151
168
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
obj = double(Object, update_all: 1)
|
157
|
-
allow(MultipleGate).to receive(:where).and_return(obj)
|
169
|
+
context "when AASM is not skipping validations" do
|
170
|
+
it "delegates state update to the helper method" do
|
171
|
+
# Let's pretend that validation is passed
|
172
|
+
allow(gate).to receive(:save).and_return(true)
|
158
173
|
|
159
|
-
|
174
|
+
gate.aasm_write_state state_sym, :left
|
160
175
|
|
161
|
-
|
162
|
-
|
176
|
+
expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :left)
|
177
|
+
expect(gate).to_not have_received :write_attribute
|
178
|
+
end
|
163
179
|
end
|
164
180
|
end
|
165
181
|
|
166
|
-
|
182
|
+
describe "aasm_write_state_without_persistence" do
|
167
183
|
it "delegates state update to the helper method" do
|
168
|
-
|
169
|
-
allow(gate).to receive(:save).and_return(true)
|
170
|
-
|
171
|
-
gate.aasm_write_state state_sym, :left
|
184
|
+
gate.aasm_write_state_without_persistence state_sym, :left
|
172
185
|
|
173
186
|
expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :left)
|
174
187
|
expect(gate).to_not have_received :write_attribute
|
175
188
|
end
|
176
189
|
end
|
190
|
+
|
191
|
+
describe "aasm_raw_attribute_value" do
|
192
|
+
it "converts state symbol to state code" do
|
193
|
+
expect(gate.send(:aasm_raw_attribute_value, state_sym))
|
194
|
+
.to eq state_code
|
195
|
+
end
|
196
|
+
end
|
177
197
|
end
|
178
198
|
|
179
|
-
|
180
|
-
|
181
|
-
gate.aasm_write_state_without_persistence state_sym, :left
|
199
|
+
context "when AASM is configured to use string field" do
|
200
|
+
let(:state_sym) { :running }
|
182
201
|
|
183
|
-
|
184
|
-
|
202
|
+
before :each do
|
203
|
+
allow(gate).to receive(:aasm_enum).and_return(nil)
|
185
204
|
end
|
186
|
-
end
|
187
205
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
206
|
+
describe "aasm_raw_attribute_value" do
|
207
|
+
it "converts state symbol to string" do
|
208
|
+
expect(gate.send(:aasm_raw_attribute_value, state_sym))
|
209
|
+
.to eq state_sym.to_s
|
210
|
+
end
|
192
211
|
end
|
193
212
|
end
|
194
|
-
end
|
195
213
|
|
196
|
-
|
197
|
-
|
214
|
+
describe "aasm_write_attribute helper method" do
|
215
|
+
let(:sym) { :sym }
|
216
|
+
let(:value) { 42 }
|
198
217
|
|
199
|
-
|
200
|
-
|
201
|
-
|
218
|
+
before :each do
|
219
|
+
allow(gate).to receive(:write_attribute)
|
220
|
+
allow(gate).to receive(:aasm_raw_attribute_value).and_return(value)
|
202
221
|
|
203
|
-
|
204
|
-
it "converts state symbol to string" do
|
205
|
-
expect(gate.send(:aasm_raw_attribute_value, state_sym))
|
206
|
-
.to eq state_sym.to_s
|
222
|
+
gate.send(:aasm_write_attribute, sym, :left)
|
207
223
|
end
|
208
|
-
end
|
209
|
-
end
|
210
224
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
before :each do
|
216
|
-
allow(gate).to receive(:write_attribute)
|
217
|
-
allow(gate).to receive(:aasm_raw_attribute_value).and_return(value)
|
225
|
+
it "generates attribute value using a helper method" do
|
226
|
+
expect(gate).to have_received(:aasm_raw_attribute_value).with(sym, :left)
|
227
|
+
end
|
218
228
|
|
219
|
-
|
229
|
+
it "writes attribute to the model" do
|
230
|
+
expect(gate).to have_received(:write_attribute).with(:aasm_state, value)
|
231
|
+
end
|
220
232
|
end
|
221
233
|
|
222
|
-
it "
|
223
|
-
expect(gate
|
234
|
+
it "should return the initial state when new and the aasm field is nil" do
|
235
|
+
expect(gate.aasm(:left).current_state).to eq(:opened)
|
224
236
|
end
|
225
237
|
|
226
|
-
it "
|
227
|
-
|
238
|
+
it "should return the aasm column when new and the aasm field is not nil" do
|
239
|
+
gate.aasm_state = "closed"
|
240
|
+
expect(gate.aasm(:left).current_state).to eq(:closed)
|
228
241
|
end
|
229
|
-
end
|
230
|
-
|
231
|
-
it "should return the initial state when new and the aasm field is nil" do
|
232
|
-
expect(gate.aasm(:left).current_state).to eq(:opened)
|
233
|
-
end
|
234
|
-
|
235
|
-
it "should return the aasm column when new and the aasm field is not nil" do
|
236
|
-
gate.aasm_state = "closed"
|
237
|
-
expect(gate.aasm(:left).current_state).to eq(:closed)
|
238
|
-
end
|
239
242
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
end
|
245
|
-
|
246
|
-
it "should allow a nil state" do
|
247
|
-
allow(gate).to receive(:new_record?).and_return(false)
|
248
|
-
gate.aasm_state = nil
|
249
|
-
expect(gate.aasm(:left).current_state).to be_nil
|
250
|
-
end
|
251
|
-
|
252
|
-
context 'on initialization' do
|
253
|
-
it "should initialize the aasm state" do
|
254
|
-
expect(MultipleGate.new.aasm_state).to eql 'opened'
|
255
|
-
expect(MultipleGate.new.aasm(:left).current_state).to eql :opened
|
243
|
+
it "should return the aasm column when not new and the aasm.attribute_name is not nil" do
|
244
|
+
allow(gate).to receive(:new_record?).and_return(false)
|
245
|
+
gate.aasm_state = "state"
|
246
|
+
expect(gate.aasm(:left).current_state).to eq(:state)
|
256
247
|
end
|
257
248
|
|
258
|
-
it "should
|
259
|
-
|
260
|
-
|
261
|
-
gate
|
262
|
-
|
263
|
-
# then we just load the gate ids
|
264
|
-
MultipleGate.select(:id).where(id: gate.id).first
|
249
|
+
it "should allow a nil state" do
|
250
|
+
allow(gate).to receive(:new_record?).and_return(false)
|
251
|
+
gate.aasm_state = nil
|
252
|
+
expect(gate.aasm(:left).current_state).to be_nil
|
265
253
|
end
|
266
|
-
end
|
267
254
|
|
268
|
-
|
255
|
+
context 'on initialization' do
|
256
|
+
it "should initialize the aasm state" do
|
257
|
+
expect(MultipleGate.new.aasm_state).to eql 'opened'
|
258
|
+
expect(MultipleGate.new.aasm(:left).current_state).to eql :opened
|
259
|
+
end
|
269
260
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
expect(f.aasm_state).to eql false
|
275
|
-
expect {
|
276
|
-
f.aasm(:left).events.map(&:name)
|
277
|
-
}.to_not raise_error
|
278
|
-
end
|
279
|
-
end
|
280
|
-
end
|
261
|
+
it "should not initialize the aasm state if it has not been loaded" do
|
262
|
+
# we have to create a gate in the database, for which we only want to
|
263
|
+
# load the id, and not the state
|
264
|
+
gate = MultipleGate.create!
|
281
265
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
266
|
+
# then we just load the gate ids
|
267
|
+
MultipleGate.select(:id).where(id: gate.id).first
|
268
|
+
end
|
269
|
+
end
|
286
270
|
|
287
|
-
it "should have the same events as its parent class" do
|
288
|
-
expect(MultipleDerivateNewDsl.aasm(:left).events).to eq(MultipleSimpleNewDsl.aasm(:left).events)
|
289
271
|
end
|
290
272
|
|
291
|
-
|
292
|
-
|
293
|
-
|
273
|
+
if ActiveRecord::VERSION::MAJOR < 4 && ActiveRecord::VERSION::MINOR < 2 # won't work with Rails >= 4.2
|
274
|
+
describe "direct state column access" do
|
275
|
+
it "accepts false states" do
|
276
|
+
f = MultipleFalseState.create!
|
277
|
+
expect(f.aasm_state).to eql false
|
278
|
+
expect {
|
279
|
+
f.aasm(:left).events.map(&:name)
|
280
|
+
}.to_not raise_error
|
281
|
+
end
|
282
|
+
end
|
294
283
|
end
|
295
|
-
end
|
296
284
|
|
297
|
-
describe
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
expect(MultipleSimpleNewDsl).to respond_to(:another_unknown_scope)
|
285
|
+
describe 'subclasses' do
|
286
|
+
it "should have the same states as its parent class" do
|
287
|
+
expect(MultipleDerivateNewDsl.aasm(:left).states).to eq(MultipleSimpleNewDsl.aasm(:left).states)
|
288
|
+
end
|
302
289
|
|
303
|
-
|
304
|
-
expect(
|
290
|
+
it "should have the same events as its parent class" do
|
291
|
+
expect(MultipleDerivateNewDsl.aasm(:left).events).to eq(MultipleSimpleNewDsl.aasm(:left).events)
|
305
292
|
end
|
306
|
-
end
|
307
293
|
|
308
|
-
|
309
|
-
|
310
|
-
expect(
|
311
|
-
expect(MultipleSimpleNewDsl.new.class).to eq(MultipleSimpleNewDsl)
|
294
|
+
it "should have the same column as its parent even for the new dsl" do
|
295
|
+
expect(MultipleSimpleNewDsl.aasm(:left).attribute_name).to eq(:status)
|
296
|
+
expect(MultipleDerivateNewDsl.aasm(:left).attribute_name).to eq(:status)
|
312
297
|
end
|
313
298
|
end
|
314
299
|
|
315
|
-
|
316
|
-
|
317
|
-
|
300
|
+
describe "named scopes with the new DSL" do
|
301
|
+
context "Does not already respond_to? the scope name" do
|
302
|
+
it "should add a scope for each state" do
|
303
|
+
expect(MultipleSimpleNewDsl).to respond_to(:unknown_scope)
|
304
|
+
expect(MultipleSimpleNewDsl).to respond_to(:another_unknown_scope)
|
318
305
|
|
319
|
-
|
320
|
-
|
321
|
-
|
306
|
+
expect(MultipleSimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
|
307
|
+
expect(MultipleSimpleNewDsl.another_unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
|
308
|
+
end
|
309
|
+
end
|
322
310
|
|
323
|
-
|
324
|
-
|
311
|
+
context "Already respond_to? the scope name" do
|
312
|
+
it "should not add a scope" do
|
313
|
+
expect(MultipleSimpleNewDsl).to respond_to(:new)
|
314
|
+
expect(MultipleSimpleNewDsl.new.class).to eq(MultipleSimpleNewDsl)
|
315
|
+
end
|
325
316
|
end
|
326
317
|
|
327
|
-
it "
|
328
|
-
expect(
|
318
|
+
it "does not create scopes if requested" do
|
319
|
+
expect(MultipleNoScope).not_to respond_to(:pending)
|
329
320
|
end
|
330
|
-
end
|
331
|
-
end # scopes
|
332
321
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
expect(obj.aasm_state.to_sym).to eql :pending
|
322
|
+
context "result of scope" do
|
323
|
+
let!(:dsl1) { MultipleSimpleNewDsl.create!(status: :new) }
|
324
|
+
let!(:dsl2) { MultipleSimpleNewDsl.create!(status: :unknown_scope) }
|
337
325
|
|
338
|
-
|
339
|
-
|
340
|
-
|
326
|
+
after do
|
327
|
+
MultipleSimpleNewDsl.destroy_all
|
328
|
+
end
|
341
329
|
|
342
|
-
|
343
|
-
|
344
|
-
|
330
|
+
it "created scope works as where(name: :scope_name)" do
|
331
|
+
expect(MultipleSimpleNewDsl.unknown_scope).to contain_exactly(dsl2)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end # scopes
|
345
335
|
|
346
|
-
|
347
|
-
|
348
|
-
|
336
|
+
describe "direct assignment" do
|
337
|
+
it "is allowed by default" do
|
338
|
+
obj = MultipleNoScope.create
|
339
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
349
340
|
|
350
|
-
|
351
|
-
|
352
|
-
|
341
|
+
obj.aasm_state = :running
|
342
|
+
expect(obj.aasm_state.to_sym).to eql :running
|
343
|
+
end
|
353
344
|
|
354
|
-
|
355
|
-
|
345
|
+
it "is forbidden if configured" do
|
346
|
+
obj = MultipleNoDirectAssignment.create
|
347
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
356
348
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
expect(obj.aasm_state.to_sym).to eql :pending
|
349
|
+
expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
|
350
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
351
|
+
end
|
361
352
|
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
expect(obj.aasm_state.to_sym).to eql :pending
|
366
|
-
end
|
367
|
-
end # direct assignment
|
353
|
+
it 'can be turned off and on again' do
|
354
|
+
obj = MultipleNoDirectAssignment.create
|
355
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
368
356
|
|
369
|
-
|
370
|
-
|
371
|
-
expect(MultipleThief.new(:skilled => true).aasm(:left).current_state).to eq(:rich)
|
372
|
-
expect(MultipleThief.new(:skilled => false).aasm(:left).current_state).to eq(:jailed)
|
373
|
-
end
|
374
|
-
end
|
357
|
+
expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
|
358
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
375
359
|
|
376
|
-
|
360
|
+
# allow it temporarily
|
361
|
+
MultipleNoDirectAssignment.aasm(:left).state_machine.config.no_direct_assignment = false
|
362
|
+
obj.aasm_state = :pending
|
363
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
377
364
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
365
|
+
# and forbid it again
|
366
|
+
MultipleNoDirectAssignment.aasm(:left).state_machine.config.no_direct_assignment = true
|
367
|
+
expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
|
368
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
369
|
+
end
|
370
|
+
end # direct assignment
|
371
|
+
|
372
|
+
describe 'initial states' do
|
373
|
+
it 'should support conditions' do
|
374
|
+
expect(MultipleThief.new(:skilled => true).aasm(:left).current_state).to eq(:rich)
|
375
|
+
expect(MultipleThief.new(:skilled => false).aasm(:left).current_state).to eq(:jailed)
|
376
|
+
end
|
383
377
|
end
|
384
378
|
|
385
|
-
|
386
|
-
validator = MultipleValidator.create(:name => 'name')
|
387
|
-
expect(validator).to be_valid
|
388
|
-
expect(validator).to be_sleeping
|
379
|
+
describe 'transitions with persistence' do
|
389
380
|
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
381
|
+
it "should work for valid models" do
|
382
|
+
valid_object = MultipleValidator.create(:name => 'name')
|
383
|
+
expect(valid_object).to be_sleeping
|
384
|
+
valid_object.status = :running
|
385
|
+
expect(valid_object).to be_running
|
386
|
+
end
|
394
387
|
|
395
|
-
|
396
|
-
|
397
|
-
|
388
|
+
it 'should not store states for invalid models' do
|
389
|
+
validator = MultipleValidator.create(:name => 'name')
|
390
|
+
expect(validator).to be_valid
|
391
|
+
expect(validator).to be_sleeping
|
398
392
|
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
393
|
+
validator.name = nil
|
394
|
+
expect(validator).not_to be_valid
|
395
|
+
expect { validator.run! }.to raise_error(ActiveRecord::RecordInvalid)
|
396
|
+
expect(validator).to be_sleeping
|
403
397
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
end
|
398
|
+
validator.reload
|
399
|
+
expect(validator).not_to be_running
|
400
|
+
expect(validator).to be_sleeping
|
408
401
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
402
|
+
validator.name = 'another name'
|
403
|
+
expect(validator).to be_valid
|
404
|
+
expect(validator.run!).to be_truthy
|
405
|
+
expect(validator).to be_running
|
413
406
|
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
407
|
+
validator.reload
|
408
|
+
expect(validator).to be_running
|
409
|
+
expect(validator).not_to be_sleeping
|
410
|
+
end
|
418
411
|
|
419
|
-
|
420
|
-
|
421
|
-
|
412
|
+
it 'should not store states for invalid models silently if configured' do
|
413
|
+
validator = MultipleSilentPersistor.create(:name => 'name')
|
414
|
+
expect(validator).to be_valid
|
415
|
+
expect(validator).to be_sleeping
|
422
416
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
417
|
+
validator.name = nil
|
418
|
+
expect(validator).not_to be_valid
|
419
|
+
expect(validator.run!).to be_falsey
|
420
|
+
expect(validator).to be_sleeping
|
427
421
|
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
end
|
422
|
+
validator.reload
|
423
|
+
expect(validator).not_to be_running
|
424
|
+
expect(validator).to be_sleeping
|
432
425
|
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
persistor.name = nil
|
439
|
-
expect(persistor).not_to be_valid
|
440
|
-
expect(persistor.run!).to be_truthy
|
441
|
-
expect(persistor).to be_running
|
442
|
-
|
443
|
-
persistor = MultipleInvalidPersistor.find(persistor.id)
|
444
|
-
persistor.valid?
|
445
|
-
expect(persistor).to be_valid
|
446
|
-
expect(persistor).to be_running
|
447
|
-
expect(persistor).not_to be_sleeping
|
448
|
-
|
449
|
-
persistor.reload
|
450
|
-
expect(persistor).to be_running
|
451
|
-
expect(persistor).not_to be_sleeping
|
452
|
-
end
|
426
|
+
validator.name = 'another name'
|
427
|
+
expect(validator).to be_valid
|
428
|
+
expect(validator.run!).to be_truthy
|
429
|
+
expect(validator).to be_running
|
453
430
|
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
it 'should rollback all changes' do
|
459
|
-
expect(transactor).to be_sleeping
|
460
|
-
expect(worker.status).to eq('sleeping')
|
431
|
+
validator.reload
|
432
|
+
expect(validator).to be_running
|
433
|
+
expect(validator).not_to be_sleeping
|
434
|
+
end
|
461
435
|
|
462
|
-
|
463
|
-
|
464
|
-
expect(
|
436
|
+
it 'should store states for invalid models if configured' do
|
437
|
+
persistor = MultipleInvalidPersistor.create(:name => 'name')
|
438
|
+
expect(persistor).to be_valid
|
439
|
+
expect(persistor).to be_sleeping
|
440
|
+
|
441
|
+
persistor.name = nil
|
442
|
+
expect(persistor).not_to be_valid
|
443
|
+
expect(persistor.run!).to be_truthy
|
444
|
+
expect(persistor).to be_running
|
445
|
+
|
446
|
+
persistor = MultipleInvalidPersistor.find(persistor.id)
|
447
|
+
persistor.valid?
|
448
|
+
expect(persistor).to be_valid
|
449
|
+
expect(persistor).to be_running
|
450
|
+
expect(persistor).not_to be_sleeping
|
451
|
+
|
452
|
+
persistor.reload
|
453
|
+
expect(persistor).to be_running
|
454
|
+
expect(persistor).not_to be_sleeping
|
465
455
|
end
|
466
456
|
|
467
|
-
|
468
|
-
|
457
|
+
describe 'transactions' do
|
458
|
+
let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
|
459
|
+
let(:transactor) { MultipleTransactor.create!(:name => 'transactor', :worker => worker) }
|
460
|
+
|
461
|
+
it 'should rollback all changes' do
|
469
462
|
expect(transactor).to be_sleeping
|
470
463
|
expect(worker.status).to eq('sleeping')
|
471
464
|
|
472
|
-
|
473
|
-
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
|
474
|
-
end
|
475
|
-
|
465
|
+
expect {transactor.run!}.to raise_error(StandardError, 'failed on purpose')
|
476
466
|
expect(transactor).to be_running
|
477
467
|
expect(worker.reload.status).to eq('sleeping')
|
478
468
|
end
|
479
469
|
|
480
|
-
|
481
|
-
|
482
|
-
|
470
|
+
context "nested transactions" do
|
471
|
+
it "should rollback all changes in nested transaction" do
|
472
|
+
expect(transactor).to be_sleeping
|
473
|
+
expect(worker.status).to eq('sleeping')
|
483
474
|
|
484
|
-
|
485
|
-
|
475
|
+
Worker.transaction do
|
476
|
+
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
|
477
|
+
end
|
486
478
|
|
487
|
-
|
488
|
-
expect
|
479
|
+
expect(transactor).to be_running
|
480
|
+
expect(worker.reload.status).to eq('sleeping')
|
489
481
|
end
|
490
482
|
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
end
|
483
|
+
it "should only rollback changes in the main transaction not the nested one" do
|
484
|
+
# change configuration to not require new transaction
|
485
|
+
AASM::StateMachineStore[MultipleTransactor][:left].config.requires_new_transaction = false
|
495
486
|
|
496
|
-
|
497
|
-
|
498
|
-
validator = MultipleValidator.create(:name => 'name')
|
499
|
-
expect(validator).to be_sleeping
|
487
|
+
expect(transactor).to be_sleeping
|
488
|
+
expect(worker.status).to eq('sleeping')
|
500
489
|
|
501
|
-
|
502
|
-
|
503
|
-
|
490
|
+
Worker.transaction do
|
491
|
+
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
|
492
|
+
end
|
504
493
|
|
505
|
-
|
506
|
-
|
507
|
-
|
494
|
+
expect(transactor).to be_running
|
495
|
+
expect(worker.reload.status).to eq('running')
|
496
|
+
end
|
508
497
|
end
|
509
498
|
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
end
|
499
|
+
describe "after_commit callback" do
|
500
|
+
it "should fire :after_commit if transaction was successful" do
|
501
|
+
validator = MultipleValidator.create(:name => 'name')
|
502
|
+
expect(validator).to be_sleeping
|
515
503
|
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
validator.run
|
520
|
-
expect(validator).to be_running
|
521
|
-
expect(validator.name).to eq("name")
|
522
|
-
end
|
504
|
+
validator.run!
|
505
|
+
expect(validator).to be_running
|
506
|
+
expect(validator.name).to eq("name changed")
|
523
507
|
|
524
|
-
|
508
|
+
validator.sleep!("sleeper")
|
509
|
+
expect(validator).to be_sleeping
|
510
|
+
expect(validator.name).to eq("sleeper")
|
511
|
+
end
|
525
512
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
513
|
+
it "should not fire :after_commit if transaction failed" do
|
514
|
+
validator = MultipleValidator.create(:name => 'name')
|
515
|
+
expect { validator.fail! }.to raise_error(StandardError, 'failed on purpose')
|
516
|
+
expect(validator.name).to eq("name")
|
517
|
+
end
|
518
|
+
|
519
|
+
it "should not fire if not saving" do
|
520
|
+
validator = MultipleValidator.create(:name => 'name')
|
521
|
+
expect(validator).to be_sleeping
|
522
|
+
validator.run
|
523
|
+
expect(validator).to be_running
|
524
|
+
expect(validator.name).to eq("name")
|
525
|
+
end
|
530
526
|
|
531
|
-
# Notice here we're calling "run" and not "run!" with a bang.
|
532
|
-
expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
|
533
|
-
expect(transactor).to be_running
|
534
|
-
expect(worker.reload.status).to eq('running')
|
535
527
|
end
|
536
528
|
|
537
|
-
|
538
|
-
|
539
|
-
|
529
|
+
context "when not persisting" do
|
530
|
+
it 'should not rollback all changes' do
|
531
|
+
expect(transactor).to be_sleeping
|
532
|
+
expect(worker.status).to eq('sleeping')
|
533
|
+
|
534
|
+
# Notice here we're calling "run" and not "run!" with a bang.
|
535
|
+
expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
|
536
|
+
expect(transactor).to be_running
|
537
|
+
expect(worker.reload.status).to eq('running')
|
538
|
+
end
|
539
|
+
|
540
|
+
it 'should not create a database transaction' do
|
541
|
+
expect(transactor.class).not_to receive(:transaction)
|
542
|
+
expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
|
543
|
+
end
|
540
544
|
end
|
541
545
|
end
|
542
546
|
end
|
543
|
-
end
|
544
547
|
|
545
|
-
describe "invalid states with persistence" do
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
548
|
+
describe "invalid states with persistence" do
|
549
|
+
it "should not store states" do
|
550
|
+
validator = MultipleValidator.create(:name => 'name')
|
551
|
+
validator.status = 'invalid_state'
|
552
|
+
expect(validator.save).to be_falsey
|
553
|
+
expect {validator.save!}.to raise_error(ActiveRecord::RecordInvalid)
|
551
554
|
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
+
validator.reload
|
556
|
+
expect(validator).to be_sleeping
|
557
|
+
end
|
555
558
|
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
559
|
+
it "should store invalid states if configured" do
|
560
|
+
persistor = MultipleInvalidPersistor.create(:name => 'name')
|
561
|
+
persistor.status = 'invalid_state'
|
562
|
+
expect(persistor.save).to be_truthy
|
560
563
|
|
561
|
-
|
562
|
-
|
564
|
+
persistor.reload
|
565
|
+
expect(persistor.status).to eq('invalid_state')
|
566
|
+
end
|
563
567
|
end
|
564
|
-
end
|
565
568
|
|
566
|
-
describe "complex example" do
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
569
|
+
describe "complex example" do
|
570
|
+
it "works" do
|
571
|
+
record = ComplexActiveRecordExample.new
|
572
|
+
expect_aasm_states record, :one, :alpha
|
573
|
+
|
574
|
+
record.save!
|
575
|
+
expect_aasm_states record, :one, :alpha
|
576
|
+
record.reload
|
577
|
+
expect_aasm_states record, :one, :alpha
|
578
|
+
|
579
|
+
record.increment!
|
580
|
+
expect_aasm_states record, :two, :alpha
|
581
|
+
record.reload
|
582
|
+
expect_aasm_states record, :two, :alpha
|
583
|
+
|
584
|
+
record.level_up!
|
585
|
+
expect_aasm_states record, :two, :beta
|
586
|
+
record.reload
|
587
|
+
expect_aasm_states record, :two, :beta
|
588
|
+
|
589
|
+
record.increment!
|
590
|
+
expect { record.increment! }.to raise_error(AASM::InvalidTransition)
|
591
|
+
expect_aasm_states record, :three, :beta
|
592
|
+
record.reload
|
593
|
+
expect_aasm_states record, :three, :beta
|
594
|
+
|
595
|
+
record.level_up!
|
596
|
+
expect_aasm_states record, :three, :gamma
|
597
|
+
record.reload
|
598
|
+
expect_aasm_states record, :three, :gamma
|
599
|
+
|
600
|
+
record.level_down # without saving
|
601
|
+
expect_aasm_states record, :three, :beta
|
602
|
+
record.reload
|
603
|
+
expect_aasm_states record, :three, :gamma
|
604
|
+
|
605
|
+
record.level_down # without saving
|
606
|
+
expect_aasm_states record, :three, :beta
|
607
|
+
record.reset!
|
608
|
+
expect_aasm_states record, :one, :beta
|
609
|
+
end
|
607
610
|
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
611
|
+
def expect_aasm_states(record, left_state, right_state)
|
612
|
+
expect(record.aasm(:left).current_state).to eql left_state.to_sym
|
613
|
+
expect(record.left).to eql left_state.to_s
|
614
|
+
expect(record.aasm(:right).current_state).to eql right_state.to_sym
|
615
|
+
expect(record.right).to eql right_state.to_s
|
616
|
+
end
|
613
617
|
end
|
614
618
|
end
|