aasm 3.0.24 → 3.1.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/.gitignore +3 -0
- data/.travis.yml +23 -4
- data/CHANGELOG.md +20 -0
- data/Gemfile +9 -1
- data/LICENSE +1 -1
- data/README.md +65 -12
- data/aasm.gemspec +5 -6
- data/gemfiles/rails_3.2.gemfile +12 -0
- data/gemfiles/rails_4.0.gemfile +11 -0
- data/gemfiles/rails_4.1.gemfile +11 -0
- data/lib/aasm/aasm.rb +31 -27
- data/lib/aasm/base.rb +35 -20
- data/lib/aasm/event.rb +27 -16
- data/lib/aasm/instance_base.rb +3 -1
- data/lib/aasm/persistence/active_record_persistence.rb +27 -9
- data/lib/aasm/persistence/base.rb +1 -1
- data/lib/aasm/persistence/mongoid_persistence.rb +10 -8
- data/lib/aasm/state.rb +1 -0
- data/lib/aasm/transition.rb +13 -6
- data/lib/aasm/version.rb +1 -1
- data/lib/aasm.rb +0 -3
- data/spec/database.rb +33 -0
- data/spec/models/guardian.rb +48 -0
- data/spec/models/mongoid/no_scope_mongoid.rb +1 -1
- data/spec/models/mongoid/simple_mongoid.rb +5 -4
- data/spec/models/mongoid/simple_new_dsl_mongoid.rb +1 -1
- data/spec/models/not_auto_loaded/process.rb +10 -8
- data/spec/models/persistence.rb +5 -13
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/api_spec.rb +12 -12
- data/spec/unit/callbacks_spec.rb +29 -45
- data/spec/unit/complex_example_spec.rb +15 -15
- data/spec/unit/event_spec.rb +89 -76
- data/spec/unit/guard_spec.rb +60 -0
- data/spec/unit/initial_state_spec.rb +4 -5
- data/spec/unit/inspection_spec.rb +40 -53
- data/spec/unit/localizer_spec.rb +22 -18
- data/spec/unit/new_dsl_spec.rb +2 -2
- data/spec/unit/persistence/active_record_persistence_spec.rb +111 -89
- data/spec/unit/persistence/mongoid_persistance_spec.rb +102 -81
- data/spec/unit/simple_example_spec.rb +20 -21
- data/spec/unit/state_spec.rb +16 -16
- data/spec/unit/subclassing_spec.rb +8 -8
- data/spec/unit/transition_spec.rb +59 -44
- metadata +28 -94
- data/lib/aasm/deprecated/aasm.rb +0 -15
- data/spec/models/callback_old_dsl.rb +0 -41
- data/spec/schema.rb +0 -35
data/spec/unit/localizer_spec.rb
CHANGED
|
@@ -10,12 +10,12 @@ class LocalizerTestModel < ActiveRecord::Base
|
|
|
10
10
|
|
|
11
11
|
attr_accessor :aasm_state
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
aasm do
|
|
14
|
+
state :opened, :initial => true
|
|
15
|
+
state :closed
|
|
16
|
+
event :close
|
|
17
|
+
event :open
|
|
18
|
+
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
describe 'localized state names' do
|
|
@@ -30,11 +30,15 @@ describe 'localized state names' do
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
it 'should localize' do
|
|
33
|
-
LocalizerTestModel.aasm.states.detect {|s| s == :opened}
|
|
33
|
+
state = LocalizerTestModel.aasm.states.detect {|s| s == :opened}
|
|
34
|
+
expect(state.localized_name).to eq("It's open now!")
|
|
35
|
+
expect(state.human_name).to eq("It's open now!")
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
it 'should use fallback' do
|
|
37
|
-
LocalizerTestModel.aasm.states.detect {|s| s == :closed}
|
|
39
|
+
state = LocalizerTestModel.aasm.states.detect {|s| s == :closed}
|
|
40
|
+
expect(state.localized_name).to eq('Closed')
|
|
41
|
+
expect(state.human_name).to eq('Closed')
|
|
38
42
|
end
|
|
39
43
|
end
|
|
40
44
|
|
|
@@ -52,23 +56,23 @@ describe AASM::Localizer, "new style" do
|
|
|
52
56
|
let (:foo_opened) { LocalizerTestModel.new }
|
|
53
57
|
let (:foo_closed) { LocalizerTestModel.new.tap { |x| x.aasm_state = :closed } }
|
|
54
58
|
|
|
55
|
-
context '
|
|
59
|
+
context 'aasm.human_state' do
|
|
56
60
|
it 'should return translated state value' do
|
|
57
|
-
foo_opened.
|
|
61
|
+
expect(foo_opened.aasm.human_state).to eq("It's open now!")
|
|
58
62
|
end
|
|
59
63
|
|
|
60
64
|
it 'should return humanized value if not localized' do
|
|
61
|
-
foo_closed.
|
|
65
|
+
expect(foo_closed.aasm.human_state).to eq("Closed")
|
|
62
66
|
end
|
|
63
67
|
end
|
|
64
68
|
|
|
65
69
|
context 'aasm_human_event_name' do
|
|
66
70
|
it 'should return translated event name' do
|
|
67
|
-
LocalizerTestModel.aasm_human_event_name(:close).
|
|
71
|
+
expect(LocalizerTestModel.aasm_human_event_name(:close)).to eq("Let's close it!")
|
|
68
72
|
end
|
|
69
73
|
|
|
70
74
|
it 'should return humanized event name' do
|
|
71
|
-
LocalizerTestModel.aasm_human_event_name(:open).
|
|
75
|
+
expect(LocalizerTestModel.aasm_human_event_name(:open)).to eq("Open")
|
|
72
76
|
end
|
|
73
77
|
end
|
|
74
78
|
end
|
|
@@ -87,23 +91,23 @@ describe AASM::Localizer, "deprecated style" do
|
|
|
87
91
|
let (:foo_opened) { LocalizerTestModel.new }
|
|
88
92
|
let (:foo_closed) { LocalizerTestModel.new.tap { |x| x.aasm_state = :closed } }
|
|
89
93
|
|
|
90
|
-
context '
|
|
94
|
+
context 'aasm.human_state' do
|
|
91
95
|
it 'should return translated state value' do
|
|
92
|
-
foo_opened.
|
|
96
|
+
expect(foo_opened.aasm.human_state).to eq("It's open now!")
|
|
93
97
|
end
|
|
94
98
|
|
|
95
99
|
it 'should return humanized value if not localized' do
|
|
96
|
-
foo_closed.
|
|
100
|
+
expect(foo_closed.aasm.human_state).to eq("Closed")
|
|
97
101
|
end
|
|
98
102
|
end
|
|
99
103
|
|
|
100
104
|
context 'aasm_human_event_name' do
|
|
101
105
|
it 'should return translated event name' do
|
|
102
|
-
LocalizerTestModel.aasm_human_event_name(:close).
|
|
106
|
+
expect(LocalizerTestModel.aasm_human_event_name(:close)).to eq("Let's close it!")
|
|
103
107
|
end
|
|
104
108
|
|
|
105
109
|
it 'should return humanized event name' do
|
|
106
|
-
LocalizerTestModel.aasm_human_event_name(:open).
|
|
110
|
+
expect(LocalizerTestModel.aasm_human_event_name(:open)).to eq("Open")
|
|
107
111
|
end
|
|
108
112
|
end
|
|
109
113
|
end
|
data/spec/unit/new_dsl_spec.rb
CHANGED
|
@@ -5,8 +5,8 @@ describe "the new dsl" do
|
|
|
5
5
|
let(:process) {ProcessWithNewDsl.new}
|
|
6
6
|
|
|
7
7
|
it 'should not conflict with other event or state methods' do
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
expect {ProcessWithNewDsl.state}.to raise_error(RuntimeError, "wrong state method")
|
|
9
|
+
expect {ProcessWithNewDsl.event}.to raise_error(RuntimeError, "wrong event method")
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
end
|
|
@@ -9,8 +9,8 @@ load_schema
|
|
|
9
9
|
|
|
10
10
|
shared_examples_for "aasm model" do
|
|
11
11
|
it "should include persistence mixins" do
|
|
12
|
-
klass.included_modules.
|
|
13
|
-
klass.included_modules.
|
|
12
|
+
expect(klass.included_modules).to be_include(AASM::Persistence::ActiveRecordPersistence)
|
|
13
|
+
expect(klass.included_modules).to be_include(AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -18,40 +18,45 @@ describe "instance methods" do
|
|
|
18
18
|
let(:gate) {Gate.new}
|
|
19
19
|
|
|
20
20
|
it "should respond to aasm persistence methods" do
|
|
21
|
-
gate.
|
|
22
|
-
gate.
|
|
23
|
-
gate.
|
|
21
|
+
expect(gate).to respond_to(:aasm_read_state)
|
|
22
|
+
expect(gate).to respond_to(:aasm_write_state)
|
|
23
|
+
expect(gate).to respond_to(:aasm_write_state_without_persistence)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
it "should return the initial state when new and the aasm field is nil" do
|
|
27
|
-
gate.
|
|
27
|
+
expect(gate.aasm.current_state).to eq(:opened)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
it "should return the aasm column when new and the aasm field is not nil" do
|
|
31
31
|
gate.aasm_state = "closed"
|
|
32
|
-
gate.
|
|
32
|
+
expect(gate.aasm.current_state).to eq(:closed)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
it "should return the aasm column when not new and the aasm_column is not nil" do
|
|
36
|
-
gate.
|
|
36
|
+
allow(gate).to receive(:new_record?).and_return(false)
|
|
37
37
|
gate.aasm_state = "state"
|
|
38
|
-
gate.
|
|
38
|
+
expect(gate.aasm.current_state).to eq(:state)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
it "should allow a nil state" do
|
|
42
|
-
gate.
|
|
42
|
+
allow(gate).to receive(:new_record?).and_return(false)
|
|
43
43
|
gate.aasm_state = nil
|
|
44
|
-
gate.
|
|
44
|
+
expect(gate.aasm.current_state).to be_nil
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
it "should call aasm_ensure_initial_state on validation before create" do
|
|
48
|
-
gate.
|
|
48
|
+
expect(gate).to receive(:aasm_ensure_initial_state).and_return(true)
|
|
49
49
|
gate.valid?
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
it "should call aasm_ensure_initial_state before create, even if skipping validations" do
|
|
53
|
+
expect(gate).to receive(:aasm_ensure_initial_state).and_return(true)
|
|
54
|
+
gate.save(:validate => false)
|
|
55
|
+
end
|
|
56
|
+
|
|
52
57
|
it "should not call aasm_ensure_initial_state on validation before update" do
|
|
53
|
-
gate.
|
|
54
|
-
gate.
|
|
58
|
+
allow(gate).to receive(:new_record?).and_return(false)
|
|
59
|
+
expect(gate).not_to receive(:aasm_ensure_initial_state)
|
|
55
60
|
gate.valid?
|
|
56
61
|
end
|
|
57
62
|
|
|
@@ -59,59 +64,36 @@ end
|
|
|
59
64
|
|
|
60
65
|
describe 'subclasses' do
|
|
61
66
|
it "should have the same states as its parent class" do
|
|
62
|
-
|
|
67
|
+
expect(DerivateNewDsl.aasm.states).to eq(SimpleNewDsl.aasm.states)
|
|
63
68
|
end
|
|
64
69
|
|
|
65
70
|
it "should have the same events as its parent class" do
|
|
66
|
-
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
it "should have the same column as its parent class" do
|
|
70
|
-
Derivate.aasm_column.should == :status
|
|
71
|
+
expect(DerivateNewDsl.aasm.events).to eq(SimpleNewDsl.aasm.events)
|
|
71
72
|
end
|
|
72
73
|
|
|
73
74
|
it "should have the same column as its parent even for the new dsl" do
|
|
74
|
-
SimpleNewDsl.aasm_column.
|
|
75
|
-
DerivateNewDsl.aasm_column.
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
describe "named scopes with the old DSL" do
|
|
80
|
-
|
|
81
|
-
context "Does not already respond_to? the scope name" do
|
|
82
|
-
it "should add a scope" do
|
|
83
|
-
Simple.should respond_to(:unknown_scope)
|
|
84
|
-
SimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation).should be_true
|
|
85
|
-
end
|
|
75
|
+
expect(SimpleNewDsl.aasm_column).to eq(:status)
|
|
76
|
+
expect(DerivateNewDsl.aasm_column).to eq(:status)
|
|
86
77
|
end
|
|
87
|
-
|
|
88
|
-
context "Already respond_to? the scope name" do
|
|
89
|
-
it "should not add a scope" do
|
|
90
|
-
Simple.should respond_to(:new)
|
|
91
|
-
Simple.new.class.should == Simple
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
78
|
end
|
|
96
79
|
|
|
97
80
|
describe "named scopes with the new DSL" do
|
|
98
|
-
|
|
99
81
|
context "Does not already respond_to? the scope name" do
|
|
100
82
|
it "should add a scope" do
|
|
101
|
-
SimpleNewDsl.
|
|
102
|
-
SimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation).
|
|
83
|
+
expect(SimpleNewDsl).to respond_to(:unknown_scope)
|
|
84
|
+
expect(SimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation)).to be_true
|
|
103
85
|
end
|
|
104
86
|
end
|
|
105
87
|
|
|
106
88
|
context "Already respond_to? the scope name" do
|
|
107
89
|
it "should not add a scope" do
|
|
108
|
-
SimpleNewDsl.
|
|
109
|
-
SimpleNewDsl.new.class.
|
|
90
|
+
expect(SimpleNewDsl).to respond_to(:new)
|
|
91
|
+
expect(SimpleNewDsl.new.class).to eq(SimpleNewDsl)
|
|
110
92
|
end
|
|
111
93
|
end
|
|
112
94
|
|
|
113
95
|
it "does not create scopes if requested" do
|
|
114
|
-
NoScope.
|
|
96
|
+
expect(NoScope).not_to respond_to(:ignored_scope)
|
|
115
97
|
end
|
|
116
98
|
|
|
117
99
|
end
|
|
@@ -119,8 +101,8 @@ end
|
|
|
119
101
|
describe 'initial states' do
|
|
120
102
|
|
|
121
103
|
it 'should support conditions' do
|
|
122
|
-
Thief.new(:skilled => true).
|
|
123
|
-
Thief.new(:skilled => false).
|
|
104
|
+
expect(Thief.new(:skilled => true).aasm.current_state).to eq(:rich)
|
|
105
|
+
expect(Thief.new(:skilled => false).aasm.current_state).to eq(:jailed)
|
|
124
106
|
end
|
|
125
107
|
end
|
|
126
108
|
|
|
@@ -128,54 +110,54 @@ describe 'transitions with persistence' do
|
|
|
128
110
|
|
|
129
111
|
it "should work for valid models" do
|
|
130
112
|
valid_object = Validator.create(:name => 'name')
|
|
131
|
-
valid_object.
|
|
113
|
+
expect(valid_object).to be_sleeping
|
|
132
114
|
valid_object.status = :running
|
|
133
|
-
valid_object.
|
|
115
|
+
expect(valid_object).to be_running
|
|
134
116
|
end
|
|
135
117
|
|
|
136
118
|
it 'should not store states for invalid models' do
|
|
137
119
|
validator = Validator.create(:name => 'name')
|
|
138
|
-
validator.
|
|
139
|
-
validator.
|
|
120
|
+
expect(validator).to be_valid
|
|
121
|
+
expect(validator).to be_sleeping
|
|
140
122
|
|
|
141
123
|
validator.name = nil
|
|
142
|
-
validator.
|
|
143
|
-
validator.run
|
|
144
|
-
validator.
|
|
124
|
+
expect(validator).not_to be_valid
|
|
125
|
+
expect(validator.run!).to be_false
|
|
126
|
+
expect(validator).to be_sleeping
|
|
145
127
|
|
|
146
128
|
validator.reload
|
|
147
|
-
validator.
|
|
148
|
-
validator.
|
|
129
|
+
expect(validator).not_to be_running
|
|
130
|
+
expect(validator).to be_sleeping
|
|
149
131
|
|
|
150
132
|
validator.name = 'another name'
|
|
151
|
-
validator.
|
|
152
|
-
validator.run
|
|
153
|
-
validator.
|
|
133
|
+
expect(validator).to be_valid
|
|
134
|
+
expect(validator.run!).to be_true
|
|
135
|
+
expect(validator).to be_running
|
|
154
136
|
|
|
155
137
|
validator.reload
|
|
156
|
-
validator.
|
|
157
|
-
validator.
|
|
138
|
+
expect(validator).to be_running
|
|
139
|
+
expect(validator).not_to be_sleeping
|
|
158
140
|
end
|
|
159
141
|
|
|
160
142
|
it 'should store states for invalid models if configured' do
|
|
161
143
|
persistor = InvalidPersistor.create(:name => 'name')
|
|
162
|
-
persistor.
|
|
163
|
-
persistor.
|
|
144
|
+
expect(persistor).to be_valid
|
|
145
|
+
expect(persistor).to be_sleeping
|
|
164
146
|
|
|
165
147
|
persistor.name = nil
|
|
166
|
-
persistor.
|
|
167
|
-
persistor.run
|
|
168
|
-
persistor.
|
|
148
|
+
expect(persistor).not_to be_valid
|
|
149
|
+
expect(persistor.run!).to be_true
|
|
150
|
+
expect(persistor).to be_running
|
|
169
151
|
|
|
170
152
|
persistor = InvalidPersistor.find(persistor.id)
|
|
171
153
|
persistor.valid?
|
|
172
|
-
persistor.
|
|
173
|
-
persistor.
|
|
174
|
-
persistor.
|
|
154
|
+
expect(persistor).to be_valid
|
|
155
|
+
expect(persistor).to be_running
|
|
156
|
+
expect(persistor).not_to be_sleeping
|
|
175
157
|
|
|
176
158
|
persistor.reload
|
|
177
|
-
persistor.
|
|
178
|
-
persistor.
|
|
159
|
+
expect(persistor).to be_running
|
|
160
|
+
expect(persistor).not_to be_sleeping
|
|
179
161
|
end
|
|
180
162
|
|
|
181
163
|
describe 'transactions' do
|
|
@@ -183,41 +165,81 @@ describe 'transitions with persistence' do
|
|
|
183
165
|
let(:transactor) { Transactor.create!(:name => 'transactor', :worker => worker) }
|
|
184
166
|
|
|
185
167
|
it 'should rollback all changes' do
|
|
186
|
-
transactor.
|
|
187
|
-
worker.status.
|
|
168
|
+
expect(transactor).to be_sleeping
|
|
169
|
+
expect(worker.status).to eq('sleeping')
|
|
188
170
|
|
|
189
|
-
|
|
190
|
-
transactor.
|
|
191
|
-
worker.reload.status.
|
|
171
|
+
expect {transactor.run!}.to raise_error(StandardError, 'failed on purpose')
|
|
172
|
+
expect(transactor).to be_running
|
|
173
|
+
expect(worker.reload.status).to eq('sleeping')
|
|
192
174
|
end
|
|
193
175
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
176
|
+
context "nested transactions" do
|
|
177
|
+
it "should rollback all changes in nested transaction" do
|
|
178
|
+
expect(transactor).to be_sleeping
|
|
179
|
+
expect(worker.status).to eq('sleeping')
|
|
197
180
|
|
|
198
|
-
|
|
199
|
-
|
|
181
|
+
Worker.transaction do
|
|
182
|
+
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
expect(transactor).to be_running
|
|
186
|
+
expect(worker.reload.status).to eq('sleeping')
|
|
200
187
|
end
|
|
201
188
|
|
|
202
|
-
|
|
203
|
-
|
|
189
|
+
it "should only rollback changes in the main transaction not the nested one" do
|
|
190
|
+
# change configuration to not require new transaction
|
|
191
|
+
AASM::StateMachine[Transactor].config.requires_new_transaction = false
|
|
192
|
+
|
|
193
|
+
expect(transactor).to be_sleeping
|
|
194
|
+
expect(worker.status).to eq('sleeping')
|
|
195
|
+
|
|
196
|
+
Worker.transaction do
|
|
197
|
+
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
expect(transactor).to be_running
|
|
201
|
+
expect(worker.reload.status).to eq('running')
|
|
202
|
+
end
|
|
204
203
|
end
|
|
205
204
|
|
|
206
205
|
describe "after_commit callback" do
|
|
207
206
|
it "should fire :after_commit if transaction was successful" do
|
|
208
207
|
validator = Validator.create(:name => 'name')
|
|
209
|
-
validator.
|
|
208
|
+
expect(validator).to be_sleeping
|
|
210
209
|
validator.run!
|
|
211
|
-
validator.
|
|
212
|
-
validator.name.
|
|
210
|
+
expect(validator).to be_running
|
|
211
|
+
expect(validator.name).not_to eq("name")
|
|
213
212
|
end
|
|
214
213
|
|
|
215
214
|
it "should not fire :after_commit if transaction failed" do
|
|
216
215
|
validator = Validator.create(:name => 'name')
|
|
217
|
-
|
|
218
|
-
validator.name.
|
|
216
|
+
expect { validator.fail! }.to raise_error(StandardError, 'failed on purpose')
|
|
217
|
+
expect(validator.name).to eq("name")
|
|
219
218
|
end
|
|
220
219
|
|
|
221
220
|
end
|
|
222
221
|
end
|
|
223
222
|
end
|
|
223
|
+
|
|
224
|
+
describe "invalid states with persistence" do
|
|
225
|
+
|
|
226
|
+
it "should not store states" do
|
|
227
|
+
validator = Validator.create(:name => 'name')
|
|
228
|
+
validator.status = 'invalid_state'
|
|
229
|
+
expect(validator.save).to be_false
|
|
230
|
+
expect {validator.save!}.to raise_error(ActiveRecord::RecordInvalid)
|
|
231
|
+
|
|
232
|
+
validator.reload
|
|
233
|
+
expect(validator).to be_sleeping
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
it "should store invalid states if configured" do
|
|
237
|
+
persistor = InvalidPersistor.create(:name => 'name')
|
|
238
|
+
persistor.status = 'invalid_state'
|
|
239
|
+
expect(persistor.save).to be_true
|
|
240
|
+
|
|
241
|
+
persistor.reload
|
|
242
|
+
expect(persistor.status).to eq('invalid_state')
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
end
|