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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +23 -4
  4. data/CHANGELOG.md +20 -0
  5. data/Gemfile +9 -1
  6. data/LICENSE +1 -1
  7. data/README.md +65 -12
  8. data/aasm.gemspec +5 -6
  9. data/gemfiles/rails_3.2.gemfile +12 -0
  10. data/gemfiles/rails_4.0.gemfile +11 -0
  11. data/gemfiles/rails_4.1.gemfile +11 -0
  12. data/lib/aasm/aasm.rb +31 -27
  13. data/lib/aasm/base.rb +35 -20
  14. data/lib/aasm/event.rb +27 -16
  15. data/lib/aasm/instance_base.rb +3 -1
  16. data/lib/aasm/persistence/active_record_persistence.rb +27 -9
  17. data/lib/aasm/persistence/base.rb +1 -1
  18. data/lib/aasm/persistence/mongoid_persistence.rb +10 -8
  19. data/lib/aasm/state.rb +1 -0
  20. data/lib/aasm/transition.rb +13 -6
  21. data/lib/aasm/version.rb +1 -1
  22. data/lib/aasm.rb +0 -3
  23. data/spec/database.rb +33 -0
  24. data/spec/models/guardian.rb +48 -0
  25. data/spec/models/mongoid/no_scope_mongoid.rb +1 -1
  26. data/spec/models/mongoid/simple_mongoid.rb +5 -4
  27. data/spec/models/mongoid/simple_new_dsl_mongoid.rb +1 -1
  28. data/spec/models/not_auto_loaded/process.rb +10 -8
  29. data/spec/models/persistence.rb +5 -13
  30. data/spec/spec_helper.rb +1 -1
  31. data/spec/unit/api_spec.rb +12 -12
  32. data/spec/unit/callbacks_spec.rb +29 -45
  33. data/spec/unit/complex_example_spec.rb +15 -15
  34. data/spec/unit/event_spec.rb +89 -76
  35. data/spec/unit/guard_spec.rb +60 -0
  36. data/spec/unit/initial_state_spec.rb +4 -5
  37. data/spec/unit/inspection_spec.rb +40 -53
  38. data/spec/unit/localizer_spec.rb +22 -18
  39. data/spec/unit/new_dsl_spec.rb +2 -2
  40. data/spec/unit/persistence/active_record_persistence_spec.rb +111 -89
  41. data/spec/unit/persistence/mongoid_persistance_spec.rb +102 -81
  42. data/spec/unit/simple_example_spec.rb +20 -21
  43. data/spec/unit/state_spec.rb +16 -16
  44. data/spec/unit/subclassing_spec.rb +8 -8
  45. data/spec/unit/transition_spec.rb +59 -44
  46. metadata +28 -94
  47. data/lib/aasm/deprecated/aasm.rb +0 -15
  48. data/spec/models/callback_old_dsl.rb +0 -41
  49. data/spec/schema.rb +0 -35
@@ -10,12 +10,12 @@ class LocalizerTestModel < ActiveRecord::Base
10
10
 
11
11
  attr_accessor :aasm_state
12
12
 
13
- aasm_initial_state :opened
14
- aasm_state :opened
15
- aasm_state :closed
16
-
17
- aasm_event :close
18
- aasm_event :open
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}.localized_name.should == "It's open now!"
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}.localized_name.should == '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 'aasm_human_state' do
59
+ context 'aasm.human_state' do
56
60
  it 'should return translated state value' do
57
- foo_opened.aasm_human_state.should == "It's open now!"
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.aasm_human_state.should == "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).should == "Let's close it!"
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).should == "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 'aasm_human_state' do
94
+ context 'aasm.human_state' do
91
95
  it 'should return translated state value' do
92
- foo_opened.aasm_human_state.should == "It's open now!"
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.aasm_human_state.should == "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).should == "Let's close it!"
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).should == "Open"
110
+ expect(LocalizerTestModel.aasm_human_event_name(:open)).to eq("Open")
107
111
  end
108
112
  end
