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

This commit is contained in:
Thibaud Guillaume-Gentil 2011-05-28 16:47:43 +02:00
commit 1a883dcf2c
8 changed files with 270 additions and 87 deletions

View File

@ -1,3 +1,10 @@
### New features:
- Pull request [#55](https://github.com/guard/guard/issues/55): It is now possible to pass `:guardfile` (a Guardfile path) or `:guardfile_contents` (the content of a Guardfile). Hence this allows to use Guard::Dsl.evaluate_guardfile in a programmatic manner. ([@anithri](https://github.com/anithri), improved by ([@rymai](https://github.com/rymai))
## 0.3.4 - April 24, 2011
### Bugs fixes: ### Bugs fixes:
- Issue [#41](https://github.com/guard/guard/issues/41): Removed useless Bundler requirement. ([@thibaudgg](https://github.com/thibaudgg)) - Issue [#41](https://github.com/guard/guard/issues/41): Removed useless Bundler requirement. ([@thibaudgg](https://github.com/thibaudgg))

View File

@ -258,7 +258,7 @@ module Guard
true true
end end
# Called on Ctrl-/ signal # Called on Ctrl-\ signal
# This method should be principally used for long action like running all specs/tests/... # This method should be principally used for long action like running all specs/tests/...
def run_all def run_all
true true

View File

@ -82,7 +82,7 @@ module Guard
def get_guard_class(name) def get_guard_class(name)
try_to_load_gem name try_to_load_gem name
self.const_get(self.constants.find{ |klass_name| klass_name.to_s.downcase == name.downcase.gsub('-', '') }) self.const_get(self.constants.find{ |klass_name| klass_name.to_s.downcase == name.to_s.downcase.gsub('-', '') })
rescue TypeError rescue TypeError
UI.error "Could not find load find gem 'guard-#{name}' or find class Guard::#{name}" UI.error "Could not find load find gem 'guard-#{name}' or find class Guard::#{name}"
end end

View File

@ -1,30 +1,84 @@
module Guard module Guard
class Dsl class Dsl
class << self class << self
# TODO: Add documentation to explain that it is possible to pass `:guardfile` (a Guardfile path)
# or `:guardfile_contents` (the content of a Guardfile). Hence this allows to use Guard::Dsl.evaluate_guardfile
# in a programmatic manner
def evaluate_guardfile(options = {}) def evaluate_guardfile(options = {})
@@options = options options.is_a?(Hash) or raise ArgumentError.new("evaluate_guardfile not passed a Hash!")
if File.exists?(guardfile_path) @@options = options.dup
begin instance_eval_guardfile(fetch_guardfile_contents)
new.instance_eval(File.read(guardfile_path), guardfile_path, 1) end
rescue
UI.error "Invalid Guardfile, original error is:\n#{$!}" def instance_eval_guardfile(contents)
exit 1 begin
end new.instance_eval(contents, @@options[:guardfile_path], 1)
else rescue
UI.error "No Guardfile in current folder, please create one." UI.error "Invalid Guardfile, original error is:\n#{$!}"
exit 1 exit 1
end end
end end
def guardfile_include?(guard_name) def guardfile_include?(guard_name)
File.read(guardfile_path).match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/) guardfile_contents.match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/)
end end
def guardfile_path def read_guardfile(guardfile_path)
begin
@@options[:guardfile_path] = guardfile_path
@@options[:guardfile_contents] = File.read(guardfile_path)
rescue
UI.error("Error reading file #{guardfile_path}")
exit 1
end
end
def fetch_guardfile_contents
# TODO: do we need .rc file interaction?
if @@options.has_key?(:guardfile_contents)
UI.info "Using inline Guardfile."
@@options[:guardfile_path] = 'Inline Guardfile'
elsif @@options.has_key?(:guardfile)
if File.exist?(@@options[:guardfile])
read_guardfile(@@options[:guardfile])
UI.info "Using Guardfile at #{@@options[:guardfile]}."
else
UI.error "No Guardfile exists at #{@@options[:guardfile]}."
exit 1
end
else
if File.exist?(guardfile_default_path)
read_guardfile(guardfile_default_path)
else
UI.error "No Guardfile in current folder, please create one with `guard init`."
exit 1
end
end
unless guardfile_contents_usable?
UI.error "The command file(#{@@options[:guardfile]}) seems to be empty."
exit 1
end
guardfile_contents
end
def guardfile_contents
@@options[:guardfile_contents]
end
def guardfile_contents_usable?
guardfile_contents && guardfile_contents.size >= 'guard :a'.size # smallest guard-definition
end
def guardfile_default_path
File.join(Dir.pwd, 'Guardfile') File.join(Dir.pwd, 'Guardfile')
end end
end end
def group(name, &guard_definition) def group(name, &guard_definition)

