statesman 3.4.1 → 3.5.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 +5 -5
- data/.circleci/config.yml +9 -9
- data/.rubocop_todo.yml +67 -2
- data/CHANGELOG.md +7 -0
- data/Gemfile +0 -4
- data/README.md +15 -1
- data/lib/generators/statesman/active_record_transition_generator.rb +1 -1
- data/lib/generators/statesman/migration_generator.rb +1 -1
- data/lib/generators/statesman/mongoid_transition_generator.rb +1 -1
- data/lib/generators/statesman/templates/create_migration.rb.erb +3 -3
- data/lib/generators/statesman/templates/update_migration.rb.erb +2 -2
- data/lib/statesman/adapters/active_record.rb +3 -3
- data/lib/statesman/adapters/active_record_queries.rb +7 -7
- data/lib/statesman/adapters/memory.rb +2 -2
- data/lib/statesman/adapters/mongoid.rb +1 -1
- data/lib/statesman/config.rb +0 -1
- data/lib/statesman/machine.rb +1 -1
- data/lib/statesman/version.rb +1 -1
- data/spec/generators/statesman/active_record_transition_generator_spec.rb +14 -9
- data/spec/generators/statesman/migration_generator_spec.rb +9 -9
- data/spec/generators/statesman/mongoid_transition_generator_spec.rb +7 -5
- data/spec/statesman/adapters/active_record_queries_spec.rb +17 -15
- data/spec/statesman/adapters/active_record_spec.rb +14 -4
- data/spec/statesman/adapters/memory_spec.rb +1 -0
- data/spec/statesman/{transition_spec.rb → adapters/memory_transition_spec.rb} +0 -0
- data/spec/statesman/adapters/mongoid_spec.rb +5 -1
- data/spec/statesman/adapters/shared_examples.rb +18 -9
- data/spec/statesman/callback_spec.rb +18 -6
- data/spec/statesman/config_spec.rb +6 -3
- data/spec/statesman/guard_spec.rb +3 -1
- data/spec/statesman/machine_spec.rb +52 -14
- data/spec/support/generators_shared_examples.rb +3 -1
- data/statesman.gemspec +12 -12
- metadata +23 -23
@@ -4,18 +4,20 @@ require "generators/statesman/mongoid_transition_generator"
|
|
4
4
|
|
5
5
|
describe Statesman::MongoidTransitionGenerator, type: :generator do
|
6
6
|
describe "the model contains the correct words" do
|
7
|
-
before { run_generator %w[Yummy::Bacon Yummy::BaconTransition] }
|
8
7
|
subject { file("app/models/yummy/bacon_transition.rb") }
|
9
8
|
|
10
|
-
|
9
|
+
before { run_generator %w[Yummy::Bacon Yummy::BaconTransition] }
|
10
|
+
|
11
|
+
it { is_expected.to_not contain(%r{:yummy/bacon}) }
|
11
12
|
it { is_expected.to contain(/class_name: 'Yummy::Bacon'/) }
|
12
13
|
end
|
13
14
|
|
14
15
|
describe "the model contains the correct words" do
|
15
|
-
before { run_generator %w[Bacon BaconTransition] }
|
16
16
|
subject { file("app/models/bacon_transition.rb") }
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
before { run_generator %w[Bacon BaconTransition] }
|
19
|
+
|
20
|
+
it { is_expected.to_not contain(/class_name:/) }
|
21
|
+
it { is_expected.to_not contain(/CreateYummy::Bacon/) }
|
20
22
|
end
|
21
23
|
end
|
@@ -6,14 +6,9 @@ describe Statesman::Adapters::ActiveRecordQueries, active_record: true do
|
|
6
6
|
prepare_transitions_table
|
7
7
|
prepare_other_model_table
|
8
8
|
prepare_other_transitions_table
|
9
|
-
end
|
10
9
|
|
11
|
-
before do
|
12
10
|
Statesman.configure { storage_adapter(Statesman::Adapters::ActiveRecord) }
|
13
|
-
end
|
14
|
-
after { Statesman.configure { storage_adapter(Statesman::Adapters::Memory) } }
|
15
11
|
|
16
|
-
before do
|
17
12
|
MyActiveRecordModel.send(:include, Statesman::Adapters::ActiveRecordQueries)
|
18
13
|
MyActiveRecordModel.class_eval do
|
19
14
|
def self.transition_class
|
@@ -36,9 +31,12 @@ describe Statesman::Adapters::ActiveRecordQueries, active_record: true do
|
|
36
31
|
:initial
|
37
32
|
end
|
38
33
|
end
|
34
|
+
|
35
|
+
MyActiveRecordModel.send(:has_one, :other_active_record_model)
|
36
|
+
OtherActiveRecordModel.send(:belongs_to, :my_active_record_model)
|
39
37
|
end
|
40
|
-
|
41
|
-
|
38
|
+
|
39
|
+
after { Statesman.configure { storage_adapter(Statesman::Adapters::Memory) } }
|
42
40
|
|
43
41
|
let!(:model) do
|
44
42
|
model = MyActiveRecordModel.create
|
@@ -66,7 +64,7 @@ describe Statesman::Adapters::ActiveRecordQueries, active_record: true do
|
|
66
64
|
subject { MyActiveRecordModel.in_state(:succeeded) }
|
67
65
|
|
68
66
|
it { is_expected.to include model }
|
69
|
-
it { is_expected.
|
67
|
+
it { is_expected.to_not include other_model }
|
70
68
|
end
|
71
69
|
|
72
70
|
context "given multiple states" do
|
@@ -104,23 +102,26 @@ describe Statesman::Adapters::ActiveRecordQueries, active_record: true do
|
|
104
102
|
describe ".not_in_state" do
|
105
103
|
context "given a single state" do
|
106
104
|
subject { MyActiveRecordModel.not_in_state(:failed) }
|
105
|
+
|
107
106
|
it { is_expected.to include model }
|
108
|
-
it { is_expected.
|
107
|
+
it { is_expected.to_not include other_model }
|
109
108
|
end
|
110
109
|
|
111
110
|
context "given multiple states" do
|
112
|
-
subject { MyActiveRecordModel.not_in_state(:succeeded, :failed) }
|
111
|
+
subject(:not_in_state) { MyActiveRecordModel.not_in_state(:succeeded, :failed) }
|
112
|
+
|
113
113
|
it do
|
114
|
-
|
115
|
-
|
114
|
+
expect(not_in_state).to match_array([initial_state_model,
|
115
|
+
returned_to_initial_model])
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
119
|
context "given an array of states" do
|
120
|
-
subject { MyActiveRecordModel.not_in_state(%i[succeeded failed]) }
|
120
|
+
subject(:not_in_state) { MyActiveRecordModel.not_in_state(%i[succeeded failed]) }
|
121
|
+
|
121
122
|
it do
|
122
|
-
|
123
|
-
|
123
|
+
expect(not_in_state).to match_array([initial_state_model,
|
124
|
+
returned_to_initial_model])
|
124
125
|
end
|
125
126
|
end
|
126
127
|
end
|
@@ -143,6 +144,7 @@ describe Statesman::Adapters::ActiveRecordQueries, active_record: true do
|
|
143
144
|
|
144
145
|
describe ".in_state" do
|
145
146
|
subject(:query) { MyActiveRecordModel.in_state(:succeeded) }
|
147
|
+
|
146
148
|
specify { expect { query }.to_not raise_error }
|
147
149
|
end
|
148
150
|
end
|
@@ -10,8 +10,10 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
before { MyActiveRecordModelTransition.serialize(:metadata, JSON) }
|
13
|
+
|
13
14
|
let(:observer) { double(Statesman::Machine, execute: nil) }
|
14
15
|
let(:model) { MyActiveRecordModel.create(current_state: :pending) }
|
16
|
+
|
15
17
|
it_behaves_like "an adapter", described_class, MyActiveRecordModelTransition
|
16
18
|
|
17
19
|
describe "#initialize" do
|
@@ -98,13 +100,14 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
98
100
|
end
|
99
101
|
|
100
102
|
describe "#create" do
|
103
|
+
subject { -> { create } }
|
104
|
+
|
101
105
|
let!(:adapter) do
|
102
106
|
described_class.new(MyActiveRecordModelTransition, model, observer)
|
103
107
|
end
|
104
108
|
let(:from) { :x }
|
105
109
|
let(:to) { :y }
|
106
110
|
let(:create) { adapter.create(from, to) }
|
107
|
-
subject { -> { create } }
|
108
111
|
|
109
112
|
context "when there is a race" do
|
110
113
|
it "raises a TransitionConflictError" do
|
@@ -132,11 +135,13 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
132
135
|
ActiveRecord::RecordNotUnique.new("unrelated", nil)
|
133
136
|
end
|
134
137
|
end
|
138
|
+
|
135
139
|
it { is_expected.to raise_exception(ActiveRecord::RecordNotUnique) }
|
136
140
|
end
|
137
141
|
|
138
142
|
context "other errors" do
|
139
143
|
let(:error) { StandardError }
|
144
|
+
|
140
145
|
it { is_expected.to raise_exception(StandardError) }
|
141
146
|
end
|
142
147
|
end
|
@@ -150,6 +155,7 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
150
155
|
|
151
156
|
context "with a previous transition" do
|
152
157
|
let!(:previous_transition) { adapter.create(from, to) }
|
158
|
+
|
153
159
|
its(:most_recent) { is_expected.to eq(true) }
|
154
160
|
|
155
161
|
it "updates the previous transition's most_recent flag" do
|
@@ -248,6 +254,7 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
248
254
|
context "with two previous transitions" do
|
249
255
|
let!(:previous_transition) { adapter.create(from, to) }
|
250
256
|
let!(:another_previous_transition) { adapter.create(from, to) }
|
257
|
+
|
251
258
|
its(:most_recent) { is_expected.to eq(true) }
|
252
259
|
|
253
260
|
it "updates the previous transition's most_recent flag" do
|
@@ -271,12 +278,13 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
271
278
|
|
272
279
|
it "caches the transition" do
|
273
280
|
expect_any_instance_of(MyActiveRecordModel).
|
274
|
-
|
281
|
+
to_not receive(:my_active_record_model_transitions)
|
275
282
|
adapter.last
|
276
283
|
end
|
277
284
|
|
278
285
|
context "after then creating a new transition" do
|
279
286
|
before { adapter.create(:y, :z, []) }
|
287
|
+
|
280
288
|
it "retrieves the new transition from the database" do
|
281
289
|
expect(adapter.last.to_state).to eq("z")
|
282
290
|
end
|
@@ -291,7 +299,7 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
291
299
|
alternate_adapter.create(:y, :z, [])
|
292
300
|
|
293
301
|
expect_any_instance_of(MyActiveRecordModel).
|
294
|
-
|
302
|
+
to_not receive(:my_active_record_model_transitions)
|
295
303
|
expect(adapter.last.to_state).to eq("y")
|
296
304
|
end
|
297
305
|
|
@@ -323,10 +331,11 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
323
331
|
|
324
332
|
context "with a pre-fetched transition history" do
|
325
333
|
before { adapter.create(:x, :y) }
|
334
|
+
|
326
335
|
before { model.my_active_record_model_transitions.load_target }
|
327
336
|
|
328
337
|
it "doesn't query the database" do
|
329
|
-
expect(MyActiveRecordModelTransition).
|
338
|
+
expect(MyActiveRecordModelTransition).to_not receive(:connection)
|
330
339
|
expect(adapter.last.to_state).to eq("y")
|
331
340
|
end
|
332
341
|
end
|
@@ -341,6 +350,7 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
341
350
|
before do
|
342
351
|
MyNamespace::MyActiveRecordModelTransition.serialize(:metadata, JSON)
|
343
352
|
end
|
353
|
+
|
344
354
|
let(:observer) { double(Statesman::Machine, execute: nil) }
|
345
355
|
let(:model) do
|
346
356
|
MyNamespace::MyActiveRecordModel.create(current_state: :pending)
|
File without changes
|
@@ -6,8 +6,10 @@ require "mongoid"
|
|
6
6
|
|
7
7
|
describe Statesman::Adapters::Mongoid, mongo: true do
|
8
8
|
after { Mongoid.purge! }
|
9
|
+
|
9
10
|
let(:observer) { double(Statesman::Machine, execute: nil) }
|
10
11
|
let(:model) { MyMongoidModel.create(current_state: :pending) }
|
12
|
+
|
11
13
|
it_behaves_like "an adapter", described_class, MyMongoidModelTransition
|
12
14
|
|
13
15
|
describe "#initialize" do
|
@@ -33,16 +35,18 @@ describe Statesman::Adapters::Mongoid, mongo: true do
|
|
33
35
|
|
34
36
|
context "with a previously looked up transition" do
|
35
37
|
before { adapter.create(:x, :y) }
|
38
|
+
|
36
39
|
before { adapter.last }
|
37
40
|
|
38
41
|
it "caches the transition" do
|
39
42
|
expect_any_instance_of(MyMongoidModel).
|
40
|
-
|
43
|
+
to_not receive(:my_mongoid_model_transitions)
|
41
44
|
adapter.last
|
42
45
|
end
|
43
46
|
|
44
47
|
context "and a new transition" do
|
45
48
|
before { adapter.create(:y, :z) }
|
49
|
+
|
46
50
|
it "retrieves the new transition from the database" do
|
47
51
|
expect(adapter.last.to_state).to eq("z")
|
48
52
|
end
|
@@ -13,8 +13,6 @@ require "spec_helper"
|
|
13
13
|
#
|
14
14
|
# NOTE This line cannot reasonably be shortened.
|
15
15
|
shared_examples_for "an adapter" do |adapter_class, transition_class, options = {}|
|
16
|
-
# rubocop:enable Metrics/LineLength
|
17
|
-
|
18
16
|
let(:observer) { double(Statesman::Machine, execute: nil) }
|
19
17
|
let(:adapter) do
|
20
18
|
adapter_class.new(transition_class,
|
@@ -23,26 +21,29 @@ shared_examples_for "an adapter" do |adapter_class, transition_class, options =
|
|
23
21
|
|
24
22
|
describe "#initialize" do
|
25
23
|
subject { adapter }
|
24
|
+
|
26
25
|
its(:transition_class) { is_expected.to be(transition_class) }
|
27
26
|
its(:parent_model) { is_expected.to be(model) }
|
28
27
|
its(:history) { is_expected.to eq([]) }
|
29
28
|
end
|
30
29
|
|
31
30
|
describe "#create" do
|
31
|
+
subject { -> { create } }
|
32
|
+
|
32
33
|
let(:from) { :x }
|
33
34
|
let(:to) { :y }
|
34
35
|
let(:there) { :z }
|
35
36
|
let(:create) { adapter.create(from, to) }
|
36
|
-
subject { -> { create } }
|
37
37
|
|
38
38
|
it { is_expected.to change(adapter.history, :count).by(1) }
|
39
39
|
|
40
40
|
context "the new transition" do
|
41
|
-
subject { create }
|
41
|
+
subject(:instance) { create }
|
42
|
+
|
42
43
|
it { is_expected.to be_a(transition_class) }
|
43
44
|
|
44
|
-
it "
|
45
|
-
expect(
|
45
|
+
it "has the initial state" do
|
46
|
+
expect(instance.to_state.to_sym).to eq(to)
|
46
47
|
end
|
47
48
|
|
48
49
|
context "with no previous transition" do
|
@@ -51,6 +52,7 @@ shared_examples_for "an adapter" do |adapter_class, transition_class, options =
|
|
51
52
|
|
52
53
|
context "with a previous transition" do
|
53
54
|
before { adapter.create(from, to) }
|
55
|
+
|
54
56
|
its(:sort_key) { is_expected.to be(20) }
|
55
57
|
end
|
56
58
|
end
|
@@ -87,33 +89,40 @@ shared_examples_for "an adapter" do |adapter_class, transition_class, options =
|
|
87
89
|
end
|
88
90
|
|
89
91
|
context "with metadata" do
|
90
|
-
let(:metadata) { { "some" => "hash" } }
|
91
92
|
subject { adapter.create(from, to, metadata) }
|
93
|
+
|
94
|
+
let(:metadata) { { "some" => "hash" } }
|
95
|
+
|
92
96
|
its(:metadata) { is_expected.to eq(metadata) }
|
93
97
|
end
|
94
98
|
end
|
95
99
|
|
96
100
|
describe "#history" do
|
97
101
|
subject { adapter.history }
|
102
|
+
|
98
103
|
it { is_expected.to eq([]) }
|
99
104
|
|
100
105
|
context "with transitions" do
|
101
106
|
let!(:transition) { adapter.create(:x, :y) }
|
107
|
+
|
102
108
|
it { is_expected.to eq([transition]) }
|
103
109
|
|
104
110
|
context "sorting" do
|
105
|
-
let!(:transition2) { adapter.create(:x, :y) }
|
106
111
|
subject { adapter.history }
|
107
112
|
|
113
|
+
let!(:transition2) { adapter.create(:x, :y) }
|
114
|
+
|
108
115
|
it { is_expected.to eq(adapter.history.sort_by(&:sort_key)) }
|
109
116
|
end
|
110
117
|
end
|
111
118
|
end
|
112
119
|
|
113
120
|
describe "#last" do
|
121
|
+
subject { adapter.last }
|
122
|
+
|
114
123
|
before { adapter.create(:x, :y) }
|
124
|
+
|
115
125
|
before { adapter.create(:y, :z) }
|
116
|
-
subject { adapter.last }
|
117
126
|
|
118
127
|
it { is_expected.to be_a(transition_class) }
|
119
128
|
specify { expect(adapter.last.to_state.to_sym).to eq(:z) }
|
@@ -3,7 +3,7 @@ require "spec_helper"
|
|
3
3
|
describe Statesman::Callback do
|
4
4
|
let(:cb_lambda) { -> {} }
|
5
5
|
let(:callback) do
|
6
|
-
|
6
|
+
described_class.new(from: nil, to: nil, callback: cb_lambda)
|
7
7
|
end
|
8
8
|
|
9
9
|
describe "#initialize" do
|
@@ -27,21 +27,24 @@ describe Statesman::Callback do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
describe "#applies_to" do
|
30
|
+
subject { callback.applies_to?(from: from, to: to) }
|
31
|
+
|
30
32
|
let(:callback) do
|
31
|
-
|
33
|
+
described_class.new(from: :x, to: :y, callback: cb_lambda)
|
32
34
|
end
|
33
|
-
subject { callback.applies_to?(from: from, to: to) }
|
34
35
|
|
35
36
|
context "with any from value" do
|
36
37
|
let(:from) { nil }
|
37
38
|
|
38
39
|
context "and an allowed to value" do
|
39
40
|
let(:to) { :y }
|
41
|
+
|
40
42
|
it { is_expected.to be_truthy }
|
41
43
|
end
|
42
44
|
|
43
45
|
context "and a disallowed to value" do
|
44
46
|
let(:to) { :a }
|
47
|
+
|
45
48
|
it { is_expected.to be_falsey }
|
46
49
|
end
|
47
50
|
end
|
@@ -51,17 +54,19 @@ describe Statesman::Callback do
|
|
51
54
|
|
52
55
|
context "and an allowed 'from' value" do
|
53
56
|
let(:from) { :x }
|
57
|
+
|
54
58
|
it { is_expected.to be_truthy }
|
55
59
|
end
|
56
60
|
|
57
61
|
context "and a disallowed 'from' value" do
|
58
62
|
let(:from) { :a }
|
63
|
+
|
59
64
|
it { is_expected.to be_falsey }
|
60
65
|
end
|
61
66
|
end
|
62
67
|
|
63
68
|
context "with any to and any from value on the callback" do
|
64
|
-
let(:callback) {
|
69
|
+
let(:callback) { described_class.new(callback: cb_lambda) }
|
65
70
|
let(:from) { :x }
|
66
71
|
let(:to) { :y }
|
67
72
|
|
@@ -70,37 +75,42 @@ describe Statesman::Callback do
|
|
70
75
|
|
71
76
|
context "with any from value on the callback" do
|
72
77
|
let(:callback) do
|
73
|
-
|
78
|
+
described_class.new(to: %i[y z], callback: cb_lambda)
|
74
79
|
end
|
75
80
|
let(:from) { :x }
|
76
81
|
|
77
82
|
context "and an allowed to value" do
|
78
83
|
let(:to) { :y }
|
84
|
+
|
79
85
|
it { is_expected.to be_truthy }
|
80
86
|
end
|
81
87
|
|
82
88
|
context "and another allowed to value" do
|
83
89
|
let(:to) { :z }
|
90
|
+
|
84
91
|
it { is_expected.to be_truthy }
|
85
92
|
end
|
86
93
|
|
87
94
|
context "and a disallowed to value" do
|
88
95
|
let(:to) { :a }
|
96
|
+
|
89
97
|
it { is_expected.to be_falsey }
|
90
98
|
end
|
91
99
|
end
|
92
100
|
|
93
101
|
context "with any to value on the callback" do
|
94
|
-
let(:callback) {
|
102
|
+
let(:callback) { described_class.new(from: :x, callback: cb_lambda) }
|
95
103
|
let(:to) { :y }
|
96
104
|
|
97
105
|
context "and an allowed to value" do
|
98
106
|
let(:from) { :x }
|
107
|
+
|
99
108
|
it { is_expected.to be_truthy }
|
100
109
|
end
|
101
110
|
|
102
111
|
context "and a disallowed to value" do
|
103
112
|
let(:from) { :a }
|
113
|
+
|
104
114
|
it { is_expected.to be_falsey }
|
105
115
|
end
|
106
116
|
end
|
@@ -108,12 +118,14 @@ describe Statesman::Callback do
|
|
108
118
|
context "with allowed 'from' and 'to' values" do
|
109
119
|
let(:from) { :x }
|
110
120
|
let(:to) { :y }
|
121
|
+
|
111
122
|
it { is_expected.to be_truthy }
|
112
123
|
end
|
113
124
|
|
114
125
|
context "with disallowed 'from' and 'to' values" do
|
115
126
|
let(:from) { :a }
|
116
127
|
let(:to) { :b }
|
128
|
+
|
117
129
|
it { is_expected.to be_falsey }
|
118
130
|
end
|
119
131
|
end
|