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/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 11f019b49..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 = <<~RBI + rbi = <<~RBI # typed: strong class StateMachine @@ -358,6 +369,19 @@ 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 + + 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_foo_without_validation!(*opts, &block); end + <% else %> sig { returns(T::Boolean) } def may_run?; end @@ -378,6 +402,7 @@ 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) } @@ -462,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