statesman 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 105f8a5aeb999d07eaeb59d69bbe52ff42317895
4
- data.tar.gz: b6ac7988cea6d0a5a260ceda12be3c1ca64b38d5
3
+ metadata.gz: a7c29dcf767aa7177b818dafdc99e8645dddfcc8
4
+ data.tar.gz: 1d4db155c8fb031a4e58dafa44febca452110dcd
5
5
  SHA512:
6
- metadata.gz: 00608fb54abb049e5615c96c0903e6564a565838c4cf6db53da2151fefef2ffdabf529b18752eae466bedcd22e26237a44fb1ce1af25fcc0f1494e3164d0454a
7
- data.tar.gz: 7715676c1ea620c9509e470e285474f31fa991795abc60bb9bd45f692d90bc10453a33918567eb3a11c0cbb19a027ecf03822684b859f0d106df4a481db1c748
6
+ metadata.gz: 1aff94dc8f2c327b232c93123fa781a5f583f4b9fa08b39df79d8dd93c8600992ca67e05b277531fb58da92014eb5c1240a9d622cff3195a84266624bef7e4ef
7
+ data.tar.gz: 6882412cf48ed98015601b22a26c4d038405d1b9208a723d0fdeeddb875629e47fb267ba97ceac712bbac5572d6c3f647188047e6f17532a528baa2c6b9897ab
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## v0.4.0, 27 February 2014
2
+ *Additions*
3
+ - Adds after_commit flag to after_transition for callbacks to be executed after the transaction has been
4
+ committed on the ActiveRecord adapter. These callbacks will still be executed on non transactional adapters.
5
+
1
6
  ## v0.3.0, 20 February 2014
2
7
  *Additions*
3
8
  - Adds Machine#allowed_transitions method (patch by [@prikha](https://github.com/prikha))
@@ -6,26 +6,28 @@ module Statesman
6
6
  attr_reader :transition_class
7
7
  attr_reader :parent_model
8
8
 
9
- def initialize(transition_class, parent_model)
9
+ def initialize(transition_class, parent_model, observer)
10
10
  unless transition_class.serialized_attributes.include?("metadata")
11
11
  raise UnserializedMetadataError,
12
12
  "#{transition_class.name}#metadata is not serialized"
13
13
  end
14
14
  @transition_class = transition_class
15
15
  @parent_model = parent_model
16
+ @observer = observer
16
17
  end
17
18
 
18
- def create(to, before_cbs, after_cbs, metadata = {})
19
+ def create(from, to, metadata = {})
19
20
  transition = transitions_for_parent.build(to_state: to,
20
21
  sort_key: next_sort_key,
21
22
  metadata: metadata)
22
23
 
23
24
  ::ActiveRecord::Base.transaction do
24
- before_cbs.each { |cb| cb.call(@parent_model, transition) }
25
+ @observer.execute(:before, from, to, transition)
25
26
  transition.save!
26
- after_cbs.each { |cb| cb.call(@parent_model, transition) }
27
+ @observer.execute(:after, from, to, transition)
27
28
  @last_transition = nil
28
29
  end
30
+ @observer.execute(:after_commit, from, to, transition)
29
31
 
30
32
  transition
31
33
  end
@@ -9,19 +9,20 @@ module Statesman
9
9
 
10
10
  # We only accept mode as a parameter to maintain a consistent interface
11
11
  # with other adapters which require it.
12
- def initialize(transition_class, parent_model)
12
+ def initialize(transition_class, parent_model, observer)
13
13
  @history = []
14
14
  @transition_class = transition_class
15
15
  @parent_model = parent_model
16
+ @observer = observer
16
17
  end
17
18
 
18
- def create(to, before_cbs, after_cbs, metadata = {})
19
+ def create(from, to, metadata = {})
19
20
  transition = transition_class.new(to, next_sort_key, metadata)
20
21
 
21
- before_cbs.each { |cb| cb.call(@parent_model, transition) }
22
+ @observer.execute(:before, from, to, transition)
22
23
  @history << transition
23
- after_cbs.each { |cb| cb.call(@parent_model, transition) }
24
-
24
+ @observer.execute(:after, from, to, transition)
25
+ @observer.execute(:after_commit, from, to, transition)
25
26
  transition
26
27
  end
27
28
 
@@ -6,22 +6,24 @@ module Statesman
6
6
  attr_reader :transition_class
7
7
  attr_reader :parent_model
8
8
 
9
- def initialize(transition_class, parent_model)
9
+ def initialize(transition_class, parent_model, observer)
10
10
  @transition_class = transition_class
11
11
  @parent_model = parent_model
