From e5152dec2126fad8a2cb65d6cbde866c548dae45 Mon Sep 17 00:00:00 2001 From: Joseph Southan Date: Fri, 10 Mar 2023 15:17:16 +0000 Subject: [PATCH 1/3] Add the source location of the Guard callback to GuardFailedError Often it can be difficult to know _which_ guard failed, especially if there are a lot of guards that may intersect. ``` Statesman::GuardFailedError: Guard on transition from: '' to '["bar"]' returned false from /Users/joesouthan/foobar/app/state_machines/foobars.rb:98 ``` --- CHANGELOG.md | 5 +++++ lib/statesman/exceptions.rb | 6 ++++-- lib/statesman/guard.rb | 2 +- lib/statesman/version.rb | 2 +- spec/statesman/exceptions_spec.rb | 8 +++++++- spec/statesman/machine_spec.rb | 4 ++-- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2018fea..b24609d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## v10.1.0 10th March 2023 + +### CHanged +- Add the source location of the guard callback to `Statesman::GuardFailedError` + ## v10.0.0 17th May 2022 ### Changed diff --git a/lib/statesman/exceptions.rb b/lib/statesman/exceptions.rb index 214b8d21..96adcc51 100644 --- a/lib/statesman/exceptions.rb +++ b/lib/statesman/exceptions.rb @@ -28,13 +28,15 @@ def _message end class GuardFailedError < StandardError - def initialize(from, to) + def initialize(from, to, callback) @from = from @to = to + @callback = callback super(_message) + set_backtrace(callback.source_location.join(":")) if callback&.source_location end - attr_reader :from, :to + attr_reader :from, :to, :callback private diff --git a/lib/statesman/guard.rb b/lib/statesman/guard.rb index 475f04b3..50a87b1b 100644 --- a/lib/statesman/guard.rb +++ b/lib/statesman/guard.rb @@ -6,7 +6,7 @@ module Statesman class Guard < Callback def call(*args) - raise GuardFailedError.new(from, to) unless super(*args) + raise GuardFailedError.new(from, to, callback) unless super(*args) end end end diff --git a/lib/statesman/version.rb b/lib/statesman/version.rb index 76b7f3e0..cc251524 100644 --- a/lib/statesman/version.rb +++ b/lib/statesman/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Statesman - VERSION = "10.0.0" + VERSION = "10.1.0" end diff --git a/spec/statesman/exceptions_spec.rb b/spec/statesman/exceptions_spec.rb index 73189385..275a6d60 100644 --- a/spec/statesman/exceptions_spec.rb +++ b/spec/statesman/exceptions_spec.rb @@ -64,12 +64,18 @@ end describe "GuardFailedError" do - subject(:error) { Statesman::GuardFailedError.new("from", "to") } + subject(:error) { Statesman::GuardFailedError.new("from", "to", callback) } + + let(:callback) { -> { "hello" } } its(:message) do is_expected.to eq("Guard on transition from: 'from' to 'to' returned false") end + its(:backtrace) do + is_expected.to eq([callback.source_location.join(":")]) + end + its "string matches its message" do expect(error.to_s).to eq(error.message) end diff --git a/spec/statesman/machine_spec.rb b/spec/statesman/machine_spec.rb index c7b8bfb6..d8b748b5 100644 --- a/spec/statesman/machine_spec.rb +++ b/spec/statesman/machine_spec.rb @@ -935,10 +935,10 @@ def after_initialize; end it { is_expected.to be(:some_state) } end - context "when it is unsuccesful" do + context "when it is unsuccessful" do before do allow(instance).to receive(:transition_to!). - and_raise(Statesman::GuardFailedError.new(:x, :some_state)) + and_raise(Statesman::GuardFailedError.new(:x, :some_state, nil)) end it { is_expected.to be_falsey } From 4710f6e2c5962e940144c454664265c644d66339 Mon Sep 17 00:00:00 2001 From: Joseph Southan Date: Fri, 10 Mar 2023 15:24:57 +0000 Subject: [PATCH 2/3] Rubocop autocorrect --- spec/statesman/adapters/active_record_queries_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/statesman/adapters/active_record_queries_spec.rb b/spec/statesman/adapters/active_record_queries_spec.rb index e3c78c9c..685734eb 100644 --- a/spec/statesman/adapters/active_record_queries_spec.rb +++ b/spec/statesman/adapters/active_record_queries_spec.rb @@ -117,8 +117,8 @@ def configure_new(klass, transition_class) subject(:not_in_state) { MyActiveRecordModel.not_in_state(:succeeded, :failed) } it do - expect(not_in_state).to match_array([initial_state_model, - returned_to_initial_model]) + expect(not_in_state).to contain_exactly(initial_state_model, + returned_to_initial_model) end end @@ -126,8 +126,8 @@ def configure_new(klass, transition_class) subject(:not_in_state) { MyActiveRecordModel.not_in_state(%i[succeeded failed]) } it do - expect(not_in_state).to match_array([initial_state_model, - returned_to_initial_model]) + expect(not_in_state).to contain_exactly(initial_state_model, + returned_to_initial_model) end end end From dc1619ba32012fee178e9a33e05adc35d8ad120c Mon Sep 17 00:00:00 2001 From: Joseph Southan Date: Fri, 10 Mar 2023 15:30:41 +0000 Subject: [PATCH 3/3] Add health check for mysql --- .github/workflows/tests.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e8268fe3..56717533 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -86,6 +86,11 @@ jobs: MYSQL_DATABASE: statesman_test ports: - "3306:3306" + options: >- + --health-cmd "mysqladmin ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 env: DATABASE_URL: mysql2://foobar:password@127.0.0.1/statesman_test DATABASE_DEPENDENCY_PORT: "3306"