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 module Guard
class Dsl class Dsl
class << self class << self
def evaluate_guardfile(options = {}) def evaluate_guardfile(options = {})
options.is_a?(Hash) or raise ArgumentError.new("evaluate_guardfile not passed a hash")
@@options = options @@options = options
prep_guardfile_contents
if File.exists?(guardfile_path) instance_eval_guardfile(guardfile_contents, actual_guardfile(), 1)
begin end
new.instance_eval(File.read(guardfile_path), guardfile_path, 1)
rescue def instance_eval_guardfile(contents, path, line)
UI.error "Invalid Guardfile, original error is:\n#{$!}" begin
exit 1 new.instance_eval(contents, path, line)
end rescue
else UI.error "Invalid Guardfile, original error is:\n#{$!}"
UI.error "No Guardfile in current folder, please create one."
exit 1 exit 1
end end
end end
def guardfile_include?(guard_name) def guardfile_include?(guard_name, guardfile = guardfile_contents)
File.read(guardfile_path).match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/) guardfile.match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/)
end 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') File.join(Dir.pwd, 'Guardfile')
end end
end
def parent_path
@@options[:parent]
end
end
def group(name, &guard_definition) def group(name, &guard_definition)
guard_definition.call if guard_definition && (@@options[:group].empty? || @@options[:group].include?(name)) guard_definition.call if guard_definition && (@@options[:group].empty? || @@options[:group].include?(name))
@ -40,6 +98,5 @@ module Guard
def watch(pattern, &action) def watch(pattern, &action)
@watchers << ::Guard::Watcher.new(pattern, action) @watchers << ::Guard::Watcher.new(pattern, action)
end end
end end
end end

View File

@ -3,13 +3,24 @@ require 'pathname'
module Guard module Guard
module Notifier module Notifier
@enable = true #todo verify this is the right default
def self.turn_off 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 end
def self.notify(message, options = {}) def self.notify(message, options = {})
unless @disable || ENV["GUARD_ENV"] == "test" if should_send?()
image = options[:image] || :success image = options[:image] || :success
title = options[:title] || "Guard" title = options[:title] || "Guard"
case Config::CONFIG['target_os'] case Config::CONFIG['target_os']

View File

@ -2,122 +2,220 @@ require 'spec_helper'
describe Guard::Dsl do describe Guard::Dsl do
subject { Guard::Dsl } subject { Guard::Dsl }
before(:each) do 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 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.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 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 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:/)
lambda {subject.evaluate_guardfile(:guardfile_contents => invalid_guardfile_string )}.should raise_error
Guard::UI.should_receive(:error).with(/Invalid Guardfile, original error is:\n/)
lambda { subject.evaluate_guardfile }.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
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 it "detects a guard specified by a string with double quotes" do
mock_guardfile_content('guard "test"') subject.guardfile_include?('test', 'guard "test" {watch("c")}').should be_true
subject.guardfile_include?('test').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 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.guardfile_include?('test', 'guard :test {watch("c")}').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.guardfile_include?('test', 'guard(:test) {watch("c")}').should be_true
subject.guardfile_include?('test').should be_true
end end
end end
describe "#group" do 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 it "should 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 "should 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 "should load a guard specified as a string from the DSL" do it "should load a guard specified as a single 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
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 end
it "should load 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
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 end
it "should receive options when specified" do 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' }) ::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 watchers when specified" do it "should receive watchers when specified" do
mock_guardfile_content(" gf_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 => gf_with_watchers)
end end
end end
private private
def fake_guardfile(name, contents)
def mock_guardfile_content(content) File.stub!(:exist?).with(name) { true }
File.stub!(:read).with(File.expand_path('../../../Guardfile', __FILE__)) { content } File.stub!(:read).with(name) { contents }
end 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 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 "should catch file update" do it "should catch 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 "should catch files update" do it "should catch files update" 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 "should catch deleted file" do it "should catch 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 "should catch moved file" do it "should catch 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 "should not process change if stopped" do it "should not process change if stopped" do

View File

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

View File

@ -9,51 +9,107 @@ describe Guard::Notifier do
ENV["GUARD_ENV"] = 'dont_mute_notify' ENV["GUARD_ENV"] = 'dont_mute_notify'
end end
#TODO someone with a mac needs to check this
if mac? if mac?
require 'growl' require 'growl'
it "uses Growl on Mac OS X" do it "uses Growl on Mac OS X" do
Growl.should_receive(:notify).with("great", Growl.should_receive(:notify).with("great",anything())
:title => "Guard", stub_and_execute "great", :name => "Guard"
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
:name => "Guard"
)
subject.notify 'great', :title => 'Guard'
end 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 end
if linux? if linux?
require 'libnotify' describe "uses Libnotify on Linux" do
it "uses Libnotify on Linux" do before(:all) {
Libnotify.should_receive(:show).with( require 'libnotify'
:body => "great", subject.turn_on
:summary => 'Guard', }
:icon_path => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s it "uses libnotify on linux" do
) Libnotify.should_receive(:show).with(hash_including(:body=>"great"))
subject.notify 'great', :title => 'Guard' 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
end end
describe ".turn_off" do describe ".turn_off" do
before(:each) { subject.turn_off } it "does nothing" do
subject.turn_off
if mac? subject.should_send?.should be_false
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
end end
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
end end