View File

@ -70,4 +70,4 @@ module Guard
end end
end end
end end

View File

@ -1,123 +1,236 @@
require 'spec_helper' require 'spec_helper'
describe Guard::Dsl do describe Guard::Dsl do
subject { Guard::Dsl } subject { described_class }
before(:each) do before(:each) do
::Guard.stub!(:add_guard) @default_guardfile = File.join(Dir.pwd, 'Guardfile')
::Guard.stub!(:options).and_return(:debug => true)
end end
it "displays an error message when no Guardfile is found" do describe "it should select the correct data source for Guardfile" do
Dir.stub!(:pwd).and_return("no_guardfile_here")
Guard::UI.should_receive(:error).with("No Guardfile in current folder, please create one.") before(:each) do
lambda { subject.evaluate_guardfile }.should raise_error ::Guard::Dsl.stub!(:instance_eval_guardfile)
end
it "should use a string for initializing" do
Guard::UI.should_not_receive(:error)
lambda { subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string) }.should_not raise_error
subject.guardfile_contents.should == valid_guardfile_string
end
it "should use a -command file over the default loc" do
fake_guardfile('/abc/Guardfile', "guard :foo")
Guard::UI.should_not_receive(:error)
lambda { subject.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
subject.guardfile_contents.should == "guard :foo"
end
it "should use a default file if no other options are given" do
fake_guardfile(@default_guardfile, "guard :bar")
Guard::UI.should_not_receive(:error)
lambda { subject.evaluate_guardfile }.should_not raise_error
subject.guardfile_contents.should == "guard :bar"
end
it "should use a string over any other method" do
fake_guardfile('/abc/Guardfile', "guard :foo")
fake_guardfile(@default_guardfile, "guard :bar")
Guard::UI.should_not_receive(:error)
lambda { subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string) }.should_not raise_error
subject.guardfile_contents.should == valid_guardfile_string
end
it "should use the given Guardfile over default Guardfile" do
fake_guardfile('/abc/Guardfile', "guard :foo")
fake_guardfile(@default_guardfile, "guard :bar")
Guard::UI.should_not_receive(:error)
lambda { subject.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
subject.guardfile_contents.should == "guard :foo"
end
end end
it "displays an error message when the Guardfile is not valid" do describe "it should correctly read data from its valid data source" do
mock_guardfile_content("This Guardfile is invalid!") before(:each) do
::Guard::Dsl.stub!(:instance_eval_guardfile)
end
Guard::UI.should_receive(:error).with(/Invalid Guardfile, original error is:\n/) it "should read correctly from a string" do
lambda { subject.evaluate_guardfile }.should raise_error lambda { subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string) }.should_not raise_error
subject.guardfile_contents.should == valid_guardfile_string
end
it "should read correctly from a Guardfile" do
fake_guardfile('/abc/Guardfile', "guard :foo" )
lambda { subject.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
subject.guardfile_contents.should == "guard :foo"
end
it "should read correctly from a Guardfile" do
fake_guardfile(File.join(Dir.pwd, 'Guardfile'), valid_guardfile_string)
lambda { subject.evaluate_guardfile }.should_not raise_error
subject.guardfile_contents.should == valid_guardfile_string
end
end
describe "It should correctly throw errors when initializing with invalid data" do
before(:each) do
::Guard::Dsl.stub!(:instance_eval_guardfile)
end
it "should raise error when there's a problem reading a file" do
File.stub!(:exist?).with('/def/Guardfile') { true }
File.stub!(:read).with('/def/Guardfile') { raise Errno::EACCES.new("permission error") }
Guard::UI.should_receive(:error).with(/^Error reading file/)
lambda { subject.evaluate_guardfile(:guardfile => '/def/Guardfile') }.should raise_error
end
it "should raise error when -guardfile doesn't exist" do
File.stub!(:exist?).with('/def/Guardfile') { false }
Guard::UI.should_receive(:error).with(/No Guardfile exists at/)
lambda { subject.evaluate_guardfile(:guardfile => '/def/Guardfile') }.should raise_error
end
it "should raise error when resorting to use default, finds no default" do
File.stub!(:exist?).with(@default_guardfile) { false }
Guard::UI.should_receive(:error).with(/No Guardfile in current folder/)
lambda { subject.evaluate_guardfile }.should raise_error
end
it "should raise error when guardfile_content ends up empty or nil" do
Guard::UI.should_receive(:error).twice.with(/The command file/)
lambda { subject.evaluate_guardfile(:guardfile_contents => "") }.should raise_error
lambda { subject.evaluate_guardfile(:guardfile_contents => nil) }.should raise_error
end
end
it "displays an error message when Guardfile is not valid" do
Guard::UI.should_receive(:error).with(/Invalid Guardfile, original error is:/)
lambda { subject.evaluate_guardfile(:guardfile_contents => invalid_guardfile_string ) }.should raise_error
end end
describe ".guardfile_include?" do describe ".guardfile_include?" do
it "detects a Guard specified by a string with simple quotes" do it "detects a guard specified by a string with double quotes" do
mock_guardfile_content("guard 'test'") subject.stub(:guardfile_contents => 'guard "test" {watch("c")}')
subject.guardfile_include?('test').should be_true subject.guardfile_include?('test').should be_true
end end
it "detects a Guard specified by a string with double quotes" do it "detects a guard specified by a string with single quote" do
mock_guardfile_content('guard "test"') subject.stub(:guardfile_contents => 'guard \'test\' {watch("c")}')
subject.guardfile_include?('test').should be_true subject.guardfile_include?('test').should be_true
end end
it "detects a Guard specified by a symbol" do it "detects a guard specified by a symbol" do
mock_guardfile_content("guard :test") subject.stub(:guardfile_contents => 'guard :test {watch("c")}')
subject.guardfile_include?('test').should be_true subject.guardfile_include?('test').should be_true
end end
it "detects a Guard wrapped in parentheses" do it "detects a guard wrapped in parentheses" do
mock_guardfile_content("guard(:test)") subject.stub(:guardfile_contents => 'guard(:test) {watch("c")}')
subject.guardfile_include?('test').should be_true subject.guardfile_include?('test').should be_true
end end
end end
describe "#group" do describe "#group" do
before do it "should evaluates only the specified group" do
mock_guardfile_content("
group 'x' do
guard 'test' do
watch('c')
end
end
group 'y' do
guard 'another' do
watch('c')
end
end")
end
it "evaluates only the specified group" do
::Guard.should_receive(:add_guard).with('test', anything, {}) ::Guard.should_receive(:add_guard).with('test', anything, {})
::Guard.should_not_receive(:add_guard).with('another', anything, {}) lambda { subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => ['x']) }.should_not raise_error
subject.evaluate_guardfile(:group => ['x'])
end end
it "should evaluates only the specified groups" do
it "evaluates only the specified groups" do
::Guard.should_receive(:add_guard).with('test', anything, {}) ::Guard.should_receive(:add_guard).with('test', anything, {})
::Guard.should_receive(:add_guard).with('another', anything, {}) ::Guard.should_receive(:add_guard).with('another', anything, {})
subject.evaluate_guardfile(:group => ['x', 'y']) lambda { subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => ['x','y']) }.should_not raise_error
end end
end end
# TODO: not sure if each seperate quoting/call type needs its own test
describe "#guard" do describe "#guard" do
it "loads a Guard specified as a string from the DSL" do it "should load a guard specified as a quoted string from the DSL" do
mock_guardfile_content("guard 'test'")
::Guard.should_receive(:add_guard).with('test', [], {}) ::Guard.should_receive(:add_guard).with('test', [], {})
subject.evaluate_guardfile
subject.evaluate_guardfile(:guardfile_contents => "guard 'test'")
end end
it "loads a Guard specified as a symbol from the DSL" do it "should load a guard specified as a symbol from the DSL" do
mock_guardfile_content("guard :test")
::Guard.should_receive(:add_guard).with(:test, [], {}) ::Guard.should_receive(:add_guard).with(:test, [], {})
subject.evaluate_guardfile
subject.evaluate_guardfile(:guardfile_contents => "guard :test")
end end
it "receives the options when specified" do it "should load a guard specified as a symbol and called with parens from the DSL" do
mock_guardfile_content("guard 'test', :opt_a => 1, :opt_b => 'fancy'") ::Guard.should_receive(:add_guard).with(:test, [], {})
subject.evaluate_guardfile(:guardfile_contents => "guard(:test)")
end
it "should receive options when specified" do
::Guard.should_receive(:add_guard).with('test', anything, { :opt_a => 1, :opt_b => 'fancy' }) ::Guard.should_receive(:add_guard).with('test', anything, { :opt_a => 1, :opt_b => 'fancy' })
subject.evaluate_guardfile
subject.evaluate_guardfile(:guardfile_contents => "guard 'test', :opt_a => 1, :opt_b => 'fancy'")
end end
end end
describe "#watch" do describe "#watch" do
it "should receive the watchers when specified" do it "should receive watchers when specified" do
mock_guardfile_content(" guardfile_with_watchers = "guard 'test' do
guard 'test' do watch('a') { 'b' }
watch('a') { 'b' } watch('c')
watch('c') end"
end")
::Guard.should_receive(:add_guard).with('test', anything, {}) do |name, watchers, options| ::Guard.should_receive(:add_guard).with('test', anything, {}) do |name, watchers, options|
watchers.size.should == 2 watchers.size.should == 2
watchers[0].pattern.should == 'a' watchers[0].pattern.should == 'a'
watchers[0].action.call.should == proc { 'b' }.call watchers[0].action.call.should == proc { 'b' }.call
watchers[1].pattern.should == 'c' watchers[1].pattern.should == 'c'
watchers[1].action.should be_nil watchers[1].action.should == nil
end end
subject.evaluate_guardfile subject.evaluate_guardfile(:guardfile_contents => guardfile_with_watchers)
end end
end end
private private
def mock_guardfile_content(content) def fake_guardfile(name, contents)
File.stub!(:read).with(File.expand_path('../../../Guardfile', __FILE__)) { content } File.stub!(:exist?).with(name) { true }
File.stub!(:read).with(name) { contents }
end end
def valid_guardfile_string
"group 'x' do
guard 'test' do
watch('c')
end
end
group 'y' do
guard 'another' do
watch('c')
end
end
group 'z' do
guard 'another' do
watch('c')
end
end"
end
def invalid_guardfile_string
"Bad Guardfile"
end
end end

View File

@ -16,7 +16,7 @@ describe Guard::Linux do
subject.should be_usable subject.should be_usable
end end
describe "#start" do describe "#start", :long_running => true do
before(:each) do before(:each) do
@listener = Guard::Linux.new @listener = Guard::Linux.new
end end
@ -40,7 +40,7 @@ describe Guard::Linux do
end end
describe "#on_change" do describe "#on_change", :long_running=> true do
before(:each) do before(:each) do
@results = [] @results = []
@listener = Guard::Linux.new @listener = Guard::Linux.new
@ -56,7 +56,7 @@ describe Guard::Linux do
FileUtils.touch file FileUtils.touch file
stop stop
File.delete file File.delete file
@results.should == ['spec/fixtures/newfile.rb'] @results.should == ['fixtures/newfile.rb']
end end
it "catches a single file update" do it "catches a single file update" do
@ -65,7 +65,7 @@ describe Guard::Linux do
start start
File.open(file, 'w') {|f| f.write('') } File.open(file, 'w') {|f| f.write('') }
stop stop
@results.should == ['spec/fixtures/folder1/file1.txt'] @results.should == ['fixtures/folder1/file1.txt']
end end
it "catches multiple file updates" do it "catches multiple file updates" do
@ -77,7 +77,7 @@ describe Guard::Linux do
File.open(file1, 'w') {|f| f.write('') } File.open(file1, 'w') {|f| f.write('') }
File.open(file2, 'w') {|f| f.write('') } File.open(file2, 'w') {|f| f.write('') }
stop stop
@results.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt'] @results.should == ['fixtures/folder1/file1.txt', 'fixtures/folder1/folder2/file2.txt']
end end
it "catches a deleted file" do it "catches a deleted file" do
@ -87,7 +87,7 @@ describe Guard::Linux do
File.delete file File.delete file
stop stop
FileUtils.touch file FileUtils.touch file
@results.should == ['spec/fixtures/folder1/file1.txt'] @results.should == ['fixtures/folder1/file1.txt']
end end
it "catches a moved file" do it "catches a moved file" do
@ -99,7 +99,7 @@ describe Guard::Linux do
FileUtils.mv file1, file2 FileUtils.mv file1, file2
stop stop
FileUtils.mv file2, file1 FileUtils.mv file2, file1
@results.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/movedfile1.txt'] @results.should == ['fixtures/folder1/file1.txt', 'fixtures/folder1/movedfile1.txt']
end end
it "doesn't process a change when it is stopped" do it "doesn't process a change when it is stopped" do

View File

@ -47,7 +47,16 @@ describe Guard do
end end
context 'with a nested Guard class' do context 'with a nested Guard class' do
it "returns the Guard class" do it "returns the Guard class when passed a Symbol" do
Guard.should_receive(:try_to_load_gem) { |classname|
classname.should == :classname
class Guard::Classname
end
}
Guard.get_guard_class(:classname).should == Guard::Classname
end
it "returns the Guard class when passed a String" do
Guard.should_receive(:try_to_load_gem) { |classname| Guard.should_receive(:try_to_load_gem) { |classname|
classname.should == 'classname' classname.should == 'classname'
class Guard::Classname class Guard::Classname