statesman 3.5.0 → 4.0.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: 9fb352daa2f2ec337dd202cc2615a7e0925741a4
4
- data.tar.gz: 9de7812c9356e6e9f98a317b503220b3a7f7709b
3
+ metadata.gz: 5063951e126e3898569d97511ea5c89149122c3e
4
+ data.tar.gz: 888fcdc8b6bfbde03dbc4471fa3d84f58fd0871e
5
5
  SHA512:
6
- metadata.gz: fbd8b2b4b822b2f7148cb7b47111153e6cd18831e227cf6c72ca073904866025eb3e1d433f503e778853734c0f522e674ad29cad44ec5bc29fdfda7e55130275
7
- data.tar.gz: d55e7635f0773babcc1b5ef9da6f75a4144c8812ce305b93e1762e6100e658bd73f2989bb867efcb0be42225f1dfa49a67aeb064c1c287df62720c8afc693d4b
6
+ metadata.gz: c4f1b64670d9b94c53f65ef3ed7182be77388c732536906d7066db8e0970016ad1e0cec26148d62b5cd32719e446a253033da3b1e8dd5c0c5e813834715e032f
7
+ data.tar.gz: 8c5ab391235a0f4b073fee60eeea93c4752e9f5a2d71a6b05ba955cebfd5bae61921bf87ac9769a967db05439cd907e5de6be1fe5f8d5a45a80fc4588b722d9a
@@ -11,7 +11,7 @@ references:
11
11
  - type: cache-restore
12
12
  key: statesman-{{ checksum "Gemfile" }}-{{ checksum "~/RAILS_VERSION.txt" }}
13
13
 
14
- - run: gem install bundler
14
+ - run: gem install bundler -v 1.3
15
15
 
16
16
  - run: bundle install --path vendor/bundle
17
17
 
