statesman 11.0.0 → 12.1.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/tests.yml +7 -3
  3. data/.gitignore +0 -3
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +1 -1
  6. data/.ruby-version +1 -1
  7. data/CHANGELOG.md +174 -43
  8. data/CONTRIBUTING.md +14 -13
  9. data/README.md +120 -62
  10. data/docs/COMPATIBILITY.md +2 -2
  11. data/lib/generators/statesman/generator_helpers.rb +1 -1
  12. data/lib/statesman/adapters/active_record.rb +24 -26
  13. data/lib/statesman/adapters/active_record_queries.rb +2 -2
  14. data/lib/statesman/callback.rb +2 -2
  15. data/lib/statesman/config.rb +3 -10
  16. data/lib/statesman/machine.rb +8 -0
  17. data/lib/statesman/version.rb +1 -1
  18. data/lib/statesman.rb +3 -5
  19. data/lib/tasks/statesman.rake +2 -2
  20. data/spec/generators/statesman/active_record_transition_generator_spec.rb +7 -1
  21. data/spec/generators/statesman/migration_generator_spec.rb +5 -1
  22. data/spec/spec_helper.rb +33 -5
  23. data/spec/statesman/adapters/active_record_queries_spec.rb +0 -2
  24. data/spec/statesman/adapters/active_record_spec.rb +48 -13
  25. data/spec/statesman/adapters/active_record_transition_spec.rb +0 -1
  26. data/spec/statesman/adapters/memory_spec.rb +0 -1
  27. data/spec/statesman/adapters/memory_transition_spec.rb +0 -1
  28. data/spec/statesman/adapters/shared_examples.rb +0 -2
  29. data/spec/statesman/adapters/type_safe_active_record_queries_spec.rb +0 -2
  30. data/spec/statesman/callback_spec.rb +0 -2
  31. data/spec/statesman/config_spec.rb +0 -2
  32. data/spec/statesman/exceptions_spec.rb +0 -2
  33. data/spec/statesman/guard_spec.rb +0 -2
  34. data/spec/statesman/machine_spec.rb +71 -2
  35. data/spec/statesman/utils_spec.rb +0 -2
  36. data/spec/support/active_record.rb +50 -8
  37. data/spec/support/exactly_query_databases.rb +35 -0
  38. data/statesman.gemspec +4 -4
  39. metadata +11 -9
@@ -40,11 +40,11 @@ module Statesman
40
40
  end
41
41
 
42
42
  def matches_from_state(from, to)
43
- (from == self.from && (to.nil? || self.to.empty?))
43
+ from == self.from && (to.nil? || self.to.empty?)
44
44
  end
45
45
 
46
46
  def matches_to_state(from, to)
47
- ((from.nil? || self.from.nil?) && self.to.include?(to))
47
+ (from.nil? || self.from.nil?) && self.to.include?(to)
48
48
  end
49
49
 
50
50
  def matches_both_states(from, to)
@@ -15,17 +15,10 @@ module Statesman
15
15
  @adapter_class = adapter_class
16
16
  end
17
17
 
18
- def mysql_gaplock_protection?
19
- return @mysql_gaplock_protection unless @mysql_gaplock_protection.nil?
20
-
18
+ def mysql_gaplock_protection?(connection)
21
19
  # If our adapter class suggests we're using mysql, enable gaplock protection by
22
20
  # default.
23
- enable_mysql_gaplock_protection if mysql_adapter?(adapter_class)
24
- @mysql_gaplock_protection
25
- end
26
-
27
- def enable_mysql_gaplock_protection
28
- @mysql_gaplock_protection = true
21
+ mysql_adapter?(connection)
29
22
  end
30
23
 
31
24
  private
@@ -34,7 +27,7 @@ module Statesman
34
27
  adapter_name = adapter_name(adapter_class)
35
28
  return false unless adapter_name
36
29
 
37
- adapter_name.start_with?("mysql")
30
+ adapter_name.downcase.start_with?("mysql", "trilogy")
38
31
  end
39
32
 
40
33
  def adapter_name(adapter_class)
@@ -233,12 +233,20 @@ module Statesman
233
233
  def initialize(object,
234
234
  options = {
235
235
  transition_class: Statesman::Adapters::MemoryTransition,
236
+ initial_transition: false,
236
237
  })
237
238
  @object = object
238
239
  @transition_class = options[:transition_class]
239
240
  @storage_adapter = adapter_class(@transition_class).new(
240
241
  @transition_class, object, self, options
241
242
  )