109
113
  end
@@ -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
- lambda {ProcessWithNewDsl.state}.should raise_error(RuntimeError, "wrong state method")
9
- lambda {ProcessWithNewDsl.event}.should raise_error(RuntimeError, "wrong event method")
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.should be_include(AASM::Persistence::ActiveRecordPersistence)
13
- klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
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.should respond_to(:aasm_read_state)
22
- gate.should respond_to(:aasm_write_state)
23
- gate.should respond_to(:aasm_write_state_without_persistence)
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.aasm_current_state.should == :opened
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.aasm_current_state.should == :closed
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.stub(:new_record?).and_return(false)
36
+ allow(gate).to receive(:new_record?).and_return(false)
37
37
  gate.aasm_state = "state"
38
- gate.aasm_current_state.should == :state
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.stub(:new_record?).and_return(false)
42
+ allow(gate).to receive(:new_record?).and_return(false)
43
43
  gate.aasm_state = nil
44
- gate.aasm_current_state.should be_nil
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.should_receive(:aasm_ensure_initial_state).and_return(true)
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.stub(:new_record?).and_return(false)
54
- gate.should_not_receive(:aasm_ensure_initial_state)
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
- Derivate.aasm_states.should == Simple.aasm_states
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
- Derivate.aasm_events.should == Simple.aasm_events
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.should == :status
75
- DerivateNewDsl.aasm_column.should == :status
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.should respond_to(:unknown_scope)
102
- SimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation).should be_true
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.should respond_to(:new)
109
- SimpleNewDsl.new.class.should == SimpleNewDsl
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.should_not respond_to(:ignored_scope)
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).aasm_current_state.should == :rich
123
- Thief.new(:skilled => false).aasm_current_state.should == :jailed
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.should be_sleeping
113
+ expect(valid_object).to be_sleeping
132
114
  valid_object.status = :running
133
- valid_object.should be_running
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.should be_valid
139
- validator.should be_sleeping
120
+ expect(validator).to be_valid
121
+ expect(validator).to be_sleeping
140
122
 
141
123
  validator.name = nil
142
- validator.should_not be_valid
143
- validator.run!.should be_false
144
- validator.should be_sleeping
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.should_not be_running
148
- validator.should be_sleeping
129
+ expect(validator).not_to be_running
130
+ expect(validator).to be_sleeping
149
131
 
150
132
  validator.name = 'another name'
151
- validator.should be_valid
152
- validator.run!.should be_true
153
- validator.should be_running
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.should be_running
157
- validator.should_not be_sleeping
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.should be_valid
163
- persistor.should be_sleeping
144
+ expect(persistor).to be_valid
145
+ expect(persistor).to be_sleeping
164
146
 
165
147
  persistor.name = nil
166
- persistor.should_not be_valid
167
- persistor.run!.should be_true
168
- persistor.should be_running
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.should be_valid
173
- persistor.should be_running
174
- persistor.should_not be_sleeping
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.should be_running
178
- persistor.should_not be_sleeping
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.should be_sleeping
187
- worker.status.should == 'sleeping'
168
+ expect(transactor).to be_sleeping
169
+ expect(worker.status).to eq('sleeping')
188
170
 
189
- lambda {transactor.run!}.should raise_error(StandardError, 'failed on purpose')
190
- transactor.should be_running
191
- worker.reload.status.should == 'sleeping'
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
- it "should rollback all changes in nested transaction" do
195
- transactor.should be_sleeping
196
- worker.status.should == 'sleeping'
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
- Worker.transaction do
199
- lambda { transactor.run! }.should raise_error(StandardError, 'failed on purpose')
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
- transactor.should be_running
203
- worker.reload.status.should == 'sleeping'
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.should be_sleeping
208
+ expect(validator).to be_sleeping
210
209
  validator.run!
211
- validator.should be_running
212
- validator.name.should_not == "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
- lambda { validator.fail! }.should raise_error(StandardError, 'failed on purpose')
218
- validator.name.should == "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