12
+ @observer = observer
12
13
  unless transition_class_hash_fields.include?('statesman_metadata')
13
14
  raise UnserializedMetadataError, metadata_field_error_message
14
15
  end
15
16
  end
16
17
 
17
- def create(to, before_cbs, after_cbs, metadata = {})
18
+ def create(from, to, metadata = {})
18
19
  transition = transitions_for_parent.build(to_state: to,
19
20
  sort_key: next_sort_key,
20
21
  statesman_metadata: metadata)
21
22
 
22
- before_cbs.each { |cb| cb.call(@parent_model, transition) }
23
+ @observer.execute(:before, from, to, transition)
23
24
  transition.save!
24
- after_cbs.each { |cb| cb.call(@parent_model, transition) }
25
+ @observer.execute(:after, from, to, transition)
26
+ @observer.execute(:after_commit, from, to, transition)
25
27
  @last_transition = nil
26
28
  transition
27
29
  end
@@ -32,16 +32,13 @@ module Statesman
32
32
  @successors ||= {}
33
33
  end
34
34
 
35
- def before_callbacks
36
- @before_callbacks ||= []
37
- end
38
-
39
- def after_callbacks
40
- @after_callbacks ||= []
41
- end
42
-
43
- def guards
44
- @guards ||= []
35
+ def callbacks
36
+ @callbacks ||= {
37
+ before: [],
38
+ after: [],
39
+ after_commit: [],
40
+ guards: []
41
+ }
45
42
  end
46
43
 
47
44
  def transition(options = { from: nil, to: nil })
@@ -60,15 +57,17 @@ module Statesman
60
57
  to = to_s_or_nil(options[:to])
61
58
 
62
59
  validate_callback_condition(from: from, to: to)
63
- before_callbacks << Callback.new(from: from, to: to, callback: block)
60
+ callbacks[:before] << Callback.new(from: from, to: to, callback: block)
64
61
  end
65
62
 
66
- def after_transition(options = { from: nil, to: nil }, &block)
63
+ def after_transition(options = { from: nil, to: nil,
64
+ after_commit: false }, &block)
67
65
  from = to_s_or_nil(options[:from])
68
66
  to = to_s_or_nil(options[:to])
69
67
 
70
68
  validate_callback_condition(from: from, to: to)
71
- after_callbacks << Callback.new(from: from, to: to, callback: block)
69
+ phase = options[:after_commit] ? :after_commit : :after
70
+ callbacks[phase] << Callback.new(from: from, to: to, callback: block)
72
71
  end
73
72
 
74
73
  def guard_transition(options = { from: nil, to: nil }, &block)
@@ -76,7 +75,7 @@ module Statesman
76
75
  to = to_s_or_nil(options[:to])
77
76
 
78
77
  validate_callback_condition(from: from, to: to)
79
- guards << Guard.new(from: from, to: to, callback: block)
78
+ callbacks[:guards] << Guard.new(from: from, to: to, callback: block)
80
79
  end
81
80
 
82
81
  def validate_callback_condition(options = { from: nil, to: nil })
@@ -143,8 +142,9 @@ module Statesman
143
142
  transition_class: Statesman::Adapters::MemoryTransition
144
143
  })
145
144
  @object = object
145
+ @transition_class = options[:transition_class]
146
146
  @storage_adapter = Statesman.storage_adapter.new(
147
- options[:transition_class], object)
147
+ @transition_class, object, self)
148
148
  end
149
149
 
150
150
  def current_state
@@ -183,14 +183,16 @@ module Statesman
183
183
  to: new_state,
184
184
  metadata: metadata)
185
185
 
186
- before_cbs = before_callbacks_for(from: initial_state, to: new_state)
187
- after_cbs = after_callbacks_for(from: initial_state, to: new_state)
188
-
189
- @storage_adapter.create(new_state, before_cbs, after_cbs, metadata)
186
+ @storage_adapter.create(initial_state, new_state, metadata)
190
187
 
191
188
  true
192
189
  end
193
190
 
191
+ def execute(phase, initial_state, new_state, transition)
192
+ callbacks = callbacks_for(phase, from: initial_state, to: new_state)
193
+ callbacks.each { |cb| cb.call(@object, transition) }
194
+ end
195
+
194
196
  def transition_to(new_state, metadata = nil)
195
197
  self.transition_to!(new_state, metadata)
196
198
  rescue
@@ -204,15 +206,11 @@ module Statesman
204
206
  end
205
207
 
206
208
  def guards_for(options = { from: nil, to: nil })