243
+
244
+ if options[:initial_transition]
245
+ if history.empty? && self.class.initial_state
246
+ @storage_adapter.create(nil, self.class.initial_state)
247
+ end
248
+ end
249
+
242
250
  send(:after_initialize) if respond_to? :after_initialize
243
251
  end
244
252
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Statesman
4
- VERSION = "11.0.0"
4
+ VERSION = "12.1.0"
5
5
  end
data/lib/statesman.rb CHANGED
@@ -6,7 +6,7 @@ module Statesman
6
6
  autoload :Callback, "statesman/callback"
7
7
  autoload :Guard, "statesman/guard"
8
8
  autoload :Utils, "statesman/utils"
9
- autoload :Version, "statesman/version"
9
+ autoload :VERSION, "statesman/version"
10
10
  module Adapters
11
11
  autoload :Memory, "statesman/adapters/memory"
12
12
  autoload :ActiveRecord, "statesman/adapters/active_record"
@@ -34,10 +34,8 @@ module Statesman
34
34
  @storage_adapter || Adapters::Memory
35
35
  end
36
36
 
37
- def self.mysql_gaplock_protection?
38
- return @mysql_gaplock_protection unless @mysql_gaplock_protection.nil?
39
-
40
- @mysql_gaplock_protection = config.mysql_gaplock_protection?
37
+ def self.mysql_gaplock_protection?(connection)
38
+ config.mysql_gaplock_protection?(connection)
41
39
  end
42
40
 
43
41
  def self.config
@@ -21,8 +21,8 @@ namespace :statesman do
21
21
  batch_size = 500
22
22
 
23
23
  parent_class.find_in_batches(batch_size: batch_size) do |models|
24
- ActiveRecord::Base.transaction(requires_new: true) do
25
- if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
24
+ transition_class.transaction(requires_new: true) do
25
+ if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?(transition_class)
26
26
  # Set all transitions' most_recent to FALSE
27
27
  transition_class.where(parent_fk => models.map(&:id)).
28
28
  update_all(most_recent: false, updated_at: updated_at)
@@ -1,10 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
3
  require "support/generators_shared_examples"
5
4
  require "generators/statesman/active_record_transition_generator"
6
5
 
7
6
  describe Statesman::ActiveRecordTransitionGenerator, type: :generator do
7
+ before do
8
+ stub_const("Bacon", Class.new(ActiveRecord::Base))
9
+ stub_const("BaconTransition", Class.new(ActiveRecord::Base))
10
+ stub_const("Yummy::Bacon", Class.new(ActiveRecord::Base))
11
+ stub_const("Yummy::BaconTransition", Class.new(ActiveRecord::Base))
12
+ end
13
+
8
14
  it_behaves_like "a generator" do
9
15
  let(:migration_name) { "db/migrate/create_bacon_transitions.rb" }
10
16
  end
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
3
  require "support/generators_shared_examples"
5
4
  require "generators/statesman/migration_generator"
6
5
 
7
6
  describe Statesman::MigrationGenerator, type: :generator do
7
+ before do
8
+ stub_const("Yummy::Bacon", Class.new(ActiveRecord::Base))
9
+ stub_const("Yummy::BaconTransition", Class.new(ActiveRecord::Base))
10
+ end
11
+
8
12
  it_behaves_like "a generator" do
9
13
  let(:migration_name) { "db/migrate/add_statesman_to_bacon_transitions.rb" }
10
14
  end
data/spec/spec_helper.rb CHANGED
@@ -5,13 +5,14 @@ require "sqlite3"
5
5
  require "mysql2"
6
6
  require "pg"
7
7
  require "active_record"
8
+ require "active_record/database_configurations"
8
9
  # We have to include all of Rails to make rspec-rails work
9
10
  require "rails"
10
11
  require "action_view"
11
12
  require "action_dispatch"
12
13
  require "action_controller"
13
14
  require "rspec/rails"
14
- require "support/active_record"
15
+ require "support/exactly_query_databases"
15
16
  require "rspec/its"
16
17
  require "pry"
17
18
 
@@ -28,10 +29,31 @@ RSpec.configure do |config|
28
29
  if config.exclusion_filter[:active_record]
29
30
  puts "Skipping ActiveRecord tests"
30
31
  else
