aasm 4.11.0 → 4.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +16 -17
- data/Appraisals +44 -0
- data/CHANGELOG.md +24 -1
- data/CONTRIBUTING.md +24 -0
- data/Gemfile +4 -21
- data/README.md +81 -34
- data/Rakefile +6 -1
- data/TESTING.md +25 -0
- data/aasm.gemspec +5 -2
- data/gemfiles/rails_3.2.gemfile +13 -0
- data/gemfiles/rails_4.0.gemfile +8 -9
- data/gemfiles/rails_4.2.gemfile +9 -9
- data/gemfiles/rails_4.2_mongoid_5.gemfile +5 -9
- data/gemfiles/rails_5.0.gemfile +7 -16
- data/lib/aasm/aasm.rb +9 -3
- data/lib/aasm/base.rb +3 -1
- data/lib/aasm/configuration.rb +4 -0
- data/lib/aasm/core/event.rb +19 -9
- data/lib/aasm/core/state.rb +7 -0
- data/lib/aasm/core/transition.rb +9 -0
- data/lib/aasm/instance_base.rb +8 -8
- data/lib/aasm/persistence/active_record_persistence.rb +1 -1
- data/lib/aasm/persistence/base.rb +3 -3
- data/lib/aasm/persistence/mongoid_persistence.rb +48 -9
- data/lib/aasm/persistence.rb +0 -3
- data/lib/aasm/state_machine.rb +4 -2
- data/lib/aasm/state_machine_store.rb +5 -2
- data/lib/aasm/version.rb +1 -1
- data/lib/generators/active_record/aasm_generator.rb +1 -1
- data/lib/motion-aasm.rb +0 -1
- data/spec/en.yml +3 -0
- data/spec/generators/active_record_generator_spec.rb +42 -34
- data/spec/generators/mongoid_generator_spec.rb +4 -6
- data/spec/models/{invalid_persistor.rb → active_record/invalid_persistor.rb} +0 -2
- data/spec/models/{silent_persistor.rb → active_record/silent_persistor.rb} +0 -2
- data/spec/models/active_record/simple_new_dsl.rb +2 -2
- data/spec/models/{transactor.rb → active_record/transactor.rb} +0 -2
- data/spec/models/{validator.rb → active_record/validator.rb} +6 -3
- data/spec/models/callbacks/basic.rb +5 -2
- data/spec/models/guard_with_params.rb +24 -0
- data/spec/models/guard_with_params_multiple.rb +18 -0
- data/spec/models/mongoid/invalid_persistor_mongoid.rb +39 -0
- data/spec/models/mongoid/silent_persistor_mongoid.rb +39 -0
- data/spec/models/mongoid/simple_mongoid.rb +2 -2
- data/spec/models/mongoid/validator_mongoid.rb +100 -0
- data/spec/models/multi_transitioner.rb +10 -0
- data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +31 -0
- data/spec/models/parametrised_event.rb +7 -0
- data/spec/models/simple_multiple_example.rb +12 -0
- data/spec/models/sub_class.rb +34 -0
- data/spec/spec_helper.rb +0 -33
- data/spec/spec_helpers/active_record.rb +7 -0
- data/spec/spec_helpers/dynamoid.rb +33 -0
- data/spec/spec_helpers/mongoid.rb +7 -0
- data/spec/spec_helpers/redis.rb +7 -0
- data/spec/spec_helpers/remove_warnings.rb +1 -0
- data/spec/spec_helpers/sequel.rb +7 -0
- data/spec/unit/api_spec.rb +76 -73
- data/spec/unit/callbacks_spec.rb +5 -0
- data/spec/unit/event_spec.rb +12 -0
- data/spec/unit/guard_with_params_multiple_spec.rb +10 -0
- data/spec/unit/guard_with_params_spec.rb +14 -0
- data/spec/unit/inspection_spec.rb +5 -0
- data/spec/unit/localizer_spec.rb +55 -53
- data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +14 -0
- data/spec/unit/override_warning_spec.rb +8 -0
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +452 -445
- data/spec/unit/persistence/active_record_persistence_spec.rb +523 -490
- data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +4 -9
- data/spec/unit/persistence/dynamoid_persistence_spec.rb +4 -9
- data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +87 -10
- data/spec/unit/persistence/mongoid_persistence_spec.rb +89 -10
- data/spec/unit/persistence/redis_persistence_spec.rb +3 -7
- data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +4 -9
- data/spec/unit/persistence/sequel_persistence_spec.rb +4 -9
- data/spec/unit/simple_multiple_example_spec.rb +28 -0
- data/spec/unit/subclassing_multiple_spec.rb +37 -2
- data/spec/unit/subclassing_spec.rb +17 -2
- metadata +77 -32
- data/gemfiles/rails_3.2_stable.gemfile +0 -15
- data/gemfiles/rails_4.0_mongo_mapper.gemfile +0 -17
- data/gemfiles/rails_4.2_mongo_mapper.gemfile +0 -18
- data/lib/aasm/persistence/mongo_mapper_persistence.rb +0 -163
- data/spec/models/mongo_mapper/complex_mongo_mapper_example.rb +0 -37
- data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +0 -21
- data/spec/models/mongo_mapper/simple_mongo_mapper.rb +0 -23
- data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +0 -25
- data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +0 -146
- data/spec/unit/persistence/mongo_mapper_persistence_spec.rb +0 -93
- /data/spec/models/{worker.rb → active_record/worker.rb} +0 -0
|
@@ -1,688 +1,721 @@
|
|
|
1
|
-
require 'active_record'
|
|
2
1
|
require 'spec_helper'
|
|
3
|
-
Dir[File.dirname(__FILE__) + "/../../models/active_record/*.rb"].sort.each do |f|
|
|
4
|
-
require File.expand_path(f)
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
load_schema
|
|
8
|
-
|
|
9
|
-
# if you want to see the statements while running the spec enable the following line
|
|
10
|
-
# require 'logger'
|
|
11
|
-
# ActiveRecord::Base.logger = Logger.new(STDERR)
|
|
12
2
|
|
|
13
|
-
|
|
14
|
-
let(:gate) {Gate.new}
|
|
3
|
+
if defined?(ActiveRecord)
|
|
15
4
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
expect(gate).to respond_to(:aasm_write_state)
|
|
19
|
-
expect(gate).to respond_to(:aasm_write_state_without_persistence)
|
|
5
|
+
Dir[File.dirname(__FILE__) + "/../../models/active_record/*.rb"].sort.each do |f|
|
|
6
|
+
require File.expand_path(f)
|
|
20
7
|
end
|
|
21
8
|
|
|
22
|
-
|
|
23
|
-
subject { lambda{ gate.send(:aasm_column_looks_like_enum) } }
|
|
9
|
+
load_schema
|
|
24
10
|
|
|
25
|
-
|
|
26
|
-
|
|
11
|
+
# if you want to see the statements while running the spec enable the following line
|
|
12
|
+
# require 'logger'
|
|
13
|
+
# ActiveRecord::Base.logger = Logger.new(STDERR)
|
|
27
14
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
allow(gate.class).to receive(:columns_hash).and_return(columns_hash)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
context "when AASM column has integer type" do
|
|
34
|
-
let(:column) { double(Object, type: :integer) }
|
|
15
|
+
describe "instance methods" do
|
|
16
|
+
let(:gate) {Gate.new}
|
|
35
17
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
18
|
+
it "should respond to aasm persistence methods" do
|
|
19
|
+
expect(gate).to respond_to(:aasm_read_state)
|
|
20
|
+
expect(gate).to respond_to(:aasm_write_state)
|
|
21
|
+
expect(gate).to respond_to(:aasm_write_state_without_persistence)
|
|
39
22
|
end
|
|
40
23
|
|
|
41
|
-
|
|
42
|
-
|
|
24
|
+
describe "aasm_column_looks_like_enum" do
|
|
25
|
+
subject { lambda{ gate.send(:aasm_column_looks_like_enum) } }
|
|
43
26
|
|
|
44
|
-
|
|
45
|
-
|
|
27
|
+
let(:column_name) { "value" }
|
|
28
|
+
let(:columns_hash) { Hash[column_name, column] }
|
|
29
|
+
|
|
30
|
+
before :each do
|
|
31
|
+
allow(gate.class.aasm).to receive(:attribute_name).and_return(column_name.to_sym)
|
|
32
|
+
allow(gate.class).to receive(:columns_hash).and_return(columns_hash)
|
|
46
33
|
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
34
|
|
|
50
|
-
|
|
51
|
-
|
|
35
|
+
context "when AASM column has integer type" do
|
|
36
|
+
let(:column) { double(Object, type: :integer) }
|
|
52
37
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
it "pluralizes AASM column name" do
|
|
58
|
-
expect(subject.call).to eq :values
|
|
59
|
-
end
|
|
60
|
-
end
|
|
38
|
+
it "returns true" do
|
|
39
|
+
expect(subject.call).to be_truthy
|
|
40
|
+
end
|
|
41
|
+
end
|
|
61
42
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
let(:with_enum) { WithEnum.new }
|
|
43
|
+
context "when AASM column has string type" do
|
|
44
|
+
let(:column) { double(Object, type: :string) }
|
|
65
45
|
|
|
66
|
-
|
|
67
|
-
|
|
46
|
+
it "returns false" do
|
|
47
|
+
expect(subject.call).to be_falsey
|
|
48
|
+
end
|
|
68
49
|
end
|
|
69
50
|
end
|
|
70
51
|
|
|
71
|
-
|
|
72
|
-
|
|
52
|
+
describe "aasm_guess_enum_method" do
|
|
53
|
+
subject { lambda{ gate.send(:aasm_guess_enum_method) } }
|
|
54
|
+
|
|
73
55
|
before :each do
|
|
74
|
-
allow(
|
|
56
|
+
allow(gate.class.aasm).to receive(:attribute_name).and_return(:value)
|
|
75
57
|
end
|
|
76
58
|
|
|
77
|
-
it "
|
|
78
|
-
expect(
|
|
59
|
+
it "pluralizes AASM column name" do
|
|
60
|
+
expect(subject.call).to eq :values
|
|
79
61
|
end
|
|
80
62
|
end
|
|
81
63
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
it "returns nil" do
|
|
86
|
-
expect(with_false_enum.send(:aasm_enum)).to be_nil
|
|
87
|
-
end
|
|
88
|
-
end
|
|
64
|
+
describe "aasm_enum" do
|
|
65
|
+
context "when AASM enum setting contains an explicit enum method name" do
|
|
66
|
+
let(:with_enum) { WithEnum.new }
|
|
89
67
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
68
|
+
it "returns whatever value was set in AASM config" do
|
|
69
|
+
expect(with_enum.send(:aasm_enum)).to eq :test
|
|
70
|
+
end
|
|
93
71
|
end
|
|
94
72
|
|
|
95
|
-
context "when AASM
|
|
73
|
+
context "when AASM enum setting is simply set to true" do
|
|
74
|
+
let(:with_true_enum) { WithTrueEnum.new }
|
|
96
75
|
before :each do
|
|
97
|
-
allow(
|
|
76
|
+
allow(WithTrueEnum.aasm).to receive(:attribute_name).and_return(:value)
|
|
98
77
|
end
|
|
99
78
|
|
|
100
79
|
it "infers enum method name from pluralized column name" do
|
|
101
|
-
expect(
|
|
80
|
+
expect(with_true_enum.send(:aasm_enum)).to eq :values
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
context "when AASM enum setting is explicitly disabled" do
|
|
85
|
+
let(:with_false_enum) { WithFalseEnum.new }
|
|
86
|
+
|
|
87
|
+
it "returns nil" do
|
|
88
|
+
expect(with_false_enum.send(:aasm_enum)).to be_nil
|
|
102
89
|
end
|
|
103
90
|
end
|
|
104
91
|
|
|
105
|
-
context "when AASM
|
|
92
|
+
context "when AASM enum setting is not enabled" do
|
|
106
93
|
before :each do
|
|
107
|
-
allow(
|
|
108
|
-
.and_return(false)
|
|
94
|
+
allow(Gate.aasm).to receive(:attribute_name).and_return(:value)
|
|
109
95
|
end
|
|
110
96
|
|
|
111
|
-
|
|
112
|
-
|
|
97
|
+
context "when AASM column looks like enum" do
|
|
98
|
+
before :each do
|
|
99
|
+
allow(gate).to receive(:aasm_column_looks_like_enum).and_return(true)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "infers enum method name from pluralized column name" do
|
|
103
|
+
expect(gate.send(:aasm_enum)).to eq :values
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
context "when AASM column doesn't look like enum'" do
|
|
108
|
+
before :each do
|
|
109
|
+
allow(gate).to receive(:aasm_column_looks_like_enum)
|
|
110
|
+
.and_return(false)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "returns nil, as we're not using enum" do
|
|
114
|
+
expect(gate.send(:aasm_enum)).to be_nil
|
|
115
|
+
end
|
|
113
116
|
end
|
|
114
117
|
end
|
|
115
|
-
end
|
|
116
118
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
if ActiveRecord::VERSION::MAJOR >= 4 && ActiveRecord::VERSION::MINOR >= 1 # won't work with Rails <= 4.1
|
|
120
|
+
# Enum are introduced from Rails 4.1, therefore enum syntax will not work on Rails <= 4.1
|
|
121
|
+
context "when AASM enum setting is not enabled and aasm column not present" do
|
|
120
122
|
|
|
121
|
-
|
|
123
|
+
let(:with_enum_without_column) {WithEnumWithoutColumn.new}
|
|
122
124
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
it "should raise NoMethodError for transitions" do
|
|
126
|
+
expect{with_enum_without_column.send(:view)}.to raise_error(NoMethodError, "undefined method 'status' for WithEnumWithoutColumn")
|
|
127
|
+
end
|
|
125
128
|
end
|
|
129
|
+
|
|
126
130
|
end
|
|
127
131
|
|
|
128
132
|
end
|
|
129
133
|
|
|
130
|
-
|
|
134
|
+
context "when AASM is configured to use enum" do
|
|
135
|
+
let(:state_sym) { :running }
|
|
136
|
+
let(:state_code) { 2 }
|
|
137
|
+
let(:enum_name) { :states }
|
|
138
|
+
let(:enum) { Hash[state_sym, state_code] }
|
|
131
139
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
let(:enum) { Hash[state_sym, state_code] }
|
|
140
|
+
before :each do
|
|
141
|
+
allow(gate).to receive(:aasm_enum).and_return(enum_name)
|
|
142
|
+
allow(gate).to receive(:aasm_write_attribute)
|
|
143
|
+
allow(gate).to receive(:write_attribute)
|
|
137
144
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
allow(gate).to receive(:aasm_write_attribute)
|
|
141
|
-
allow(gate).to receive(:write_attribute)
|
|
145
|
+
allow(Gate).to receive(enum_name).and_return(enum)
|
|
146
|
+
end
|
|
142
147
|
|
|
143
|
-
|
|
144
|
-
|
|
148
|
+
describe "aasm_write_state" do
|
|
149
|
+
context "when AASM is configured to skip validations on save" do
|
|
150
|
+
before :each do
|
|
151
|
+
allow(gate).to receive(:aasm_skipping_validations).and_return(true)
|
|
152
|
+
end
|
|
145
153
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
it "passes state code instead of state symbol to update_all" do
|
|
155
|
+
# stub_chain does not allow us to give expectations on call
|
|
156
|
+
# parameters in the middle of the chain, so we need to use
|
|
157
|
+
# intermediate object instead.
|
|
158
|
+
obj = double(Object, update_all: 1)
|
|
159
|
+
allow(Gate).to receive_message_chain(:unscoped, :where).and_return(obj)
|
|
160
|
+
|
|
161
|
+
gate.aasm_write_state state_sym
|
|
162
|
+
|
|
163
|
+
expect(obj).to have_received(:update_all)
|
|
164
|
+
.with(Hash[gate.class.aasm.attribute_name, state_code])
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it "searches model outside of default_scope when update_all" do
|
|
168
|
+
# stub_chain does not allow us to give expectations on call
|
|
169
|
+
# parameters in the middle of the chain, so we need to use
|
|
170
|
+
# intermediate object instead.
|
|
171
|
+
unscoped = double(Object, update_all: 1)
|
|
172
|
+
scoped = double(Object, update_all: 1)
|
|
173
|
+
|
|
174
|
+
allow(Gate).to receive(:unscoped).and_return(unscoped)
|
|
175
|
+
allow(Gate).to receive(:where).and_return(scoped)
|
|
176
|
+
allow(unscoped).to receive(:where).and_return(unscoped)
|
|
177
|
+
|
|
178
|
+
gate.aasm_write_state state_sym
|
|
179
|
+
|
|
180
|
+
expect(unscoped).to have_received(:update_all)
|
|
181
|
+
.with(Hash[gate.class.aasm.attribute_name, state_code])
|
|
182
|
+
expect(scoped).to_not have_received(:update_all)
|
|
183
|
+
.with(Hash[gate.class.aasm.attribute_name, state_code])
|
|
184
|
+
end
|
|
150
185
|
end
|
|
151
186
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
obj = double(Object, update_all: 1)
|
|
157
|
-
allow(Gate).to receive(:where).and_return(obj)
|
|
187
|
+
context "when AASM is not skipping validations" do
|
|
188
|
+
it "delegates state update to the helper method" do
|
|
189
|
+
# Let's pretend that validation is passed
|
|
190
|
+
allow(gate).to receive(:save).and_return(true)
|
|
158
191
|
|
|
159
|
-
|
|
192
|
+
gate.aasm_write_state state_sym
|
|
160
193
|
|
|
161
|
-
|
|
162
|
-
|
|
194
|
+
expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :default)
|
|
195
|
+
expect(gate).to_not have_received :write_attribute
|
|
196
|
+
end
|
|
163
197
|
end
|
|
164
198
|
end
|
|
165
199
|
|
|
166
|
-
|
|
200
|
+
describe "aasm_write_state_without_persistence" do
|
|
167
201
|
it "delegates state update to the helper method" do
|
|
168
|
-
|
|
169
|
-
allow(gate).to receive(:save).and_return(true)
|
|
170
|
-
|
|
171
|
-
gate.aasm_write_state state_sym
|
|
202
|
+
gate.aasm_write_state_without_persistence state_sym
|
|
172
203
|
|
|
173
204
|
expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :default)
|
|
174
205
|
expect(gate).to_not have_received :write_attribute
|
|
175
206
|
end
|
|
176
207
|
end
|
|
208
|
+
|
|
209
|
+
describe "aasm_raw_attribute_value" do
|
|
210
|
+
it "converts state symbol to state code" do
|
|
211
|
+
expect(gate.send(:aasm_raw_attribute_value, state_sym))
|
|
212
|
+
.to eq state_code
|
|
213
|
+
end
|
|
214
|
+
end
|
|
177
215
|
end
|
|
178
216
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
gate.aasm_write_state_without_persistence state_sym
|
|
217
|
+
context "when AASM is configured to use string field" do
|
|
218
|
+
let(:state_sym) { :running }
|
|
182
219
|
|
|
183
|
-
|
|
184
|
-
|
|
220
|
+
before :each do
|
|
221
|
+
allow(gate).to receive(:aasm_enum).and_return(nil)
|
|
185
222
|
end
|
|
186
|
-
end
|
|
187
223
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
224
|
+
describe "aasm_raw_attribute_value" do
|
|
225
|
+
it "converts state symbol to string" do
|
|
226
|
+
expect(gate.send(:aasm_raw_attribute_value, state_sym))
|
|
227
|
+
.to eq state_sym.to_s
|
|
228
|
+
end
|
|
192
229
|
end
|
|
193
230
|
end
|
|
194
|
-
end
|
|
195
231
|
|
|
196
|
-
|
|
197
|
-
|
|
232
|
+
describe "aasm_write_attribute helper method" do
|
|
233
|
+
let(:sym) { :sym }
|
|
234
|
+
let(:value) { 42 }
|
|
198
235
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
236
|
+
before :each do
|
|
237
|
+
allow(gate).to receive(:write_attribute)
|
|
238
|
+
allow(gate).to receive(:aasm_raw_attribute_value).and_return(value)
|
|
202
239
|
|
|
203
|
-
|
|
204
|
-
it "converts state symbol to string" do
|
|
205
|
-
expect(gate.send(:aasm_raw_attribute_value, state_sym))
|
|
206
|
-
.to eq state_sym.to_s
|
|
240
|
+
gate.send(:aasm_write_attribute, sym)
|
|
207
241
|
end
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
describe "aasm_write_attribute helper method" do
|
|
212
|
-
let(:sym) { :sym }
|
|
213
|
-
let(:value) { 42 }
|
|
214
242
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
243
|
+
it "generates attribute value using a helper method" do
|
|
244
|
+
expect(gate).to have_received(:aasm_raw_attribute_value).with(sym, :default)
|
|
245
|
+
end
|
|
218
246
|
|
|
219
|
-
|
|
247
|
+
it "writes attribute to the model" do
|
|
248
|
+
expect(gate).to have_received(:write_attribute).with(:aasm_state, value)
|
|
249
|
+
end
|
|
220
250
|
end
|
|
221
251
|
|
|
222
|
-
it "
|
|
223
|
-
expect(gate).to
|
|
252
|
+
it "should return the initial state when new and the aasm field is nil" do
|
|
253
|
+
expect(gate.aasm.current_state).to eq(:opened)
|
|
224
254
|
end
|
|
225
255
|
|
|
226
|
-
it "
|
|
227
|
-
|
|
256
|
+
it "should return the aasm column when new and the aasm field is not nil" do
|
|
257
|
+
gate.aasm_state = "closed"
|
|
258
|
+
expect(gate.aasm.current_state).to eq(:closed)
|
|
228
259
|
end
|
|
229
|
-
end
|
|
230
260
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
261
|
+
it "should return the aasm column when not new and the aasm.attribute_name is not nil" do
|
|
262
|
+
allow(gate).to receive(:new_record?).and_return(false)
|
|
263
|
+
gate.aasm_state = "state"
|
|
264
|
+
expect(gate.aasm.current_state).to eq(:state)
|
|
265
|
+
end
|
|
234
266
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
267
|
+
it "should allow a nil state" do
|
|
268
|
+
allow(gate).to receive(:new_record?).and_return(false)
|
|
269
|
+
gate.aasm_state = nil
|
|
270
|
+
expect(gate.aasm.current_state).to be_nil
|
|
271
|
+
end
|
|
239
272
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
273
|
+
context 'on initialization' do
|
|
274
|
+
it "should initialize the aasm state" do
|
|
275
|
+
expect(Gate.new.aasm_state).to eql 'opened'
|
|
276
|
+
expect(Gate.new.aasm.current_state).to eql :opened
|
|
277
|
+
end
|
|
245
278
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
end
|
|
279
|
+
it "should not initialize the aasm state if it has not been loaded" do
|
|
280
|
+
# we have to create a gate in the database, for which we only want to
|
|
281
|
+
# load the id, and not the state
|
|
282
|
+
gate = Gate.create!
|
|
251
283
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
expect(Gate.new.aasm.current_state).to eql :opened
|
|
284
|
+
# then we just load the gate ids
|
|
285
|
+
Gate.select(:id).where(id: gate.id).first
|
|
286
|
+
end
|
|
256
287
|
end
|
|
257
288
|
|
|
258
|
-
|
|
259
|
-
# we have to create a gate in the database, for which we only want to
|
|
260
|
-
# load the id, and not the state
|
|
261
|
-
gate = Gate.create!
|
|
289
|
+
end
|
|
262
290
|
|
|
263
|
-
|
|
264
|
-
|
|
291
|
+
if ActiveRecord::VERSION::MAJOR < 4 && ActiveRecord::VERSION::MINOR < 2 # won't work with Rails >= 4.2
|
|
292
|
+
describe "direct state column access" do
|
|
293
|
+
it "accepts false states" do
|
|
294
|
+
f = FalseState.create!
|
|
295
|
+
expect(f.aasm_state).to eql false
|
|
296
|
+
expect {
|
|
297
|
+
f.aasm.events.map(&:name)
|
|
298
|
+
}.to_not raise_error
|
|
265
299
|
end
|
|
266
300
|
end
|
|
301
|
+
end
|
|
267
302
|
|
|
268
|
-
|
|
303
|
+
describe 'subclasses' do
|
|
304
|
+
it "should have the same states as its parent class" do
|
|
305
|
+
expect(DerivateNewDsl.aasm.states).to eq(SimpleNewDsl.aasm.states)
|
|
306
|
+
end
|
|
269
307
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
f = FalseState.create!
|
|
274
|
-
expect(f.aasm_state).to eql false
|
|
275
|
-
expect {
|
|
276
|
-
f.aasm.events.map(&:name)
|
|
277
|
-
}.to_not raise_error
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
end
|
|
308
|
+
it "should have the same events as its parent class" do
|
|
309
|
+
expect(DerivateNewDsl.aasm.events).to eq(SimpleNewDsl.aasm.events)
|
|
310
|
+
end
|
|
281
311
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
312
|
+
it "should have the same column as its parent even for the new dsl" do
|
|
313
|
+
expect(SimpleNewDsl.aasm.attribute_name).to eq(:status)
|
|
314
|
+
expect(DerivateNewDsl.aasm.attribute_name).to eq(:status)
|
|
315
|
+
end
|
|
285
316
|
end
|
|
286
317
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
318
|
+
describe "named scopes with the new DSL" do
|
|
319
|
+
context "Does not already respond_to? the scope name" do
|
|
320
|
+
it "should add a scope for each state" do
|
|
321
|
+
expect(SimpleNewDsl).to respond_to(:unknown_scope)
|
|
322
|
+
expect(SimpleNewDsl).to respond_to(:another_unknown_scope)
|
|
290
323
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
end
|
|
324
|
+
expect(SimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
|
|
325
|
+
expect(SimpleNewDsl.another_unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
|
|
326
|
+
end
|
|
327
|
+
end
|
|
296
328
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
329
|
+
context "Already respond_to? the scope name" do
|
|
330
|
+
it "should not add a scope" do
|
|
331
|
+
expect(SimpleNewDsl).to respond_to(:new)
|
|
332
|
+
expect(SimpleNewDsl.new.class).to eq(SimpleNewDsl)
|
|
333
|
+
end
|
|
302
334
|
end
|
|
303
|
-
end
|
|
304
335
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
expect(SimpleNewDsl).to respond_to(:new)
|
|
308
|
-
expect(SimpleNewDsl.new.class).to eq(SimpleNewDsl)
|
|
336
|
+
it "does not create scopes if requested" do
|
|
337
|
+
expect(NoScope).not_to respond_to(:pending)
|
|
309
338
|
end
|
|
310
|
-
end
|
|
311
339
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
340
|
+
context "result of scope" do
|
|
341
|
+
let!(:dsl1) { SimpleNewDsl.create!(status: :new) }
|
|
342
|
+
let!(:dsl2) { SimpleNewDsl.create!(status: :unknown_scope) }
|
|
315
343
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
344
|
+
after do
|
|
345
|
+
SimpleNewDsl.destroy_all
|
|
346
|
+
end
|
|
319
347
|
|
|
320
|
-
|
|
321
|
-
|
|
348
|
+
it "created scope works as where(name: :scope_name)" do
|
|
349
|
+
expect(SimpleNewDsl.unknown_scope).to contain_exactly(dsl2)
|
|
350
|
+
end
|
|
322
351
|
end
|
|
352
|
+
end # scopes
|
|
323
353
|
|
|
324
|
-
|
|
325
|
-
|
|
354
|
+
describe "direct assignment" do
|
|
355
|
+
it "is allowed by default" do
|
|
356
|
+
obj = NoScope.create
|
|
357
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
|
358
|
+
|
|
359
|
+
obj.aasm_state = :running
|
|
360
|
+
expect(obj.aasm_state.to_sym).to eql :running
|
|
326
361
|
end
|
|
327
|
-
end
|
|
328
|
-
end # scopes
|
|
329
362
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
expect(obj.aasm_state.to_sym).to eql :pending
|
|
363
|
+
it "is forbidden if configured" do
|
|
364
|
+
obj = NoDirectAssignment.create
|
|
365
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
|
334
366
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
367
|
+
expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
|
|
368
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
|
369
|
+
end
|
|
338
370
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
371
|
+
it 'can be turned off and on again' do
|
|
372
|
+
obj = NoDirectAssignment.create
|
|
373
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
|
342
374
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
end
|
|
375
|
+
expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
|
|
376
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
|
346
377
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
378
|
+
# allow it temporarily
|
|
379
|
+
NoDirectAssignment.aasm.state_machine.config.no_direct_assignment = false
|
|
380
|
+
obj.aasm_state = :pending
|
|
381
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
|
350
382
|
|
|
351
|
-
|
|
352
|
-
|
|
383
|
+
# and forbid it again
|
|
384
|
+
NoDirectAssignment.aasm.state_machine.config.no_direct_assignment = true
|
|
385
|
+
expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
|
|
386
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
|
387
|
+
end
|
|
388
|
+
end # direct assignment
|
|
353
389
|
|
|
354
|
-
|
|
355
|
-
NoDirectAssignment.aasm.state_machine.config.no_direct_assignment = false
|
|
356
|
-
obj.aasm_state = :pending
|
|
357
|
-
expect(obj.aasm_state.to_sym).to eql :pending
|
|
390
|
+
describe 'initial states' do
|
|
358
391
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
392
|
+
it 'should support conditions' do
|
|
393
|
+
expect(Thief.new(:skilled => true).aasm.current_state).to eq(:rich)
|
|
394
|
+
expect(Thief.new(:skilled => false).aasm.current_state).to eq(:jailed)
|
|
395
|
+
end
|
|
363
396
|
end
|
|
364
|
-
end # direct assignment
|
|
365
397
|
|
|
366
|
-
describe '
|
|
398
|
+
describe 'transitions with persistence' do
|
|
367
399
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
400
|
+
it "should work for valid models" do
|
|
401
|
+
valid_object = Validator.create(:name => 'name')
|
|
402
|
+
expect(valid_object).to be_sleeping
|
|
403
|
+
valid_object.status = :running
|
|
404
|
+
expect(valid_object).to be_running
|
|
405
|
+
end
|
|
373
406
|
|
|
374
|
-
|
|
407
|
+
it 'should not store states for invalid models' do
|
|
408
|
+
validator = Validator.create(:name => 'name')
|
|
409
|
+
expect(validator).to be_valid
|
|
410
|
+
expect(validator).to be_sleeping
|
|
375
411
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
expect(valid_object).to be_running
|
|
381
|
-
end
|
|
412
|
+
validator.name = nil
|
|
413
|
+
expect(validator).not_to be_valid
|
|
414
|
+
expect { validator.run! }.to raise_error(ActiveRecord::RecordInvalid)
|
|
415
|
+
expect(validator).to be_sleeping
|
|
382
416
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
expect(validator).to be_sleeping
|
|
417
|
+
validator.reload
|
|
418
|
+
expect(validator).not_to be_running
|
|
419
|
+
expect(validator).to be_sleeping
|
|
387
420
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
421
|
+
validator.name = 'another name'
|
|
422
|
+
expect(validator).to be_valid
|
|
423
|
+
expect(validator.run!).to be_truthy
|
|
424
|
+
expect(validator).to be_running
|
|
392
425
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
426
|
+
validator.reload
|
|
427
|
+
expect(validator).to be_running
|
|
428
|
+
expect(validator).not_to be_sleeping
|
|
429
|
+
end
|
|
396
430
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
431
|
+
it 'should not store states for invalid models silently if configured' do
|
|
432
|
+
validator = SilentPersistor.create(:name => 'name')
|
|
433
|
+
expect(validator).to be_valid
|
|
434
|
+
expect(validator).to be_sleeping
|
|
401
435
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
436
|
+
validator.name = nil
|
|
437
|
+
expect(validator).not_to be_valid
|
|
438
|
+
expect(validator.run!).to be_falsey
|
|
439
|
+
expect(validator).to be_sleeping
|
|
406
440
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
expect(validator).to be_sleeping
|
|
441
|
+
validator.reload
|
|
442
|
+
expect(validator).not_to be_running
|
|
443
|
+
expect(validator).to be_sleeping
|
|
411
444
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
445
|
+
validator.name = 'another name'
|
|
446
|
+
expect(validator).to be_valid
|
|
447
|
+
expect(validator.run!).to be_truthy
|
|
448
|
+
expect(validator).to be_running
|
|
416
449
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
450
|
+
validator.reload
|
|
451
|
+
expect(validator).to be_running
|
|
452
|
+
expect(validator).not_to be_sleeping
|
|
453
|
+
end
|
|
420
454
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
455
|
+
it 'should store states for invalid models if configured' do
|
|
456
|
+
persistor = InvalidPersistor.create(:name => 'name')
|
|
457
|
+
expect(persistor).to be_valid
|
|
458
|
+
expect(persistor).to be_sleeping
|
|
425
459
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
460
|
+
persistor.name = nil
|
|
461
|
+
expect(persistor).not_to be_valid
|
|
462
|
+
expect(persistor.run!).to be_truthy
|
|
463
|
+
expect(persistor).to be_running
|
|
430
464
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
persistor = InvalidPersistor.find(persistor.id)
|
|
442
|
-
persistor.valid?
|
|
443
|
-
expect(persistor).to be_valid
|
|
444
|
-
expect(persistor).to be_running
|
|
445
|
-
expect(persistor).not_to be_sleeping
|
|
446
|
-
|
|
447
|
-
persistor.reload
|
|
448
|
-
expect(persistor).to be_running
|
|
449
|
-
expect(persistor).not_to be_sleeping
|
|
450
|
-
end
|
|
465
|
+
persistor = InvalidPersistor.find(persistor.id)
|
|
466
|
+
persistor.valid?
|
|
467
|
+
expect(persistor).to be_valid
|
|
468
|
+
expect(persistor).to be_running
|
|
469
|
+
expect(persistor).not_to be_sleeping
|
|
470
|
+
|
|
471
|
+
persistor.reload
|
|
472
|
+
expect(persistor).to be_running
|
|
473
|
+
expect(persistor).not_to be_sleeping
|
|
474
|
+
end
|
|
451
475
|
|
|
452
|
-
|
|
453
|
-
|
|
476
|
+
describe 'pessimistic locking' do
|
|
477
|
+
let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
|
|
454
478
|
|
|
455
|
-
|
|
479
|
+
subject { transactor.run! }
|
|
456
480
|
|
|
457
|
-
|
|
458
|
-
|
|
481
|
+
context 'no lock' do
|
|
482
|
+
let(:transactor) { NoLockTransactor.create!(:name => 'no_lock_transactor', :worker => worker) }
|
|
459
483
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
484
|
+
it 'should not invoke lock!' do
|
|
485
|
+
expect(transactor).to_not receive(:lock!)
|
|
486
|
+
subject
|
|
487
|
+
end
|
|
463
488
|
end
|
|
464
|
-
end
|
|
465
489
|
|
|
466
|
-
|
|
467
|
-
|
|
490
|
+
context 'a default lock' do
|
|
491
|
+
let(:transactor) { LockTransactor.create!(:name => 'lock_transactor', :worker => worker) }
|
|
468
492
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
493
|
+
it 'should invoke lock! with true' do
|
|
494
|
+
expect(transactor).to receive(:lock!).with(true).and_call_original
|
|
495
|
+
subject
|
|
496
|
+
end
|
|
472
497
|
end
|
|
473
|
-
end
|
|
474
498
|
|
|
475
|
-
|
|
476
|
-
|
|
499
|
+
context 'a FOR UPDATE NOWAIT lock' do
|
|
500
|
+
let(:transactor) { LockNoWaitTransactor.create!(:name => 'lock_no_wait_transactor', :worker => worker) }
|
|
477
501
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
502
|
+
it 'should invoke lock! with FOR UPDATE NOWAIT' do
|
|
503
|
+
expect(transactor).to receive(:lock!).with('FOR UPDATE NOWAIT').and_call_original
|
|
504
|
+
subject
|
|
505
|
+
end
|
|
481
506
|
end
|
|
482
507
|
end
|
|
483
|
-
end
|
|
484
|
-
|
|
485
|
-
describe 'transactions' do
|
|
486
|
-
let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
|
|
487
|
-
let(:transactor) { Transactor.create!(:name => 'transactor', :worker => worker) }
|
|
488
508
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
509
|
+
describe 'transactions' do
|
|
510
|
+
let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
|
|
511
|
+
let(:transactor) { Transactor.create!(:name => 'transactor', :worker => worker) }
|
|
492
512
|
|
|
493
|
-
|
|
494
|
-
expect(transactor).to be_running
|
|
495
|
-
expect(worker.reload.status).to eq('sleeping')
|
|
496
|
-
end
|
|
497
|
-
|
|
498
|
-
context "nested transactions" do
|
|
499
|
-
it "should rollback all changes in nested transaction" do
|
|
513
|
+
it 'should rollback all changes' do
|
|
500
514
|
expect(transactor).to be_sleeping
|
|
501
515
|
expect(worker.status).to eq('sleeping')
|
|
502
516
|
|
|
503
|
-
|
|
504
|
-
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
|
|
505
|
-
end
|
|
506
|
-
|
|
517
|
+
expect {transactor.run!}.to raise_error(StandardError, 'failed on purpose')
|
|
507
518
|
expect(transactor).to be_running
|
|
508
519
|
expect(worker.reload.status).to eq('sleeping')
|
|
509
520
|
end
|
|
510
521
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
522
|
+
context "nested transactions" do
|
|
523
|
+
it "should rollback all changes in nested transaction" do
|
|
524
|
+
expect(transactor).to be_sleeping
|
|
525
|
+
expect(worker.status).to eq('sleeping')
|
|
514
526
|
|
|
515
|
-
|
|
516
|
-
|
|
527
|
+
Worker.transaction do
|
|
528
|
+
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
|
|
529
|
+
end
|
|
517
530
|
|
|
518
|
-
|
|
519
|
-
expect
|
|
531
|
+
expect(transactor).to be_running
|
|
532
|
+
expect(worker.reload.status).to eq('sleeping')
|
|
520
533
|
end
|
|
521
534
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
end
|
|
535
|
+
it "should only rollback changes in the main transaction not the nested one" do
|
|
536
|
+
# change configuration to not require new transaction
|
|
537
|
+
AASM::StateMachineStore[Transactor][:default].config.requires_new_transaction = false
|
|
526
538
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
validator = Validator.create(:name => 'name')
|
|
530
|
-
expect(validator).to be_sleeping
|
|
539
|
+
expect(transactor).to be_sleeping
|
|
540
|
+
expect(worker.status).to eq('sleeping')
|
|
531
541
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
542
|
+
Worker.transaction do
|
|
543
|
+
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
|
|
544
|
+
end
|
|
535
545
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
546
|
+
expect(transactor).to be_running
|
|
547
|
+
expect(worker.reload.status).to eq('running')
|
|
548
|
+
end
|
|
539
549
|
end
|
|
540
550
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
end
|
|
551
|
+
describe "after_commit callback" do
|
|
552
|
+
it "should fire :after_commit if transaction was successful" do
|
|
553
|
+
validator = Validator.create(:name => 'name')
|
|
554
|
+
expect(validator).to be_sleeping
|
|
546
555
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
validator.run
|
|
551
|
-
expect(validator).to be_running
|
|
552
|
-
expect(validator.name).to eq("name")
|
|
553
|
-
end
|
|
554
|
-
end
|
|
556
|
+
validator.run!
|
|
557
|
+
expect(validator).to be_running
|
|
558
|
+
expect(validator.name).to eq("name changed")
|
|
555
559
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
validator = Validator.create(:name => 'name')
|
|
561
|
-
expect(validator).to be_sleeping
|
|
560
|
+
validator.sleep!("sleeper")
|
|
561
|
+
expect(validator).to be_sleeping
|
|
562
|
+
expect(validator.name).to eq("sleeper")
|
|
563
|
+
end
|
|
562
564
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
565
|
+
it "should not fire :after_commit if transaction failed" do
|
|
566
|
+
validator = Validator.create(:name => 'name')
|
|
567
|
+
expect { validator.fail! }.to raise_error(StandardError, 'failed on purpose')
|
|
568
|
+
expect(validator.name).to eq("name")
|
|
569
|
+
end
|
|
566
570
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
end.to change { validator.send("#{event_type}_transaction_performed_on_fail") }.from(nil).to(true)
|
|
575
|
-
expect(validator).to_not be_running
|
|
576
|
-
end
|
|
571
|
+
it "should not fire :after_commit if validation failed when saving object" do
|
|
572
|
+
validator = Validator.create(:name => 'name')
|
|
573
|
+
validator.invalid = true
|
|
574
|
+
expect { validator.run! }.to raise_error(ActiveRecord::RecordInvalid, 'Invalid record')
|
|
575
|
+
expect(validator).to be_sleeping
|
|
576
|
+
expect(validator.name).to eq("name")
|
|
577
|
+
end
|
|
577
578
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
end
|
|
579
|
+
it "should not fire if not saving" do
|
|
580
|
+
validator = Validator.create(:name => 'name')
|
|
581
|
+
expect(validator).to be_sleeping
|
|
582
|
+
validator.run
|
|
583
|
+
expect(validator).to be_running
|
|
584
|
+
expect(validator.name).to eq("name")
|
|
585
585
|
end
|
|
586
586
|
end
|
|
587
|
-
end
|
|
588
587
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
588
|
+
describe 'before and after transaction callbacks' do
|
|
589
|
+
[:after, :before].each do |event_type|
|
|
590
|
+
describe "#{event_type}_transaction callback" do
|
|
591
|
+
it "should fire :#{event_type}_transaction if transaction was successful" do
|
|
592
|
+
validator = Validator.create(:name => 'name')
|
|
593
|
+
expect(validator).to be_sleeping
|
|
594
|
+
|
|
595
|
+
expect { validator.run! }.to change { validator.send("#{event_type}_transaction_performed_on_run") }.from(nil).to(true)
|
|
596
|
+
expect(validator).to be_running
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
it "should fire :#{event_type}_transaction if transaction failed" do
|
|
600
|
+
validator = Validator.create(:name => 'name')
|
|
601
|
+
expect do
|
|
602
|
+
begin
|
|
603
|
+
validator.fail!
|
|
604
|
+
rescue => ignored
|
|
605
|
+
end
|
|
606
|
+
end.to change { validator.send("#{event_type}_transaction_performed_on_fail") }.from(nil).to(true)
|
|
607
|
+
expect(validator).to_not be_running
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
it "should not fire :#{event_type}_transaction if not saving" do
|
|
611
|
+
validator = Validator.create(:name => 'name')
|
|
612
|
+
expect(validator).to be_sleeping
|
|
613
|
+
expect { validator.run }.to_not change { validator.send("#{event_type}_transaction_performed_on_run") }
|
|
614
|
+
expect(validator).to be_running
|
|
615
|
+
expect(validator.name).to eq("name")
|
|
616
|
+
end
|
|
609
617
|
end
|
|
618
|
+
end
|
|
619
|
+
end
|
|
610
620
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
621
|
+
describe 'before and after all transactions callbacks' do
|
|
622
|
+
[:after, :before].each do |event_type|
|
|
623
|
+
describe "#{event_type}_all_transactions callback" do
|
|
624
|
+
it "should fire :#{event_type}_all_transactions if transaction was successful" do
|
|
625
|
+
validator = Validator.create(:name => 'name')
|
|
626
|
+
expect(validator).to be_sleeping
|
|
627
|
+
|
|
628
|
+
expect { validator.run! }.to change { validator.send("#{event_type}_all_transactions_performed") }.from(nil).to(true)
|
|
629
|
+
expect(validator).to be_running
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
it "should fire :#{event_type}_all_transactions if transaction failed" do
|
|
633
|
+
validator = Validator.create(:name => 'name')
|
|
634
|
+
expect do
|
|
635
|
+
begin
|
|
636
|
+
validator.fail!
|
|
637
|
+
rescue => ignored
|
|
638
|
+
end
|
|
639
|
+
end.to change { validator.send("#{event_type}_all_transactions_performed") }.from(nil).to(true)
|
|
640
|
+
expect(validator).to_not be_running
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
it "should not fire :#{event_type}_all_transactions if not saving" do
|
|
644
|
+
validator = Validator.create(:name => 'name')
|
|
645
|
+
expect(validator).to be_sleeping
|
|
646
|
+
expect { validator.run }.to_not change { validator.send("#{event_type}_all_transactions_performed") }
|
|
647
|
+
expect(validator).to be_running
|
|
648
|
+
expect(validator.name).to eq("name")
|
|
649
|
+
end
|
|
617
650
|
end
|
|
618
651
|
end
|
|
619
652
|
end
|
|
620
|
-
end
|
|
621
653
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
654
|
+
context "when not persisting" do
|
|
655
|
+
it 'should not rollback all changes' do
|
|
656
|
+
expect(transactor).to be_sleeping
|
|
657
|
+
expect(worker.status).to eq('sleeping')
|
|
626
658
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
659
|
+
# Notice here we're calling "run" and not "run!" with a bang.
|
|
660
|
+
expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
|
|
661
|
+
expect(transactor).to be_running
|
|
662
|
+
expect(worker.reload.status).to eq('running')
|
|
663
|
+
end
|
|
632
664
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
665
|
+
it 'should not create a database transaction' do
|
|
666
|
+
expect(transactor.class).not_to receive(:transaction)
|
|
667
|
+
expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
|
|
668
|
+
end
|
|
636
669
|
end
|
|
637
670
|
end
|
|
638
671
|
end
|
|
639
|
-
end
|
|
640
672
|
|
|
641
|
-
describe "invalid states with persistence" do
|
|
673
|
+
describe "invalid states with persistence" do
|
|
642
674
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
675
|
+
it "should not store states" do
|
|
676
|
+
validator = Validator.create(:name => 'name')
|
|
677
|
+
validator.status = 'invalid_state'
|
|
678
|
+
expect(validator.save).to be_falsey
|
|
679
|
+
expect {validator.save!}.to raise_error(ActiveRecord::RecordInvalid)
|
|
648
680
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
681
|
+
validator.reload
|
|
682
|
+
expect(validator).to be_sleeping
|
|
683
|
+
end
|
|
652
684
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
685
|
+
it "should store invalid states if configured" do
|
|
686
|
+
persistor = InvalidPersistor.create(:name => 'name')
|
|
687
|
+
persistor.status = 'invalid_state'
|
|
688
|
+
expect(persistor.save).to be_truthy
|
|
657
689
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
690
|
+
persistor.reload
|
|
691
|
+
expect(persistor.status).to eq('invalid_state')
|
|
692
|
+
end
|
|
661
693
|
|
|
662
|
-
end
|
|
694
|
+
end
|
|
663
695
|
|
|
664
|
-
describe 'basic example with two state machines' do
|
|
665
|
-
|
|
696
|
+
describe 'basic example with two state machines' do
|
|
697
|
+
let(:example) { BasicActiveRecordTwoStateMachinesExample.new }
|
|
666
698
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
699
|
+
it 'should initialise properly' do
|
|
700
|
+
expect(example.aasm(:search).current_state).to eql :initialised
|
|
701
|
+
expect(example.aasm(:sync).current_state).to eql :unsynced
|
|
702
|
+
end
|
|
670
703
|
end
|
|
671
|
-
end
|
|
672
704
|
|
|
673
|
-
describe 'testing the README examples' do
|
|
674
|
-
|
|
675
|
-
|
|
705
|
+
describe 'testing the README examples' do
|
|
706
|
+
it 'Usage' do
|
|
707
|
+
job = ReadmeJob.new
|
|
676
708
|
|
|
677
|
-
|
|
678
|
-
|
|
709
|
+
expect(job.sleeping?).to eql true
|
|
710
|
+
expect(job.may_run?).to eql true
|
|
679
711
|
|
|
680
|
-
|
|
712
|
+
job.run
|
|
681
713
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
714
|
+
expect(job.running?).to eql true
|
|
715
|
+
expect(job.sleeping?).to eql false
|
|
716
|
+
expect(job.may_run?).to eql false
|
|
685
717
|
|
|
686
|
-
|
|
718
|
+
expect { job.run }.to raise_error(AASM::InvalidTransition)
|
|
719
|
+
end
|
|
687
720
|
end
|
|
688
721
|
end
|