207
- select_callbacks_for(self.class.guards, options)
208
- end
209
-
210
- def before_callbacks_for(options = { from: nil, to: nil })
211
- select_callbacks_for(self.class.before_callbacks, options)
209
+ select_callbacks_for(self.class.callbacks[:guards], options)
212
210
  end
213
211
 
214
- def after_callbacks_for(options = { from: nil, to: nil })
215
- select_callbacks_for(self.class.after_callbacks, options)
212
+ def callbacks_for(phase, options = { from: nil, to: nil })
213
+ select_callbacks_for(self.class.callbacks[phase], options)
216
214
  end
217
215
 
218
216
  def select_callbacks_for(callbacks, options = { from: nil, to: nil })
@@ -1,3 +1,3 @@
1
1
  module Statesman
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -9,7 +9,11 @@ describe Statesman::Adapters::ActiveRecord do
9
9
  end
10
10
 
11
11
  before { MyActiveRecordModelTransition.serialize(:metadata, JSON) }
12
-
12
+ let(:observer) do
13
+ result = double(Statesman::Machine)
14
+ result.stub(:execute)
15
+ result
16
+ end
13
17
  let(:model) { MyActiveRecordModel.create(current_state: :pending) }
14
18
  it_behaves_like "an adapter", described_class, MyActiveRecordModelTransition
15
19
 
@@ -20,18 +24,20 @@ describe Statesman::Adapters::ActiveRecord do
20
24
  it "raises an exception if metadata is not serialized" do
21
25
  expect do
22
26
  described_class.new(MyActiveRecordModelTransition,
23
- MyActiveRecordModel)
27
+ MyActiveRecordModel, observer)
24
28
  end.to raise_exception(Statesman::UnserializedMetadataError)
25
29
  end
26
30
  end
27
31
  end
28
32
 
29
33
  describe "#last" do
30
- let(:adapter) { described_class.new(MyActiveRecordModelTransition, model) }
34
+ let(:adapter) do
35
+ described_class.new(MyActiveRecordModelTransition, model, observer)
36
+ end
31
37
 
32
38
  context "with a previously looked up transition" do
33
39
  before do
34
- adapter.create(:y, [], [])
40
+ adapter.create(:x, :y)
35
41
  adapter.last
36
42
  end
37
43
 
@@ -42,7 +48,7 @@ describe Statesman::Adapters::ActiveRecord do
42
48
  end
43
49
 
44
50
  context "and a new transition" do
45
- before { adapter.create(:z, [], []) }
51
+ before { adapter.create(:y, :z, []) }
46
52
  it "retrieves the new transition from the database" do
47
53
  expect(adapter.last.to_state).to eq("z")
48
54
  end
@@ -9,7 +9,11 @@ describe Statesman::Adapters::Mongoid, mongo: true do
9
9
  after do
10
10
  Mongoid.purge!
11
11
  end
12
-
12
+ let(:observer) do
13
+ result = double(Statesman::Machine)
14
+ result.stub(:execute)
15
+ result
16
+ end
13
17
  let(:model) { MyMongoidModel.create(current_state: :pending) }
14
18
  it_behaves_like "an adapter", described_class, MyMongoidModelTransition
15
19
 
@@ -21,18 +25,21 @@ describe Statesman::Adapters::Mongoid, mongo: true do
21
25
 
22
26
  it "raises an exception if metadata is not serialized" do
23
27
  expect do
24
- described_class.new(MyMongoidModelTransition, MyMongoidModel)
28
+ described_class.new(MyMongoidModelTransition, MyMongoidModel,
29
+ observer)
25
30
  end.to raise_exception(Statesman::UnserializedMetadataError)
26
31
  end
27
32
  end
28
33
  end
29
34
 
30
35
  describe "#last" do
31
- let(:adapter) { described_class.new(MyMongoidModelTransition, model) }
36
+ let(:adapter) do
37
+ described_class.new(MyMongoidModelTransition, model, observer)
38
+ end
32
39
 
33
40
  context "with a previously looked up transition" do
34
41
  before do
35
- adapter.create(:y, [], [])
42
+ adapter.create(:x, :y)
36
43
  adapter.last
37
44
  end
38
45
 
@@ -43,7 +50,7 @@ describe Statesman::Adapters::Mongoid, mongo: true do
43
50
  end
44
51
 
45
52
  context "and a new transition" do
46
- before { adapter.create(:z, [], []) }
53
+ before { adapter.create(:y, :z) }
47
54
  it "retrieves the new transition from the database" do
48
55
  expect(adapter.last.to_state).to eq("z")
49
56
  end
@@ -12,9 +12,12 @@ require "spec_helper"
12
12
  # last: Returns the latest transition history item
