statesman 7.2.0 → 8.0.1

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
  SHA256:
3
- metadata.gz: 421da0e6d201060df4733a0e52e2742df2b1d527f241873dfd4b820c74cc587f
4
- data.tar.gz: 20cd64e291373c21b83886cfdc12c7e68450759515557f3853bfc68bfff8ed38
3
+ metadata.gz: aa4a88371cc60e6698eb820b60096f681fbaedf8dbbbec5991d47db81daf695d
4
+ data.tar.gz: b67153a87f62722deb2053ddf661e79e7918fca5339f9a6829a086e30a2abf02
5
5
  SHA512:
6
- metadata.gz: c48d5415915aee8e4f595b78b9018b9a048bfb3d8e3ff33a3efce0ba962540f71af1fe982a389d5b51104c8284fc485f4bfa62390a3c92f03fa7ce4592a2ffbf
7
- data.tar.gz: cf11b1793461381b835efe9b2343b54c348a01de85fc0cbd63623a0c18cf21f9935f6cb4251d9117a7d73b52df7041458a943d160628073afc96eb5c16237d73
6
+ metadata.gz: ac43c11e5dc1791bac7a00ba8e84b459daad104be9c9b460626c7569e4b9bdf215f0d080b5151ff754ee109640e70d1ca6cec4b958acfeadd974aab6962f16fd
7
+ data.tar.gz: 965b0a4020631cb18c39ce6af8870f3e165c87058aaf25f40f217a71d51cf65345818f974569191156a5ed94021c63595cf049c45fa11b62e132b1631e6d3d46
@@ -86,11 +86,11 @@ jobs:
86
86
  - POSTGRES_DB=statesman_test
87
87
  - POSTGRES_PASSWORD=statesman
88
88
  steps: *steps
89
- build-ruby265-rails-master-mysql:
89
+ build-ruby265-rails-main-mysql:
90
90
  docker:
91
91
  - image: circleci/ruby:2.6.5-node
92
92
  environment:
93
- - RAILS_VERSION=master
93
+ - RAILS_VERSION=main
94
94
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
95
95
  - DATABASE_DEPENDENCY_PORT=3306
96
96
  - image: circleci/mysql:5.7.18
@@ -100,11 +100,11 @@ jobs:
100
100
  - MYSQL_PASSWORD=
101
101
  - MYSQL_DATABASE=statesman_test
102
102
  steps: *steps
103
- build-ruby265-rails-master-postgres:
103
+ build-ruby265-rails-main-postgres:
104
104
  docker:
105
105
  - image: circleci/ruby:2.6.5-node
106
106
  environment:
107
- - RAILS_VERSION=master
107
+ - RAILS_VERSION=main
108
108
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
109
109
  - EXCLUDE_MONGOID=true
110
110
  - DATABASE_DEPENDENCY_PORT=5432
@@ -142,11 +142,11 @@ jobs:
142
142
  - POSTGRES_DB=statesman_test
143
143
  - POSTGRES_PASSWORD=statesman
144
144
  steps: *steps
145
- build-ruby270-rails-master-mysql:
145
+ build-ruby270-rails-main-mysql:
146
146
  docker:
147
147
  - image: circleci/ruby:2.7.0-node
148
148
  environment:
149
- - RAILS_VERSION=master
149
+ - RAILS_VERSION=main
150
150
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
151
151
  - DATABASE_DEPENDENCY_PORT=3306
152
152
  - image: circleci/mysql:5.7.18
@@ -156,11 +156,11 @@ jobs:
156
156
  - MYSQL_PASSWORD=
157
157
  - MYSQL_DATABASE=statesman_test
158
158
  steps: *steps
159
- build-ruby270-rails-master-postgres:
159
+ build-ruby270-rails-main-postgres:
160
160
  docker:
161
161
  - image: circleci/ruby:2.7.0-node
162
162
  environment:
163
- - RAILS_VERSION=master
163
+ - RAILS_VERSION=main
164
164
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
165
165
  - EXCLUDE_MONGOID=true
166
166
  - DATABASE_DEPENDENCY_PORT=5432
@@ -179,9 +179,9 @@ workflows:
179
179
  - build-ruby249-rails-524-postgres
180
180
  - build-ruby265-rails-602-mysql
181
181
  - build-ruby265-rails-602-postgres
182
- - build-ruby265-rails-master-mysql
183
- - build-ruby265-rails-master-postgres
182
+ - build-ruby265-rails-main-mysql
183
+ - build-ruby265-rails-main-postgres
184
184
  - build-ruby270-rails-602-mysql
185
185
  - build-ruby270-rails-602-postgres
