aasm 4.12.0 → 4.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -1
  3. data/Appraisals +2 -0
  4. data/CHANGELOG.md +5 -0
  5. data/Gemfile +1 -1
  6. data/README.md +102 -8
  7. data/gemfiles/rails_4.2.gemfile +1 -0
  8. data/gemfiles/rails_5.0.gemfile +1 -0
  9. data/lib/aasm/base.rb +8 -0
  10. data/lib/aasm/core/event.rb +1 -1
  11. data/lib/aasm/core/transition.rb +1 -1
  12. data/lib/aasm/instance_base.rb +0 -2
  13. data/lib/aasm/minitest.rb +5 -0
  14. data/lib/aasm/minitest/allow_event.rb +13 -0
  15. data/lib/aasm/minitest/allow_transition_to.rb +13 -0
  16. data/lib/aasm/minitest/have_state.rb +13 -0
  17. data/lib/aasm/minitest/transition_from.rb +21 -0
  18. data/lib/aasm/minitest_spec.rb +15 -0
  19. data/lib/aasm/persistence/active_record_persistence.rb +25 -101
  20. data/lib/aasm/persistence/base.rb +7 -3
  21. data/lib/aasm/persistence/mongoid_persistence.rb +15 -60
  22. data/lib/aasm/persistence/orm.rb +142 -0
  23. data/lib/aasm/persistence/redis_persistence.rb +16 -11
  24. data/lib/aasm/persistence/sequel_persistence.rb +36 -63
  25. data/lib/aasm/version.rb +1 -1
  26. data/lib/generators/active_record/templates/migration.rb +1 -1
  27. data/lib/generators/active_record/templates/migration_existing.rb +1 -1
  28. data/lib/motion-aasm.rb +2 -0
  29. data/spec/models/active_record/complex_active_record_example.rb +5 -1
  30. data/spec/models/guardian_without_from_specified.rb +18 -0
  31. data/spec/models/namespaced_multiple_example.rb +14 -0
  32. data/spec/models/redis/complex_redis_example.rb +40 -0
  33. data/spec/models/redis/redis_multiple.rb +20 -0
  34. data/spec/models/redis/redis_simple.rb +20 -0
  35. data/spec/models/sequel/complex_sequel_example.rb +4 -3
  36. data/spec/models/sequel/invalid_persistor.rb +52 -0
  37. data/spec/models/sequel/sequel_multiple.rb +13 -13
  38. data/spec/models/sequel/sequel_simple.rb +13 -12
  39. data/spec/models/sequel/silent_persistor.rb +50 -0
  40. data/spec/models/sequel/transactor.rb +112 -0
  41. data/spec/models/sequel/validator.rb +93 -0
  42. data/spec/models/sequel/worker.rb +12 -0
  43. data/spec/spec_helpers/redis.rb +8 -0
  44. data/spec/unit/guard_spec.rb +17 -0
  45. data/spec/unit/guard_without_from_specified_spec.rb +10 -0
  46. data/spec/unit/namespaced_multiple_example_spec.rb +22 -0
  47. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +4 -4
  48. data/spec/unit/persistence/active_record_persistence_spec.rb +4 -4
  49. data/spec/unit/persistence/redis_persistence_multiple_spec.rb +88 -0
  50. data/spec/unit/persistence/redis_persistence_spec.rb +5 -25
  51. data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +2 -2
  52. data/spec/unit/persistence/sequel_persistence_spec.rb +275 -2
  53. data/test/minitest_helper.rb +57 -0
  54. data/test/unit/minitest_matcher_test.rb +80 -0
  55. metadata +35 -2
