Make Guard implementation of :task_has_failed simple.

This change makes sure that Guard implementation can just
`throw :task_has_failed` without knowing whether the enclosing
group has enabled :halt_on_fail.

The problem with throw/catch is, that when you throw a :symbol,
you'll have to catch it, otherwise you get a 'uncatched throw' error.

When the Guard group has not enabled :halt_on_fail, we catch the throw
when execute the supervised task.
This commit is contained in:
Michael Kessler 2011-09-30 18:35:04 +02:00
parent e9ddb1c110
commit e33f5df518
2 changed files with 83 additions and 12 deletions

View File

@ -192,14 +192,14 @@ module Guard
# Loop through all groups and run the given task for each Guard.
#
# Stop the task run for the all Guards within a group if one Guard
# throws `:task_has_failed` and the group has its `:halt_on_fail` option to `true`.
# throws `:task_has_failed`.
#
# @param [Symbol] task the task to run
# @param [Array<String>] files the list of files to pass to the task
#
def run_guard_task(task, files = nil)
groups.each do |group|
catch group.options[:halt_on_fail] == true ? :task_has_failed : :no_catch do
catch :task_has_failed do
guards(:group => group.name).each do |guard|
if task == :run_on_change
run_on_change_task(files, guard, task)
@ -262,17 +262,22 @@ module Guard
# Run a Guard task, but remove the Guard when his work leads to a system failure.
#
# When the Group has `:halt_on_fail` disabled, we've to catch `:task_has_failed`
# here in order to avoid an uncaught throw error.
#
# @param [Guard::Guard] guard the Guard to execute
# @param [Symbol] task the task to run
# @param [Array] args the arguments for the task
# @raise [:task_has_failed] when task has failed
#
def run_supervised_task(guard, task, *args)
catch guard_symbol(guard) do
guard.hook("#{ task }_begin", *args)
result = guard.send(task, *args)
guard.hook("#{ task }_end", result)
result
end
rescue Exception => ex
UI.error("#{ guard.class.name } failed to achieve its <#{ task.to_s }>, exception was:" +
@ -284,6 +289,25 @@ module Guard
ex
end
# Get the symbol we have to catch when running a supervised task.
# If we are within a Guard group that has the `:halt_on_fail`
# option set, we do NOT catch it here, it will be catched at the
# group level.
#
# @see .run_guard_task
#
# @param [Guard::Guard] guard the Guard to execute
# @return [Symbol] the symbol to catch
#
def guard_symbol(guard)
if guard.group.class == Symbol
group = groups(guard.group)
group.options[:halt_on_fail] ? :no_catch : :task_has_failed
else
:task_has_failed
end
end
# Add a Guard to use.
#
# @param [String] name the Guard name

View File

@ -369,7 +369,7 @@ describe Guard do
end
end
context "one guard fails (by returning false)" do
context "one guard fails" do
before do
subject.guards.each_with_index do |g, i|
g.stub!(:task) do
@ -437,6 +437,7 @@ describe Guard do
@g = mock(Guard::Guard).as_null_object
subject.guards.push(@g)
subject.add_group(:foo, { :halt_on_fail => true })
subject.add_group(:bar, { :halt_on_fail => false })
end
context "with a task that succeed" do
@ -486,7 +487,8 @@ describe Guard do
end
end
context "with a task that return false and guard's group has the :halt_on_fail option == true" do
context "with a task that throw :task_has_failed" do
context "for a guard's group has the :halt_on_fail option == true" do
before(:each) { @g.stub!(:group) { :foo }; @g.stub!(:failing) { throw :task_has_failed } }
it "throws :task_has_failed" do
@ -494,8 +496,17 @@ describe Guard do
end
end
context "for a guard's group has the :halt_on_fail option == false" do
before(:each) { @g.stub!(:group) { :bar }; @g.stub!(:failing) { throw :task_has_failed } }
it "catches :task_has_failed" do
expect { subject.run_supervised_task(@g, :failing) }.to_not throw_symbol(:task_has_failed)
end
end
end
context "with a task that raises an exception" do
before(:each) { @g.stub!(:failing) { raise "I break your system" } }
before(:each) { @g.stub!(:group) { :foo }; @g.stub!(:failing) { raise "I break your system" } }
it "fires the Guard" do
lambda { subject.run_supervised_task(@g, :failing) }.should change(subject.guards, :size).by(-1)
@ -510,6 +521,42 @@ describe Guard do
end
end
describe '.guard_symbol' do
let(:guard) { mock(Guard::Guard).as_null_object }
it 'returns :task_has_failed when the group is missing' do
subject.guard_symbol(guard).should eql :task_has_failed
end
context 'for a group with :halt_on_fail' do
let(:group) { mock(Guard::Group) }
before do
guard.stub(:group).and_return :foo
group.stub(:options).and_return({ :halt_on_fail => true })
end
it 'returns :task_has_failed when the group is missing' do
subject.should_receive(:groups).with(:foo).and_return group
subject.guard_symbol(guard).should eql :no_catch
end
end
context 'for a group without :halt_on_fail' do
let(:group) { mock(Guard::Group) }
before do
guard.stub(:group).and_return :foo
group.stub(:options).and_return({ :halt_on_fail => false })
end
it 'returns :task_has_failed when the group is missing' do
subject.should_receive(:groups).with(:foo).and_return group
subject.guard_symbol(guard).should eql :task_has_failed
end
end
end
describe ".debug_command_execution" do
subject { ::Guard.setup }