statesman 10.2.1 → 10.2.2
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 +4 -4
- data/CHANGELOG.md +5 -0
- data/lib/statesman/adapters/active_record.rb +27 -10
- data/lib/statesman/adapters/active_record_queries.rb +8 -0
- data/lib/statesman/version.rb +1 -1
- data/spec/spec_helper.rb +11 -0
- data/spec/statesman/adapters/active_record_spec.rb +54 -0
- data/spec/statesman/machine_spec.rb +6 -6
- data/spec/support/active_record.rb +103 -12
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '07548e72198d3efbe3164700acee48b30c0fae61606b8e56304a078f8f2b7d37'
|
4
|
+
data.tar.gz: 750c6c7f3fa4f9099d64afffe2ed3faa55c6536e33da9b4dd14283e3a3fc39ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15a2d47316506e5e2345242a94e6aef09110f0944c2f8b2a4bb726027e45f500c7b51661093df8028db21b60f1afbf1bfc7792f37fb315edf7fad4291ef64712
|
7
|
+
data.tar.gz: 25bf0708566b48822316977e446fc543823ce702219c5584eb439b4f10e5bac51f08636e1fc207fc840fbe73c7d9dbe3b0a1c041923184bd5da6d5693033965b
|
data/CHANGELOG.md
CHANGED
@@ -76,8 +76,9 @@ module Statesman
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def reset
|
79
|
-
|
80
|
-
|
79
|
+
if instance_variable_defined?(:@last_transition)
|
80
|
+
remove_instance_variable(:@last_transition)
|
81
|
+
end
|
81
82
|
end
|
82
83
|
|
83
84
|
private
|
@@ -158,13 +159,24 @@ module Statesman
|
|
158
159
|
|
159
160
|
def most_recent_transitions(most_recent_id = nil)
|
160
161
|
if most_recent_id
|
161
|
-
|
162
|
+
concrete_transitions_of_parent.and(
|
162
163
|
transition_table[:id].eq(most_recent_id).or(
|
163
164
|
transition_table[:most_recent].eq(true),
|
164
165
|
),
|
165
166
|
)
|
166
167
|
else
|
167
|
-
|
168
|
+
concrete_transitions_of_parent.and(transition_table[:most_recent].eq(true))
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def concrete_transitions_of_parent
|
173
|
+
if transition_sti?
|
174
|
+
transitions_of_parent.and(
|
175
|
+
transition_table[transition_class.inheritance_column].
|
176
|
+
eq(transition_class.name),
|
177
|
+
)
|
178
|
+
else
|
179
|
+
transitions_of_parent
|
168
180
|
end
|
169
181
|
end
|
170
182
|
|
@@ -263,13 +275,18 @@ module Statesman
|
|
263
275
|
end
|
264
276
|
end
|
265
277
|
|
266
|
-
def
|
267
|
-
|
268
|
-
|
269
|
-
reflect_on_all_associations(:has_many).
|
270
|
-
find { |r| r.name.to_s == @association_name.to_s }
|
278
|
+
def transition_sti?
|
279
|
+
transition_class.column_names.include?(transition_class.inheritance_column)
|
280
|
+
end
|
271
281
|
|
272
|
-
|
282
|
+
def parent_association
|
283
|
+
parent_model.class.
|
284
|
+
reflect_on_all_associations(:has_many).
|
285
|
+
find { |r| r.name.to_s == @association_name.to_s }
|
286
|
+
end
|
287
|
+
|
288
|
+
def parent_join_foreign_key
|
289
|
+
association_join_primary_key(parent_association)
|
273
290
|
end
|
274
291
|
|
275
292
|
def association_join_primary_key(association)
|
@@ -49,6 +49,14 @@ module Statesman
|
|
49
49
|
|
50
50
|
define_in_state(base, query_builder)
|
51
51
|
define_not_in_state(base, query_builder)
|
52
|
+
|
53
|
+
define_method(:reload) do |*a|
|
54
|
+
instance = super(*a)
|
55
|
+
if instance.respond_to?(:state_machine, true)
|
56
|
+
instance.state_machine.reset
|
57
|
+
end
|
58
|
+
instance
|
59
|
+
end
|
52
60
|
end
|
53
61
|
|
54
62
|
private
|
data/lib/statesman/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -48,6 +48,8 @@ RSpec.configure do |config|
|
|
48
48
|
my_namespace_my_active_record_model_transitions
|
49
49
|
other_active_record_models
|
50
50
|
other_active_record_model_transitions
|
51
|
+
sti_active_record_models
|
52
|
+
sti_active_record_model_transitions
|
51
53
|
]
|
52
54
|
tables.each do |table_name|
|
53
55
|
sql = "DROP TABLE IF EXISTS #{table_name};"
|
@@ -72,6 +74,15 @@ RSpec.configure do |config|
|
|
72
74
|
OtherActiveRecordModelTransition.reset_column_information
|
73
75
|
end
|
74
76
|
|
77
|
+
def prepare_sti_model_table
|
78
|
+
CreateStiActiveRecordModelMigration.migrate(:up)
|
79
|
+
end
|
80
|
+
|
81
|
+
def prepare_sti_transitions_table
|
82
|
+
CreateStiActiveRecordModelTransitionMigration.migrate(:up)
|
83
|
+
StiActiveRecordModelTransition.reset_column_information
|
84
|
+
end
|
85
|
+
|
75
86
|
MyNamespace::MyActiveRecordModelTransition.serialize(:metadata, JSON)
|
76
87
|
end
|
77
88
|
end
|
@@ -12,6 +12,9 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
12
12
|
|
13
13
|
MyActiveRecordModelTransition.serialize(:metadata, JSON)
|
14
14
|
|
15
|
+
prepare_sti_model_table
|
16
|
+
prepare_sti_transitions_table
|
17
|
+
|
15
18
|
Statesman.configure do
|
16
19
|
# Rubocop requires described_class to be used, but this block
|
17
20
|
# is instance_eval'd and described_class won't be defined
|
@@ -300,6 +303,57 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
300
303
|
from(true).to be_falsey
|
301
304
|
end
|
302
305
|
end
|
306
|
+
|
307
|
+
context "when transition uses STI" do
|
308
|
+
let(:sti_model) { StiActiveRecordModel.create }
|
309
|
+
|
310
|
+
let(:adapter_a) do
|
311
|
+
described_class.new(
|
312
|
+
StiAActiveRecordModelTransition,
|
313
|
+
sti_model,
|
314
|
+
observer,
|
315
|
+
{ association_name: :sti_a_active_record_model_transitions },
|
316
|
+
)
|
317
|
+
end
|
318
|
+
let(:adapter_b) do
|
319
|
+
described_class.new(
|
320
|
+
StiBActiveRecordModelTransition,
|
321
|
+
sti_model,
|
322
|
+
observer,
|
323
|
+
{ association_name: :sti_b_active_record_model_transitions },
|
324
|
+
)
|
325
|
+
end
|
326
|
+
let(:create) { adapter_a.create(from, to) }
|
327
|
+
|
328
|
+
context "with a previous unrelated transition" do
|
329
|
+
let!(:transition_b) { adapter_b.create(from, to) }
|
330
|
+
|
331
|
+
its(:most_recent) { is_expected.to eq(true) }
|
332
|
+
|
333
|
+
it "doesn't update the previous transition's most_recent flag" do
|
334
|
+
expect { create }.
|
335
|
+
to_not(change { transition_b.reload.most_recent })
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
context "with previous related and unrelated transitions" do
|
340
|
+
let!(:transition_a) { adapter_a.create(from, to) }
|
341
|
+
let!(:transition_b) { adapter_b.create(from, to) }
|
342
|
+
|
343
|
+
its(:most_recent) { is_expected.to eq(true) }
|
344
|
+
|
345
|
+
it "updates the previous transition's most_recent flag" do
|
346
|
+
expect { create }.
|
347
|
+
to change { transition_a.reload.most_recent }.
|
348
|
+
from(true).to be_falsey
|
349
|
+
end
|
350
|
+
|
351
|
+
it "doesn't update the previous unrelated transition's most_recent flag" do
|
352
|
+
expect { create }.
|
353
|
+
to_not(change { transition_b.reload.most_recent })
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
303
357
|
end
|
304
358
|
end
|
305
359
|
|
@@ -976,20 +976,20 @@ describe Statesman::Machine do
|
|
976
976
|
end
|
977
977
|
|
978
978
|
context "with defined callbacks" do
|
979
|
-
let(:
|
980
|
-
let(:
|
979
|
+
let(:callback_one) { -> { "Hi" } }
|
980
|
+
let(:callback_two) { -> { "Bye" } }
|
981
981
|
|
982
982
|
before do
|
983
|
-
machine.send(definer, from: :x, to: :y, &
|
984
|
-
machine.send(definer, from: :y, to: :z, &
|
983
|
+
machine.send(definer, from: :x, to: :y, &callback_one)
|
984
|
+
machine.send(definer, from: :y, to: :z, &callback_two)
|
985
985
|
end
|
986
986
|
|
987
987
|
it "contains the relevant callback" do
|
988
|
-
expect(callbacks.map(&:callback)).to include(
|
988
|
+
expect(callbacks.map(&:callback)).to include(callback_one)
|
989
989
|
end
|
990
990
|
|
991
991
|
it "does not contain the irrelevant callback" do
|
992
|
-
expect(callbacks.map(&:callback)).to_not include(
|
992
|
+
expect(callbacks.map(&:callback)).to_not include(callback_two)
|
993
993
|
end
|
994
994
|
end
|
995
995
|
end
|
@@ -20,10 +20,22 @@ class MyStateMachine
|
|
20
20
|
transition from: :failed, to: :initial
|
21
21
|
end
|
22
22
|
|
23
|
+
class MyActiveRecordModelTransition < ActiveRecord::Base
|
24
|
+
include Statesman::Adapters::ActiveRecordTransition
|
25
|
+
|
26
|
+
belongs_to :my_active_record_model
|
27
|
+
serialize :metadata, JSON
|
28
|
+
end
|
29
|
+
|
23
30
|
class MyActiveRecordModel < ActiveRecord::Base
|
24
31
|
has_many :my_active_record_model_transitions, autosave: false
|
25
32
|
alias_method :transitions, :my_active_record_model_transitions
|
26
33
|
|
34
|
+
include Statesman::Adapters::ActiveRecordQueries[
|
35
|
+
transition_class: MyActiveRecordModelTransition,
|
36
|
+
initial_state: :initial
|
37
|
+
]
|
38
|
+
|
27
39
|
def state_machine
|
28
40
|
@state_machine ||= MyStateMachine.new(
|
29
41
|
self, transition_class: MyActiveRecordModelTransition
|
@@ -33,18 +45,6 @@ class MyActiveRecordModel < ActiveRecord::Base
|
|
33
45
|
def metadata
|
34
46
|
super || {}
|
35
47
|
end
|
36
|
-
|
37
|
-
def reload(*)
|
38
|
-
state_machine.reset
|
39
|
-
super
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
class MyActiveRecordModelTransition < ActiveRecord::Base
|
44
|
-
include Statesman::Adapters::ActiveRecordTransition
|
45
|
-
|
46
|
-
belongs_to :my_active_record_model
|
47
|
-
serialize :metadata, JSON
|
48
48
|
end
|
49
49
|
|
50
50
|
class MyActiveRecordModelTransitionWithoutInclude < ActiveRecord::Base
|
@@ -278,3 +278,94 @@ class CreateNamespacedARModelTransitionMigration < MIGRATION_CLASS
|
|
278
278
|
end
|
279
279
|
end
|
280
280
|
end
|
281
|
+
|
282
|
+
class StiActiveRecordModel < ActiveRecord::Base
|
283
|
+
has_many :sti_a_active_record_model_transitions, autosave: false
|
284
|
+
has_many :sti_b_active_record_model_transitions, autosave: false
|
285
|
+
|
286
|
+
def state_machine_a
|
287
|
+
@state_machine_a ||= MyStateMachine.new(
|
288
|
+
self, transition_class: StiAActiveRecordModelTransition
|
289
|
+
)
|
290
|
+
end
|
291
|
+
|
292
|
+
def state_machine_b
|
293
|
+
@state_machine_b ||= MyStateMachine.new(
|
294
|
+
self, transition_class: StiBActiveRecordModelTransition
|
295
|
+
)
|
296
|
+
end
|
297
|
+
|
298
|
+
def metadata
|
299
|
+
super || {}
|
300
|
+
end
|
301
|
+
|
302
|
+
def reload(*)
|
303
|
+
state_machine_a.reset
|
304
|
+
state_machine_b.reset
|
305
|
+
super
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
class StiActiveRecordModelTransition < ActiveRecord::Base
|
310
|
+
include Statesman::Adapters::ActiveRecordTransition
|
311
|
+
|
312
|
+
belongs_to :sti_active_record_model
|
313
|
+
serialize :metadata, JSON
|
314
|
+
end
|
315
|
+
|
316
|
+
class StiAActiveRecordModelTransition < StiActiveRecordModelTransition
|
317
|
+
end
|
318
|
+
|
319
|
+
class StiBActiveRecordModelTransition < StiActiveRecordModelTransition
|
320
|
+
end
|
321
|
+
|
322
|
+
class CreateStiActiveRecordModelMigration < MIGRATION_CLASS
|
323
|
+
def change
|
324
|
+
create_table :sti_active_record_models do |t|
|
325
|
+
t.timestamps null: false
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
class CreateStiActiveRecordModelTransitionMigration < MIGRATION_CLASS
|
331
|
+
def change
|
332
|
+
create_table :sti_active_record_model_transitions do |t|
|
333
|
+
t.string :to_state
|
334
|
+
t.integer :sti_active_record_model_id
|
335
|
+
t.integer :sort_key
|
336
|
+
t.string :type
|
337
|
+
|
338
|
+
# MySQL doesn't allow default values on text fields
|
339
|
+
if ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
340
|
+
t.text :metadata
|
341
|
+
else
|
342
|
+
t.text :metadata, default: "{}"
|
343
|
+
end
|
344
|
+
|
345
|
+
if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
|
346
|
+
t.boolean :most_recent, default: true, null: false
|
347
|
+
else
|
348
|
+
t.boolean :most_recent, default: true
|
349
|
+
end
|
350
|
+
|
351
|
+
t.timestamps null: false
|
352
|
+
end
|
353
|
+
|
354
|
+
add_index :sti_active_record_model_transitions,
|
355
|
+
%i[type sti_active_record_model_id sort_key],
|
356
|
+
unique: true, name: "sti_sort_key_index"
|
357
|
+
|
358
|
+
if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
|
359
|
+
add_index :sti_active_record_model_transitions,
|
360
|
+
%i[type sti_active_record_model_id most_recent],
|
361
|
+
unique: true,
|
362
|
+
where: "most_recent",
|
363
|
+
name: "index_sti_active_record_model_transitions_parent_latest"
|
364
|
+
else
|
365
|
+
add_index :sti_active_record_model_transitions,
|
366
|
+
%i[type sti_active_record_model_id most_recent],
|
367
|
+
unique: true,
|
368
|
+
name: "index_sti_active_record_model_transitions_parent_latest"
|
369
|
+
end
|
370
|
+
end
|
371
|
+
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: 10.2.
|
4
|
+
version: 10.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GoCardless
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ammeter
|