Merge branch 'master' of github.com:guard/guard

Conflicts:
	lib/guard.rb
	lib/guard/interactor.rb
	spec/guard_spec.rb
This commit is contained in:
Thibaud Guillaume-Gentil 2010-11-30 21:23:53 +01:00
commit 2fc6745837
11 changed files with 277 additions and 125 deletions

View File

@ -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) == 0.2.2 (Oct 25, 2010)
Bugs fixes: Bugs fixes:

View File

@ -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-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-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-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-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-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] - {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-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-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-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-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 a guard to your Guardfile
Add it to your Gemfile (inside test group): 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: Guardfile DSL consists of just two simple methods: guard & watch. Example:
guard 'rspec', :version => 2 do guard 'rspec', :version => 2 do
watch('^spec/(.*)_spec.rb') watch(%|^spec/(.*)_spec\.rb|)
watch('^lib/(.*)\.rb') { |m| "spec/lib/#{m[1]}_spec.rb" } watch(%|^lib/(.*)\.rb|) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('^spec/spec_helper.rb') { "spec" } watch(%|^spec/spec_helper\.rb|) { "spec" }
watch('^spec/spec_helper.rb') { `say hello` } watch(%|^spec/models/.*\.rb|) { ["spec/models", "spec/acceptance"] }
watch(%|^spec/spec_helper\.rb|) { `say hello` }
end end
- "guard" method allow to add a guard with an optional options hash - "guard" method allow to add a guard with an optional options hash

View File

@ -17,7 +17,7 @@ module Guard
@options = options @options = options
@listener = Listener.select_and_init @listener = Listener.select_and_init
@guards = [] @guards = []
return self self
end end
def start(options = {}) def start(options = {})
@ -40,6 +40,7 @@ module Guard
loop do loop do
if !running? && !listener.changed_files.empty? if !running? && !listener.changed_files.empty?
changed_files = listener.get_and_clear_changed_files changed_files = listener.get_and_clear_changed_files
if Watcher.match_files?(guards, files)
run do run do
guards.each do |guard| guards.each do |guard|
paths = Watcher.match_files(guard, changed_files) paths = Watcher.match_files(guard, changed_files)
@ -47,6 +48,7 @@ module Guard
end end
end end
end end
end
sleep 0.2 sleep 0.2
end end
end end

View File

@ -3,7 +3,7 @@ module Guard
def self.evaluate_guardfile def self.evaluate_guardfile
guardfile = "#{Dir.pwd}/Guardfile" guardfile = "#{Dir.pwd}/Guardfile"
if File.exists? guardfile if File.exists?(guardfile)
begin begin
dsl = new dsl = new
dsl.instance_eval(File.read(guardfile.to_s), guardfile.to_s, 1) dsl.instance_eval(File.read(guardfile.to_s), guardfile.to_s, 1)

View File

@ -14,9 +14,9 @@ module Guard
content = File.read('Guardfile') content = File.read('Guardfile')
guard = File.read("#{::Guard.locate_guard(name)}/lib/guard/#{name}/templates/Guardfile") guard = File.read("#{::Guard.locate_guard(name)}/lib/guard/#{name}/templates/Guardfile")
File.open('Guardfile', 'wb') do |f| File.open('Guardfile', 'wb') do |f|
f.puts content f.puts(content)
f.puts "" f.puts("")
f.puts guard f.puts(guard)
end end
::Guard::UI.info "#{name} guard added to Guardfile, feel free to edit it" ::Guard::UI.info "#{name} guard added to Guardfile, feel free to edit it"
end end

View File

@ -5,7 +5,7 @@ module Guard
# Run all (Ctrl-\) # Run all (Ctrl-\)
Signal.trap('QUIT') do Signal.trap('QUIT') do
::Guard.run 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
end end
@ -13,14 +13,14 @@ module Guard
Signal.trap('INT') do Signal.trap('INT') do
UI.info "Bye bye...", :reset => true UI.info "Bye bye...", :reset => true
::Guard.listener.stop ::Guard.listener.stop
::Guard.guards.each { |g| ::Guard.supervised_task g, :stop } ::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :stop) }
abort("\n") abort("\n")
end end
# Reload (Ctrl-Z) # Reload (Ctrl-Z)
Signal.trap('TSTP') do Signal.trap('TSTP') do
::Guard.run 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 end
end end

View File

@ -9,12 +9,16 @@ module Guard
end end
end end
def error(message) def error(message, options = {})
unless ENV["GUARD_ENV"] == "test"
reset_line if options[:reset]
puts "ERROR: #{message}" puts "ERROR: #{message}"
end end
end
def debug(message) def debug(message, options = {})
unless ENV["GUARD_ENV"] == "test" unless ENV["GUARD_ENV"] == "test"
reset_line if options[:reset]
puts "DEBUG: #{message}" if ::Guard.options && ::Guard.options[:debug] puts "DEBUG: #{message}" if ::Guard.options && ::Guard.options[:debug]
end end
end end

