diff --git a/lib/guard/dsl.rb b/lib/guard/dsl.rb index fbbb2ec..5b85ef9 100644 --- a/lib/guard/dsl.rb +++ b/lib/guard/dsl.rb @@ -1,30 +1,81 @@ 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." + 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) diff --git a/lib/guard/notifier.rb b/lib/guard/notifier.rb index 5ed0afc..52bb95c 100644 --- a/lib/guard/notifier.rb +++ b/lib/guard/notifier.rb @@ -70,4 +70,4 @@ module Guard end end -end \ No newline at end of file +end diff --git a/spec/guard/dsl_spec.rb b/spec/guard/dsl_spec.rb index f72ba85..89729a2 100644 --- a/spec/guard/dsl_spec.rb +++ b/spec/guard/dsl_spec.rb @@ -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 diff --git a/spec/guard/listeners/linux_spec.rb b/spec/guard/listeners/linux_spec.rb index a36b33c..1f7c27e 100644 --- a/spec/guard/listeners/linux_spec.rb +++ b/spec/guard/listeners/linux_spec.rb @@ -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