186
- - build-ruby270-rails-master-mysql
187
- - build-ruby270-rails-master-postgres
186
+ - build-ruby270-rails-main-mysql
187
+ - build-ruby270-rails-main-postgres
@@ -1,3 +1,40 @@
1
+ ## v8.0.0 6th January 2021
2
+
3
+ ### Added
4
+
5
+ - Use AR Arel table to type cast booleans in order to avoid deprecation warning [#421](https://github.com/gocardless/statesman/pull/421)
6
+ - Support relationships that doesn't use `id` as a Primary Key
7
+ [#422](https://github.com/gocardless/statesman/pull/422)
8
+
9
+ ## v7.4.1 11th November 2020
10
+
11
+ ### Added
12
+
13
+ - Add #reset method to state machine and adapter interfaces
14
+ [#417](https://github.com/gocardless/statesman/pull/417)
15
+
16
+ ## v7.4.0 26th August 2020
17
+
18
+ ### Added
19
+
20
+ - [Gem Metadata](https://guides.rubygems.org/specification-reference/#metadata)
21
+ to make finding changes between releases even easier.
22
+
23
+ ## v7.3.0, 24th August 2020
24
+
25
+ ### Changed
26
+
27
+ - Use correct Arel for null [#409](https://github.com/gocardless/statesman/pull/#409)
28
+
29
+ ## v7.2.0, 19th May 2020
30
+
31
+ ### Changed
32
+
33
+ - Set non-empty password for postgres tests [#398](https://github.com/gocardless/statesman/pull/#398)
34
+ - Handle transitions differently for MySQL [#399](https://github.com/gocardless/statesman/pull/#399)
35
+ - pg requirement from >= 0.18, <= 1.1 to >= 0.18, <= 1.3 [#400](https://github.com/gocardless/statesman/pull/#400)
36
+ - Lazily enable mysql gaplock protection [#402](https://github.com/gocardless/statesman/pull/#402)
37
+
1
38
  ## v7.1.0, 10th Feb 2020
2
39
 
3
40
  - Fix `to_s` on `TransitionFailedError` & `GuardFailedError`. `.message` and
data/Gemfile CHANGED
@@ -5,8 +5,8 @@ source 'https://rubygems.org'
5
5
  gemspec
6
6
 
7
7
  # rubocop:disable Bundler/DuplicatedGem
8
- if ENV['RAILS_VERSION'] == 'master'
9
- gem "rails", git: "https://github.com/rails/rails"
8
+ if ENV['RAILS_VERSION'] == 'main'
9
+ gem "rails", git: "https://github.com/rails/rails", branch: "main"
10
10
  elsif ENV['RAILS_VERSION']
11
11
  gem "rails", "~> #{ENV['RAILS_VERSION']}"
12
12
  end
data/README.md CHANGED
@@ -322,6 +322,10 @@ Machine.successors
322
322
  #### `Machine#current_state`
323
323
  Returns the current state based on existing transition objects.
324
324
 
325
+ Takes an optional keyword argument to force a reload of data from the
326
+ database.
327
+ e.g `current_state(force_reload: true)`
328
+
325
329
  #### `Machine#in_state?(:state_1, :state_2, ...)`
326
330
  Returns true if the machine is in any of the given states.
327
331
 
@@ -404,10 +408,12 @@ Model.in_state(:state_1).or(
404
408
  #### Storing the state on the model object
405
409
 
406
410
  If you wish to store the model state on the model directly, you can keep it up
407
- to date using an `after_transition` hook:
411
+ to date using an `after_transition` hook.
412
+ Combine it with the `after_commit` option to ensure the model state will only be
413
+ saved once the transition has made it irreversibly to the database:
408
414
 
409
415
  ```ruby
410
- after_transition do |model, transition|
416
+ after_transition(after_commit: true) do |model, transition|
411
417
  model.state = transition.to_state
412
418
  model.save!
413
419
  end
@@ -39,8 +39,16 @@ module Statesman
39
39
  end
40
40
 
41
41
  def mysql?
42
- ActiveRecord::Base.configurations[Rails.env].
43
- try(:[], "adapter").try(:match, /mysql/)
42
+ configuration.try(:[], "adapter").try(:match, /mysql/)
43
+ end
44
+
45
+ # [] is deprecated and will be removed in 6.2
46
+ def configuration
47
+ if ActiveRecord::Base.configurations.respond_to?(:configs_for)
48
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first
49
+ else
50
+ ActiveRecord::Base.configurations[Rails.env]
51
+ end
44
52
  end
45
53
 
46
54
  def database_supports_partial_indexes?
@@ -67,6 +67,10 @@ module Statesman
67
67
  end
68
68
  end
69
69
 
70
+ def reset
71
+ @last_transition = nil
72
+ end
73
+
70
74
  private
71
75
 
72
76
  # rubocop:disable Metrics/MethodLength
@@ -113,7 +117,7 @@ module Statesman
113
117
  to_state: to,
114
118
  sort_key: next_sort_key,
115
119
  metadata: metadata,
116
- most_recent: not_most_recent_value,
120
+ most_recent: not_most_recent_value(db_cast: false),
117
121
  }
118
122
  end
119
123
 
@@ -301,19 +305,21 @@ module Statesman
301
305
  end
302
306
 
303
307
  def db_true
304
- value = ::ActiveRecord::Base.connection.type_cast(
305
- true,
306
- transition_class.columns_hash["most_recent"],
307
- )
308
- ::ActiveRecord::Base.connection.quote(value)
308
+ ::ActiveRecord::Base.connection.quote(type_cast(true))
309
309
  end
310
310
 
311
311
  def db_false
312
- value = ::ActiveRecord::Base.connection.type_cast(
313
- false,
314
- transition_class.columns_hash["most_recent"],
315
- )
316
- ::ActiveRecord::Base.connection.quote(value)
312
+ ::ActiveRecord::Base.connection.quote(type_cast(false))
313
+ end
314
+
315
+ def db_null
316
+ Arel::Nodes::SqlLiteral.new("NULL")
317
+ end
318
+
319
+ # Type casting against a column is deprecated and will be removed in Rails 6.2.
320
+ # See https://github.com/rails/arel/commit/6160bfbda1d1781c3b08a33ec4955f170e95be11
321
+ def type_cast(value)
322
+ ::ActiveRecord::Base.connection.type_cast(value)
317
323
  end
318
324
 
319
325
  # Check whether the `most_recent` column allows null values. If it doesn't, set old
@@ -323,10 +329,12 @@ module Statesman
323
329
  # indexes. By doing the conditioning on the column, rather than Rails' opinion of
324
330
  # whether the database supports partial indexes, we're robust to DBs later adding
325
331
  # support for partial indexes.
326
- def not_most_recent_value
327
- return db_false if transition_class.columns_hash["most_recent"].null == false
332
+ def not_most_recent_value(db_cast: true)
333
+ if transition_class.columns_hash["most_recent"].null == false
334
+ return db_cast ? db_false : false
335
+ end
328
336
 
329
- nil
337
+ db_cast ? db_null : nil
330
338
  end
331
339
  end
332
340
 
@@ -104,7 +104,7 @@ module Statesman
104
104
 
105
105
  def most_recent_transition_join
106
106
  "LEFT OUTER JOIN #{model_table} AS #{most_recent_transition_alias} " \
107
- "ON #{model.table_name}.id = " \
107
+ "ON #{model.table_name}.#{model_primary_key} = " \
108
108
  "#{most_recent_transition_alias}.#{model_foreign_key} " \
109
109
  "AND #{most_recent_transition_alias}.most_recent = #{db_true}"
110
110
  end
@@ -127,6 +127,10 @@ module Statesman
127
127
  "and #{transition_class}."
128
128
  end
129
129
 
130
+ def model_primary_key
131
+ transition_reflection.active_record_primary_key
132
+ end
133
+
130
134
  def model_foreign_key
131
135
  transition_reflection.foreign_key
132
136
  end
@@ -37,6 +37,10 @@ module Statesman
37
37
  @history
38
38
  end
39
39
 
40
+ def reset
41
+ @history = []
42
+ end
43
+
40
44
  private
41
45
 
42
46
  def next_sort_key
@@ -257,6 +257,10 @@ module Statesman
257
257
  false
258
258
  end
259
259
 
260
+ def reset
261
+ @storage_adapter.reset
262
+ end
263
+
260
264
  private
261
265
 
262
266
  def adapter_class(transition_class)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Statesman
4
- VERSION = "7.2.0"
4
+ VERSION = "8.0.1"
5
5
  end
@@ -154,6 +154,31 @@ describe Statesman::Adapters::ActiveRecordQueries, active_record: true do
154
154
  end
155
155
  end
156
156
 
157
+ context "with a custom primary key for the model" do
158
+ before do
159
+ # Switch to using OtherActiveRecordModelTransition, so the existing
160
+ # relation with MyActiveRecordModelTransition doesn't interfere with
161
+ # this spec.
162
+ # Configure the relationship to use a different primary key,
163
+ MyActiveRecordModel.send(:has_many,
164
+ :custom_name,
165
+ class_name: "OtherActiveRecordModelTransition",
166
+ primary_key: :external_id)
167
+
168
+ MyActiveRecordModel.class_eval do
169
+ def self.transition_class
170
+ OtherActiveRecordModelTransition
171
+ end
172
+ end
173
+ end
174
+
175
+ describe ".in_state" do
176
+ subject(:query) { MyActiveRecordModel.in_state(:succeeded) }
177
+
178
+ specify { expect { query }.to_not raise_error }
179
+ end
180
+ end
181
+
157
182
  context "after_commit transactional integrity" do
158
183
  before do
159
184
  MyStateMachine.class_eval do
@@ -355,6 +355,19 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
355
355
  end
356
356
  end
357
357
 
358
+ it "resets last with #reload" do
359
+ model.save!
360
+ ActiveRecord::Base.transaction do
361
+ model.state_machine.transition_to!(:succeeded)
362
+ # force to cache value in last_transition instance variable
363
+ expect(model.state_machine.current_state).to eq("succeeded")
364
+ raise ActiveRecord::Rollback
365
+ end
366
+ expect(model.state_machine.current_state).to eq("succeeded")
367
+ model.reload
368
+ expect(model.state_machine.current_state).to eq("initial")
369
+ end
370
+
358
371
  context "with a namespaced model" do
359
372
  before do
360
373
  CreateNamespacedARModelMigration.migrate(:up)
@@ -33,6 +33,11 @@ class MyActiveRecordModel < ActiveRecord::Base
33
33
  def metadata
34
34
  super || {}
35
35
  end
36
+
37
+ def reload(*)
38
+ state_machine.reset
39
+ super
40
+ end
36
41
  end
37
42
 
38
43
  class MyActiveRecordModelTransition < ActiveRecord::Base
@@ -4,6 +4,8 @@ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require "statesman/version"
6
6
 
7
+ GITHUB_URL = "https://github.com/gocardless/statesman"
8
+
7
9
  Gem::Specification.new do |spec|
8
10
  spec.name = "statesman"
9
11
  spec.version = Statesman::VERSION
@@ -11,7 +13,7 @@ Gem::Specification.new do |spec|
11
13
  spec.email = ["developers@gocardless.com"]
12
14
  spec.description = "A statesman-like state machine library"
13
15
  spec.summary = spec.description
14
- spec.homepage = "https://github.com/gocardless/statesman"
16
+ spec.homepage = GITHUB_URL
15
17
  spec.license = "MIT"
16
18
 
17
19
  spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
@@ -35,4 +37,12 @@ Gem::Specification.new do |spec|
35
37
  spec.add_development_dependency "rspec_junit_formatter", "~> 0.4.0"
36
38
  spec.add_development_dependency "sqlite3", "~> 1.4.2"
37
39
  spec.add_development_dependency "timecop", "~> 0.9.1"
40
+
41
+ spec.metadata = {
42
+ "bug_tracker_uri" => "#{GITHUB_URL}/issues",
43
+ "changelog_uri" => "#{GITHUB_URL}/blob/master/CHANGELOG.md",
44
+ "documentation_uri" => "#{GITHUB_URL}/blob/master/README.md",
45
+ "homepage_uri" => GITHUB_URL,
46
+ "source_code_uri" => GITHUB_URL,
47
+ }
38
48
  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: 7.2.0
4
+ version: 8.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - GoCardless
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-19 00:00:00.000000000 Z
11
+ date: 2021-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ammeter
@@ -282,8 +282,13 @@ files:
282
282
  homepage: https://github.com/gocardless/statesman
283
283
  licenses:
284
284
  - MIT
285
- metadata: {}
286
- post_install_message:
285
+ metadata:
286
+ bug_tracker_uri: https://github.com/gocardless/statesman/issues
287
+ changelog_uri: https://github.com/gocardless/statesman/blob/master/CHANGELOG.md
288
+ documentation_uri: https://github.com/gocardless/statesman/blob/master/README.md
289
+ homepage_uri: https://github.com/gocardless/statesman
290
+ source_code_uri: https://github.com/gocardless/statesman
291
+ post_install_message:
287
292
  rdoc_options: []
288
293
  require_paths:
289
294
  - lib
@@ -298,8 +303,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
298
303
  - !ruby/object:Gem::Version
299
304
  version: '0'
300
305
  requirements: []
301
- rubygems_version: 3.1.1
302
- signing_key:
306
+ rubygems_version: 3.1.2
307
+ signing_key:
303
308
  specification_version: 4
304
309
  summary: A statesman-like state machine library
305
310
  test_files: