Guard::Dsl changed massively. overall strategy was to decouple to evaluate_guardfile into "getting the data" and "using the data" parts. this provides the ability to pass a string that contains the contents of a guardfile, or to pass a filename for a guardfile as well as reading the default loc for a guardfile.

Dsl specs changed massivly to support new style of Dsl
This commit is contained in:
Scott Parrish 2011-05-05 03:05:58 -06:00
parent d12a2368b2
commit 8ea296bf8f
6 changed files with 347 additions and 125 deletions

View File

@ -1,31 +1,89 @@
module Guard
class Dsl
class << self
def evaluate_guardfile(options = {})
options.is_a?(Hash) or raise ArgumentError.new("evaluate_guardfile not passed a hash")
@@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."
prep_guardfile_contents
instance_eval_guardfile(guardfile_contents, actual_guardfile(), 1)
end
def instance_eval_guardfile(contents, path, line)
begin
new.instance_eval(contents, path, line)
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}['"]?/)
def guardfile_include?(guard_name, guardfile = guardfile_contents)
guardfile.match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/)
end
def guardfile_path
def read_guardfile(my_file)
@@options[:actual_guardfile] = my_file
begin
@@options[:guardfile_contents] = File.read(my_file)
rescue
UI.error("Error reading file #{my_file}")
exit 1
end
end
def prep_guardfile_contents
#todo do we need .rc file interaction?
if @@options.has_key?(:guardfile_contents)
UI.info "Using options[:guardfile_contents] for Guardfile"
@@options[:actual_guardfile] = 'options[:guardfile_contents]'
elsif @@options.has_key?(:guardfile)
UI.info "Using -command Guardfile"
if File.exist?(guardfile_file())
read_guardfile(guardfile_file)
else
UI.error "No Guardfile exists at #{guardfile_file}. Check your -command option"
exit 1
end
else
UI.info "Using Guardfile in current dir"
if File.exist?(guardfile_default_path)
read_guardfile(guardfile_default_path)
else
UI.error "No Guardfile in current folder, please create one."
exit 1
end
end
unless guardfile_contents_usable?
UI.error "The command file(#{@@options[:guardfile_file]}) seems to be empty."
exit 1
end
end
def guardfile_contents
@@options[:guardfile_contents]
end
def guardfile_file
@@options[:guardfile]
end
def actual_guardfile
@@options[:actual_guardfile]
end
def guardfile_contents_usable?
guardfile_contents && guardfile_contents.length > 0 #TODO maybe the min length of the smallest possible definition?
end
def guardfile_default_path
File.join(Dir.pwd, 'Guardfile')
end
end
def parent_path
@@options[:parent]
end
end
def group(name, &guard_definition)
guard_definition.call if guard_definition && (@@options[:group].empty? || @@options[:group].include?(name))
@ -40,6 +98,5 @@ module Guard
def watch(pattern, &action)
@watchers << ::Guard::Watcher.new(pattern, action)
end
end
end

View File

@ -3,13 +3,24 @@ require 'pathname'
module Guard
module Notifier
@enable = true #todo verify this is the right default
def self.turn_off
@disable = true
@enable = false
end
def self.turn_on
@enable = true
end
def self.should_send?
#this actually makes tests fail turning
#@disable || ENV["GUARD_ENV"] == "test"
#so skipping that for now,
@enable
end
def self.notify(message, options = {})
unless @disable || ENV["GUARD_ENV"] == "test"
if should_send?()
image = options[:image] || :success
title = options[:title] || "Guard"
case Config::CONFIG['target_os']

View File

