statesman 10.2.1 → 10.2.3

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: 84f52a986a8c32bd6f42d6e450da1b26b8608cf4dc7434a163ee602d84425a36
4
- data.tar.gz: 8aaf748af7917715006dd2bd8b7946656ecbaa3cfd9dd7df9bd776da06f5ce41
3
+ metadata.gz: 0cc749949e382ced6f27722a0eb033efc0383ba11a06265a84e1da3b593eb02d
4
+ data.tar.gz: f2dd851b5c9ccb0aacbde8076c10fcf23bfe8a00926e58acfb9dd714c5d61b99
5
5
  SHA512:
6
- metadata.gz: dc43e626d95b225a002c423092dc0906aa93365b028f8d9fef20a19c4657bef8761c430321db3046dece32d5b9ed3f8fac667a502ba13bfb9f5df8460d9cfd5d
7
- data.tar.gz: 7aa34ddc4e22e611d942db5b6c9751496a203bcb678b5a89ff8772c251515edfa27a4bca92f2c999d499b63105624464dd0e7d42dba5dcb8670d6bafcc1bd517
6
+ metadata.gz: 4ac72394722dc0d193874967e79bb30df1a1c7ba63e4eccd20294f2298b2e78637b3c7ffff7fe4ef4f4ad5d3ca522efe89444400fa09231684622848754a7683
7
+ data.tar.gz: e995d62de4ec4471ec1a66fea6e290bfbc5f0a3420c3bd915117a0e92f08ca20994b59e84ed5f73a93d091156ea47ef35548ea61f50ad52a1942a69438d4f446
data/.rubocop.yml CHANGED
@@ -15,3 +15,6 @@ Metrics/CyclomaticComplexity:
15
15
 
16
16
  Metrics/PerceivedComplexity:
17
17
  Max: 11
18
+
19
+ Gemspec/DevelopmentDependencies:
20
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## v10.2.3 2nd Aug 2023
2
+
3
+ ### Changed
4
+ - Fixed calls to reloading internal cache is the state_machine was made private / protected
5
+
6
+ ## v10.2.2 21st April 2023
7
+
8
+ ### Changed
9
+ - Calling `active_record.reload` resets the adapater's internal cache
10
+
1
11
  ## v10.2.1 3rd April 2023
2
12
 
3
13
  ### Changed
@@ -11,7 +11,7 @@ module Statesman
11
11
  end
12
12
 
13
13
  def migration_class_name
14
- klass.gsub(/::/, "").pluralize
14
+ klass.gsub("::", "").pluralize
15
15
  end
16
16
 
17
17
  def next_migration_number
@@ -76,8 +76,9 @@ module Statesman
76
76
  end
77
77
 
78
78
  def reset
79
- remove_instance_variable(:@last_transition) \
80
- if instance_variable_defined?(:@last_transition)
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
- transitions_of_parent.and(
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
- 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
168
180
  end
169
181
  end
170
182
 
@@ -263,13 +275,18 @@ module Statesman
263
275
  end
264
276
  end
265
277
 
266
- def parent_join_foreign_key
267
- association =
268
- parent_model.class.
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
- 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)
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.send(: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.1"
4
+ VERSION = "10.2.3"
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
 
@@ -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.1
4
+ version: 10.2.3
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-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ammeter