Added #callback DSL, modified Guard and Guard::Hook a bit in consequence.

Signed-off-by: Rémy Coutable <remy@jilion.com>
This commit is contained in:
Rémy Coutable 2011-04-16 23:02:13 +02:00 committed by Rémy Coutable
parent b646ae53f6
commit b83653db2e
6 changed files with 100 additions and 64 deletions

View File

@ -58,12 +58,12 @@ module Guard
# Let a guard execute its task but
# fire it if his work leads to a system failure
def supervised_task(guard, task_to_supervise, *args)
guard.hook "#{task_to_supervise.to_s}_begin"
guard.hook "#{task_to_supervise}_begin"
result = guard.send(task_to_supervise, *args)
guard.hook "#{task_to_supervise.to_s}_end"
guard.hook "#{task_to_supervise}_end"
result
rescue Exception
UI.error("#{guard.class.name} guard failed to achieve its <#{task_to_supervise.to_s}> command: #{$!}")
UI.error("#{guard.class.name} guard failed to achieve its <#{task_to_supervise}> command: #{$!}")
::Guard.guards.delete guard
UI.info("Guard #{guard.class.name} has just been fired")
return $!
@ -79,9 +79,11 @@ module Guard
listener.start
end
def add_guard(name, watchers = [], options = {})
def add_guard(name, watchers = [], callbacks = [], options = {})
guard_class = get_guard_class(name)
watchers = watchers.map { |watcher| ::Guard::Watcher.new(watcher[:pattern], watcher[:action]) }
@guards << guard_class.new(watchers, options)
callbacks.each { |callback| ::Guard::Hook.add_callback(callback[:listener], guard_class, callback[:events]) }
end
def get_guard_class(name)

View File

@ -4,7 +4,7 @@ module Guard
class << self
def evaluate_guardfile(options = {})
@@options = options
if File.exists?(guardfile_path)
begin
new.instance_eval(File.read(guardfile_path), guardfile_path, 1)
@ -21,7 +21,7 @@ module Guard
def guardfile_include?(guard_name)
File.read(guardfile_path).match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/)
end
def guardfile_path
File.join(Dir.pwd, 'Guardfile')
end
@ -31,14 +31,24 @@ module Guard
guard_definition.call if guard_definition && (@@options[:group].empty? || @@options[:group].include?(name))
end
def guard(name, options = {}, &watch_definition)
@watchers = []
watch_definition.call if watch_definition
::Guard.add_guard(name, @watchers, options)
def guard(name, options = {}, &watch_and_callback_definition)
@watchers = []
@callbacks = []
watch_and_callback_definition.call if watch_and_callback_definition
::Guard.add_guard(name, @watchers, @callbacks, options)
end
def watch(pattern, &action)
@watchers << ::Guard::Watcher.new(pattern, action)
@watchers << { :pattern => pattern, :action => action }
end
def callback(*args, &listener)
listener, events = if args.size > 1
args
else
[listener, args[0]]
end
@callbacks << { :events => events, :listener => listener }
end
end

View File

