diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc index 4211747..54690a7 100644 --- a/CHANGELOG.rdoc +++ b/CHANGELOG.rdoc @@ -1,3 +1,15 @@ +== Nov 26, 2010 [by rymai] + +Features: +- It's now possible to return an enumerable in the 'watch' optional blocks in the Guardfile. +- Listener now continue to watch changed files even when guards plugin are running. + +Specs: +- Guard::Watcher + +Bugs fixes: +- Avoid launching run_on_change guards method when no files matched. --clear guard argument is now usable. + == 0.2.2 (Oct 25, 2010) Bugs fixes: diff --git a/README.rdoc b/README.rdoc index 253fb3d..b7b4477 100644 --- a/README.rdoc +++ b/README.rdoc @@ -83,6 +83,8 @@ Signal handlers are used to interact with Guard: - {guard-coffeescript}[http://github.com/guard/guard-coffeescript] by {Michael Kessler}[http://github.com/netzpirat] - {guard-compass}[http://github.com/guard/guard-compass] by {Olivier Amblet}[http://github.com/oliamb] - {guard-cucumber}[http://github.com/guard/guard-cucumber] by {Michael Kessler}[http://github.com/netzpirat] +- {guard-ego}[http://github.com/guard/guard-ego] by {Fabio Kuhn}[http://github.com/mordaroso] +- {guard-jammit}[http://github.com/guard/guard-jammit] by {Pelle Braendgaard}[http://github.com/pelle] - {guard-livereload}[http://github.com/guard/guard-livereload] by {Thibaud Guillaume-Gentil}[http://github.com/thibaudgg] - {guard-minitest}[http://github.com/guard/guard-minitest] by {Yann Lugrin}[http://github.com/yannlugrin] - {guard-nanoc}[http://github.com/guard/guard-nanoc] by {Yann Lugrin}[http://github.com/yannlugrin] @@ -90,13 +92,9 @@ Signal handlers are used to interact with Guard: - {guard-rspec}[http://github.com/guard/guard-rspec] by {Thibaud Guillaume-Gentil}[http://github.com/thibaudgg] - {guard-sass}[http://github.com/guard/guard-sass] by {Joshua Hawxwell}[http://github.com/hawx] - {guard-shell}[http://github.com/guard/guard-shell] by {Joshua Hawxwell}[http://github.com/hawx] +- {guard-spork}[http://github.com/guard/guard-spork] by {Thibaud Guillaume-Gentil}[http://github.com/thibaudgg] - {guard-test}[http://github.com/guard/guard-test] by {Rémy Coutable}[http://github.com/rymai] -guard ideas: - -- guard-spork -- others ideas? - === Add a guard to your Guardfile Add it to your Gemfile (inside test group): @@ -178,10 +176,11 @@ Looks at available guards code for more concrete example. Guardfile DSL consists of just two simple methods: guard & watch. Example: guard 'rspec', :version => 2 do - watch('^spec/(.*)_spec.rb') - watch('^lib/(.*)\.rb') { |m| "spec/lib/#{m[1]}_spec.rb" } - watch('^spec/spec_helper.rb') { "spec" } - watch('^spec/spec_helper.rb') { `say hello` } + watch(%|^spec/(.*)_spec\.rb|) + watch(%|^lib/(.*)\.rb|) { |m| "spec/lib/#{m[1]}_spec.rb" } + watch(%|^spec/spec_helper\.rb|) { "spec" } + watch(%|^spec/models/.*\.rb|) { ["spec/models", "spec/acceptance"] } + watch(%|^spec/spec_helper\.rb|) { `say hello` } end - "guard" method allow to add a guard with an optional options hash diff --git a/lib/guard.rb b/lib/guard.rb index 1e5c91c..9b5b5d6 100644 --- a/lib/guard.rb +++ b/lib/guard.rb @@ -17,7 +17,7 @@ module Guard @options = options @listener = Listener.select_and_init @guards = [] - return self + self end def start(options = {}) @@ -40,10 +40,12 @@ module Guard loop do if !running? && !listener.changed_files.empty? changed_files = listener.get_and_clear_changed_files - run do - guards.each do |guard| - paths = Watcher.match_files(guard, changed_files) - supervised_task(guard, :run_on_change, paths) unless paths.empty? + if Watcher.match_files?(guards, files) + run do + guards.each do |guard| + paths = Watcher.match_files(guard, changed_files) + supervised_task(guard, :run_on_change, paths) unless paths.empty? + end end end end diff --git a/lib/guard/dsl.rb b/lib/guard/dsl.rb index 35ae282..2ef364f 100644 --- a/lib/guard/dsl.rb +++ b/lib/guard/dsl.rb @@ -3,7 +3,7 @@ module Guard def self.evaluate_guardfile guardfile = "#{Dir.pwd}/Guardfile" - if File.exists? guardfile + if File.exists?(guardfile) begin dsl = new dsl.instance_eval(File.read(guardfile.to_s), guardfile.to_s, 1) diff --git a/lib/guard/guard.rb b/lib/guard/guard.rb index f205a1d..c4886c7 100644 --- a/lib/guard/guard.rb +++ b/lib/guard/guard.rb @@ -14,9 +14,9 @@ module Guard content = File.read('Guardfile') guard = File.read("#{::Guard.locate_guard(name)}/lib/guard/#{name}/templates/Guardfile") File.open('Guardfile', 'wb') do |f| - f.puts content - f.puts "" - f.puts guard + f.puts(content) + f.puts("") + f.puts(guard) end ::Guard::UI.info "#{name} guard added to Guardfile, feel free to edit it" end diff --git a/lib/guard/interactor.rb b/lib/guard/interactor.rb index 62bf14e..f5247eb 100644 --- a/lib/guard/interactor.rb +++ b/lib/guard/interactor.rb @@ -5,7 +5,7 @@ module Guard # Run all (Ctrl-\) Signal.trap('QUIT') do ::Guard.run do - ::Guard.guards.each { |g| ::Guard.supervised_task g, :run_all } + ::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :run_all) } end end @@ -13,14 +13,14 @@ module Guard Signal.trap('INT') do UI.info "Bye bye...", :reset => true ::Guard.listener.stop - ::Guard.guards.each { |g| ::Guard.supervised_task g, :stop } + ::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :stop) } abort("\n") end # Reload (Ctrl-Z) Signal.trap('TSTP') do ::Guard.run do - ::Guard.guards.each { |g| ::Guard.supervised_task g, :reload } + ::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :reload) } end end end diff --git a/lib/guard/ui.rb b/lib/guard/ui.rb index 197b29d..bb3f96e 100644 --- a/lib/guard/ui.rb +++ b/lib/guard/ui.rb @@ -9,12 +9,16 @@ module Guard end end - def error(message) - puts "ERROR: #{message}" + def error(message, options = {}) + unless ENV["GUARD_ENV"] == "test" + reset_line if options[:reset] + puts "ERROR: #{message}" + end end - def debug(message) + def debug(message, options = {}) unless ENV["GUARD_ENV"] == "test" + reset_line if options[:reset] puts "DEBUG: #{message}" if ::Guard.options && ::Guard.options[:debug] end end diff --git a/lib/guard/watcher.rb b/lib/guard/watcher.rb index 593203b..bbda600 100644 --- a/lib/guard/watcher.rb +++ b/lib/guard/watcher.rb @@ -9,24 +9,36 @@ module Guard def self.match_files(guard, files) guard.watchers.inject([]) do |paths, watcher| files.each do |file| - if matches = file.match(watcher.pattern) + if matches = watcher.match_file?(file) if watcher.action - begin - if watcher.action.arity == 1 - result = watcher.action.call(matches) - else - result = watcher.action.call - end - rescue - UI.info "Problem with watch action" - end - paths << result if result.is_a?(String) && result != '' + result = watcher.call_action(matches) + paths << Array(result) if result.respond_to?(:empty?) && !result.empty? else paths << matches[0] end end end - paths + paths.flatten.map { |p| p.to_s } + end + end + + def self.match_files?(guards, files) + guards.any? do |guard| + guard.watchers.any? do |watcher| + files.any? { |file| watcher.match_file?(file) } + end + end + end + + def match_file?(file) + file.match(@pattern) + end + + def call_action(matches) + begin + @action.arity > 0 ? @action.call(matches) : @action.call + rescue + UI.error "Problem with watch action!" end end diff --git a/spec/guard/watcher_spec.rb b/spec/guard/watcher_spec.rb new file mode 100644 index 0000000..abaca88 --- /dev/null +++ b/spec/guard/watcher_spec.rb @@ -0,0 +1,135 @@ +require 'spec_helper' +require 'guard/guard' + +describe Guard::Watcher do + + describe "pattern" do + it "should be required" do + expect { Guard::Watcher.new }.to raise_error(ArgumentError) + end + + it "should be set" do + Guard::Watcher.new(%|spec_helper\.rb|).pattern.should == %|spec_helper\.rb| + end + end + + describe "action" do + it "should set action to nil by default" do + Guard::Watcher.new(%|spec_helper\.rb|).action.should be_nil + end + + it "should set action with a block" do + action = lambda { |m| "spec/#{m[1]}_spec.rb" } + Guard::Watcher.new(%|^lib/(.*).rb|, action).action.should == action + end + end + + describe ".match_files" do + before(:all) { @guard = Guard::Guard.new } + + describe "a watcher's with no action" do + before(:all) { @guard.watchers = [Guard::Watcher.new(%|.*_spec\.rb|)] } + + it "should return paths as they came" do + Guard::Watcher.match_files(@guard, ['guard_rocks_spec.rb']).should == ['guard_rocks_spec.rb'] + end + end + + describe "a watcher's action with an arity equal to 0" do + before(:all) do + @guard.watchers = [ + Guard::Watcher.new(%|spec_helper\.rb|, lambda { 'spec' }), + Guard::Watcher.new(%|addition\.rb|, lambda { 1 + 1 }), + Guard::Watcher.new(%|hash\.rb|, lambda { Hash[:foo, 'bar'] }), + Guard::Watcher.new(%|array\.rb|, lambda { ['foo', 'bar'] }), + Guard::Watcher.new(%|blank\.rb|, lambda { '' }), + Guard::Watcher.new(%|uptime\.rb|, lambda { `uptime > /dev/null` }) + ] + end + + it "should return paths specified in the watcher's action" do + Guard::Watcher.match_files(@guard, ['spec_helper.rb']).should == ['spec'] + end + it "should return nothing if action.call doesn't respond_to :empty?" do + Guard::Watcher.match_files(@guard, ['addition.rb']).should == [] + end + it "should return action.call.to_a if result respond_to :empty?" do + Guard::Watcher.match_files(@guard, ['hash.rb']).should == ['foo', 'bar'] + end + it "should return files including files from array if paths are an array" do + Guard::Watcher.match_files(@guard, ['spec_helper.rb', 'array.rb']).should == ['spec', 'foo', 'bar'] + end + it "should return nothing if action.call return ''" do + Guard::Watcher.match_files(@guard, ['blank.rb']).should == [] + end + it "should return nothing if action.call return nil" do + Guard::Watcher.match_files(@guard, ['uptime.rb']).should == [] + end + end + + describe "a watcher's action with an arity equal to 1" do + before(:all) do + @guard.watchers = [ + Guard::Watcher.new(%|lib/(.*)\.rb|, lambda { |m| "spec/#{m[1]}_spec.rb" }), + Guard::Watcher.new(%|addition(.*)\.rb|, lambda { |m| 1 + 1 }), + Guard::Watcher.new(%|hash\.rb|, lambda { Hash[:foo, 'bar'] }), + Guard::Watcher.new(%|array(.*)\.rb|, lambda { |m| ['foo', 'bar'] }), + Guard::Watcher.new(%|blank(.*)\.rb|, lambda { |m| '' }), + Guard::Watcher.new(%|uptime(.*)\.rb|, lambda { |m| `uptime > /dev/null` }) + ] + end + + it "should return paths after watcher's action has been called against them" do + Guard::Watcher.match_files(@guard, ['lib/my_wonderful_lib.rb']).should == ['spec/my_wonderful_lib_spec.rb'] + end + it "should return nothing if action.call doesn't respond_to :empty?" do + Guard::Watcher.match_files(@guard, ['addition.rb']).should == [] + end + it "should return action.call.to_a if result respond_to :empty?" do + Guard::Watcher.match_files(@guard, ['hash.rb']).should == ['foo', 'bar'] + end + it "should return files including files from array if paths are an array" do + Guard::Watcher.match_files(@guard, ['lib/my_wonderful_lib.rb', 'array.rb']).should == ['spec/my_wonderful_lib_spec.rb', 'foo', 'bar'] + end + it "should return nothing if action.call return ''" do + Guard::Watcher.match_files(@guard, ['blank.rb']).should == [] + end + it "should return nothing if action.call return nil" do + Guard::Watcher.match_files(@guard, ['uptime.rb']).should == [] + end + end + + describe "an exception is raised" do + before(:all) { @guard.watchers = [Guard::Watcher.new('evil.rb', lambda { raise "EVIL" })] } + + it "should display an error" do + Guard::UI.should_receive(:error).with("Problem with watch action!") + Guard::Watcher.match_files(@guard, ['evil.rb']) + end + end + end + + describe ".match_files?" do + before(:all) do + @guard1 = Guard::Guard.new([Guard::Watcher.new(%|.*_spec\.rb|)]) + @guard2 = Guard::Guard.new([Guard::Watcher.new(%|spec_helper\.rb|, 'spec')]) + @guards = [@guard1, @guard2] + end + + describe "with at least on watcher that match a file given" do + specify { Guard::Watcher.match_files?(@guards, ['lib/my_wonderful_lib.rb', 'guard_rocks_spec.rb']).should be_true } + end + + describe "with no watcher matching a file given" do + specify { Guard::Watcher.match_files?(@guards, ['lib/my_wonderful_lib.rb']).should be_false } + end + end + + describe "#match_file?" do + subject { Guard::Watcher.new(%|.*_spec\.rb|) } + + specify { subject.match_file?('lib/my_wonderful_lib.rb').should be_false } + specify { subject.match_file?('guard_rocks_spec.rb').should be_true } + end + +end \ No newline at end of file diff --git a/spec/guard_spec.rb b/spec/guard_spec.rb index 276d434..74b9653 100644 --- a/spec/guard_spec.rb +++ b/spec/guard_spec.rb @@ -1,97 +1,85 @@ require 'spec_helper' -# mute UI -module Guard::UI - class << self - def info(message, options = {}) - end - - def error(message) - end - - def debug(message) - end - end -end - describe Guard do - describe "get_guard_class" do - - it "should return Guard::RSpec" do - Guard.get_guard_class('rspec').should == Guard::RSpec + describe "Class Methods" do + describe ".setup" do + subject { ::Guard.setup } + + it "should retrieve itself for chaining" do + subject.should be_kind_of(Module) + end + + it "should init guards array" do + ::Guard.guards.should be_kind_of(Array) + end + + it "should init options" do + opts = { :my_opts => true } + ::Guard.setup(opts).options.should include(:my_opts) + end + + it "should init listener" do + ::Guard.listener.should be_kind_of(Guard::Listener) + end end - end - - describe "locate_guard" do - - it "should return guard-rspec gem path" do - guard_path = Guard.locate_guard('rspec') - guard_path.should match(/^.*\/guard-rspec-.*$/) - guard_path.should == guard_path.chomp + describe ".get_guard_class" do + it "should return Guard::RSpec" do + Guard.get_guard_class('rspec').should == Guard::RSpec + end end - end - - describe "init" do - subject { ::Guard.setup } - - it "Should retrieve itself for chaining" do - subject.should be_kind_of(Module) + describe ".locate_guard" do + it "should return guard-rspec gem path" do + guard_path = Guard.locate_guard('rspec') + guard_path.should match(/^.*\/guard-rspec-.*$/) + guard_path.should == guard_path.chomp + end end - it "Should init guards array" do - ::Guard.guards.should be_kind_of(Array) - end - - it "Should init options" do - opts = {:my_opts => true} - ::Guard.setup(opts).options.should be_include(:my_opts) - end - - it "Should init listeners" do - ::Guard.listener.should be_kind_of(Guard::Listener) + describe ".supervised_task" do + subject { ::Guard.setup } + before(:each) do + @g = mock(Guard::Guard) + subject.guards.push(@g) + end + + describe "tasks that succeed" do + before(:each) do + @g.stub!(:regular) { true } + @g.stub!(:regular_with_arg).with("given_path") { "i'm a success" } + end + + it "should not fire the guard with a supervised method without argument" do + lambda { subject.supervised_task(@g, :regular) }.should_not change(subject.guards, :size) + end + + it "should not fire the guard with a supervised method with argument" do + lambda { subject.supervised_task(@g, :regular_with_arg, "given_path") }.should_not change(subject.guards, :size) + end + + it "should return the result of the supervised method" do + ::Guard.supervised_task(@g, :regular).should be_true + ::Guard.supervised_task(@g, :regular_with_arg, "given_path").should == "i'm a success" + end + end + + describe "tasks that raise an exception" do + before(:each) { @g.stub!(:failing) { raise "I break your system" } } + + it "should fire the guard" do + lambda { subject.supervised_task(@g, :failing) }.should change(subject.guards, :size).by(-1) + subject.guards.should_not include(@g) + end + + it "should return the exception object" do + failing_result = ::Guard.supervised_task(@g, :failing) + failing_result.should be_kind_of(Exception) + failing_result.message.should == 'I break your system' + end + end end end - describe "supervised_task" do - subject { ::Guard.setup } - - before :each do - @g = mock(Guard::Guard) - @g.stub!(:regular).and_return { true } - @g.stub!(:spy).and_return { raise "I break your system" } - @g.stub!(:pirate).and_raise Exception.new("I blow your system up") - @g.stub!(:regular_arg).with("given_path").and_return { "given_path" } - subject.guards.push @g - end - - it "should let it go when nothing special occurs" do - subject.guards.should be_include(@g) - subject.supervised_task(@g, :regular).should be_true - subject.guards.should be_include(@g) - end - - it "should let it work with some tools" do - subject.guards.should be_include(@g) - subject.supervised_task(@g, :regular).should be_true - subject.guards.should be_include(@g) - end - - it "should fire the guard on spy act discovery" do - subject.guards.should be_include(@g) - ::Guard.supervised_task(@g, :spy).should be_kind_of(Exception) - subject.guards.should_not be_include(@g) - ::Guard.supervised_task(@g, :spy).message.should == 'I break your system' - end - - it "should fire the guard on pirate act discovery" do - subject.guards.should be_include(@g) - ::Guard.supervised_task(@g, :regular_arg, "given_path").should be_kind_of(String) - subject.guards.should be_include(@g) - ::Guard.supervised_task(@g, :regular_arg, "given_path").should == "given_path" - end - end - -end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index fe70582..bbbe914 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,8 @@ require 'rubygems' require 'guard' require 'rspec' +ENV["GUARD_ENV"] = 'test' + Dir["#{File.expand_path('..', __FILE__)}/support/**/*.rb"].each { |f| require f } puts "Please do not update/create files while tests are running." @@ -9,13 +11,11 @@ puts "Please do not update/create files while tests are running." RSpec.configure do |config| config.color_enabled = true + config.filter_run :focus => true + config.run_all_when_everything_filtered = true + config.before(:each) do - ENV["GUARD_ENV"] = 'test' @fixture_path = Pathname.new(File.expand_path('../fixtures/', __FILE__)) end - - config.after(:each) do - ENV["GUARD_ENV"] = nil - end end \ No newline at end of file