13
13
  #
14
14
  shared_examples_for "an adapter" do |adapter_class, transition_class|
15
- let(:adapter) { adapter_class.new(transition_class, model) }
16
- let(:before_cbs) { [] }
17
- let(:after_cbs) { [] }
15
+ let(:observer) do
16
+ result = double(Statesman::Machine)
17
+ result.stub(:execute)
18
+ result
19
+ end
20
+ let(:adapter) { adapter_class.new(transition_class, model, observer) }
18
21
 
19
22
  describe "#initialize" do
20
23
  subject { adapter }
@@ -24,8 +27,9 @@ shared_examples_for "an adapter" do |adapter_class, transition_class|
24
27
  end
25
28
 
26
29
  describe "#create" do
30
+ let(:from) { :x }
27
31
  let(:to) { :y }
28
- let(:create) { adapter.create(to, before_cbs, after_cbs) }
32
+ let(:create) { adapter.create(from, to) }
29
33
  subject { -> { create } }
30
34
 
31
35
  it { should change(adapter.history, :count).by(1) }
@@ -43,43 +47,35 @@ shared_examples_for "an adapter" do |adapter_class, transition_class|
43
47
  end
44
48
 
45
49
  context "with a previous transition" do
46
- before { adapter.create(:x, before_cbs, after_cbs) }
50
+ before { adapter.create(from, to) }
47
51
  its(:sort_key) { should be(10) }
48
52
  end
49
53
  end
50
54
 
51
55
  context "with before callbacks" do
52
- let(:callback) { double(call: true) }
53
- let(:before_cbs) { [callback] }
54
-
55
56
  it "is called before the state transition" do
56
- callback.should_receive(:call).with do |passed_model, transition|
57
- expect(passed_model).to be(model)
58
- expect(transition).to be_a(adapter.transition_class)
59
- expect(adapter.history.length).to eq(0)
57
+ observer.should_receive(:execute).with do
58
+ |phase, from_state, to_state, transition|
59
+ expect(adapter.history.length).to eq(0) if phase == :before
60
60
  end.once
61
-
62
- adapter.create(:x, before_cbs, after_cbs)
61
+ adapter.create(from, to)
63
62
  expect(adapter.history.length).to eq(1)
64
63
  end
65
64
  end
66
65
 
67
66
  context "with after callbacks" do
68
- let(:callback) { double(call: true) }
69
- let(:after_cbs) { [callback] }
70
-
71
67
  it "is called after the state transition" do
72
- callback.should_receive(:call).with do |passed_model, transition|
73
- expect(passed_model).to be(model)
74
- expect(adapter.last).to eq(transition)
68
+ observer.should_receive(:execute).with do
69
+ |phase, from_state, to_state, transition|
70
+ expect(adapter.last).to eq(transition) if phase == :after
75
71
  end.once
76
- adapter.create(:x, before_cbs, after_cbs)
72
+ adapter.create(from, to)
77
73
  end
78
74
  end
79
75
 
80
76
  context "with metadata" do
81
77
  let(:metadata) { { "some" => "hash" } }
82
- subject { adapter.create(to, before_cbs, after_cbs, metadata) }
78
+ subject { adapter.create(from, to, metadata) }
83
79
  its(:metadata) { should eq(metadata) }
84
80
  end
85
81
  end
@@ -89,12 +85,11 @@ shared_examples_for "an adapter" do |adapter_class, transition_class|
89
85
  it { should eq([]) }
90
86
 
91
87
  context "with transitions" do
92
- let!(:transition) { adapter.create(:y, before_cbs, after_cbs) }
88
+ let!(:transition) { adapter.create(:x, :y) }
93
89
  it { should eq([transition]) }
94
90
 
95
91
  context "sorting" do
96
- let!(:transition) { adapter.create(:y, before_cbs, after_cbs) }
97
- let!(:transition2) { adapter.create(:y, before_cbs, after_cbs) }
92
+ let!(:transition2) { adapter.create(:x, :y) }
98
93
  subject { adapter.history }
99
94
  it { should eq(adapter.history.sort_by(&:sort_key)) }
100
95
  end
@@ -103,8 +98,8 @@ shared_examples_for "an adapter" do |adapter_class, transition_class|
103
98
 
104
99
  describe "#last" do
105
100
  before do
106
- adapter.create(:y, before_cbs, after_cbs)
107
- adapter.create(:z, before_cbs, after_cbs)
101
+ adapter.create(:x, :y)
102
+ adapter.create(:y, :z)
108
103
  end
109
104
  subject { adapter.last }
110
105
 