@ -5,17 +5,16 @@ module Guard
end
module InstanceMethods
# When passed a sybmol, #hook will generate a hook name
# from the symbol and calling method name. When passed
# a string, #hook will turn the string into a symbol
# directly.
def hook(event)
if event.class == Symbol
hook_name = if event.is_a? Symbol
calling_method = caller[0][/`([^']*)'/, 1]
hook_name = "#{calling_method}_#{event}".to_sym
"#{calling_method}_#{event}".to_sym
else
hook_name = event.to_sym
event.to_sym
end
UI.info "\nHook :#{hook_name} executed for #{self.class}"
@ -28,15 +27,15 @@ module Guard
@callbacks ||= Hash.new { |hash, key| hash[key] = [] }
end
def add(listener, guard_class, events)
_events = events.class == Array ? events : [events]
def add_callback(listener, guard_class, events)
_events = events.is_a?(Array) ? events : [events]
_events.each do |event|
callbacks[[guard_class, event]] << listener
end
end
def has_callback?(listener, guard_class, event)
callbacks[[guard_class, event]].include? listener
callbacks[[guard_class, event]].include?(listener)
end
def notify(guard_class, event)
@ -45,7 +44,7 @@ module Guard
end
end
def reset!
def reset_callbacks!
@callbacks = nil
end
end

View File

@ -59,56 +59,75 @@ describe Guard::Dsl do
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, {})
it "evaluates only the specified group" do
::Guard.should_receive(:add_guard).with('test', anything, anything, {})
::Guard.should_not_receive(:add_guard).with('another', anything, anything, {})
subject.evaluate_guardfile(:group => ['x'])
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, {})
it "evaluates only the specified groups" do
::Guard.should_receive(:add_guard).with('test', anything, anything, {})
::Guard.should_receive(:add_guard).with('another', anything, anything, {})
subject.evaluate_guardfile(:group => ['x', 'y'])
end
end
describe "#guard" do
it "should load a guard specified as a string from the DSL" do
it "loads a guard specified by a string" do
mock_guardfile_content("guard 'test'")
::Guard.should_receive(:add_guard).with('test', [], {})
::Guard.should_receive(:add_guard).with('test', [], [], {})
subject.evaluate_guardfile
end
it "should load a guard specified as a symbol from the DSL" do
it "loads 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
end
it "should receive options when specified" do
it "accepts options" 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, anything, { :opt_a => 1, :opt_b => 'fancy' })
subject.evaluate_guardfile
end
end
describe "#watch" do
it "should receive watchers when specified" do
it "creates watchers for the guard" do
mock_guardfile_content("
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
::Guard.should_receive(:add_guard).with('test', anything, anything, {}) do |name, watchers, callbacks, options|
watchers.should have(2).items
watchers[0][:pattern].should == 'a'
watchers[0][:action].call.should == proc { 'b' }.call
watchers[1][:pattern].should == 'c'
watchers[1][:action].should be_nil
end
subject.evaluate_guardfile
end
end
describe "#callback" do
it "creates callbacks for the guard" do
class MyCustomCallback
end
mock_guardfile_content("
guard 'test' do
callback(:start_end) { 'Guard::Test started!' }
callback(MyCustomCallback, [:start_begin, :run_all_begin])
end")
::Guard.should_receive(:add_guard).with('test', anything, anything, {}) do |name, watchers, callbacks, options|
callbacks.should have(2).items
callbacks[0][:events].should == :start_end
callbacks[0][:listener].call.should == proc { 'Guard::Test started!' }.call
callbacks[1][:events].should == [:start_begin, :run_all_begin]
callbacks[1][:listener].should == MyCustomCallback
end
subject.evaluate_guardfile
end

View File

@ -1,8 +1,8 @@
require "spec_helper"
require 'guard/guard'
require "guard/hook"
require 'spec_helper'
describe Guard::Hook do
subject { Guard::Hook }
class Guard::Dummy < Guard::Guard
include Guard::Hook
@ -16,53 +16,49 @@ describe Guard::Hook do
let(:listener) { double('listener').as_null_object }
context "--module methods--" do
before { subject.add(listener, guard_class, :start_begin) }
before { subject.add_callback(listener, guard_class, :start_begin) }
after { subject.reset! }
after { subject.reset_callbacks! }
describe ".add " do
describe ".add_callback" do
it "can add a single callback" do
subject.has_callback?(listener, guard_class, :start_begin).should be_true
end
it "can add multiple callbacks" do
subject.add(listener, guard_class, [:event1, :event2])
subject.add_callback(listener, guard_class, [:event1, :event2])
subject.has_callback?(listener, guard_class, :event1).should be_true
subject.has_callback?(listener, guard_class, :event2).should be_true
end
end
describe ".notify " do
describe ".notify" do
it "sends :call to the given Guard class's callbacks" do
listener.should_receive(:call).with(guard_class, :start_begin)
subject.notify(guard_class, :start_begin)
end
it "doesn't run callbacks of different types" do
it "runs only the given callbacks" do
listener2 = double('listener2')
subject.add(listener2, guard_class, :start_end)
subject.add_callback(listener2, guard_class, :start_end)
listener2.should_not_receive(:call).with(guard_class, :start_end)
subject.notify(guard_class, :start_begin)
end
it "doesn't run callbacks of the wrong class" do
it "runs callbacks only for the guard given" do
guard2_class = double('Guard::Dummy2').class
subject.add(listener, guard2_class, :start_begin)
subject.add_callback(listener, guard2_class, :start_begin)
listener.should_not_receive(:call).with(guard2_class, :start_begin)
subject.notify(guard_class, :start_begin)
end
end
end
describe "#hook " do
describe "#hook" do
it "calls Guard::Hook.notify" do
guard = guard_class.new
Guard::Hook.should_receive(:notify).
with(guard_class, :run_all_begin)
Guard::Hook.should_receive(:notify).
with(guard_class, :run_all_end)
Guard::Hook.should_receive(:notify).with(guard_class, :run_all_begin)
Guard::Hook.should_receive(:notify).with(guard_class, :run_all_end)
guard.run_all
end
@ -74,9 +70,9 @@ describe Guard::Hook do
end
guard = guard_class.new
Guard::Hook.should_receive(:notify).
with(guard_class, :my_hook)
Guard::Hook.should_receive(:notify).with(guard_class, :my_hook)
guard.start
end
end
end

View File

@ -91,6 +91,12 @@ describe Guard do
::Guard.supervised_task(@g, :regular).should be_true
::Guard.supervised_task(@g, :regular_with_arg, "given_path").should == "i'm a success"
end
it "calls the default hooks" do
@g.should_receive(:hook).with("regular_begin")
@g.should_receive(:hook).with("regular_end")
::Guard.supervised_task(@g, :regular)
end
end
describe "tasks that raise an exception" do
@ -106,9 +112,13 @@ describe Guard do
failing_result.should be_kind_of(Exception)
failing_result.message.should == 'I break your system'
end
end
it "calls the default hooks"
it "calls the default begin hook but not the default end hook" do
@g.should_receive(:hook).with("failing_begin")
@g.should_not_receive(:hook).with("failing_end")
::Guard.supervised_task(@g, :failing)
end
end
end
describe ".locate_guard" do