First implementation of #97 "Guard dependencies".
This commit is contained in:
parent
22001c5ecd
commit
b1b69924a7
49
lib/guard.rb
49
lib/guard.rb
@ -16,7 +16,7 @@ module Guard
|
||||
def setup(options = {})
|
||||
@options = options
|
||||
@guards = []
|
||||
@groups = [:default]
|
||||
@groups = [{ :name => :default, :options => {} }]
|
||||
@interactor = Interactor.new
|
||||
@listener = Listener.select_and_init(@options[:watchdir] ? File.expand_path(@options[:watchdir]) : Dir.pwd)
|
||||
|
||||
@ -39,7 +39,8 @@ module Guard
|
||||
end
|
||||
|
||||
UI.info "Guard is now watching at '#{listener.directory}'"
|
||||
guards.each { |guard| supervised_task(guard, :start) }
|
||||
|
||||
execute_supervised_task_for_all_guards(:start)
|
||||
|
||||
interactor.start
|
||||
listener.start
|
||||
@ -48,19 +49,19 @@ module Guard
|
||||
def stop
|
||||
UI.info "Bye bye...", :reset => true
|
||||
listener.stop
|
||||
guards.each { |guard| supervised_task(guard, :stop) }
|
||||
execute_supervised_task_for_all_guards(:stop)
|
||||
abort
|
||||
end
|
||||
|
||||
def reload
|
||||
run do
|
||||
guards.each { |guard| supervised_task(guard, :reload) }
|
||||
execute_supervised_task_for_all_guards(:reload)
|
||||
end
|
||||
end
|
||||
|
||||
def run_all
|
||||
run do
|
||||
guards.each { |guard| supervised_task(guard, :run_all) }
|
||||
execute_supervised_task_for_all_guards(:run_all)
|
||||
end
|
||||
end
|
||||
|
||||
@ -77,13 +78,7 @@ module Guard
|
||||
|
||||
def run_on_change(files)
|
||||
run do
|
||||
guards.each do |guard|
|
||||
paths = Watcher.match_files(guard, files)
|
||||
unless paths.empty?
|
||||
UI.debug "#{guard.class.name}#run_on_change with #{paths.inspect}"
|
||||
supervised_task(guard, :run_on_change, paths)
|
||||
end
|
||||
end
|
||||
execute_supervised_task_for_all_guards(:run_on_change, files)
|
||||
end
|
||||
end
|
||||
|
||||
@ -99,13 +94,37 @@ module Guard
|
||||
listener.unlock
|
||||
end
|
||||
|
||||
def execute_supervised_task_for_all_guards(task, files = nil)
|
||||
groups.each do |group_hash|
|
||||
catch :task_has_failed do
|
||||
guards.find_all { |guard| guard.group == group_hash[:name] }.each do |guard|
|
||||
paths = Watcher.match_files(guard, files) if files
|
||||
if paths && !paths.empty?
|
||||
UI.debug "#{guard.class.name}##{task} with #{paths.inspect}"
|
||||
supervised_task(guard, task, paths)
|
||||
else
|
||||
supervised_task(guard, task)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Let a guard execute its task but
|
||||
# fire it if his work leads to a system failure
|
||||
def supervised_task(guard, task_to_supervise, *args)
|
||||
guard.hook("#{task_to_supervise}_begin", *args)
|
||||
result = guard.send(task_to_supervise, *args)
|
||||
guard.hook("#{task_to_supervise}_end", result)
|
||||
result
|
||||
|
||||
group = @groups.find { |group| group[:name] == guard.group }
|
||||
if result === false && group[:options][:halt_on_fail] == true
|
||||
UI.error "#{guard.class.name}##{task_to_supervise} failed."
|
||||
throw :task_has_failed
|
||||
else
|
||||
result
|
||||
end
|
||||
|
||||
rescue Exception => ex
|
||||
UI.error("#{guard.class.name} failed to achieve its <#{task_to_supervise.to_s}>, exception was:" +
|
||||
"\n#{ex.class}: #{ex.message}\n#{ex.backtrace.join("\n")}")
|
||||
@ -124,8 +143,8 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
def add_group(name)
|
||||
@groups << name.to_sym unless name.nil?
|
||||
def add_group(name, options = {})
|
||||
@groups << { :name => name.to_sym, :options => options } unless name.nil? || @groups.find { |group| group[:name] == name }
|
||||
end
|
||||
|
||||
def get_guard_class(name)
|
||||
|
@ -15,6 +15,7 @@ module Guard
|
||||
|
||||
def reevaluate_guardfile
|
||||
::Guard.guards.clear
|
||||
::Guard.groups.clear
|
||||
@@options.delete(:guardfile_contents)
|
||||
Dsl.evaluate_guardfile(@@options)
|
||||
msg = "Guardfile has been re-evaluated."
|
||||
@ -112,11 +113,12 @@ module Guard
|
||||
|
||||
end
|
||||
|
||||
def group(name, &guard_definition)
|
||||
def group(name, options = {}, &guard_definition)
|
||||
@groups = @@options[:group] || []
|
||||
name = name.to_sym
|
||||
|
||||
if guard_definition && (@groups.empty? || @groups.map(&:to_sym).include?(name))
|
||||
::Guard.add_group(name.to_s.downcase.to_sym, options)
|
||||
@current_group = name
|
||||
guard_definition.call
|
||||
@current_group = nil
|
||||
|
@ -9,6 +9,7 @@ describe Guard::Dsl do
|
||||
@local_guardfile_path = File.join(Dir.pwd, 'Guardfile')
|
||||
@home_guardfile_path = File.expand_path(File.join("~", ".Guardfile"))
|
||||
@user_config_path = File.expand_path(File.join("~", ".guard.rb"))
|
||||
::Guard.setup
|
||||
::Guard.stub!(:options).and_return(:debug => true)
|
||||
::Guard.stub!(:guards).and_return([mock('Guard')])
|
||||
end
|
||||
@ -220,16 +221,16 @@ describe Guard::Dsl do
|
||||
|
||||
describe "#ignore_paths" do
|
||||
disable_user_config
|
||||
|
||||
|
||||
it "adds the paths to the listener's ignore_paths" do
|
||||
::Guard.stub!(:listener).and_return(mock('Listener'))
|
||||
::Guard.listener.should_receive(:ignore_paths).and_return(ignore_paths = ['faz'])
|
||||
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => "ignore_paths 'foo', 'bar'")
|
||||
ignore_paths.should == ['faz', 'foo', 'bar']
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "#group" do
|
||||
disable_user_config
|
||||
|
||||
@ -238,6 +239,8 @@ describe Guard::Dsl do
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :w })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => ['w'])
|
||||
|
||||
::Guard.groups.should eql [{ :name => :default, :options => {} }, { :name => :w, :options => {} }]
|
||||
end
|
||||
|
||||
it "evaluates only the specified symbol group" do
|
||||
@ -245,15 +248,19 @@ describe Guard::Dsl do
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :w })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:w])
|
||||
|
||||
::Guard.groups.should eql [{ :name => :default, :options => {} }, { :name => :w, :options => {} }]
|
||||
end
|
||||
|
||||
it "evaluates only the specified groups" do
|
||||
it "evaluates only the specified groups (with their options)" do
|
||||
::Guard.should_receive(:add_guard).with(:pow, [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with(:rspec, [], [], { :group => :x })
|
||||
::Guard.should_receive(:add_guard).with(:ronn, [], [], { :group => :x })
|
||||
::Guard.should_receive(:add_guard).with(:less, [], [], { :group => :y })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:x, :y])
|
||||
|
||||
::Guard.groups.should eql [{ :name => :default, :options => {} }, { :name => :x, :options => { :halt_on_fail => true } }, { :name => :y, :options => {} }]
|
||||
end
|
||||
|
||||
it "evaluates always guard outside any group (even when a group is given)" do
|
||||
@ -261,9 +268,11 @@ describe Guard::Dsl do
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :w })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:w])
|
||||
|
||||
::Guard.groups.should eql [{ :name => :default, :options => {} }, { :name => :w, :options => {} }]
|
||||
end
|
||||
|
||||
it "evaluates all groups when no group option is specified" do
|
||||
it "evaluates all groups when no group option is specified (with their options)" do
|
||||
::Guard.should_receive(:add_guard).with(:pow, [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :w })
|
||||
::Guard.should_receive(:add_guard).with(:rspec, [], [], { :group => :x })
|
||||
@ -271,6 +280,9 @@ describe Guard::Dsl do
|
||||
::Guard.should_receive(:add_guard).with(:less, [], [], { :group => :y })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string)
|
||||
|
||||
::Guard.groups.should eql [{ :name => :default, :options => {} }, { :name => :w, :options => {} }, { :name => :x, :options => { :halt_on_fail => true } }, { :name => :y, :options => {} }]
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@ -365,7 +377,7 @@ private
|
||||
guard 'test'
|
||||
end
|
||||
|
||||
group :x do
|
||||
group :x, :halt_on_fail => true do
|
||||
guard 'rspec'
|
||||
guard :ronn
|
||||
end
|
||||
|
@ -15,7 +15,7 @@ describe Guard do
|
||||
end
|
||||
|
||||
it "initializes @groups" do
|
||||
Guard.groups.should eql [:default]
|
||||
Guard.groups.should eql [{ :name => :default, :options => {} }]
|
||||
end
|
||||
|
||||
it "initializes the options" do
|
||||
@ -129,22 +129,25 @@ describe Guard do
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe ".add_group" do
|
||||
before(:each) do
|
||||
Guard.setup
|
||||
end
|
||||
subject { ::Guard.setup }
|
||||
|
||||
it "accepts group name as string" do
|
||||
Guard.add_group('backend')
|
||||
subject.add_group('backend')
|
||||
|
||||
Guard.groups.should eql [:default, :backend]
|
||||
subject.groups.should eql [{ :name => :default, :options => {} }, { :name => :backend, :options => {} }]
|
||||
end
|
||||
|
||||
it "accepts group name as symbol" do
|
||||
Guard.add_group(:backend)
|
||||
subject.add_group(:backend)
|
||||
|
||||
Guard.groups.should eql [:default, :backend]
|
||||
subject.groups.should eql [{ :name => :default, :options => {} }, { :name => :backend, :options => {} }]
|
||||
end
|
||||
|
||||
it "accepts options" do
|
||||
subject.add_group(:backend, { :halt_on_fail => true })
|
||||
|
||||
subject.groups.should eql [{ :name => :default, :options => {} }, { :name => :backend, :options => { :halt_on_fail => true } }]
|
||||
end
|
||||
end
|
||||
|
||||
@ -223,12 +226,54 @@ describe Guard do
|
||||
end
|
||||
end
|
||||
|
||||
describe ".execute_supervised_task_for_all_guards" do
|
||||
subject { ::Guard.setup }
|
||||
|
||||
before do
|
||||
class Guard::Dummy < Guard::Guard; end
|
||||
|
||||
subject.add_group(:foo, { :halt_on_fail => true })
|
||||
subject.add_group(:bar)
|
||||
subject.add_guard(:dummy, [], [], { :group => :foo })
|
||||
subject.add_guard(:dummy, [], [], { :group => :foo })
|
||||
subject.add_guard(:dummy, [], [], { :group => :bar })
|
||||
subject.add_guard(:dummy, [], [], { :group => :bar })
|
||||
@sum = { :foo => 0, :bar => 0}
|
||||
end
|
||||
|
||||
context "all tasks succeed" do
|
||||
before do
|
||||
subject.guards.each { |guard| guard.stub!(:task) { @sum[guard.group] += 1; true } }
|
||||
end
|
||||
|
||||
it "executes the task for each guard in each group" do
|
||||
subject.execute_supervised_task_for_all_guards(:task)
|
||||
|
||||
@sum.all? { |k, v| v == 2 }.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context "one guard fails (by returning false)" do
|
||||
before do
|
||||
subject.guards.each_with_index { |g, i| g.stub!(:task) { @sum[g.group] += i+1; i != 0 && i != 2 } }
|
||||
end
|
||||
|
||||
it "executes the task only for guards that didn't fail for group with :halt_on_fail == true" do
|
||||
subject.execute_supervised_task_for_all_guards(:task)
|
||||
|
||||
@sum[:foo].should eql 1
|
||||
@sum[:bar].should eql 7
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".supervised_task" do
|
||||
subject { ::Guard.setup }
|
||||
|
||||
before(:each) do
|
||||
before do
|
||||
@g = mock(Guard::Guard).as_null_object
|
||||
subject.guards.push(@g)
|
||||
subject.groups.push({ :name => :foo, :options => { :halt_on_fail => true } })
|
||||
end
|
||||
|
||||
context "with a task that succeed" do
|
||||
@ -278,6 +323,14 @@ 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) { false } }
|
||||
|
||||
it "throws :task_has_failed" do
|
||||
expect { subject.supervised_task(@g, :failing) }.to throw_symbol(:task_has_failed)
|
||||
end
|
||||
end
|
||||
|
||||
context "with a task that raises an exception" do
|
||||
before(:each) { @g.stub!(:failing) { raise "I break your system" } }
|
||||
|
||||
|
@ -12,6 +12,7 @@ RSpec.configure do |config|
|
||||
config.color_enabled = true
|
||||
|
||||
config.filter_run :focus => true
|
||||
config.treat_symbols_as_metadata_keys_with_true_values = true
|
||||
config.run_all_when_everything_filtered = true
|
||||
|
||||
config.before(:each) do
|
||||
|
Loading…
Reference in New Issue
Block a user