View File

@ -9,24 +9,36 @@ module Guard
def self.match_files(guard, files) def self.match_files(guard, files)
guard.watchers.inject([]) do |paths, watcher| guard.watchers.inject([]) do |paths, watcher|
files.each do |file| files.each do |file|
if matches = file.match(watcher.pattern) if matches = watcher.match_file?(file)
if watcher.action if watcher.action
begin result = watcher.call_action(matches)
if watcher.action.arity == 1 paths << Array(result) if result.respond_to?(:empty?) && !result.empty?
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 != ''
else else
paths << matches[0] paths << matches[0]
end end
end 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
end end

135
spec/guard/watcher_spec.rb Normal file
View File

@ -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

View File

@ -1,96 +1,84 @@
require 'spec_helper' 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 Guard do
describe "get_guard_class" do 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
describe ".get_guard_class" do
it "should return Guard::RSpec" do it "should return Guard::RSpec" do
Guard.get_guard_class('rspec').should == Guard::RSpec Guard.get_guard_class('rspec').should == Guard::RSpec
end end
end end
describe "locate_guard" do describe ".locate_guard" do
it "should return guard-rspec gem path" do it "should return guard-rspec gem path" do
guard_path = Guard.locate_guard('rspec') guard_path = Guard.locate_guard('rspec')
guard_path.should match(/^.*\/guard-rspec-.*$/) guard_path.should match(/^.*\/guard-rspec-.*$/)
guard_path.should == guard_path.chomp guard_path.should == guard_path.chomp
end end
end end
describe "init" do describe ".supervised_task" do
subject { ::Guard.setup } subject { ::Guard.setup }
before(:each) do
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 be_include(:my_opts)
end
it "Should init listeners" do
::Guard.listener.should be_kind_of(Guard::Listener)
end
end
describe "supervised_task" do
subject { ::Guard.setup }
before :each do
@g = mock(Guard::Guard) @g = mock(Guard::Guard)
@g.stub!(:regular).and_return { true } subject.guards.push(@g)
@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 end
it "should let it go when nothing special occurs" do describe "tasks that succeed" do
subject.guards.should be_include(@g) before(:each) do
subject.supervised_task(@g, :regular).should be_true @g.stub!(:regular) { true }
subject.guards.should be_include(@g) @g.stub!(:regular_with_arg).with("given_path") { "i'm a success" }
end end
it "should let it work with some tools" do it "should not fire the guard with a supervised method without argument" do
subject.guards.should be_include(@g) lambda { subject.supervised_task(@g, :regular) }.should_not change(subject.guards, :size)
subject.supervised_task(@g, :regular).should be_true
subject.guards.should be_include(@g)
end end
it "should fire the guard on spy act discovery" do it "should not fire the guard with a supervised method with argument" do
subject.guards.should be_include(@g) lambda { subject.supervised_task(@g, :regular_with_arg, "given_path") }.should_not change(subject.guards, :size)
::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 end
it "should fire the guard on pirate act discovery" do it "should return the result of the supervised method" do
subject.guards.should be_include(@g) ::Guard.supervised_task(@g, :regular).should be_true
::Guard.supervised_task(@g, :regular_arg, "given_path").should be_kind_of(String) ::Guard.supervised_task(@g, :regular_with_arg, "given_path").should == "i'm a success"
subject.guards.should be_include(@g) end
::Guard.supervised_task(@g, :regular_arg, "given_path").should == "given_path" 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
end end

View File

@ -2,6 +2,8 @@ require 'rubygems'
require 'guard' require 'guard'
require 'rspec' require 'rspec'
ENV["GUARD_ENV"] = 'test'
Dir["#{File.expand_path('..', __FILE__)}/support/**/*.rb"].each { |f| require f } Dir["#{File.expand_path('..', __FILE__)}/support/**/*.rb"].each { |f| require f }
puts "Please do not update/create files while tests are running." 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| RSpec.configure do |config|
config.color_enabled = true config.color_enabled = true
config.filter_run :focus => true
config.run_all_when_everything_filtered = true
config.before(:each) do config.before(:each) do
ENV["GUARD_ENV"] = 'test'
@fixture_path = Pathname.new(File.expand_path('../fixtures/', __FILE__)) @fixture_path = Pathname.new(File.expand_path('../fixtures/', __FILE__))
end end
config.after(:each) do
ENV["GUARD_ENV"] = nil
end
end end