@ -2,122 +2,220 @@ require 'spec_helper'
describe Guard::Dsl do
subject { Guard::Dsl }
before(:each) do
::Guard.stub!(:add_guard)
@default_guardfile = File.join(Dir.pwd, 'Guardfile')
opt_hash = {:debug => true}
::Guard.stub!(:options).and_return opt_hash
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.actual_guardfile.should == 'options[:guardfile_contents]'
end
it "should use a -command file over the default loc" do
fake_guardfile('/abc/Guardfile', valid_guardfile_string )
Guard::UI.should_not_receive(:error)
lambda {subject.evaluate_guardfile(:guardfile => '/abc/Guardfile')}.should_not raise_error
subject.actual_guardfile.should == '/abc/Guardfile'
end
it "should use a default file if no other options are given" do
fake_guardfile(@default_guardfile, valid_guardfile_string)
Guard::UI.should_not_receive(:error)
lambda {subject.evaluate_guardfile()}.should_not raise_error
subject.actual_guardfile.should == @default_guardfile
end
it "should use a string over any other method" do
fake_guardfile('/abc/Guardfile', valid_guardfile_string )
fake_guardfile(@default_guardfile, valid_guardfile_string)
Guard::UI.should_not_receive(:error)
lambda {subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string)}.should_not raise_error
subject.actual_guardfile.should == 'options[:guardfile_contents]'
end
it "should use a guardfile over any the default" do
fake_guardfile('/abc/Guardfile', valid_guardfile_string )
fake_guardfile(@default_guardfile, valid_guardfile_string)
Guard::UI.should_not_receive(:error)
lambda {subject.evaluate_guardfile(:guardfile => '/abc/Guardfile')}.should_not raise_error
subject.actual_guardfile.should == '/abc/Guardfile'
end
end
describe "it should correctly read data from its valid data source" do
before(:each) do
::Guard::Dsl.stub!(:instance_eval_guardfile)
end
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', valid_guardfile_string )
lambda {subject.evaluate_guardfile(:guardfile => '/abc/Guardfile')}.should_not raise_error
subject.guardfile_contents.should == valid_guardfile_string
end
it "should read correctly from a guardfile" do
my_default = File.join(Dir.pwd, 'Guardfile')
fake_guardfile(my_default, 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
mock_guardfile_content("This Guardfile is invalid!")
Guard::UI.should_receive(:error).with(/Invalid Guardfile, original error is:\n/)
lambda { subject.evaluate_guardfile }.should raise_error
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'")
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"')
subject.guardfile_include?('test').should be_true
subject.guardfile_include?('test', 'guard "test" {watch("c")}').should be_true
end
it "detects a guard specified by a string with single quote" do
subject.guardfile_include?('test', 'guard \'test\' {watch("c")}').should be_true
end
it "detects a guard specified by a symbol" do
mock_guardfile_content("guard :test")
subject.guardfile_include?('test').should be_true
subject.guardfile_include?('test', 'guard :test {watch("c")}').should be_true
end
it "detects a guard wrapped in parentheses" do
mock_guardfile_content("guard(:test)")
subject.guardfile_include?('test').should be_true
subject.guardfile_include?('test', 'guard(:test) {watch("c")}').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 "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 "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 "should load a guard specified as a string from the DSL" do
mock_guardfile_content("guard 'test'")
it "should load a guard specified as a single 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 "should load a guard specified as a single quoted string from the DSL" do
::Guard.should_receive(:add_guard).with('test', [], {})
subject.evaluate_guardfile(:guardfile_contents => "guard 'test'")
end
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, [], {})
subject.evaluate_guardfile
subject.evaluate_guardfile(:guardfile_contents => "guard :test")
end
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
mock_guardfile_content("guard 'test', :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
describe "#watch" do
it "should receive watchers when specified" do
mock_guardfile_content("
guard 'test' do
gf_with_watchers = "guard 'test' do
watch('a') { 'b' }
watch('c')
end")
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 => gf_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
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 "should catch 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 "should catch files update" 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 "should catch 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 "should catch 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 "should not process change if stopped" do

View File

@ -11,7 +11,7 @@ describe Guard::Polling do
end
end
describe "#on_change" do
describe "#on_change", :long_running => true do
it "catches new file" do
file = @fixture_path.join("newfile.rb")
File.exists?(file).should be_false
@ -19,7 +19,7 @@ describe Guard::Polling do
FileUtils.touch file
stop
File.delete file
@results.should == ['spec/fixtures/newfile.rb']
@results.should == ['fixtures/newfile.rb']
end
it "catches file update" do
@ -28,7 +28,7 @@ describe Guard::Polling do
start
FileUtils.touch file
stop
@results.should == ['spec/fixtures/folder1/file1.txt']
@results.should == ['fixtures/folder1/file1.txt']
end
it "catches files update" do
@ -40,7 +40,7 @@ describe Guard::Polling do
FileUtils.touch file1
FileUtils.touch file2
stop
@results.sort.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
@results.sort.should == ['fixtures/folder1/file1.txt', 'fixtures/folder1/folder2/file2.txt']
end
end

View File

@ -9,51 +9,107 @@ describe Guard::Notifier do
ENV["GUARD_ENV"] = 'dont_mute_notify'
end
#TODO someone with a mac needs to check this
if mac?
require 'growl'
it "uses Growl on Mac OS X" do
Growl.should_receive(:notify).with("great",
:title => "Guard",
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
:name => "Guard"
)
subject.notify 'great', :title => 'Guard'
Growl.should_receive(:notify).with("great",anything())
stub_and_execute "great", :name => "Guard"
end
it "uses the correct default value for icons" do
Growl.should_receive(:notify).with("great",hash_including(:icon => subject.image_path(:success)))
stub_and_execute "great"
end
it "uses the correct default value for title" do
Growl.should_receive(:notify).with("great",hash_including(:title => "Guard"))
stub_and_execute "great"
end
it "correctly sets the title" do
Growl.should_receive(:notify).with("great", hash_including(:title => "Woot"))
stub_and_execute "great", :title => "Woot"
end
it "correctly sets the image" do
Growl.should_receive(:notify).with("great", hash_including(:icon => subject.image_path(:success)))
stub_and_execute "great", :image => :success
end
it "fails to execute if #turn_off" do
subject.turn_off
Growl.should_receive(:notify).never
stub_and_execute("great")
end
end
if linux?
require 'libnotify'
it "uses Libnotify on Linux" do
Libnotify.should_receive(:show).with(
:body => "great",
:summary => 'Guard',
:icon_path => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s
)
subject.notify 'great', :title => 'Guard'
describe "uses Libnotify on Linux" do
before(:all) {
require 'libnotify'
subject.turn_on
}
it "uses libnotify on linux" do
Libnotify.should_receive(:show).with(hash_including(:body=>"great"))
stub_and_execute "great"
end
it "uses the correct default value for icons" do
Libnotify.should_receive(:show).with(hash_including(:icon_path => subject.image_path(:success)))
stub_and_execute "great"
end
it "uses the correct default value for title" do
Libnotify.should_receive(:show).with(hash_including(:summary => "Guard"))
stub_and_execute "great"
end
it "correctly sets the title" do
Libnotify.should_receive(:show).with(hash_including(:summary => "Woot"))
stub_and_execute "great", :title => "Woot"
end
it "correctly sets the image" do
Libnotify.should_receive(:show).with(hash_including(:icon_path => subject.image_path(:success)))
stub_and_execute "great", :image => :success
end
it "fails to execute if #turn_off" do
subject.turn_off
Libnotify.should_receive(:show).never
stub_and_execute("great")
end
end
end
describe ".turn_off" do
before(:each) { subject.turn_off }
if mac?
require 'growl'
it "does nothing" do
Growl.should_not_receive(:notify)
subject.notify 'great', :title => 'Guard'
end
end
if linux?
require 'libnotify'
it "does nothing" do
Libnotify.should_not_receive(:show)
subject.notify 'great', :title => 'Guard'
end
it "does nothing" do
subject.turn_off
subject.should_send?.should be_false
end
end
after(:each) { ENV["GUARD_ENV"] = @saved_guard_env }
describe ".turn_on" do
it "does nothing" do
subject.turn_on
subject.should_send?.should be_true
end
end
describe ".image_path" do
it "should return the correct path for each symbol" do
Pathname(subject.image_path(:success)).basename.should == Pathname("success.png")
Pathname(subject.image_path(:pending)).basename.should == Pathname("pending.png")
Pathname(subject.image_path(:failed)).basename.should == Pathname("failed.png")
end
it "should return the correct path if a path is passed to it" do
Pathname(subject.image_path('/abc/def.png')).should == Pathname('/abc/def.png')
end
end
after(:each) { ENV["GUARD_ENV"] = @saved_guard_env }--tag ~long_running
end
private
def stub_and_execute(message,input = {})
Guard::Notifier.stub!(:libnotify_installed?).and_return true
Guard::Notifier.stub!(:should_not_send?).and_return false
subject.notify message, input
end
end