aasm 4.12.0 → 4.12.1
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 +5 -1
- data/Appraisals +2 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +1 -1
- data/README.md +102 -8
- data/gemfiles/rails_4.2.gemfile +1 -0
- data/gemfiles/rails_5.0.gemfile +1 -0
- data/lib/aasm/base.rb +8 -0
- data/lib/aasm/core/event.rb +1 -1
- data/lib/aasm/core/transition.rb +1 -1
- data/lib/aasm/instance_base.rb +0 -2
- data/lib/aasm/minitest.rb +5 -0
- data/lib/aasm/minitest/allow_event.rb +13 -0
- data/lib/aasm/minitest/allow_transition_to.rb +13 -0
- data/lib/aasm/minitest/have_state.rb +13 -0
- data/lib/aasm/minitest/transition_from.rb +21 -0
- data/lib/aasm/minitest_spec.rb +15 -0
- data/lib/aasm/persistence/active_record_persistence.rb +25 -101
- data/lib/aasm/persistence/base.rb +7 -3
- data/lib/aasm/persistence/mongoid_persistence.rb +15 -60
- data/lib/aasm/persistence/orm.rb +142 -0
- data/lib/aasm/persistence/redis_persistence.rb +16 -11
- data/lib/aasm/persistence/sequel_persistence.rb +36 -63
- data/lib/aasm/version.rb +1 -1
- data/lib/generators/active_record/templates/migration.rb +1 -1
- data/lib/generators/active_record/templates/migration_existing.rb +1 -1
- data/lib/motion-aasm.rb +2 -0
- data/spec/models/active_record/complex_active_record_example.rb +5 -1
- data/spec/models/guardian_without_from_specified.rb +18 -0
- data/spec/models/namespaced_multiple_example.rb +14 -0
- data/spec/models/redis/complex_redis_example.rb +40 -0
- data/spec/models/redis/redis_multiple.rb +20 -0
- data/spec/models/redis/redis_simple.rb +20 -0
- data/spec/models/sequel/complex_sequel_example.rb +4 -3
- data/spec/models/sequel/invalid_persistor.rb +52 -0
- data/spec/models/sequel/sequel_multiple.rb +13 -13
- data/spec/models/sequel/sequel_simple.rb +13 -12
- data/spec/models/sequel/silent_persistor.rb +50 -0
- data/spec/models/sequel/transactor.rb +112 -0
- data/spec/models/sequel/validator.rb +93 -0
- data/spec/models/sequel/worker.rb +12 -0
- data/spec/spec_helpers/redis.rb +8 -0
- data/spec/unit/guard_spec.rb +17 -0
- data/spec/unit/guard_without_from_specified_spec.rb +10 -0
- data/spec/unit/namespaced_multiple_example_spec.rb +22 -0
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +4 -4
- data/spec/unit/persistence/active_record_persistence_spec.rb +4 -4
- data/spec/unit/persistence/redis_persistence_multiple_spec.rb +88 -0
- data/spec/unit/persistence/redis_persistence_spec.rb +5 -25
- data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +2 -2
- data/spec/unit/persistence/sequel_persistence_spec.rb +275 -2
- data/test/minitest_helper.rb +57 -0
- data/test/unit/minitest_matcher_test.rb +80 -0
- 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
|
data/spec/spec_helpers/redis.rb
CHANGED
@@ -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
|
data/spec/unit/guard_spec.rb
CHANGED
@@ -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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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
|
-
|
7
|
-
|
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
|
-
|
22
|
-
|
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 =
|
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 =
|
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 =
|
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
|