@@ -127,13 +127,12 @@ describe Statesman::Machine do
127
127
  it "stores callbacks" do
128
128
  expect do
129
129
  machine.send(assignment_method) {}
130
- end.to change(machine.send(callback_store), :count).by(1)
130
+ end.to change(machine.callbacks[callback_store], :count).by(1)
131
131
  end
132
132
 
133
133
  it "stores callback instances" do
134
134
  machine.send(assignment_method) {}
135
-
136
- machine.send(callback_store).each do |callback|
135
+ machine.callbacks[callback_store].each do |callback|
137
136
  expect(callback).to be_a(Statesman::Callback)
138
137
  end
139
138
  end
@@ -174,11 +173,11 @@ describe Statesman::Machine do
174
173
  end
175
174
 
176
175
  describe ".before_transition" do
177
- it_behaves_like "a callback store", :before_transition, :before_callbacks
176
+ it_behaves_like "a callback store", :before_transition, :before
178
177
  end
179
178
 
180
179
  describe ".after_transition" do
181
- it_behaves_like "a callback store", :after_transition, :after_callbacks
180
+ it_behaves_like "a callback store", :after_transition, :after
182
181
  end
183
182
 
184
183
  describe ".guard_transition" do
@@ -194,14 +193,14 @@ describe Statesman::Machine do
194
193
  context "transition class" do
195
194
  it "sets a default" do
196
195
  Statesman.storage_adapter.should_receive(:new).once
197
- .with(Statesman::Adapters::MemoryTransition, my_model)
196
+ .with(Statesman::Adapters::MemoryTransition, my_model, anything)
198
197
  machine.new(my_model)
199
198
  end
200
199
 
201
200
  it "sets the passed class" do
202
201
  my_transition_class = Class.new
203
202
  Statesman.storage_adapter.should_receive(:new).once
204
- .with(my_transition_class, my_model)
203
+ .with(my_transition_class, my_model, anything)
205
204
  machine.new(my_model, transition_class: my_transition_class)
206
205
  end
207
206
  end
@@ -396,28 +395,6 @@ describe Statesman::Machine do
396
395
  end
397
396
  end
398
397
  end
399
-
400
- context "with a before callback" do
401
- let(:callbacks) { [] }
402
- before { instance.stub(:before_callbacks_for).and_return(callbacks) }
403
-
404
- it "is passed to the adapter" do
405
- Statesman::Adapters::Memory.any_instance.should_receive(:create)
406
- .with("y", callbacks, anything, anything)
407
- instance.transition_to!(:y)
408
- end
409
- end
410
-
411
- context "with an after callback" do
412
- let(:callbacks) { [] }
413
- before { instance.stub(:after_callbacks_for).and_return(callbacks) }
414
-
415
- it "is passed to the adapter" do
416
- Statesman::Adapters::Memory.any_instance.should_receive(:create)
417
- .with("y", anything, callbacks, anything)
418
- instance.transition_to!(:y)
419
- end
420
- end
421
398
  end
422
399
  end
423
400
 
@@ -442,7 +419,7 @@ describe Statesman::Machine do
442
419
  end
443
420
  end
444
421
 
445
- shared_examples "a callback filter" do |definer, getter|
422
+ shared_examples "a callback filter" do |definer, phase|
446
423
  before do
447
424
  machine.class_eval do
448
425
  state :x
@@ -454,7 +431,7 @@ describe Statesman::Machine do
454
431
  end
455
432
 
456
433
  let(:instance) { machine.new(my_model) }
457
- let(:callbacks) { instance.send(getter, from: :x, to: :y) }
434
+ let(:callbacks) { instance.send(:callbacks_for, phase, from: :x, to: :y) }
458
435
 
459
436
  context "with no defined callbacks" do
460
437
  specify { expect(callbacks).to eq([]) }
@@ -480,16 +457,16 @@ describe Statesman::Machine do
480
457
  end
481
458
 
482
459
  describe "#guards_for" do
483
- it_behaves_like "a callback filter", :guard_transition, :guards_for
460
+ it_behaves_like "a callback filter", :guard_transition, :guards
484
461
  end
485
462
 
486
463
  describe "#before_callbacks_for" do
487
464
  it_behaves_like "a callback filter", :before_transition,
488
- :before_callbacks_for
465
+ :before
489
466
  end
490
467
 
491
468
  describe "#after_callbacks_for" do
492
469
  it_behaves_like "a callback filter", :after_transition,
493
- :after_callbacks_for
470
+ :after
494
471
  end
495
472
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statesman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harry Marr
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-02-20 00:00:00.000000000 Z
12
+ date: 2014-03-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler