From e33f5df518f296166eb1f4fdf5136478b52a8da4 Mon Sep 17 00:00:00 2001 From: Michael Kessler Date: Fri, 30 Sep 2011 18:35:04 +0200 Subject: [PATCH] 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. --- lib/guard.rb | 36 +++++++++++++++++++++++----- spec/guard_spec.rb | 59 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/lib/guard.rb b/lib/guard.rb index 2367f93..74d5599 100644 --- a/lib/guard.rb +++ b/lib/guard.rb @@ -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] 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) - guard.hook("#{ task }_begin", *args) - result = guard.send(task, *args) - guard.hook("#{ task }_end", result) + catch guard_symbol(guard) do + guard.hook("#{ task }_begin", *args) + result = guard.send(task, *args) + guard.hook("#{ task }_end", result) - 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 diff --git a/spec/guard_spec.rb b/spec/guard_spec.rb index 6c64078..57857bd 100644 --- a/spec/guard_spec.rb +++ b/spec/guard_spec.rb @@ -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,16 +487,26 @@ describe Guard do end end - context "with a task that return false and guard's group has the :halt_on_fail option == true" do - before(:each) { @g.stub!(:group) { :foo }; @g.stub!(:failing) { throw :task_has_failed } } + 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 - expect { subject.run_supervised_task(@g, :failing) }.to throw_symbol(:task_has_failed) + it "throws :task_has_failed" do + expect { subject.run_supervised_task(@g, :failing) }.to throw_symbol(:task_has_failed) + 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 }