@@ -0,0 +1,93 @@
1
+ db = Sequel::DATABASES.first || Sequel.connect(SEQUEL_DB)
2
+
3
+ db.create_table(:validators) do
4
+ primary_key :id
5
+ String "name"
6
+ String "status"
7
+ Fixnum "worker_id"
8
+ end
9
+
10
+ module Sequel
11
+ class Validator < Sequel::Model(:validators)
12
+ plugin :validation_helpers
13
+
14
+ attr_accessor :after_all_transactions_performed,
15
+ :after_transaction_performed_on_fail,
16
+ :after_transaction_performed_on_run,
17
+ :before_all_transactions_performed,
18
+ :before_transaction_performed_on_fail,
19
+ :before_transaction_performed_on_run,
20
+ :invalid
21
+
22
+ def validate
23
+ super
24
+ errors.add(:validator, "invalid") if invalid
25
+ validates_presence :name
26
+ end
27
+
28
+ include AASM
29
+
30
+ aasm :column => :status, :whiny_persistence => true do
31
+ before_all_transactions :before_all_transactions
32
+ after_all_transactions :after_all_transactions
33
+
34
+ state :sleeping, :initial => true
35
+ state :running
36
+ state :failed, :after_enter => :fail
37
+
38
+ event :run, :after_commit => :change_name! do
39
+ after_transaction do
40
+ @after_transaction_performed_on_run = true
41
+ end
42
+
43
+ before_transaction do
44
+ @before_transaction_performed_on_run = true
45
+ end
46
+
47
+ transitions :to => :running, :from => :sleeping
48
+ end
49
+
50
+ event :sleep do
51
+ after_commit do |name|
52
+ change_name_on_sleep name
53
+ end
54
+ transitions :to => :sleeping, :from => :running
55
+ end
56
+
57
+ event :fail do
58
+ after_transaction do
59
+ @after_transaction_performed_on_fail = true
60
+ end
61
+
62
+ before_transaction do
63
+ @before_transaction_performed_on_fail = true
64
+ end
65
+
66
+ transitions :to => :failed, :from => [:sleeping, :running]
67
+ end
68
+ end
69
+
70
+ def change_name!
71
+ self.name = "name changed"
72
+ save(raise_on_failure: true)
73
+ end
74
+
75
+ def change_name_on_sleep name
76
+ self.name = name
77
+ save(raise_on_failure: true)
78
+ end
79
+
80
+ def fail
81
+ raise StandardError.new('failed on purpose')
82
+ end
83
+
84
+ def after_all_transactions
85
+ @after_all_transactions_performed = true
86
+ end
87
+
88
+ def before_all_transactions
89
+ @before_all_transactions_performed = true
90
+ end
91
+ end
92
+
93
+ end
@@ -0,0 +1,12 @@
1
+ db = Sequel::DATABASES.first || Sequel.connect(SEQUEL_DB)
2
+
3
+ db.create_table(:workers) do
4
+ primary_key :id
5
+ String "name"
6
+ String "status"
7
+ end
8
+
9
+ module Sequel
10
+ class Worker < Sequel::Model(:workers)
11
+ end
12
+ end
@@ -2,6 +2,14 @@
2
2
  begin
3
3
  require 'redis-objects'
4
4
  puts "redis-objects gem found, running Redis specs \e[32m#{'✔'}\e[0m"
5
+
6
+ Redis.current = Redis.new(host: '127.0.0.1', port: 6379)
7
+
8
+ RSpec.configure do |c|
9
+ c.before(:each) do
10
+ Redis.current.keys('redis_*').each { |k| Redis.current.del k }
11
+ end
12
+ end
5
13
  rescue LoadError
6
14
  puts "redis-objects gem not found, not running Redis specs \e[31m#{'✖'}\e[0m"
7
15
  end
@@ -70,3 +70,20 @@ describe "event guards" do
70
70
  end
71
71
 
72
72
  end
