diff --git a/lib/aasm/core/event.rb b/lib/aasm/core/event.rb index 5e076fa..114abd0 100644 --- a/lib/aasm/core/event.rb +++ b/lib/aasm/core/event.rb @@ -73,7 +73,7 @@ module AASM::Core end def failed_callbacks - @transitions.flat_map(&:failures) + transitions.flat_map(&:failures) end private diff --git a/lib/aasm/core/transition.rb b/lib/aasm/core/transition.rb index 767ed19..449db99 100644 --- a/lib/aasm/core/transition.rb +++ b/lib/aasm/core/transition.rb @@ -57,7 +57,9 @@ module AASM::Core failures << code unless result result when Proc - code.arity == 0 ? record.instance_exec(&code) : record.instance_exec(*args, &code) + result = (code.arity == 0 ? record.instance_exec(&code) : record.instance_exec(*args, &code)) + failures << code.source_location.join('#') unless result + result when Array if options[:guard] # invoke guard callbacks diff --git a/spec/unit/callback_multiple_spec.rb b/spec/unit/callback_multiple_spec.rb index c5c6ef2..8e94558 100644 --- a/spec/unit/callback_multiple_spec.rb +++ b/spec/unit/callback_multiple_spec.rb @@ -1,6 +1,16 @@ require 'spec_helper' Dir[File.dirname(__FILE__) + "/../models/callbacks/*.rb"].sort.each { |f| require File.expand_path(f) } +def safe_error(callback = nil) + error = nil + begin + yield + rescue Exception => e + error = e + return error + end +end + describe 'callbacks for the new DSL' do it "be called in order" do @@ -47,9 +57,13 @@ describe 'callbacks for the new DSL' do expect(callback).to_not receive(:after_enter_closed) expect(callback).to_not receive(:after_event) - expect { - callback.left_close! - }.to raise_error(AASM::InvalidTransition) + error = safe_error { callback.left_close! } + + expect(error.class).to eq AASM::InvalidTransition + expect(error.message).to eq( + "Event 'left_close' cannot transition from 'open'. Failed callback(s): [:after_transition, :event_guard]." + ) + end it "handles private callback methods as well" do @@ -85,9 +99,12 @@ describe 'callbacks for the new DSL' do expect(callback).to_not receive(:after_event) end - expect { - callback.left_close! - }.to raise_error(AASM::InvalidTransition) + error = safe_error { callback.left_close! } + + expect(error.class).to eq AASM::InvalidTransition + expect(error.message).to eq( + "Event 'left_close' cannot transition from 'open'. Failed callback(s): [:after_transition, :event_guard, :transition_guard]." + ) end it "does not run transition_guard twice for multiple permitted transitions" do @@ -133,9 +150,10 @@ describe 'callbacks for the new DSL' do expect(callback).to_not receive(:after_enter_closed) expect(callback).to_not receive(:after) - expect { - callback.close! - }.to raise_error(AASM::InvalidTransition) + error = safe_error(callback) { callback.close! } + + expect(error.class).to eq AASM::InvalidTransition + expect(error.message).to eq "Event 'close' cannot transition from 'open'. Failed callback(s): [\"/Users/woodrich/Dropbox/personal/aasm/spec/models/callbacks/guard_within_block_multiple.rb#30\"]." end end @@ -277,12 +295,25 @@ describe 'event callbacks' do it 'should call it when transition failed for bang fire' do expect(@foo).to receive(:aasm_event_failed).with(:null, :open) - expect {@foo.null!}.to raise_error(AASM::InvalidTransition) + #expect {@foo.null!}.to raise_error(AASM::InvalidTransition) + + error = safe_error { @foo.null! } + + expect(error.class).to eq AASM::InvalidTransition + expect(error.message).to eq( + "Event 'null' cannot transition from 'open'. Failed callback(s): [:always_false]." + ) + end it 'should call it when transition failed for non-bang fire' do expect(@foo).to receive(:aasm_event_failed).with(:null, :open) - expect {@foo.null}.to raise_error(AASM::InvalidTransition) + error = safe_error { @foo.null } + + expect(error.class).to eq AASM::InvalidTransition + expect(error.message).to eq( + "Event 'null' cannot transition from 'open'. Failed callback(s): [:always_false]." + ) end it 'should not call it if persist fails for bang fire' do diff --git a/spec/unit/event_spec.rb b/spec/unit/event_spec.rb index cddeea9..fa0ea9e 100644 --- a/spec/unit/event_spec.rb +++ b/spec/unit/event_spec.rb @@ -109,6 +109,35 @@ describe 'firing an event' do expect(event.fire(obj, {}, nil, 'arg1', 'arg2')).to eq(:closed) end + context 'when given a gaurd proc' do + it 'should have access to callback failures in the transitions' do + event = AASM::Core::Event.new(:graduate, state_machine) do + transitions :to => :alumni, :from => [:student, :applicant], + :guard => Proc.new { 1 + 1 == 3 } + end + line_number = __LINE__ - 2 + obj = double('object', :aasm => double('aasm', :current_state => :student)) + + event.fire(obj, {}, nil) + expect(event.failed_callbacks).to eq ["#{__FILE__}##{line_number}"] + end + end + + context 'when given a guard symbol' do + it 'should have access to callback failures in the transitions' do + event = AASM::Core::Event.new(:graduate, state_machine) do + transitions :to => :alumni, :from => [:student, :applicant], + guard: :paid_tuition? + end + + obj = double('object', :aasm => double('aasm', :current_state => :student)) + allow(obj).to receive(:paid_tuition?).and_return(false) + + event.fire(obj, {}, nil) + expect(event.failed_callbacks).to eq [:paid_tuition?] + end + end + end describe 'should fire callbacks' do diff --git a/spec/unit/transition_spec.rb b/spec/unit/transition_spec.rb index 3fcbca1..e841cfd 100644 --- a/spec/unit/transition_spec.rb +++ b/spec/unit/transition_spec.rb @@ -145,6 +145,17 @@ describe AASM::Core::Transition, '- when performing guard checks' do expect(st.allowed?(obj)).to be false end + it 'should add the name of the failed method calls to the failures instance var' do + opts = {:from => 'foo', :to => 'bar', :guard => :test} + st = AASM::Core::Transition.new(event, opts) + + obj = double('object') + expect(obj).to receive(:test) + + st.allowed?(obj) + expect(st.failures).to eq [:test] + end + it 'should call the method on the object if unless is a symbol' do opts = {:from => 'foo', :to => 'bar', :unless => :test} st = AASM::Core::Transition.new(event, opts)