Merge branch 'master' of github.com:guard/guard

Conflicts:
	lib/guard.rb
	spec/guard_spec.rb
This commit is contained in:
Thibaud Guillaume-Gentil 2011-05-08 22:01:35 +02:00
commit aabf0583b3
9 changed files with 218 additions and 40 deletions

View File

@ -13,5 +13,6 @@ if Config::CONFIG['target_os'] =~ /linux/i
gem 'libnotify', '~> 0.1.3', :require => false gem 'libnotify', '~> 0.1.3', :require => false
end end
if Config::CONFIG['target_os'] =~ /mswin|mingw/i if Config::CONFIG['target_os'] =~ /mswin|mingw/i
gem 'win32console' gem 'win32console', :require => false
gem 'rb-fchange', '>= 0.0.2', :require => false
end end

View File

@ -10,6 +10,7 @@ Features
* [FSEvent](http://en.wikipedia.org/wiki/FSEvents) support on Mac OS X 10.5+ (without RubyCocoa!, [rb-fsevent gem, >= 0.3.5](https://rubygems.org/gems/rb-fsevent) required). * [FSEvent](http://en.wikipedia.org/wiki/FSEvents) support on Mac OS X 10.5+ (without RubyCocoa!, [rb-fsevent gem, >= 0.3.5](https://rubygems.org/gems/rb-fsevent) required).
* [Inotify](http://en.wikipedia.org/wiki/Inotify) support on Linux ([rb-inotify gem, >= 0.5.1](https://rubygems.org/gems/rb-inotify) required). * [Inotify](http://en.wikipedia.org/wiki/Inotify) support on Linux ([rb-inotify gem, >= 0.5.1](https://rubygems.org/gems/rb-inotify) required).
* [Directory Change Notification](http://msdn.microsoft.com/en-us/library/aa365261\(VS.85\).aspx) support on Windows ([rb-fchange, >= 0.0.2](https://rubygems.org/gems/rb-fchange) required).
* Polling on the other operating systems (help us to support more OS). * Polling on the other operating systems (help us to support more OS).
* Automatic & Super fast (when polling is not used) files modifications detection (even new files are detected). * Automatic & Super fast (when polling is not used) files modifications detection (even new files are detected).
* Growl notifications ([growlnotify](http://growl.info/documentation/growlnotify.php) & [growl gem](https://rubygems.org/gems/growl) required). * Growl notifications ([growlnotify](http://growl.info/documentation/growlnotify.php) & [growl gem](https://rubygems.org/gems/growl) required).
@ -21,9 +22,11 @@ Install
Install the gem: Install the gem:
``` bash
$ gem install guard $ gem install guard
```
Add it to your Gemfile (inside the <tt>test</tt> group): Add it to your Gemfile (inside the `test` group):
``` ruby ``` ruby
gem 'guard' gem 'guard'
@ -31,7 +34,9 @@ gem 'guard'
Generate an empty Guardfile with: Generate an empty Guardfile with:
``` bash
$ guard init $ guard init
```
Add the guards you need to your Guardfile (see the existing guards below). Add the guards you need to your Guardfile (see the existing guards below).
@ -39,11 +44,15 @@ Add the guards you need to your Guardfile (see the existing guards below).
Install the rb-fsevent gem for [FSEvent](http://en.wikipedia.org/wiki/FSEvents) support: Install the rb-fsevent gem for [FSEvent](http://en.wikipedia.org/wiki/FSEvents) support:
``` bash
$ gem install rb-fsevent $ gem install rb-fsevent
```
Install the Growl gem if you want notification support: Install the Growl gem if you want notification support:
``` bash
$ gem install growl $ gem install growl
```
And add it to you Gemfile: And add it to you Gemfile:
@ -55,11 +64,15 @@ gem 'growl'
Install the rb-inotify gem for [inotify](http://en.wikipedia.org/wiki/Inotify) support: Install the rb-inotify gem for [inotify](http://en.wikipedia.org/wiki/Inotify) support:
``` bash
$ gem install rb-inotify $ gem install rb-inotify
```
Install the Libnotify gem if you want notification support: Install the Libnotify gem if you want notification support:
``` bash
$ gem install libnotify $ gem install libnotify
```
And add it to you Gemfile: And add it to you Gemfile:
@ -67,49 +80,69 @@ And add it to you Gemfile:
gem 'libnotify' gem 'libnotify'
``` ```
### On Windows
Install the rb-fchange gem for [Directory Change Notification](http://msdn.microsoft.com/en-us/library/aa365261\(VS.85\).aspx) support:
``` bash
$ gem install rb-fchange
```
Usage Usage
----- -----
Just launch Guard inside your Ruby / Rails project with: Just launch Guard inside your Ruby / Rails project with:
``` bash
$ guard [start] $ guard [start]
```
or if you use Bundler, to run the Guard executable specific to your bundle: or if you use Bundler, to run the Guard executable specific to your bundle:
``` bash
$ bundle exec guard $ bundle exec guard
```
Command line options Command line options
-------------------- --------------------
Shell can be cleared after each change with: Shell can be cleared after each change with:
``` bash
$ guard --clear $ guard --clear
$ guard -c # shortcut $ guard -c # shortcut
```
Notifications (growl/libnotify) can be disabled with: Notifications (growl/libnotify) can be disabled with:
``` bash
$ guard --notify false $ guard --notify false
$ guard -n false # shortcut $ guard -n false # shortcut
```
Notifications can also be disabled by setting a `GUARD_NOTIFY` environment variable to `false` Notifications can also be disabled by setting a `GUARD_NOTIFY` environment variable to `false`
The guards to start can be specified by group (see the Guardfile DSL below) specifying the <tt>--group</tt> (or <tt>-g</tt>) option: The guards to start can be specified by group (see the Guardfile DSL below) specifying the `--group` (or `-g`) option:
``` bash
$ guard --group group_name another_group_name $ guard --group group_name another_group_name
$ guard -g group_name another_group_name # shortcut $ guard -g group_name another_group_name # shortcut
```
Options list is available with: Options list is available with:
``` bash
$ guard help [TASK] $ guard help [TASK]
```
Signal handlers Signal handlers
--------------- ---------------
Signal handlers are used to interact with Guard: Signal handlers are used to interact with Guard:
* <tt>Ctrl-C</tt> - Calls each guard's <tt>stop</tt> method, in the same order they are declared in the Guardfile, and then quits Guard itself. * `Ctrl-C` - Calls each guard's `stop` method, in the same order they are declared in the Guardfile, and then quits Guard itself.
* <tt>Ctrl-\\</tt> - Calls each guard's <tt>run_all</tt> method, in the same order they are declared in the Guardfile. * `Ctrl-\` - Calls each guard's `run_all` method, in the same order they are declared in the Guardfile.
* <tt>Ctrl-Z</tt> - Calls each guard's <tt>reload</tt> method, in the same order they are declared in the Guardfile. * `Ctrl-Z` - Calls each guard's `reload` method, in the same order they are declared in the Guardfile.
Available Guards Available Guards
---------------- ----------------
@ -118,7 +151,7 @@ Available Guards
### Add a guard to your Guardfile ### Add a guard to your Guardfile
Add it to your Gemfile (inside the <tt>test</tt> group): Add it to your Gemfile (inside the `test` group):
``` ruby ``` ruby
gem '<guard-name>' gem '<guard-name>'
@ -126,21 +159,25 @@ gem '<guard-name>'
Insert default guard's definition to your Guardfile by running this command: Insert default guard's definition to your Guardfile by running this command:
``` bash
$ guard init <guard-name> $ guard init <guard-name>
```
You are good to go! You are good to go!
Guardfile DSL Guardfile DSL
------------- -------------
The Guardfile DSL consists of just three simple methods: <tt>guard</tt>, <tt>watch</tt> & <tt>group</tt>. The Guardfile DSL consists of just three simple methods: `guard`, `watch` & `group`.
Required: Required:
* The <tt>guard</tt> method allows you to add a guard with an optional hash of options.
* The <tt>watch</tt> method allows you to define which files are supervised by this guard. An optional block can be added to overwrite the paths sent to the <tt>run_on_change</tt> guard method or to launch any arbitrary command. * The `guard` method allows you to add a guard with an optional hash of options.
* The `watch` method allows you to define which files are supervised by this guard. An optional block can be added to overwrite the paths sent to the `run_on_change` guard method or to launch any arbitrary command.
Optional: Optional:
* The <tt>group</tt> method allows you to group several guards together. Groups to be run can be specified with the Guard DSL option <tt>--group</tt> (or <tt>-g</tt>). This comes in handy especially when you have a huge Guardfile and want to focus your development on a certain part.
* The `group` method allows you to group several guards together. Groups to be run can be specified with the Guard DSL option `--group` (or `-g`). This comes in handy especially when you have a huge Guardfile and want to focus your development on a certain part.
Example: Example:
@ -176,7 +213,7 @@ end
Create a new guard Create a new guard
------------------ ------------------
Creating a new guard is very easy, just create a new gem (<tt>bundle gem</tt> if you use Bundler) with this basic structure: Creating a new guard is very easy, just create a new gem (`bundle gem` if you use Bundler) with this basic structure:
lib/ lib/
guard/ guard/
@ -185,7 +222,7 @@ Creating a new guard is very easy, just create a new gem (<tt>bundle gem</tt> if
Guardfile (needed for guard init <guard-name>) Guardfile (needed for guard init <guard-name>)
guard-name.rb guard-name.rb
<tt>Guard::GuardName</tt> (in <tt>lib/guard/guard-name.rb</tt>) must inherit from <tt>Guard::Guard</tt> and should overwrite at least one of the five basic <tt>Guard::Guard</tt> instance methods. Example: `Guard::GuardName` (in `lib/guard/guard-name.rb`) must inherit from `Guard::Guard` and should overwrite at least one of the five basic `Guard::Guard` instance methods. Example:
``` ruby ``` ruby
require 'guard' require 'guard'
@ -246,7 +283,7 @@ Alternatively, a new guard can be added inline to a Guardfile with this basic st
require 'guard/guard' require 'guard/guard'
module ::Guard module ::Guard
class Example < ::Guard::Guard class InlineGuard < ::Guard::Guard
def run_all def run_all
true true
end end
@ -271,3 +308,8 @@ Author
------ ------
[Thibaud Guillaume-Gentil](https://github.com/thibaudgg) [Thibaud Guillaume-Gentil](https://github.com/thibaudgg)
Contributors
------
https://github.com/guard/guard/contributors

View File

@ -16,7 +16,7 @@ module Guard
@listener = Listener.select_and_init @listener = Listener.select_and_init
@guards = [] @guards = []
options[:notify] && ENV["GUARD_NOTIFY"] != 'false' ? Notifier.turn_on : Notifier.turn_off @options[:notify] && ENV["GUARD_NOTIFY"] != 'false' ? Notifier.turn_on : Notifier.turn_off
self self
end end
@ -60,7 +60,7 @@ module Guard
guard.send(task_to_supervise, *args) guard.send(task_to_supervise, *args)
rescue Exception 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.to_s}> command: #{$!}")
::Guard.guards.delete guard guards.delete guard
UI.info("Guard #{guard.class.name} has just been fired") UI.info("Guard #{guard.class.name} has just been fired")
return $! return $!
end end

View File

@ -4,6 +4,7 @@ module Guard
autoload :Darwin, 'guard/listeners/darwin' autoload :Darwin, 'guard/listeners/darwin'
autoload :Linux, 'guard/listeners/linux' autoload :Linux, 'guard/listeners/linux'
autoload :Windows, 'guard/listeners/windows'
autoload :Polling, 'guard/listeners/polling' autoload :Polling, 'guard/listeners/polling'
class Listener class Listener
@ -14,6 +15,8 @@ module Guard
Darwin.new Darwin.new
elsif linux? && Linux.usable? elsif linux? && Linux.usable?
Linux.new Linux.new
elsif windows? && Windows.usable?
Windows.new
else else
UI.info "Using polling (Please help us to support your system better than that.)" UI.info "Using polling (Please help us to support your system better than that.)"
Polling.new Polling.new
@ -54,6 +57,10 @@ module Guard
Config::CONFIG['target_os'] =~ /linux/i Config::CONFIG['target_os'] =~ /linux/i
end end
def self.windows?
Config::CONFIG['target_os'] =~ /mswin|mingw/i
end
end end
end end

View File

@ -0,0 +1,36 @@
module Guard
class Windows < Listener
attr_reader :fchange
def initialize
super
@fchange = FChange::Notifier.new
end
def on_change(&callback)
@fchange.watch(Dir.pwd, :all_events, :recursive) do |event|
paths = [File.expand_path(event.watcher.path) + '/']
files = modified_files(paths, {:all => true})
update_last_event
callback.call(files)
end
end
def start
@fchange.run
end
def stop
@fchange.stop
end
def self.usable?
require 'rb-fchange'
true
rescue LoadError
UI.info "Please install rb-fchange gem for Windows file events support"
false
end
end
end

View File

@ -14,10 +14,10 @@ describe Guard::Listener do
subject.select_and_init subject.select_and_init
end end
it "uses polling listener on Windows" do it "uses windows listener on Windows" do
Config::CONFIG['target_os'] = 'win32' Config::CONFIG['target_os'] = 'mingw'
Guard::Polling.stub(:usable?).and_return(true) Guard::Windows.stub(:usable?).and_return(true)
Guard::Polling.should_receive(:new) Guard::Windows.should_receive(:new)
subject.select_and_init subject.select_and_init
end end

View File

@ -0,0 +1,88 @@
require 'spec_helper'
require 'guard/listeners/windows'
describe Guard::Windows do
subject { Guard::Windows }
if linux?
it "isn't usable on linux" do
subject.should_not be_usable
end
end
if mac?
it "isn't usable on Mac" do
subject.should_not be_usable
end
end
if windows?
it "is usable on Windows 2000 and later" do
subject.should be_usable
end
describe "#on_change" do
before(:each) do
@results = []
@listener = Guard::Windows.new
@listener.on_change do |files|
@results += files
end
end
it "catches new file" do
file = @fixture_path.join("newfile.rb")
if File.exists?(file)
begin
File.delete file
rescue
end
end
File.exists?(file).should be_false
start
FileUtils.touch file
stop
begin
File.delete file
rescue
end
@results.should == ['spec/fixtures/newfile.rb']
end
it "catches file update" do
file = @fixture_path.join("folder1/file1.txt")
File.exists?(file).should be_true
start
FileUtils.touch file
stop
@results.should == ['spec/fixtures/folder1/file1.txt']
end
it "catches files update" do
file1 = @fixture_path.join("folder1/file1.txt")
file2 = @fixture_path.join("folder1/folder2/file2.txt")
File.exists?(file1).should be_true
File.exists?(file2).should be_true
start
FileUtils.touch file1
FileUtils.touch file2
stop
@results.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
end
end
end
private
def start
sleep 0.6
Thread.new { @listener.start }
sleep 0.6
end
def stop
sleep 0.6
@listener.stop
end
end

View File

@ -38,14 +38,14 @@ describe Guard::Notifier do
end end
describe ".turn_off" do describe ".turn_off" do
before(:each) { subject.turn_off }
if mac? && growl_installed? if mac? && growl_installed?
it "does nothing" do it "does nothing" do
Growl.should_not_receive(:notify) Growl.should_not_receive(:notify)
subject.notify 'great', :title => 'Guard' subject.notify 'great', :title => 'Guard'
end end
end elsif linux? && libnotify_installed?
if linux? && libnotify_installed?
it "does nothing" do it "does nothing" do
Libnotify.should_not_receive(:show) Libnotify.should_not_receive(:show)
subject.notify 'great', :title => 'Guard' subject.notify 'great', :title => 'Guard'

View File

@ -5,3 +5,7 @@ end
def linux? def linux?
Config::CONFIG['target_os'] =~ /linux/i Config::CONFIG['target_os'] =~ /linux/i
end end
def windows?
Config::CONFIG['target_os'] =~ /mswin|mingw/i
end