73
+
74
+ if defined?(ActiveRecord)
75
+
76
+ Dir[File.dirname(__FILE__) + "/../models/active_record/*.rb"].sort.each do |f|
77
+ require File.expand_path(f)
78
+ end
79
+
80
+ load_schema
81
+
82
+ describe "ActiveRecord per-transition guards" do
83
+ let(:example) { ComplexActiveRecordExample.new }
84
+
85
+ it "should be able to increment" do
86
+ expect(example.may_increment?).to be true
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe "transitions without from specified" do
4
+ let(:guardian) { GuardianWithoutFromSpecified.new }
5
+
6
+ it "allows the transitions if guard succeeds" do
7
+ expect { guardian.use_guards_where_the_first_fails! }.to_not raise_error
8
+ expect(guardian).to be_gamma
9
+ end
10
+ end
@@ -11,6 +11,10 @@ describe 'state machine' do
11
11
  expect(namespaced.aasm(:review_status).current_state).to eq(:unapproved)
12
12
  expect(namespaced).to respond_to(:review_unapproved?)
13
13
  expect(namespaced).to be_review_unapproved
14
+
15
+ expect(namespaced.aasm(:car).current_state).to eq(:unsold)
16
+ expect(namespaced).to respond_to(:car_unsold?)
17
+ expect(namespaced).to be_car_unsold
14
18
  end
15
19
 
16
20
  it 'allows transitions to other states' do
@@ -25,6 +29,12 @@ describe 'state machine' do
25
29
  namespaced.approve_review!
26
30
  expect(namespaced).to respond_to(:review_approved?)
27
31
  expect(namespaced).to be_review_approved
32
+
33
+ expect(namespaced).to respond_to(:sell_car)
34
+ expect(namespaced).to respond_to(:sell_car!)
35
+ namespaced.sell_car!
36
+ expect(namespaced).to respond_to(:car_sold?)
37
+ expect(namespaced).to be_car_sold
28
38
  end
29
39
 
30
40
  it 'denies transitions to other states' do
@@ -41,6 +51,13 @@ describe 'state machine' do
41
51
  expect {namespaced.approve_review}.to raise_error(AASM::InvalidTransition)
42
52
  expect {namespaced.approve_review!}.to raise_error(AASM::InvalidTransition)
43
53
  namespaced.unapprove_review
54
+
55
+ expect {namespaced.return_car}.to raise_error(AASM::InvalidTransition)
56
+ expect {namespaced.return_car!}.to raise_error(AASM::InvalidTransition)
57
+ namespaced.sell_car
58
+ expect {namespaced.sell_car}.to raise_error(AASM::InvalidTransition)
59
+ expect {namespaced.sell_car!}.to raise_error(AASM::InvalidTransition)
60
+ namespaced.return_car
44
61
  end
45
62
 
46
63
  it 'defines constants for each state name' do
@@ -49,5 +66,10 @@ describe 'state machine' do
49
66
 
50
67
  expect(NamespacedMultipleExample::STATE_REVIEW_UNAPPROVED).to eq(:unapproved)
51
68
  expect(NamespacedMultipleExample::STATE_REVIEW_APPROVED).to eq(:approved)
69
+
70
+ expect(NamespacedMultipleExample::STATE_CAR_UNSOLD).to eq(:unsold)
71
+ expect(NamespacedMultipleExample::STATE_CAR_SOLD).to eq(:sold)
52
72
  end
73
+
74
+
53
75
  end
@@ -139,7 +139,7 @@ if defined?(ActiveRecord)
139
139
 
140
140
  before :each do
141
141
  allow(gate).to receive(:aasm_enum).and_return(enum_name)
142
- allow(gate).to receive(:aasm_write_attribute)
142
+ allow(gate).to receive(:aasm_write_state_attribute)
143
143
  allow(gate).to receive(:write_attribute)
144
144
 
145
145
  allow(MultipleGate).to receive(enum_name).and_return(enum)
@@ -173,7 +173,7 @@ if defined?(ActiveRecord)
173
173
 
174
174
  gate.aasm_write_state state_sym, :left
175
175
 