31
- # Connect to the database for activerecord tests
32
- db_conn_spec = ENV["DATABASE_URL"]
33
- db_conn_spec ||= { adapter: "sqlite3", database: ":memory:" }
34
- ActiveRecord::Base.establish_connection(db_conn_spec)
32
+ current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
33
+
34
+ # We have to parse this to a hash since ActiveRecord::Base.configurations
35
+ # will only consider a single URL config.
36
+ url_config = if ENV["DATABASE_URL"]
37
+ ActiveRecord::DatabaseConfigurations::ConnectionUrlResolver.
38
+ new(ENV["DATABASE_URL"]).to_hash.merge({ sslmode: "disable" })
39
+ end
40
+
41
+ db_config = {
42
+ current_env => {
43
+ primary: url_config || {
44
+ adapter: "sqlite3",
45
+ database: "/tmp/statesman.db",
46
+ },
47
+ secondary: url_config || {
48
+ adapter: "sqlite3",
49
+ database: "/tmp/statesman.db",
50
+ },
51
+ },
52
+ }
53
+
54
+ # Connect to the primary database for activerecord tests.
55
+ ActiveRecord::Base.configurations = db_config
56
+ ActiveRecord::Base.establish_connection(:primary)
35
57
 
36
58
  db_adapter = ActiveRecord::Base.connection.adapter_name
37
59
  puts "Running with database adapter '#{db_adapter}'"
@@ -40,6 +62,8 @@ RSpec.configure do |config|
40
62
  ActiveRecord::Migration.verbose = false
41
63
  end
42
64
 
65
+ # Since our primary and secondary connections point to the same database, we don't
66
+ # need to worry about applying these actions to both.
43
67
  config.before(:each, :active_record) do
44
68
  tables = %w[
45
69
  my_active_record_models
@@ -53,6 +77,7 @@ RSpec.configure do |config|
53
77
  ]
54
78
  tables.each do |table_name|
55
79
  sql = "DROP TABLE IF EXISTS #{table_name};"
80
+
56
81
  ActiveRecord::Base.connection.execute(sql)
57
82
  end
58
83
 
@@ -84,3 +109,6 @@ RSpec.configure do |config|
84
109
  end
85
110
  end
86
111
  end
112
+
113
+ # We have to require this after the databases are configured.
114
+ require "support/active_record"
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
-
5
3
  describe Statesman::Adapters::ActiveRecordQueries, :active_record do
6
4
  def configure_old(klass, transition_class)
7
5
  klass.define_singleton_method(:transition_class) { transition_class }
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
3
  require "timecop"
5
4
  require "statesman/adapters/shared_examples"
6
5
  require "statesman/exceptions"
@@ -10,8 +9,6 @@ describe Statesman::Adapters::ActiveRecord, :active_record do
10
9
  prepare_model_table
11
10
  prepare_transitions_table
12
11
 
13
- # MyActiveRecordModelTransition.serialize(:metadata, JSON)
14
-
15
12
  prepare_sti_model_table
16
13
  prepare_sti_transitions_table
17
14
 
@@ -26,8 +23,10 @@ describe Statesman::Adapters::ActiveRecord, :active_record do
26
23
 
27
24
  after { Statesman.configure { storage_adapter(Statesman::Adapters::Memory) } }
28
25
 
26
+ let(:model_class) { MyActiveRecordModel }
27
+ let(:transition_class) { MyActiveRecordModelTransition }
29
28
  let(:observer) { double(Statesman::Machine, execute: nil) }
30
- let(:model) { MyActiveRecordModel.create(current_state: :pending) }
29
+ let(:model) { model_class.create(current_state: :pending) }
31
30
 
32
31
  it_behaves_like "an adapter", described_class, MyActiveRecordModelTransition
33
32
 
@@ -36,8 +35,8 @@ describe Statesman::Adapters::ActiveRecord, :active_record do
36
35
  before do
37
36
  metadata_column = double
38
37
  allow(metadata_column).to receive_messages(sql_type: "")
39
- allow(MyActiveRecordModelTransition).to receive_messages(columns_hash:
40
- { "metadata" => metadata_column })
38
+ allow(MyActiveRecordModelTransition).
39
+ to receive_messages(columns_hash: { "metadata" => metadata_column })
41
40
  expect(MyActiveRecordModelTransition).
42
41
  to receive(:type_for_attribute).with("metadata").
43
42
  and_return(ActiveRecord::Type::Value.new)
@@ -105,13 +104,15 @@ describe Statesman::Adapters::ActiveRecord, :active_record do
105
104
  describe "#create" do
106
105
  subject(:transition) { create }
107
106
 
108
- let!(:adapter) do
109
- described_class.new(MyActiveRecordModelTransition, model, observer)
110
- end
107
+ let!(:adapter) { described_class.new(transition_class, model, observer) }
111
108
  let(:from) { :x }
112
109
  let(:to) { :y }
113
110
  let(:create) { adapter.create(from, to) }
114
111
 
112
+ it "only connects to the primary database" do
113
+ expect { create }.to exactly_query_databases({ primary: [:writing] })
114
+ end
115
+
115
116
  context "when there is a race" do
116
117
  it "raises a TransitionConflictError" do
117
118
  adapter2 = adapter.dup
@@ -119,7 +120,8 @@ describe Statesman::Adapters::ActiveRecord, :active_record do
119
120
  adapter.last
120
121
  adapter2.create(:y, :z)
121
122
  expect { adapter.create(:y, :z) }.
122
- to raise_exception(Statesman::TransitionConflictError)
123
+ to raise_exception(Statesman::TransitionConflictError).
124
+ and exactly_query_databases({ primary: [:writing] })
123
125
  end
124
126
 
125
127
  it "does not pollute the state when the transition fails" do
@@ -343,12 +345,34 @@ describe Statesman::Adapters::ActiveRecord, :active_record do
343
345
  end
344
346
  end
345
347
  end
348
+
349
+ context "when using the secondary database" do
350
+ let(:model_class) { SecondaryActiveRecordModel }
351
+ let(:transition_class) { SecondaryActiveRecordModelTransition }
352
+
353
+ it "doesn't connect to the primary database" do
354
+ expect { create }.to exactly_query_databases({ secondary: [:writing] })
355
+ expect(adapter.last.to_state).to eq("y")
356
+ end
357
+
358
+ context "when there is a race" do
359
+ it "raises a TransitionConflictError and uses the correct database" do
360
+ adapter2 = adapter.dup
361
+ adapter2.create(:x, :y)
362
+ adapter.last
363
+ adapter2.create(:y, :z)
364
+
365
+ expect { adapter.create(:y, :z) }.
366
+ to raise_exception(Statesman::TransitionConflictError).
367
+ and exactly_query_databases({ secondary: [:writing] })
368
+ end
369
+ end
370
+ end
346
371
  end
347
372
 
348
373
  describe "#last" do
349
- let(:adapter) do
350
- described_class.new(MyActiveRecordModelTransition, model, observer)
351
- end
374
+ let(:transition_class) { MyActiveRecordModelTransition }
375
+ let(:adapter) { described_class.new(transition_class, model, observer) }
352
376
 
353
377
  context "with a previously looked up transition" do
354
378
  before { adapter.create(:x, :y) }
@@ -365,8 +389,19 @@ describe Statesman::Adapters::ActiveRecord, :active_record do
365
389
  before { adapter.create(:y, :z, []) }
366
390
 
367
391
  it "retrieves the new transition from the database" do
392
+ expect { adapter.last.to_state }.to exactly_query_databases({ primary: [:writing] })
368
393
  expect(adapter.last.to_state).to eq("z")
369
394
  end
395
+
396
+ context "when using the secondary database" do
397
+ let(:model_class) { SecondaryActiveRecordModel }
398
+ let(:transition_class) { SecondaryActiveRecordModelTransition }
399
+
400
+ it "retrieves the new transition from the database" do
401
+ expect { adapter.last.to_state }.to exactly_query_databases({ secondary: [:writing] })
402
+ expect(adapter.last.to_state).to eq("z")
403
+ end
404
+ end
370
405
  end
371
406
 
372
407
  context "when a new transition has been created elsewhere" do
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
3
  require "json"
5
4
 
6
5
  describe Statesman::Adapters::ActiveRecordTransition do
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
3
  require "statesman/adapters/shared_examples"
5
4
  require "statesman/adapters/memory_transition"
6
5
 
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
3
  require "statesman/adapters/memory_transition"
5
4
 
6
5
  describe Statesman::Adapters::MemoryTransition do
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
-
5
3
  # All adpators must define seven methods:
6
4
  # initialize: Accepts a transition class, parent model and state_attr.
7
5
  # transition_class: Returns the transition class object passed to initialize.
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
-
5
3
  describe Statesman::Adapters::TypeSafeActiveRecordQueries, :active_record do
6
4
  def configure(klass, transition_class)
7
5
  klass.send(:extend, described_class)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
-
5
3
  describe Statesman::Callback do
6
4
  let(:cb_lambda) { -> {} }
7
5
  let(:callback) do
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
-
5
3
  describe Statesman::Config do
