statesman 3.4.1 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +9 -9
  3. data/.rubocop_todo.yml +67 -2
  4. data/CHANGELOG.md +7 -0
  5. data/Gemfile +0 -4
  6. data/README.md +15 -1
  7. data/lib/generators/statesman/active_record_transition_generator.rb +1 -1
  8. data/lib/generators/statesman/migration_generator.rb +1 -1
  9. data/lib/generators/statesman/mongoid_transition_generator.rb +1 -1
  10. data/lib/generators/statesman/templates/create_migration.rb.erb +3 -3
  11. data/lib/generators/statesman/templates/update_migration.rb.erb +2 -2
  12. data/lib/statesman/adapters/active_record.rb +3 -3
  13. data/lib/statesman/adapters/active_record_queries.rb +7 -7
  14. data/lib/statesman/adapters/memory.rb +2 -2
  15. data/lib/statesman/adapters/mongoid.rb +1 -1
  16. data/lib/statesman/config.rb +0 -1
  17. data/lib/statesman/machine.rb +1 -1
  18. data/lib/statesman/version.rb +1 -1
  19. data/spec/generators/statesman/active_record_transition_generator_spec.rb +14 -9
  20. data/spec/generators/statesman/migration_generator_spec.rb +9 -9
  21. data/spec/generators/statesman/mongoid_transition_generator_spec.rb +7 -5
  22. data/spec/statesman/adapters/active_record_queries_spec.rb +17 -15
  23. data/spec/statesman/adapters/active_record_spec.rb +14 -4
  24. data/spec/statesman/adapters/memory_spec.rb +1 -0
  25. data/spec/statesman/{transition_spec.rb → adapters/memory_transition_spec.rb} +0 -0
  26. data/spec/statesman/adapters/mongoid_spec.rb +5 -1
  27. data/spec/statesman/adapters/shared_examples.rb +18 -9
  28. data/spec/statesman/callback_spec.rb +18 -6
  29. data/spec/statesman/config_spec.rb +6 -3
  30. data/spec/statesman/guard_spec.rb +3 -1
  31. data/spec/statesman/machine_spec.rb +52 -14
  32. data/spec/support/generators_shared_examples.rb +3 -1
  33. data/statesman.gemspec +12 -12
  34. 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
- it { is_expected.not_to contain(%r{:yummy/bacon}) }
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
- it { is_expected.not_to contain(/class_name:/) }
19
- it { is_expected.not_to contain(/CreateYummy::Bacon/) }
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
- before { MyActiveRecordModel.send(:has_one, :other_active_record_model) }
41
- before { OtherActiveRecordModel.send(:belongs_to, :my_active_record_model) }
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.not_to include other_model }
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.not_to include other_model }
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
- is_expected.to match_array([initial_state_model,
115
- returned_to_initial_model])
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
- is_expected.to match_array([initial_state_model,
123
- returned_to_initial_model])
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
- to receive(:my_active_record_model_transitions).never
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
- to receive(:my_active_record_model_transitions).never
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).not_to receive(:connection)
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)
@@ -4,6 +4,7 @@ require "statesman/adapters/memory_transition"
4
4
 
5
5
  describe Statesman::Adapters::Memory do
6
6
  let(:model) { Class.new { attr_accessor :current_state }.new }
7
+
7
8
  it_behaves_like "an adapter", described_class,
8
9
  Statesman::Adapters::MemoryTransition
9
10
  end
@@ -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
- to receive(:my_mongoid_model_transitions).never
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 "should have the initial state" do
45
- expect(subject.to_state.to_sym).to eq(to)
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
- Statesman::Callback.new(from: nil, to: nil, callback: cb_lambda)
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
- Statesman::Callback.new(from: :x, to: :y, callback: cb_lambda)
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) { Statesman::Callback.new(callback: cb_lambda) }
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
- Statesman::Callback.new(to: %i[y z], callback: cb_lambda)
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) { Statesman::Callback.new(from: :x, callback: cb_lambda) }
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