176
- expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :left)
176
+ expect(gate).to have_received(:aasm_write_state_attribute).with(state_sym, :left)
177
177
  expect(gate).to_not have_received :write_attribute
178
178
  end
179
179
  end
@@ -183,7 +183,7 @@ if defined?(ActiveRecord)
183
183
  it "delegates state update to the helper method" do
184
184
  gate.aasm_write_state_without_persistence state_sym, :left
185
185
 
186
- expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :left)
186
+ expect(gate).to have_received(:aasm_write_state_attribute).with(state_sym, :left)
187
187
  expect(gate).to_not have_received :write_attribute
188
188
  end
189
189
  end
@@ -219,7 +219,7 @@ if defined?(ActiveRecord)
219
219
  allow(gate).to receive(:write_attribute)
220
220
  allow(gate).to receive(:aasm_raw_attribute_value).and_return(value)
221
221
 
222
- gate.send(:aasm_write_attribute, sym, :left)
222
+ gate.send(:aasm_write_state_attribute, sym, :left)
223
223
  end
224
224
 
225
225
  it "generates attribute value using a helper method" do
@@ -139,7 +139,7 @@ if defined?(ActiveRecord)
139
139
 
140
140
  before :each do
141
141
  allow(gate).to receive(:aasm_enum).and_return(enum_name)
142
- allow(gate).to receive(:aasm_write_attribute)
142
+ allow(gate).to receive(:aasm_write_state_attribute)
143
143
  allow(gate).to receive(:write_attribute)
144
144
 
145
145
  allow(Gate).to receive(enum_name).and_return(enum)
@@ -191,7 +191,7 @@ if defined?(ActiveRecord)
191
191
 
192
192
  gate.aasm_write_state state_sym
193
193
 
194
- expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :default)
194
+ expect(gate).to have_received(:aasm_write_state_attribute).with(state_sym, :default)
195
195
  expect(gate).to_not have_received :write_attribute
196
196
  end
197
197
  end
@@ -201,7 +201,7 @@ if defined?(ActiveRecord)
201
201
  it "delegates state update to the helper method" do
202
202
  gate.aasm_write_state_without_persistence state_sym
203
203
 
204
- expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :default)
204
+ expect(gate).to have_received(:aasm_write_state_attribute).with(state_sym, :default)
205
205
  expect(gate).to_not have_received :write_attribute
206
206
  end
207
207
  end
@@ -237,7 +237,7 @@ if defined?(ActiveRecord)
237
237
  allow(gate).to receive(:write_attribute)
238
238
  allow(gate).to receive(:aasm_raw_attribute_value).and_return(value)
239
239
 
240
- gate.send(:aasm_write_attribute, sym)
240
+ gate.send(:aasm_write_state_attribute, sym)
241
241
  end
242
242
 
