statesman 3.5.0 → 4.0.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 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