@@ -1,3 +1,9 @@
1
+ ## v4.0.0, 22 February 2019
2
+
3
+ - Forces Statesman to use a new transactions with `requires_new: true` (https://github.com/gocardless/statesman/pull/249)
4
+ - Fixes an issue with `after_commit` transition blocks that where being
5
+ executed even if the transaction rolled back. ([patch](https://github.com/gocardless/statesman/pull/338) by [@matid](https://github.com/matid))
6
+
1
7
  ## v3.5.0, 2 November 2018
2
8
 
3
9
  - Expose `most_recent_transition_join` - ActiveRecords `or` requires that both
@@ -25,6 +25,7 @@ module Statesman
25
25
  elsif serialized && JSON_COLUMN_TYPES.include?(column_type)
26
26
  raise IncompatibleSerializationError, transition_class.name
27
27
  end
28
+
28
29
  @transition_class = transition_class
29
30
  @parent_model = parent_model
30
31
  @observer = observer
@@ -36,6 +37,7 @@ module Statesman
36
37
  create_transition(from.to_s, to.to_s, metadata)
37
38
  rescue ::ActiveRecord::RecordNotUnique => e
38
39
  raise TransitionConflictError, e.message if transition_conflict_error? e
40
+
39
41
  raise
40
42
  ensure
41
43
  @last_transition = nil
@@ -70,18 +72,26 @@ module Statesman
70
72
 
71
73
  transition = transitions_for_parent.build(transition_attributes)
72
74
 
73
- ::ActiveRecord::Base.transaction do
75
+ ::ActiveRecord::Base.transaction(requires_new: true) do
74
76
  @observer.execute(:before, from, to, transition)
75
77
  unset_old_most_recent
76
78
  transition.save!
77
79
  @last_transition = transition
78
80
  @observer.execute(:after, from, to, transition)
81
+ add_after_commit_callback(from, to, transition)
79
82
  end
80
- @observer.execute(:after_commit, from, to, transition)
81
83
 
82
84
  transition
83
85
  end
84
86
 
87
+ def add_after_commit_callback(from, to, transition)
88
+ ::ActiveRecord::Base.connection.add_transaction_record(
89
+ ActiveRecordAfterCommitWrap.new do
90
+ @observer.execute(:after_commit, from, to, transition)
91
+ end,
92
+ )
93
+ end
94
+
85
95
  def transitions_for_parent
86
96
  @parent_model.send(@association_name)
87
97
  end
@@ -147,5 +157,31 @@ module Statesman
147
157
  params.merge(column => timestamp)
148
158
  end
149
159
  end
160
+
161
+ class ActiveRecordAfterCommitWrap
162
+ def initialize
163
+ @callback = Proc.new
164
+ @connection = ::ActiveRecord::Base.connection
165
+ end
166
+
167
+ # rubocop: disable Naming/PredicateName
168
+ def has_transactional_callbacks?
169
+ true
170
+ end
171
+ # rubocop: enable Naming/PredicateName
172
+
173
+ def committed!(*)
174
+ @callback.call
175
+ end
176
+
177
+ def before_committed!(*); end
178
+
179
+ def rolledback!(*); end
180
+
181
+ # Required for +transaction(requires_new: true)+
182
+ def add_to_transaction(*)
183
+ @connection.add_transaction_record(self)
184
+ end
185
+ end
150
186
  end
151
187
  end
@@ -46,10 +46,10 @@ module Statesman
46
46
 
47
47
  def callbacks
48
48
  @callbacks ||= {
49
- before: [],
50
- after: [],
49
+ before: [],
50
+ after: [],
51
51
  after_commit: [],
52
- guards: [],
52
+ guards: [],
53
53
  }
54
54
  end
55
55
 
@@ -1,3 +1,3 @@
1
1
  module Statesman
2
- VERSION = "3.5.0".freeze
2
+ VERSION = "4.0.0".freeze
3
3
  end
@@ -19,7 +19,7 @@ namespace :statesman do
19
19
  batch_size = 500
20
20
 
21
21
  parent_class.find_in_batches(batch_size: batch_size) do |models|
22
- ActiveRecord::Base.transaction do
22
+ ActiveRecord::Base.transaction(requires_new: true) do
23
23
  if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
24
24
  # Set all transitions' most_recent to FALSE
25
25
  transition_class.where(parent_fk => models.map(&:id)).
@@ -168,4 +168,38 @@ describe Statesman::Adapters::ActiveRecordQueries, active_record: true do
168
168
  end
169
169
  end
170
170
  end
171
+
172
+ context "after_commit transactional integrity" do
173
+ before do
174
+ MyStateMachine.class_eval do
175
+ cattr_accessor(:after_commit_callback_executed) { false }
176
+
177
+ after_transition(from: :initial, to: :succeeded, after_commit: true) do
178
+ # This leaks state in a testable way if transactional integrity is broken.
179
+ MyStateMachine.after_commit_callback_executed = true
180
+ end
181
+ end
182
+ end
183
+
184
+ after do
185
+ MyStateMachine.class_eval do
186
+ callbacks[:after_commit] = []
187
+ end
188
+ end
189
+
190
+ let!(:model) do
191
+ MyActiveRecordModel.create
192
+ end
193
+
194
+ # rubocop:disable RSpec/ExampleLength
195
+ it do
196
+ expect do
197
+ ActiveRecord::Base.transaction do
198
+ model.state_machine.transition_to!(:succeeded)
199
+ raise ActiveRecord::Rollback
200
+ end
201
+ end.to_not change(MyStateMachine, :after_commit_callback_executed)
202
+ end
203
+ # rubocop:enable RSpec/ExampleLength
204
+ end
171
205
  end
@@ -227,6 +227,7 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
227
227
  it "still has the old state" do
228
228
  allow(observer).to receive(:execute) do |phase|
229
229
  next unless phase == :before
230
+
230
231
  expect(
231
232
  model.transitions.where(most_recent: true).first.to_state,
232
233
  ).to eq("y")
@@ -240,6 +241,7 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
240
241
  it "still has the old state" do
241
242
  allow(observer).to receive(:execute) do |phase|
242
243
  next unless phase == :after
244
+
243
245
  expect(
244
246
  model.transitions.where(most_recent: true).first.to_state,
245
247
  ).to eq("z")
@@ -31,6 +31,6 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency "rspec-its", "~> 1.1"
32
32
  spec.add_development_dependency "rspec-rails", "~> 3.1"
33
33
  spec.add_development_dependency "rspec_junit_formatter", "~> 0.4.0"
34
- spec.add_development_dependency "sqlite3", "~> 1.3"
34
+ spec.add_development_dependency "sqlite3", "~> 1.3.6"
35
35
  spec.add_development_dependency "timecop", "~> 0.9.1"
36
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statesman
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GoCardless
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-02 00:00:00.000000000 Z
11
+ date: 2019-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ammeter
@@ -184,14 +184,14 @@ dependencies:
184
184
  requirements:
185
185
  - - "~>"
186
186
  - !ruby/object:Gem::Version
187
- version: '1.3'
187
+ version: 1.3.6
188
188
  type: :development
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - "~>"
193
193
  - !ruby/object:Gem::Version
194
- version: '1.3'
194
+ version: 1.3.6
195
195
  - !ruby/object:Gem::Dependency
196
196
  name: timecop
197
197
  requirement: !ruby/object:Gem::Requirement