243
243
  it "generates attribute value using a helper method" do
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ if defined?(Redis)
4
+ describe 'redis' do
5
+
6
+ Dir[File.dirname(__FILE__) + "/../../models/redis/*.rb"].sort.each do |f|
7
+ require File.expand_path(f)
8
+ end
9
+
10
+ before(:all) do
11
+ @model = RedisMultiple
12
+ end
13
+
14
+ describe "instance methods" do
15
+ let(:model) {@model.new}
16
+
17
+ it "should respond to aasm persistence methods" do
18
+ expect(model).to respond_to(:aasm_read_state)
19
+ expect(model).to respond_to(:aasm_write_state)
20
+ expect(model).to respond_to(:aasm_write_state_without_persistence)
21
+ end
22
+
23
+ it "should return the initial state when new and the aasm field is nil" do
24
+ expect(model.aasm(:left).current_state).to eq(:alpha)
25
+ end
26
+
27
+ it "should save the initial state" do
28
+ expect(model.status).to eq("alpha")
29
+ end
30
+
31
+ it "should return the aasm column the aasm field is not nil" do
32
+ model.status = "beta"
33
+ expect(model.aasm(:left).current_state).to eq(:beta)
34
+ end
35
+
36
+ it "should allow a nil state" do
37
+ model.status = nil
38
+ expect(model.aasm(:left).current_state).to be_nil
39
+ end
40
+ end
41
+
42
+ describe 'subclasses' do
43
+ it "should have the same states as its parent class" do
44
+ expect(Class.new(@model).aasm(:left).states).to eq(@model.aasm(:left).states)
45
+ end
46
+
47
+ it "should have the same events as its parent class" do
48
+ expect(Class.new(@model).aasm(:left).events).to eq(@model.aasm(:left).events)
49
+ end
50
+
51
+ it "should have the same column as its parent even for the new dsl" do
52
+ expect(@model.aasm(:left).attribute_name).to eq(:status)
53
+ expect(Class.new(@model).aasm(:left).attribute_name).to eq(:status)
54
+ end
55
+ end
56
+
57
+ describe "complex example" do
58
+ it "works" do
59
+ record = RedisComplexExample.new
60
+
61
+ expect(record.aasm(:left).current_state).to eql :one
62
+ expect(record.aasm(:right).current_state).to eql :alpha
63
+
64
+ expect_aasm_states record, :one, :alpha
65
+
66
+ record.increment!
67
+ expect_aasm_states record, :two, :alpha
68
+
69
+ record.level_up!
70
+ expect_aasm_states record, :two, :beta
71
+
72
+ record.increment!
73
+ expect { record.increment! }.to raise_error(AASM::InvalidTransition)
74
+ expect_aasm_states record, :three, :beta
75
+
76
+ record.level_up!
77
+ expect_aasm_states record, :three, :gamma
78
+ end
79
+
80
+ def expect_aasm_states(record, left_state, right_state)
81
+ expect(record.aasm(:left).current_state).to eql left_state.to_sym
82
+ expect(record.left.value.to_s).to eql left_state.to_s
83
+ expect(record.aasm(:right).current_state).to eql right_state.to_sym
84
+ expect(record.right.value.to_s).to eql right_state.to_s
85
+ end
86
+ end
87
+ end
88
+ end
@@ -3,31 +3,12 @@ require 'spec_helper'
3
3
  if defined?(Redis::Objects)
4
4
  describe 'redis' do
5
5
 
6
- before(:all) do
7
- Redis.current = Redis.new(host: '127.0.0.1', port: 6379)
8
-
9
- @model = Class.new do
10
- attr_accessor :default
11
-
12
- include Redis::Objects
13
- include AASM
14
-
15
- value :status
16
-
17
- def id
18
- 1
19
- end
6
+ Dir[File.dirname(__FILE__) + "/../../models/redis/*.rb"].sort.each do |f|
7
+ require File.expand_path(f)
8
+ end
20
9
 
21
- aasm column: :status
22
- aasm do
23
- state :alpha, initial: true
24
- state :beta
25
- state :gamma
26
- event :release do
27
- transitions from: [:alpha, :beta, :gamma], to: :beta
28
- end
29
- end
30
- end
10
+ before(:all) do
11
+ @model = RedisSimple
31
12
  end
32
13
 
33
14
  describe "instance methods" do
@@ -68,6 +49,5 @@ if defined?(Redis::Objects)
68
49
  expect(Class.new(@model).aasm.attribute_name).to eq(:status)
69
50
  end
70
51
  end
71
-
72
52
  end
73
53
  end
@@ -8,7 +8,7 @@ if defined?(Sequel)
8
8
  end
9
9
 
10
10
  before(:all) do
11
- @model = SequelMultiple
11
+ @model = Sequel::Multiple
12
12
  end
13
13
 
14
14
  describe "instance methods" do
@@ -93,7 +93,7 @@ if defined?(Sequel)
93
93
 
94
94
  describe "complex example" do
95
95
  it "works" do
96
- record = ComplexSequelExample.new
96
+ record = Sequel::ComplexExample.new
97
97
  expect(record.aasm(:left).current_state).to eql :one
98
98
  expect(record.left).to be_nil
99
99
  expect(record.aasm(:right).current_state).to eql :alpha
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
-
3
2
  if defined?(Sequel)
4
3
  describe 'sequel' do
5
4
 
@@ -8,7 +7,7 @@ if defined?(Sequel)
8
7
  end
9
8
 
10
9
  before(:all) do
11
- @model = SequelSimple
10
+ @model = Sequel::Simple
12
11
  end
13
12
 
14
13
  describe "instance methods" do
@@ -91,5 +90,279 @@ if defined?(Sequel)
91
90
  end
92
91
  end
93
92
 
93
+ describe 'transitions with persistence' do
94
+
95
+ it "should work for valid models" do
96
+ valid_object = Sequel::Validator.create(:name => 'name')
97
+ expect(valid_object).to be_sleeping
98
+ valid_object.status = :running
99
+ expect(valid_object).to be_running
100
+ end
101
+
102
+ it 'should not store states for invalid models' do
103
+ validator = Sequel::Validator.create(:name => 'name')
104
+ expect(validator).to be_valid
105
+ expect(validator).to be_sleeping
106
+
107
+ validator.name = nil
108
+ expect(validator).not_to be_valid
109
+ expect { validator.run! }.to raise_error(Sequel::ValidationFailed)
110
+ expect(validator).to be_sleeping
111
+
112
+ validator.reload
113
+ expect(validator).not_to be_running
114
+ expect(validator).to be_sleeping
115
+
116
+ validator.name = 'another name'
117
+ expect(validator).to be_valid
118
+ expect(validator.run!).to be_truthy
119
+ expect(validator).to be_running
120
+
121
+ validator.reload
122
+ expect(validator).to be_running
123
+ expect(validator).not_to be_sleeping
124
+ end
125
+
126
+ it 'should not store states for invalid models silently if configured' do
127
+ validator = Sequel::SilentPersistor.create(:name => 'name')
128
+ expect(validator).to be_valid
129
+ expect(validator).to be_sleeping
130
+
131
+ validator.name = nil
132
+ expect(validator).not_to be_valid
133
+ expect(validator.run!).to be_falsey
134
+ expect(validator).to be_sleeping
135
+
136
+ validator.reload
137
+ expect(validator).not_to be_running
138
+ expect(validator).to be_sleeping
139
+
140
+ validator.name = 'another name'
141
+ expect(validator).to be_valid
142
+ expect(validator.run!).to be_truthy
143
+ expect(validator).to be_running
144
+
145
+ validator.reload
146
+ expect(validator).to be_running
147
+ expect(validator).not_to be_sleeping
148
+ end
149
+
150
+ it 'should store states for invalid models if configured' do
151
+ persistor = Sequel::InvalidPersistor.create(:name => 'name')
152
+ expect(persistor).to be_valid
153
+ expect(persistor).to be_sleeping
154
+
155
+ persistor.name = nil
156
+ expect(persistor).not_to be_valid
157
+ expect(persistor.run!).to be_truthy
158
+ expect(persistor).to be_running
159
+
160
+ persistor = Sequel::InvalidPersistor[persistor.id]
161
+ persistor.valid?
162
+ expect(persistor).to be_valid
163
+ expect(persistor).to be_running
164
+ expect(persistor).not_to be_sleeping
165
+
166
+ persistor.reload
167
+ expect(persistor).to be_running
168
+ expect(persistor).not_to be_sleeping
169
+ end
170
+
171
+ describe 'pessimistic locking' do
172
+ let(:worker) { Sequel::Worker.create(:name => 'worker', :status => 'sleeping') }
173
+
174
+ subject { transactor.run! }
175
+
176
+ context 'no lock' do
177
+ let(:transactor) { Sequel::NoLockTransactor.create(:name => 'no_lock_transactor', :worker => worker) }
178
+
179
+ it 'should not invoke lock!' do
180
+ expect(transactor).to_not receive(:lock!)
181
+ subject
182
+ end
183
+ end
184
+
185
+ context 'a default lock' do
186
+ let(:transactor) { Sequel::LockTransactor.create(:name => 'lock_transactor', :worker => worker) }
187
+
188
+ it 'should invoke lock!' do
189
+ expect(transactor).to receive(:lock!).and_call_original
190
+ subject
191
+ end
192
+ end
193
+
194
+ context 'a FOR UPDATE NOWAIT lock' do
195
+ let(:transactor) { Sequel::LockNoWaitTransactor.create(:name => 'lock_no_wait_transactor', :worker => worker) }
196
+
197
+ it 'should invoke lock! with FOR UPDATE NOWAIT' do
198
+ # TODO: With and_call_original, get an error with syntax, should look into it.
199
+ expect(transactor).to receive(:lock!).with('FOR UPDATE NOWAIT')# .and_call_original
200
+ subject
201
+ end
202
+ end
203
+ end
204
+
205
+ describe 'transactions' do
206
+ let(:worker) { Sequel::Worker.create(:name => 'worker', :status => 'sleeping') }
207
+ let(:transactor) { Sequel::Transactor.create(:name => 'transactor', :worker => worker) }
208
+
209
+ it 'should rollback all changes' do
210
+ expect(transactor).to be_sleeping
211
+ expect(worker.status).to eq('sleeping')
212
+
213
+ expect {transactor.run!}.to raise_error(StandardError, 'failed on purpose')
214
+ expect(transactor).to be_running
215
+ expect(worker.reload.status).to eq('sleeping')
216
+ end
217
+
218
+ context "nested transactions" do
219
+ it "should rollback all changes in nested transaction" do
220
+ expect(transactor).to be_sleeping
221
+ expect(worker.status).to eq('sleeping')
222
+
223
+ Sequel::Worker.db.transaction do
224
+ expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
225
+ end
226
+
227
+ expect(transactor).to be_running
228
+ expect(worker.reload.status).to eq('sleeping')
229
+ end
230
+
231
+ it "should only rollback changes in the main transaction not the nested one" do
232
+ # change configuration to not require new transaction
233
+ AASM::StateMachineStore[Sequel::Transactor][:default].config.requires_new_transaction = false
234
+
235
+ expect(transactor).to be_sleeping
236
+ expect(worker.status).to eq('sleeping')
237
+ Sequel::Worker.db.transaction do
238
+ expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
239
+ end
240
+ expect(transactor).to be_running
241
+ expect(worker.reload.status).to eq('running')
242
+ end
243
+ end
244
+
245
+ describe "after_commit callback" do
246
+ it "should fire :after_commit if transaction was successful" do
247
+ validator = Sequel::Validator.create(:name => 'name')
248
+ expect(validator).to be_sleeping
249
+
250
+ validator.run!
251
+ expect(validator).to be_running
252
+ expect(validator.name).to eq("name changed")
253
+
254
+ validator.sleep!("sleeper")
255
+ expect(validator).to be_sleeping
256
+ expect(validator.name).to eq("sleeper")
257
+ end
258
+
259
+ it "should not fire :after_commit if transaction failed" do
260
+ validator = Sequel::Validator.create(:name => 'name')
261
+ expect { validator.fail! }.to raise_error(StandardError, 'failed on purpose')
262
+ expect(validator.name).to eq("name")
263
+ end
264
+
265
+ it "should not fire :after_commit if validation failed when saving object" do
266
+ validator = Sequel::Validator.create(:name => 'name')
267
+ validator.invalid = true
268
+ expect { validator.run! }.to raise_error(Sequel::ValidationFailed, 'validator invalid')
269
+ expect(validator).to be_sleeping
270
+ expect(validator.name).to eq("name")
271
+ end
272
+
273
+ it "should not fire if not saving" do
274
+ validator = Sequel::Validator.create(:name => 'name')
275
+ expect(validator).to be_sleeping
276
+ validator.run
277
+ expect(validator).to be_running
278
+ expect(validator.name).to eq("name")
279
+ end
280
+ end
281
+
282
+ describe 'before and after transaction callbacks' do
283
+ [:after, :before].each do |event_type|
284
+ describe "#{event_type}_transaction callback" do
285
+ it "should fire :#{event_type}_transaction if transaction was successful" do
286
+ validator = Sequel::Validator.create(:name => 'name')
287
+ expect(validator).to be_sleeping
288
+
289
+ expect { validator.run! }.to change { validator.send("#{event_type}_transaction_performed_on_run") }.from(nil).to(true)
290
+ expect(validator).to be_running
291
+ end
292
+
293
+ it "should fire :#{event_type}_transaction if transaction failed" do
294
+ validator = Sequel::Validator.create(:name => 'name')
295
+ expect do
296
+ begin
297
+ validator.fail!
298
+ rescue => ignored
299
+ end
300
+ end.to change { validator.send("#{event_type}_transaction_performed_on_fail") }.from(nil).to(true)
301
+ expect(validator).to_not be_running
302
+ end
303
+
304
+ it "should not fire :#{event_type}_transaction if not saving" do
305
+ validator = Sequel::Validator.create(:name => 'name')
306
+ expect(validator).to be_sleeping
307
+ expect { validator.run }.to_not change { validator.send("#{event_type}_transaction_performed_on_run") }
308
+ expect(validator).to be_running
309
+ expect(validator.name).to eq("name")
310
+ end
311
+ end
312
+ end
313
+ end
314
+
315
+ describe 'before and after all transactions callbacks' do
316
+ [:after, :before].each do |event_type|
317
+ describe "#{event_type}_all_transactions callback" do
318
+ it "should fire :#{event_type}_all_transactions if transaction was successful" do
319
+ validator = Sequel::Validator.create(:name => 'name')
320
+ expect(validator).to be_sleeping
321
+
322
+ expect { validator.run! }.to change { validator.send("#{event_type}_all_transactions_performed") }.from(nil).to(true)
323
+ expect(validator).to be_running
324
+ end
325
+
326
+ it "should fire :#{event_type}_all_transactions if transaction failed" do
327
+ validator = Sequel::Validator.create(:name => 'name')
328
+ expect do
329
+ begin
330
+ validator.fail!
331
+ rescue => ignored
332
+ end
333
+ end.to change { validator.send("#{event_type}_all_transactions_performed") }.from(nil).to(true)
334
+ expect(validator).to_not be_running
335
+ end
336
+
337
+ it "should not fire :#{event_type}_all_transactions if not saving" do
338
+ validator = Sequel::Validator.create(:name => 'name')
339
+ expect(validator).to be_sleeping
340
+ expect { validator.run }.to_not change { validator.send("#{event_type}_all_transactions_performed") }
341
+ expect(validator).to be_running
342
+ expect(validator.name).to eq("name")
343
+ end
344
+ end
345
+ end
346
+ end
347
+
348
+ context "when not persisting" do
349
+ it 'should not rollback all changes' do
350
+ expect(transactor).to be_sleeping
351
+ expect(worker.status).to eq('sleeping')
352
+
353
+ # Notice here we're calling "run" and not "run!" with a bang.
354
+ expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
355
+ expect(transactor).to be_running
356
+ expect(worker.reload.status).to eq('running')
357
+ end
358
+
359
+ it 'should not create a database transaction' do
360
+ expect(transactor.class).not_to receive(:transaction)
361
+ expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
362
+ end
363
+ end
364
+ end
365
+ end
366
+
94
367
  end
95
368
  end