6
4
  let(:instance) { described_class.new }
7
5
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
-
5
3
  describe "Exceptions" do
6
4
  describe "InvalidStateError" do
7
5
  subject(:error) { Statesman::InvalidStateError.new }
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
-
5
3
  describe Statesman::Guard do
6
4
  let(:callback) { -> {} }
7
5
  let(:guard) { described_class.new(from: nil, to: nil, callback: callback) }
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
-
5
3
  describe Statesman::Machine do
6
4
  let(:machine) { Class.new { include Statesman::Machine } }
7
5
  let(:my_model) { Class.new { attr_accessor :current_state }.new }
@@ -480,12 +478,83 @@ describe Statesman::Machine do
480
478
  it_behaves_like "a callback store", :after_guard_failure, :after_guard_failure
481
479
  end
482
480
 
481
+ shared_examples "initial transition is not created" do
482
+ it "doesn't call .create on storage adapter" do
483
+ expect_any_instance_of(Statesman.storage_adapter).to_not receive(:create)
484
+ machine.new(my_model, options)
485
+ end
486
+ end
487
+
488
+ shared_examples "initial transition is created" do
489
+ it "calls .create on storage adapter" do
490
+ expect_any_instance_of(Statesman.storage_adapter).to receive(:create).with(nil, "x")
491
+ machine.new(my_model, options)
492
+ end
493
+
494
+ it "creates a new transition object" do
495
+ instance = machine.new(my_model, options)
496
+
497
+ expect(instance.history.count).to eq(1)
498
+ expect(instance.history.first.to_state).to eq("x")
499
+ end
500
+ end
501
+
483
502
  describe "#initialize" do
484
503
  it "accepts an object to manipulate" do
485
504
  machine_instance = machine.new(my_model)
486
505
  expect(machine_instance.object).to be(my_model)
487
506
  end
488
507
 
508
+ context "initial_transition is not provided" do
509
+ let(:options) { {} }
510
+
511
+ it_behaves_like "initial transition is not created"
512
+ end
513
+
514
+ context "initial_transition is provided" do
515
+ context "initial_transition is true" do
516
+ let(:options) do
517
+ { initial_transition: true,
518
+ transition_class: Statesman::Adapters::MemoryTransition }
519
+ end
520
+
521
+ context "history is empty" do
522
+ context "initial state is defined" do
523
+ before { machine.state(:x, initial: true) }
524
+
525
+ it_behaves_like "initial transition is created"
526
+ end
527
+
528
+ context "initial state is not defined" do
529
+ it_behaves_like "initial transition is not created"
530
+ end
531
+ end
532
+
533
+ context "history is not empty" do
534
+ before do
535
+ allow_any_instance_of(Statesman.storage_adapter).to receive(:history).
536
+ and_return([{}])
537
+ end
538
+
539
+ context "initial state is defined" do
540
+ before { machine.state(:x, initial: true) }
541
+
542
+ it_behaves_like "initial transition is not created"
543
+ end
544
+
545
+ context "initial state is not defined" do
546
+ it_behaves_like "initial transition is not created"
547
+ end
548
+ end
549
+ end
550
+
551
+ context "initial_transition is false" do
552
+ let(:options) { { initial_transition: false } }
553
+
554
+ it_behaves_like "initial transition is not created"
555
+ end
556
+ end
557
+
489
558
  context "transition class" do
490
559
  it "sets a default" do
491
560
  expect(Statesman.storage_adapter).to receive(:new).once.
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
4
-
5
3
  describe Statesman::Utils do
6
4
  describe ".rails_major_version" do
7
5
  subject { described_class.rails_major_version }
@@ -81,7 +81,7 @@ class CreateMyActiveRecordModelTransitionMigration < MIGRATION_CLASS
81
81
  t.text :metadata, default: "{}"
82
82
  end
83
83
 
84
- if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
84
+ if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?(ActiveRecord::Base)
85
85
  t.boolean :most_recent, default: true, null: false
86
86
  else
87
87
  t.boolean :most_recent, default: true
@@ -98,7 +98,7 @@ class CreateMyActiveRecordModelTransitionMigration < MIGRATION_CLASS
98
98
  %i[my_active_record_model_id sort_key],
99
99
  unique: true, name: "sort_key_index"
100
100
 
101
- if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
101
+ if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?(ActiveRecord::Base)
102
102
  add_index :my_active_record_model_transitions,
103
103
  %i[my_active_record_model_id most_recent],
104
104
  unique: true,
