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:
- 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
end
# Called on Ctrl-/ signal
# Called on Ctrl-\ signal
# This method should be principally used for long action like running all specs/tests/...
def run_all
true

View File

@ -82,7 +82,7 @@ module Guard
def get_guard_class(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
UI.error "Could not find load find gem 'guard-#{name}' or find class Guard::#{name}"
end

View File

@ -1,30 +1,84 @@
module Guard
class Dsl
class << self
def evaluate_guardfile(options = {})
@@options = options
if File.exists?(guardfile_path)
begin
new.instance_eval(File.read(guardfile_path), guardfile_path, 1)
rescue
UI.error "Invalid Guardfile, original error is:\n#{$!}"
exit 1
end
else
UI.error "No Guardfile in current folder, please create one."
# 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 = {})
options.is_a?(Hash) or raise ArgumentError.new("evaluate_guardfile not passed a Hash!")
@@options = options.dup
instance_eval_guardfile(fetch_guardfile_contents)
end
def instance_eval_guardfile(contents)
begin
new.instance_eval(contents, @@options[:guardfile_path], 1)
rescue
UI.error "Invalid Guardfile, original error is:\n#{$!}"
exit 1
end
end
def guardfile_include?(guard_name)
File.read(guardfile_path).match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/)
guardfile_contents.match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/)
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')
end
end
def group(name, &guard_definition)

View File

@ -1,123 +1,236 @@
require 'spec_helper'
describe Guard::Dsl do
subject { Guard::Dsl }
subject { described_class }
before(:each) do
::Guard.stub!(:add_guard)
@default_guardfile = File.join(Dir.pwd, 'Guardfile')
::Guard.stub!(:options).and_return(:debug => true)
end
it "displays an error message when no Guardfile is found" do
Dir.stub!(:pwd).and_return("no_guardfile_here")
describe "it should select the correct data source for Guardfile" do
Guard::UI.should_receive(:error).with("No Guardfile in current folder, please create one.")
lambda { subject.evaluate_guardfile }.should raise_error
before(:each) do
::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
it "displays an error message when the Guardfile is not valid" do
mock_guardfile_content("This Guardfile is invalid!")
describe "it should correctly read data from its valid data source" do
before(:each) do
::Guard::Dsl.stub!(:instance_eval_guardfile)
end
Guard::UI.should_receive(:error).with(/Invalid Guardfile, original error is:\n/)
lambda { subject.evaluate_guardfile }.should raise_error
it "should read correctly from a string" do
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
describe ".guardfile_include?" do
it "detects a Guard specified by a string with simple quotes" do
mock_guardfile_content("guard 'test'")
it "detects a guard specified by a string with double quotes" do
subject.stub(:guardfile_contents => 'guard "test" {watch("c")}')
subject.guardfile_include?('test').should be_true
end
it "detects a Guard specified by a string with double quotes" do
mock_guardfile_content('guard "test"')
it "detects a guard specified by a string with single quote" do
subject.stub(:guardfile_contents => 'guard \'test\' {watch("c")}')
subject.guardfile_include?('test').should be_true
end
it "detects a Guard specified by a symbol" do
mock_guardfile_content("guard :test")
it "detects a guard specified by a symbol" do
subject.stub(:guardfile_contents => 'guard :test {watch("c")}')
subject.guardfile_include?('test').should be_true
end
it "detects a Guard wrapped in parentheses" do
mock_guardfile_content("guard(:test)")
it "detects a guard wrapped in parentheses" do
subject.stub(:guardfile_contents => 'guard(:test) {watch("c")}')
subject.guardfile_include?('test').should be_true
end
end
describe "#group" do
before 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
it "should evaluates only the specified group" do
::Guard.should_receive(:add_guard).with('test', anything, {})
::Guard.should_not_receive(:add_guard).with('another', anything, {})
subject.evaluate_guardfile(:group => ['x'])
lambda { subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => ['x']) }.should_not raise_error
end
it "evaluates only the specified groups" do
it "should evaluates only the specified groups" do
::Guard.should_receive(:add_guard).with('test', 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
# TODO: not sure if each seperate quoting/call type needs its own test
describe "#guard" do
it "loads a Guard specified as a string from the DSL" do
mock_guardfile_content("guard 'test'")
it "should load a guard specified as a quoted string from the DSL" do
::Guard.should_receive(:add_guard).with('test', [], {})
subject.evaluate_guardfile
subject.evaluate_guardfile(:guardfile_contents => "guard 'test'")
end
it "loads a Guard specified as a symbol from the DSL" do
mock_guardfile_content("guard :test")
it "should load a guard specified as a symbol from the DSL" do
::Guard.should_receive(:add_guard).with(:test, [], {})
subject.evaluate_guardfile
subject.evaluate_guardfile(:guardfile_contents => "guard :test")
end
it "receives the options when specified" do
mock_guardfile_content("guard 'test', :opt_a => 1, :opt_b => 'fancy'")
it "should load a guard specified as a symbol and called with parens from the DSL" do
::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' })
subject.evaluate_guardfile
subject.evaluate_guardfile(:guardfile_contents => "guard 'test', :opt_a => 1, :opt_b => 'fancy'")
end
end
describe "#watch" do
it "should receive the watchers when specified" do
mock_guardfile_content("
guard 'test' do
watch('a') { 'b' }
watch('c')
end")
it "should receive watchers when specified" do
guardfile_with_watchers = "guard 'test' do
watch('a') { 'b' }
watch('c')
end"
::Guard.should_receive(:add_guard).with('test', anything, {}) do |name, watchers, options|
watchers.size.should == 2
watchers[0].pattern.should == 'a'
watchers[0].action.call.should == proc { 'b' }.call
watchers[1].pattern.should == 'c'
watchers[1].action.should be_nil
watchers[1].action.should == nil
end
subject.evaluate_guardfile
subject.evaluate_guardfile(:guardfile_contents => guardfile_with_watchers)
end
end
private
def mock_guardfile_content(content)
File.stub!(:read).with(File.expand_path('../../../Guardfile', __FILE__)) { content }
def fake_guardfile(name, contents)
File.stub!(:exist?).with(name) { true }
File.stub!(:read).with(name) { contents }
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

View File

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

View File

@ -47,7 +47,16 @@ describe Guard do
end
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|
classname.should == 'classname'
class Guard::Classname