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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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