@@ -134,6 +134,48 @@ class OtherActiveRecordModelTransition < ActiveRecord::Base
134
134
  belongs_to :other_active_record_model
135
135
  end
136
136
 
137
+ class SecondaryRecord < ActiveRecord::Base
138
+ self.abstract_class = true
139
+
140
+ connects_to database: { writing: :secondary, reading: :secondary }
141
+ end
142
+
143
+ class SecondaryActiveRecordModelTransition < SecondaryRecord
144
+ self.table_name = "my_active_record_model_transitions"
145
+
146
+ include Statesman::Adapters::ActiveRecordTransition
147
+
148
+ belongs_to :my_active_record_model,
149
+ class_name: "SecondaryActiveRecordModel",
150
+ foreign_key: "my_active_record_model_transition_id"
151
+ end
152
+
153
+ class SecondaryActiveRecordModel < SecondaryRecord
154
+ self.table_name = "my_active_record_models"
155
+
156
+ has_many :my_active_record_model_transitions,
157
+ class_name: "SecondaryActiveRecordModelTransition",
158
+ foreign_key: "my_active_record_model_id",
159
+ autosave: false
160
+
161
+ alias_method :transitions, :my_active_record_model_transitions
162
+
163
+ include Statesman::Adapters::ActiveRecordQueries[
164
+ transition_class: SecondaryActiveRecordModelTransition,
165
+ initial_state: :initial
166
+ ]
167
+
168
+ def state_machine
169
+ @state_machine ||= MyStateMachine.new(
170
+ self, transition_class: SecondaryActiveRecordModelTransition
171
+ )
172
+ end
173
+
174
+ def metadata
175
+ super || {}
176
+ end
177
+ end
178
+
137
179
  class CreateOtherActiveRecordModelMigration < MIGRATION_CLASS
138
180
  def change
139
181
  create_table :other_active_record_models do |t|
@@ -158,7 +200,7 @@ class CreateOtherActiveRecordModelTransitionMigration < MIGRATION_CLASS
158
200
  t.text :metadata, default: "{}"
159
201
  end
160
202
 
161
- if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
203
+ if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?(ActiveRecord::Base)
162
204
  t.boolean :most_recent, default: true, null: false
163
205
  else
164
206
  t.boolean :most_recent, default: true
@@ -171,7 +213,7 @@ class CreateOtherActiveRecordModelTransitionMigration < MIGRATION_CLASS
171
213
  %i[other_active_record_model_id sort_key],
172
214
  unique: true, name: "other_sort_key_index"
173
215
 
174
- if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
216
+ if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?(ActiveRecord::Base)
175
217
  add_index :other_active_record_model_transitions,
176
218
  %i[other_active_record_model_id most_recent],
177
219
  unique: true,
@@ -253,7 +295,7 @@ class CreateNamespacedARModelTransitionMigration < MIGRATION_CLASS
253
295
  t.text :metadata, default: "{}"
254
296
  end
255
297
 
256
- if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
298
+ if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?(ActiveRecord::Base)
257
299
  t.boolean :most_recent, default: true, null: false
258
300
  else
259
301
  t.boolean :most_recent, default: true
@@ -265,7 +307,7 @@ class CreateNamespacedARModelTransitionMigration < MIGRATION_CLASS
265
307
  add_index :my_namespace_my_active_record_model_transitions, :sort_key,
266
308
  unique: true, name: "my_namespaced_key"
267
309
 
268
- if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
310
+ if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?(ActiveRecord::Base)
269
311
  add_index :my_namespace_my_active_record_model_transitions,
270
312
  %i[my_active_record_model_id most_recent],
271
313
  unique: true,
@@ -342,7 +384,7 @@ class CreateStiActiveRecordModelTransitionMigration < MIGRATION_CLASS
342
384
  t.text :metadata, default: "{}"
343
385
  end
344
386
 
345
- if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
387
+ if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?(ActiveRecord::Base)
346
388
  t.boolean :most_recent, default: true, null: false
347
389
  else
348
390
  t.boolean :most_recent, default: true
@@ -355,7 +397,7 @@ class CreateStiActiveRecordModelTransitionMigration < MIGRATION_CLASS
355
397
  %i[type sti_active_record_model_id sort_key],
356
398
  unique: true, name: "sti_sort_key_index"
357
399
 
358
- if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
400
+ if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?(ActiveRecord::Base)
359
401
  add_index :sti_active_record_model_transitions,
360
402
  %i[type sti_active_record_model_id most_recent],
361
403
  unique: true,