statesman 10.2.0 → 10.2.2

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
  SHA256:
3
- metadata.gz: 5f18f4e346afec7b89e6057602992d3d69f73f7c18f99d88bb3604d34724f9b0
4
- data.tar.gz: 9888c2c3bc47bb78a52f2b086d9bc3729ddefec9ab776663ebf04ec9118c4d05
3
+ metadata.gz: '07548e72198d3efbe3164700acee48b30c0fae61606b8e56304a078f8f2b7d37'
4
+ data.tar.gz: 750c6c7f3fa4f9099d64afffe2ed3faa55c6536e33da9b4dd14283e3a3fc39ab
5
5
  SHA512:
6
- metadata.gz: 50cdb42aa87bf9ab25e4f5a0de6f4f52647866453d9298811351ac16fdec88e7ff7b210948f756b9af59983e56cc5ca842232ae240be54f1a9e37185958eee9d
7
- data.tar.gz: c9835aa4537d9e8adab3a75d6f63b020eaac4d4ec5aa15a8be4a4f2a6f41a869a54b5398862b23b84929cf219aff6b86eac00af3fd4a86b3e4c0ff3a6c84bae7
6
+ metadata.gz: 15a2d47316506e5e2345242a94e6aef09110f0944c2f8b2a4bb726027e45f500c7b51661093df8028db21b60f1afbf1bfc7792f37fb315edf7fad4291ef64712
7
+ data.tar.gz: 25bf0708566b48822316977e446fc543823ce702219c5584eb439b4f10e5bac51f08636e1fc207fc840fbe73c7d9dbe3b0a1c041923184bd5da6d5693033965b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## v10.2.2 21st April 2023
2
+
3
+ ### Changed
4
+ - Calling `active_record.reload` resets the adapater's internal cache
5
+
6
+ ## v10.2.1 3rd April 2023
7
+
8
+ ### Changed
9
+ - Fixed an edge case where `adapter.reset` were failing if the cache is empty
10
+
1
11
  ## v10.2.0 3rd April 2023
2
12
 
3
13
  ### Changed
@@ -52,7 +52,7 @@ module Statesman
52
52
 
53
53
  raise
54
54
  ensure
55
- remove_instance_variable(:@last_transition)
55
+ reset
56
56
  end
57
57
 
58
58
  def history(force_reload: false)
@@ -76,7 +76,9 @@ module Statesman
76
76
  end
77
77
 
78
78
  def reset
79
- remove_instance_variable(:@last_transition)
79
+ if instance_variable_defined?(:@last_transition)
80
+ remove_instance_variable(:@last_transition)
81
+ end
80
82
  end
81
83
 
82
84
  private
@@ -157,13 +159,24 @@ module Statesman
157
159
 
158
160
  def most_recent_transitions(most_recent_id = nil)
159
161
  if most_recent_id
160
- transitions_of_parent.and(
162
+ concrete_transitions_of_parent.and(
161
163
  transition_table[:id].eq(most_recent_id).or(
162
164
  transition_table[:most_recent].eq(true),
163
165
  ),
164
166
  )
165
167
  else
166
- transitions_of_parent.and(transition_table[:most_recent].eq(true))
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
167
180
  end
168
181
  end
169
182
 
@@ -262,13 +275,18 @@ module Statesman
262
275
  end
263
276
  end
264
277
 
265
- def parent_join_foreign_key
266
- association =
267
- parent_model.class.
268
- reflect_on_all_associations(:has_many).
269
- 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
270
281
 
271
- association_join_primary_key(association)
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)
272
290
  end
273
291
 
274
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Statesman
4
- VERSION = "10.2.0"
4
+ VERSION = "10.2.2"
5
5
  end
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
 
@@ -388,6 +442,12 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
388
442
  end
389
443
  end
390
444
 
445
+ describe "#reset" do
446
+ it "works with empty cache" do
447
+ expect { model.state_machine.reset }.to_not raise_error
448
+ end
449
+ end
450
+
391
451
  it "resets last with #reload" do
392
452
  model.save!
393
453
  ActiveRecord::Base.transaction do
@@ -976,20 +976,20 @@ describe Statesman::Machine do
976
976
  end
977
977
 
978
978
  context "with defined callbacks" do
979
- let(:callback_1) { -> { "Hi" } }
980
- let(:callback_2) { -> { "Bye" } }
979
+ let(:callback_one) { -> { "Hi" } }
980
+ let(:callback_two) { -> { "Bye" } }
981
981
 
982
982
  before do
983
- machine.send(definer, from: :x, to: :y, &callback_1)
984
- machine.send(definer, from: :y, to: :z, &callback_2)
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(callback_1)
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(callback_2)
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.0
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-03 00:00:00.000000000 Z
11
+ date: 2023-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ammeter