From 68ea7e6f5b5008c052307673ca167c31ae52d67e Mon Sep 17 00:00:00 2001 From: Allan Reyes Date: Tue, 14 Jul 2026 19:01:40 -0400 Subject: [PATCH 1/3] Make aasm compiler v6.0 aware --- lib/tapioca/dsl/compilers/aasm.rb | 23 ++++++++++++++--------- spec/tapioca/dsl/compilers/aasm_spec.rb | 11 +---------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/lib/tapioca/dsl/compilers/aasm.rb b/lib/tapioca/dsl/compilers/aasm.rb index 823cb4682..52bd06904 100644 --- a/lib/tapioca/dsl/compilers/aasm.rb +++ b/lib/tapioca/dsl/compilers/aasm.rb @@ -94,14 +94,19 @@ def decorate create_rest_param("opts", type: "T.untyped"), create_block_param("block", type: "T.nilable(T.proc.void)"), ] + aasm_6_or_newer = ::Gem::Requirement.new(">= 6").satisfied_by?(::Gem::Version.new(::AASM::VERSION)) + state_machine.events.each do |event| - model.create_method(event.name.to_s, parameters: parameters) - model.create_method("#{event.name}!", parameters: parameters) - model.create_method("#{event.name}_without_validation!", parameters: parameters) - model.create_method("may_#{event.name}?", return_type: "T::Boolean") + # As of aasm 6.0 a namespaced state machine defines only the + # namespaced event methods (e.g. `run_foo!`) and not the plain + # methods (`run!`) as aliases. + unless namespace && aasm_6_or_newer + model.create_method(event.name.to_s, parameters: parameters) + model.create_method("#{event.name}!", parameters: parameters) + model.create_method("#{event.name}_without_validation!", parameters: parameters) + model.create_method("may_#{event.name}?", return_type: "T::Boolean") + end - # For events, if there's a namespace the default methods are created in addition to - # namespaced ones. next unless namespace name = "#{event.name}_#{namespace}" @@ -110,9 +115,9 @@ def decorate model.create_method("#{name}!", parameters: parameters) model.create_method("may_#{name}?", return_type: "T::Boolean") - # There's no namespaced method created for `_without_validation`, so skip - # defining a method for: - # "#{name}_without_validation!" + # aasm < 6 does not define a namespaced `_without_validation!` + # method. + model.create_method("#{name}_without_validation!", parameters: parameters) if aasm_6_or_newer end end diff --git a/spec/tapioca/dsl/compilers/aasm_spec.rb b/spec/tapioca/dsl/compilers/aasm_spec.rb index 11f019b49..d5e0ac64c 100644 --- a/spec/tapioca/dsl/compilers/aasm_spec.rb +++ b/spec/tapioca/dsl/compilers/aasm_spec.rb @@ -358,18 +358,9 @@ def foo_running?; end sig { returns(T::Boolean) } def foo_sleeping?; end - sig { returns(T::Boolean) } - def may_run?; end - sig { returns(T::Boolean) } def may_run_foo?; end - sig { params(opts: T.untyped, block: T.nilable(T.proc.void)).returns(T.untyped) } - def run(*opts, &block); end - - sig { params(opts: T.untyped, block: T.nilable(T.proc.void)).returns(T.untyped) } - def run!(*opts, &block); end - sig { params(opts: T.untyped, block: T.nilable(T.proc.void)).returns(T.untyped) } def run_foo(*opts, &block); end @@ -377,7 +368,7 @@ def run_foo(*opts, &block); end def run_foo!(*opts, &block); end sig { params(opts: T.untyped, block: T.nilable(T.proc.void)).returns(T.untyped) } - def run_without_validation!(*opts, &block); end + def run_foo_without_validation!(*opts, &block); end class << self sig { params(args: T.untyped, block: T.nilable(T.proc.bind(PrivateAASMMachine).void)).returns(PrivateAASMMachine) } From 341ea65979e2fa18a9dd532bdcecea9a033bac94 Mon Sep 17 00:00:00 2001 From: Allan Reyes Date: Mon, 20 Jul 2026 16:35:32 -0400 Subject: [PATCH 2/3] Add test coverage for aasm < 6 --- lib/tapioca/helpers/test/template.rb | 5 +++++ spec/tapioca/dsl/compilers/aasm_spec.rb | 25 ++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/tapioca/helpers/test/template.rb b/lib/tapioca/helpers/test/template.rb index 3eca63406..929774e25 100644 --- a/lib/tapioca/helpers/test/template.rb +++ b/lib/tapioca/helpers/test/template.rb @@ -16,6 +16,11 @@ def rails_version(selector) ::Gem::Requirement.new(selector).satisfied_by?(ActiveSupport.gem_version) end + #: (String selector) -> bool + def aasm_version(selector) + ::Gem::Requirement.new(selector).satisfied_by?(::Gem::Version.new(::AASM::VERSION)) + end + #: (String src, ?trim_mode: String) -> String def template(src, trim_mode: ">") erb = ::ERB.new(src, trim_mode: trim_mode) diff --git a/spec/tapioca/dsl/compilers/aasm_spec.rb b/spec/tapioca/dsl/compilers/aasm_spec.rb index d5e0ac64c..52aebda2d 100644 --- a/spec/tapioca/dsl/compilers/aasm_spec.rb +++ b/spec/tapioca/dsl/compilers/aasm_spec.rb @@ -345,7 +345,7 @@ def before_run; end end RUBY - expected = <<~RBI + expected = template(<<~RBI) # typed: strong class StateMachine @@ -358,6 +358,7 @@ def foo_running?; end sig { returns(T::Boolean) } def foo_sleeping?; end + <% if aasm_version(">= 6") %> sig { returns(T::Boolean) } def may_run_foo?; end @@ -369,6 +370,28 @@ def run_foo!(*opts, &block); end sig { params(opts: T.untyped, block: T.nilable(T.proc.void)).returns(T.untyped) } def run_foo_without_validation!(*opts, &block); end + <% else %> + sig { returns(T::Boolean) } + def may_run?; end + + sig { returns(T::Boolean) } + def may_run_foo?; end + + sig { params(opts: T.untyped, block: T.nilable(T.proc.void)).returns(T.untyped) } + def run(*opts, &block); end + + sig { params(opts: T.untyped, block: T.nilable(T.proc.void)).returns(T.untyped) } + def run!(*opts, &block); end + + sig { params(opts: T.untyped, block: T.nilable(T.proc.void)).returns(T.untyped) } + def run_foo(*opts, &block); end + + sig { params(opts: T.untyped, block: T.nilable(T.proc.void)).returns(T.untyped) } + def run_foo!(*opts, &block); end + + sig { params(opts: T.untyped, block: T.nilable(T.proc.void)).returns(T.untyped) } + def run_without_validation!(*opts, &block); end + <% end %> class << self sig { params(args: T.untyped, block: T.nilable(T.proc.bind(PrivateAASMMachine).void)).returns(PrivateAASMMachine) } From e5de056ce8b9e2ca44aa514e81b6ff462445da62 Mon Sep 17 00:00:00 2001 From: Allan Reyes Date: Tue, 21 Jul 2026 12:54:15 -0400 Subject: [PATCH 3/3] Add stub and coverage for aasm < 6 --- spec/tapioca/dsl/compilers/aasm_spec.rb | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/spec/tapioca/dsl/compilers/aasm_spec.rb b/spec/tapioca/dsl/compilers/aasm_spec.rb index 52aebda2d..d6df99e27 100644 --- a/spec/tapioca/dsl/compilers/aasm_spec.rb +++ b/spec/tapioca/dsl/compilers/aasm_spec.rb @@ -14,6 +14,17 @@ def before_setup require "aasm" end + # Runs the block with `AASM::VERSION` stubbed, so both the aasm < 6 and + # aasm >= 6 code paths can be exercised regardless of the installed version. + #: (String version) { -> void } -> void + def with_aasm_version(version, &block) + original = ::AASM::VERSION + ::Tapioca::Runtime.silence_warnings { ::AASM.const_set(:VERSION, version) } + block.call + ensure + ::Tapioca::Runtime.silence_warnings { ::AASM.const_set(:VERSION, original) } + end + describe "initialize" do it "gathers no constants if there are no classes that include AASM" do assert_empty(gathered_constants) @@ -345,7 +356,7 @@ def before_run; end end RUBY - expected = template(<<~RBI) + rbi = <<~RBI # typed: strong class StateMachine @@ -476,7 +487,14 @@ def success(&block); end end RBI - assert_equal(expected, rbi_for(:StateMachine)) + # Assert against the currently installed aasm version. + assert_equal(template(rbi), rbi_for(:StateMachine)) + + # Also exercise the aasm < 6 code path, which no longer matches the + # installed version once aasm >= 6 is in use. + with_aasm_version("5.5.2") do + assert_equal(template(rbi), rbi_for(:StateMachine)) + end end end end