Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge commit '2ff6a2229aabaa63444511cc5f0b0c5e31acd8ef'

  • Loading branch information...
commit a0245de6cd8172ab4e5e8286a199fa223686eb2e 2 parents ad3def3 + 2ff6a22
@stereobooster authored
View
90 CHANGELOG.markdown
@@ -1,90 +0,0 @@
-### Bugs fixes:
-
-- Issue [#41](https://github.com/guard/guard/issues/41): Removed useless Bundler requirement. ([@thibaudgg](https://github.com/thibaudgg))
-
-### New features:
-
-- Changed CHANGELOG from RDOC to Markdown and cleaned it! Let's celebrate! ([@rymai](https://github.com/rymai))
-- Changed README from RDOC to Markdown! Let's celebrate! ([@thibaudgg](https://github.com/thibaudgg))
-- Issue [#48](https://github.com/guard/guard/issues/48): Adding support for inline Guard classes rather than requiring a gem. ([@jrsacks](https://github.com/jrsacks))
-
-
-## 0.3.3 - April 18, 2011
-
-### Bugs fixes:
-
-- Fixed `new_modified_files` rerun conditions on `Guard.run_on_change_for_all_guards`. ([@thibaudgg](https://github.com/thibaudgg))
-
-
-## 0.3.2 - April 17, 2011
-
-### Bugs fixes:
-
-- Pull request [#43](https://github.com/guard/guard/issues/43): Fixed `guard init` command. ([@brainopia](https://github.com/brainopia))
-
-
-## 0.3.1 - April 14, 2011
-
-### Bugs fixes:
-
-- Return unique filenames from Linux listener. (Marian Schubert)
-- `Guard.get_guard_class` return wrong class when loaded nested class. ([@koshigoe](https://github.com/koshigoe))
-- Issue [#35](https://github.com/guard/guard/issues/35): Fixed open-gem/gem_open dependency problem by using `gem which` to locate guards gem path. (reported by [@thierryhenrio](https://github.com/thierryhenrio), fixed by [@thibaudgg](https://github.com/thibaudgg))
-- Issue [#38](https://github.com/guard/guard/issues/38) & Pull request [#39](https://github.com/guard/guard/issues/39): Fixed an invalid ANSI escape code in `Guard::UI.reset_line`. ([@gix](https://github.com/gix))
-
-### New features:
-
-- Issue [#28](https://github.com/guard/guard/issues/28): New `-n` command line option to disable notifications (Growl / Libnotify). ([@thibaudgg](https://github.com/thibaudgg))
-
-
-## 0.3.0 - January 19, 2011
-
-### Bugs fixes:
-
-- Avoid launching run_on_change guards method when no files matched. --clear guard argument is now usable. ([@thibaudgg](https://github.com/thibaudgg))
-
-### New features:
-
-- The whole directory is now watched during `run_on_change` to detect new files modifications. ([@thibaudgg](https://github.com/thibaudgg))
-- Pull request [#26](https://github.com/guard/guard/issues/26): New DSL method: `group` allows you to group several guards. ([@netzpirat](https://github.com/netzpirat))
-- Pull request [#26](https://github.com/guard/guard/issues/26): New CLI option: `--group group_name` to specify certain groups of guards to start. ([@netzpirat](https://github.com/netzpirat))
-- `watch` patterns are now more strict: strings are matched with `String#==`, `Regexp` are matched with `Regexp#match`. ([@rymai](https://github.com/rymai))
-- A deprecation warning is displayed if your `Guardfile` contains `String` that look like `Regexp` (bad!). ([@rymai](https://github.com/rymai))
-- It's now possible to return an `Enumerable` in the `watch` optional blocks in the `Guardfile`. ([@rymai](https://github.com/rymai))
-
-### New specs:
-
-- Guard::Watcher. ([@rymai](https://github.com/rymai))
-- Pull request [#13](https://github.com/guard/guard/issues/13): Guard::Dsl. ([@oliamb](https://github.com/oliamb))
-
-
-## 0.2.2 - October 25, 2010
-
-### Bugs fixes:
-
-- Issue [#5](https://github.com/guard/guard/issues/5): avoid creating new copy of `fsevent_watch` every time a file is changed. (reported by [@stouset](https://github.com/stouset), fixed by [@thibaudgg](https://github.com/thibaudgg))
-
-
-## 0.2.1 - October 24, 2010
-
-### Bugs fixes:
-
-- Pull request [#7](https://github.com/guard/guard/issues/7): Fixes for Linux support. ([@yannlugrin](https://github.com/yannlugrin)))
-- Pull request [#6](https://github.com/guard/guard/issues/6): Locate guard now chomp newline in result path. ([@yannlugrin](https://github.com/yannlugrin)))
-
-
-## 0.2.0 - October 21, 2010
-
-### Bugs fixes:
-
-- Issue [#3](https://github.com/guard/guard/issues/3): `guard init <guard-name>` no more need `Gemfile` but `open_gem` is required now. (reported by [@wereHamster](https://github.com/wereHamster), fixed by [@thibaudgg](https://github.com/thibaudgg))
-- Issue [#2](https://github.com/guard/guard/issues/2): 1.8.6 compatibility. (reported by [@veged](https://github.com/veged), fixed by [@thibaudgg](https://github.com/thibaudgg))
-- Removes Growl & Libnotify dependencies. ([@thibaudgg](https://github.com/thibaudgg))
-
-
-## 0.2.0.beta.1 - October 17, 2010
-
-### New features:
-
-- Improved listeners support (`rb-fsevent` & `rb-inotify`). ([@thibaudgg](https://github.com/thibaudgg))
-- Added polling listening fallback. ([@thibaudgg](https://github.com/thibaudgg))
View
174 CHANGELOG.md
@@ -0,0 +1,174 @@
+## Master
+
+## Improvements
+
+- Clear the terminal on start when the `:clear` option is given. ([@rymai][])
+
+## 0.4.2 - June 7, 2011
+
+### Bugs fixes:
+
+- Fixed Guard::Version in ruby 1.8.7 ([@thibaudgg][])
+- Fix ([@mislav][]) link in CHANGELOG (this is a recursive CHANGELOG item :P). ([@fnichol][])
+
+## 0.4.1 - June 7, 2011
+
+### Improvements
+
+- Pull request [#77](https://github.com/guard/guard/pull/77): Refactor `get_guard_class` to first try the constant and fallback to require + various tweaks. ([@mislav][])
+- Notifier improvement, don't use system notification library if could not be required. ([@yannlugrin][])
+
+## 0.4.0 - June 5, 2011
+
+### Bugs fixes:
+
+- In Ruby < 1.9, Symbol#downcase doesn't exist! ([@rymai][])
+
+### New features:
+
+- Pull request [#73](https://github.com/guard/guard/pull/73): Allow DSL's `group` method to accept a Symbol as group name. ([@johnbintz][])
+- Pull request [#51](https://github.com/guard/guard/pull/51): Allow options (like :priority) to be passed through to the Notifier. ([@indirect][] and [@netzpirat][])
+
+### Improvements
+
+- Pull request [#74](https://github.com/guard/guard/pull/74): Added link definitions to make the CHANGELOG more DRY! That's for sure now, we have the cleanest CHANGELOG ever! (even the link definitions are sorted alphabetically!) ([@pcreux][])
+
+## 0.4.0.rc - May 28, 2011
+
+### Bugs fixes:
+
+- Pull request [#69](https://github.com/guard/guard/pull/69): Fixed typo in README: Ctr-/ => Ctr-\\. ([@tinogomes][])
+- Pull request [#66](https://github.com/guard/guard/pull/66): Support for dashes in guard names. ([@johnbintz][])
+- Require `guard/ui` because `Guard::Notifier` can be required without full Guard. ([@yannlugrin][])
+- Handled quick file (<1s) modification. Avoid to catch modified files without content modification (sha1 checksum). ([@thibaudgg][] and [@netzpirat][])
+- Fixed `Guard::Notifier` (when growl/libnotify not present). ([@thibaudgg][])
+- Fixed Rubygems deprecation messages. ([@thibaudgg][])
+
+### New features:
+
+- Pull request [#67](https://github.com/guard/guard/pull/67): Allow Guardfile in `$HOME` folder. ([@hashrocketeer][])
+- Pull request [#64](https://github.com/guard/guard/pull/64): Windows notifications support. ([@stereobooster][])
+- Pull request [#63](https://github.com/guard/guard/pull/63): Refactor listeners to work as a library. ([@niklas][])
+- Use `ENV["GUARD_NOTIFY"]` to disable notifications. ([@thibaudgg][])
+- Cleaning up all specs. ([@netzpirat][])
+- Pull request [#60](https://github.com/guard/guard/pull/60): Added Windows support. ([@stereobooster][])
+- Pull request [#58](https://github.com/guard/guard/pull/58): Extract code from signal handlers into methods. ([@nicksieger][])
+- Pull request [#55](https://github.com/guard/guard/pull/55): It is now possible to pass `:guardfile` (a Guardfile path) or `:guardfile_contents` (the content of a Guardfile) to `Guard::Dsl.evaluate_guardfile`. Hence this allows the use of `Guard::Dsl.evaluate_guardfile` in a programmatic manner. ([@anithri][], improved by [@rymai][])
+
+
+## 0.3.4 - April 24, 2011
+
+### Bugs fixes:
+
+- Issue [#41](https://github.com/guard/guard/issues/41): Removed useless Bundler requirement. ([@thibaudgg][])
+
+### New features:
+
+- Changed CHANGELOG from RDOC to Markdown and cleaned it! Let's celebrate! ([@rymai][])
+- Changed README from RDOC to Markdown! Let's celebrate! ([@thibaudgg][])
+- Issue [#48](https://github.com/guard/guard/issues/48): Adding support for inline Guard classes rather than requiring a gem. ([@jrsacks][])
+
+
+## 0.3.3 - April 18, 2011
+
+### Bugs fixes:
+
+- Fixed `new_modified_files` rerun conditions on `Guard.run_on_change_for_all_guards`. ([@thibaudgg][])
+
+
+## 0.3.2 - April 17, 2011
+
+### Bugs fixes:
+
+- Pull request [#43](https://github.com/guard/guard/pull/43): Fixed `guard init` command. ([@brainopia][])
+
+
+## 0.3.1 - April 14, 2011
+
+### Bugs fixes:
+
+- Return unique filenames from Linux listener. (Marian Schubert)
+- `Guard.get_guard_class` return wrong class when loaded nested class. ([@koshigoe][])
+- Issue [#35](https://github.com/guard/guard/issues/35): Fixed open-gem/gem_open dependency problem by using `gem which` to locate guards gem path. (reported by [@thierryhenrio][], fixed by [@thibaudgg][])
+- Issue [#38](https://github.com/guard/guard/issues/38) & Pull request [#39](https://github.com/guard/guard/issues/39): Fixed an invalid ANSI escape code in `Guard::UI.reset_line`. ([@gix][])
+
+### New features:
+
+- Issue [#28](https://github.com/guard/guard/issues/28): New `-n` command line option to disable notifications (Growl / Libnotify). ([@thibaudgg][])
+
+
+## 0.3.0 - January 19, 2011
+
+### Bugs fixes:
+
+- Avoid launching `run_on_change` guards method when no files matched. `--clear` guard argument is now usable. ([@thibaudgg][])
+
+### New features:
+
+- The whole directory is now watched during `run_on_change` to detect new files modifications. ([@thibaudgg][])
+- Pull request [#26](https://github.com/guard/guard/pull/26): New DSL method: `group` allows you to group several guards. New CLI option: `--group group_name` to specify certain groups of guards to start. ([@netzpirat][])
+- `watch` patterns are now more strict: strings are matched with `String#==`, `Regexp` are matched with `Regexp#match`. ([@rymai][])
+- A deprecation warning is displayed if your `Guardfile` contains `String` that look like `Regexp` (bad!). ([@rymai][])
+- It's now possible to return an `Enumerable` in the `watch` optional blocks in the `Guardfile`. ([@rymai][])
+
+### New specs:
+
+- `Guard::Watcher`. ([@rymai][])
+- Pull request [#13](https://github.com/guard/guard/pull/13): `Guard::Dsl`. ([@oliamb][])
+
+
+## 0.2.2 - October 25, 2010
+
+### Bugs fixes:
+
+- Issue [#5](https://github.com/guard/guard/issues/5): avoid creating new copy of `fsevent_watch` every time a file is changed. (reported by [@stouset][], fixed by [@thibaudgg][])
+
+
+## 0.2.1 - October 24, 2010
+
+### Bugs fixes:
+
+- Pull request [#7](https://github.com/guard/guard/pull/7): Fixes for Linux support. ([@yannlugrin][]))
+- Pull request [#6](https://github.com/guard/guard/pull/6): Locate guard now chomp newline in result path. ([@yannlugrin][]))
+
+
+## 0.2.0 - October 21, 2010
+
+### Bugs fixes:
+
+- Issue [#3](https://github.com/guard/guard/issues/3): `guard init <guard-name>` no more need `Gemfile` but `open_gem` is required now. (reported by [@wereHamster][], fixed by [@thibaudgg][])
+- Issue [#2](https://github.com/guard/guard/issues/2): 1.8.6 compatibility. (reported by [@veged][], fixed by [@thibaudgg][])
+- Removes Growl & Libnotify dependencies. ([@thibaudgg][])
+
+
+## 0.2.0.beta.1 - October 17, 2010
+
+### New features:
+
+- Improved listeners support (`rb-fsevent` & `rb-inotify`). ([@thibaudgg][])
+- Added polling listening fallback. ([@thibaudgg][])
+
+[@anithri]: https://github.com/anithri
+[@brainopia]: https://github.com/brainopia
+[@fnichol]: https://github.com/fnichol
+[@gix]: https://github.com/gix
+[@hashrocketeer]: https://github.com/hashrocketeer
+[@indirect]: https://github.com/indirect
+[@johnbintz]: https://github.com/johnbintz
+[@jrsacks]: https://github.com/jrsacks
+[@koshigoe]: https://github.com/koshigoe
+[@mislav]: https://github.com/mislav
+[@netzpirat]: https://github.com/netzpirat
+[@nicksieger]: https://github.com/nicksieger
+[@niklas]: https://github.com/niklas
+[@oliamb]: https://github.com/oliamb
+[@pcreux]: https://github.com/pcreux
+[@rymai]: https://github.com/rymai
+[@stereobooster]: https://github.com/stereobooster
+[@stouset]: https://github.com/stouset
+[@thibaudgg]: https://github.com/thibaudgg
+[@thierryhenrio]: https://github.com/thierryhenrio
+[@tinogomes]: https://github.com/tinogomes
+[@veged]: https://github.com/veged
+[@wereHamster]: https://github.com/wereHamster
+[@yannlugrin]: https://github.com/yannlugrin
View
12 Gemfile
@@ -2,17 +2,19 @@ source "http://rubygems.org"
gemspec
+gem 'rake'
+
require 'rbconfig'
-if Config::CONFIG['target_os'] =~ /darwin/i
- gem 'rb-fsevent', '>= 0.3.9', :require => false
+if RbConfig::CONFIG['target_os'] =~ /darwin/i
+ gem 'rb-fsevent', '>= 0.4.0', :require => false
gem 'growl', '~> 1.0.3', :require => false
end
-if Config::CONFIG['target_os'] =~ /linux/i
- gem 'rb-inotify', '>= 0.5.1', :require => false
+if RbConfig::CONFIG['target_os'] =~ /linux/i
+ gem 'rb-inotify', '>= 0.8.5', :require => false
gem 'libnotify', '~> 0.1.3', :require => false
end
-if Config::CONFIG['target_os'] =~ /mswin|mingw/i
+if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
gem 'win32console', :require => false
gem 'rb-fchange', '>= 0.0.2', :require => false
gem 'rb-notifu', '>= 0.0.4', :require => false
View
64 README.markdown → README.md
@@ -3,6 +3,8 @@ Guard [![Build Status](http://travis-ci.org/guard/guard.png)](http://travis-ci.o
Guard is a command line tool that easily handle events on files modifications.
+If you have any questions please join us on our [Google group](http://groups.google.com/group/guard-dev) or on `#guard` (irc.freenode.net).
+
Features
--------
@@ -36,6 +38,8 @@ Generate an empty Guardfile with:
$ guard init
```
+You may optionally place this Guardfile in your home directory to use it across multiple projects.
+
Add the guards you need to your Guardfile (see the existing guards below).
### On Mac OS X
@@ -52,9 +56,10 @@ Install the Growl gem if you want notification support:
$ gem install growl
```
-And add it to you Gemfile:
+And add them to your Gemfile:
``` ruby
+gem 'rb-fsevent'
gem 'growl'
```
@@ -72,9 +77,10 @@ Install the Libnotify gem if you want notification support:
$ gem install libnotify
```
-And add it to you Gemfile:
+And add them to your Gemfile:
``` ruby
+gem 'rb-inotify'
gem 'libnotify'
```
@@ -86,6 +92,19 @@ Install the rb-fchange gem for [Directory Change Notification](http://msdn.micro
$ gem install rb-fchange
```
+Install the Notifu gem if you want notification support:
+
+``` bash
+$ gem install rb-notifu
+```
+
+And add them to your Gemfile:
+
+``` ruby
+gem 'rb-fchange'
+gem 'rb-notifu'
+```
+
Usage
-----
@@ -101,6 +120,8 @@ or if you use Bundler, to run the Guard executable specific to your bundle:
$ bundle exec guard
```
+Guard will look for a Guardfile in your current directory. If it does not find one, it will look in your `$HOME` directory for one.
+
Command line options
--------------------
@@ -118,7 +139,7 @@ $ guard --notify false
$ guard -n f # shortcut
```
-Notifications can also be disabled by setting a `GUARD_NOTIFY` environment variable to `false`
+Notifications can also be disabled globally 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 `--group` (or `-g`) option:
@@ -142,6 +163,8 @@ Signal handlers are used to interact with Guard:
* `Ctrl-\` - Calls each guard's `run_all` 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.
+You can read more about [configure the signal keyboard shortcuts](https://github.com/guard/guard/wiki/Configure-keyboard-shortcuts) on the wiki.
+
Available Guards
----------------
@@ -208,6 +231,31 @@ group 'frontend' do
end
```
+The Guardfile DSL can also be used in a programmatic fashion by calling directly `Guard::Dsl.evaluate_guardfile`.
+Available options are as follow:
+
+* `:guardfile` - The path to a valid Guardfile.
+* `:guardfile_contents` - A string representing the content of a valid Guardfile
+
+Without any options given, Guard will look for a Guardfile in your current directory and if it does not find one, it will look in your `$HOME` directory for one.
+
+For instance, you could use it as follow:
+
+``` ruby
+gem 'guard'
+require 'guard'
+
+Guard.setup
+
+Guard::Dsl.evaluate_guardfile(:guardfile => '/Your/Custom/Path/To/A/Valid/Guardfile')
+# or
+Guard::Dsl.evaluate_guardfile(:guardfile_contents => "
+ guard 'rspec' do
+ watch(%r{^spec/.+_spec\.rb})
+ end
+")
+```
+
Create a new guard
------------------
@@ -258,7 +306,7 @@ module Guard
true
end
- # Called on Ctrl-/ signal
+ # Called on Ctrl-\ signal
# This method should be principally used for long action like running all specs/tests/...
def run_all
true
@@ -297,10 +345,12 @@ Development
-----------
* Source hosted at [GitHub](https://github.com/guard/guard).
-* Report Issues/Questions/Feature requests on [GitHub Issues](https://github.com/guard/guard/issues).
+* Report issues and feature requests to [GitHub Issues](https://github.com/guard/guard/issues).
Pull requests are very welcome! Make sure your patches are well tested. Please create a topic branch for every separate change
-you make.
+you make. Please do not change the version in your pull-request.
+
+For questions please join us on our [Google group](http://groups.google.com/group/guard-dev) or on `#guard` (irc.freenode.net).
Author
------
@@ -310,4 +360,4 @@ Author
Contributors
------
-https://github.com/guard/guard/contributors
+https://github.com/guard/guard/contributors
View
10 Rakefile
@@ -7,14 +7,14 @@ task :default => :spec
require 'rbconfig'
namespace(:spec) do
- if Config::CONFIG['host_os'] =~ /mswin|mingw/i
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/i
desc "Run all specs on multiple ruby versions (requires pik)"
task(:portability) do
%w[187 192 161].each do |version|
system "cmd /c echo -----------#{version}------------ & " +
"pik use #{version} & " +
"bundle install & " +
- "rake spec"
+ "bundle exec rake spec"
end
end
else
@@ -26,9 +26,9 @@ namespace(:spec) do
rvm #{version};
echo "--------- version #{version} ----------\n";
bundle install;
- rake spec'
+ bundle exec rake spec'
BASH
end
end
- end
-end
+ end
+end
View
11 guard.gemspec
@@ -1,6 +1,5 @@
# -*- encoding: utf-8 -*-
-$:.push File.expand_path('../lib', __FILE__)
-require 'guard/version'
+Kernel.load File.expand_path('../lib/guard/version.rb', __FILE__)
Gem::Specification.new do |s|
s.name = 'guard'
@@ -8,9 +7,9 @@ Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.authors = ['Thibaud Guillaume-Gentil']
s.email = ['thibaud@thibaud.me']
- s.homepage = 'http://rubygems.org/gems/guard'
- s.summary = 'Guard keep an eye on your files modifications.'
- s.description = 'Guard is a command line tool to easily handle events on files modifications.'
+ s.homepage = 'https://github.com/guard/guard'
+ s.summary = 'Guard keeps an eye on your file modifications'
+ s.description = 'Guard is a command line tool to easily handle events on file system modifications.'
s.required_rubygems_version = '>= 1.3.6'
s.rubyforge_project = 'guard'
@@ -21,7 +20,7 @@ Gem::Specification.new do |s|
s.add_dependency 'thor', '~> 0.14.6'
- s.files = Dir.glob('{bin,images,lib}/**/*') + %w[LICENSE README.markdown]
+ s.files = Dir.glob('{bin,images,lib}/**/*') + %w[LICENSE README.md CHANGELOG.md]
s.executable = 'guard'
s.require_path = 'lib'
end
View
25 lib/guard.rb
@@ -22,6 +22,7 @@ def setup(options = {})
end
def start(options = {})
+ UI.clear if options[:clear]
setup(options)
Interactor.init_signal_traps
@@ -81,15 +82,21 @@ def add_guard(name, watchers = [], options = {})
end
def get_guard_class(name)
- try_to_load_gem name
- self.const_get(self.constants.find{ |klass_name| klass_name.to_s.downcase == name.downcase })
- rescue TypeError
- UI.error "Could not find load find gem 'guard-#{name}' or find class Guard::#{name}"
- end
-
- def try_to_load_gem(name)
- require "guard/#{name.downcase}"
- rescue LoadError
+ try_require = false
+ const_name = name.to_s.downcase.gsub('-', '')
+ begin
+ require "guard/#{name.to_s.downcase}" if try_require
+ self.const_get(self.constants.find {|c| c.to_s.downcase == const_name })
+ rescue TypeError
+ unless try_require
+ try_require = true
+ retry
+ else
+ UI.error "Could not find class Guard::#{const_name.capitalize}"
+ end
+ rescue LoadError
+ UI.error "Could not load 'guard/#{name.downcase}' or find class Guard::#{const_name.capitalize}"
+ end
end
def locate_guard(name)
View
92 lib/guard/dsl.rb
@@ -1,34 +1,96 @@
module Guard
class Dsl
-
class << self
+ @@options = nil
+
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#{$!}"
+ 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)
+ guardfile_contents.match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/)
+ end
+
+ 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
- UI.error "No Guardfile in current folder, please create one."
+ if File.exist?(guardfile_default_path)
+ read_guardfile(guardfile_default_path)
+ else
+ UI.error "No Guardfile found, 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_include?(guard_name)
- File.read(guardfile_path).match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/)
+ def guardfile_contents
+ @@options ? @@options[:guardfile_contents] : ""
end
-
- def guardfile_path
- File.join(Dir.pwd, 'Guardfile')
+
+ def guardfile_contents_usable?
+ guardfile_contents && guardfile_contents.size >= 'guard :a'.size # smallest guard-definition
end
+
+ def guardfile_default_path
+ File.exist?(local_guardfile_path) ? local_guardfile_path : home_guardfile_path
+ end
+
+ private
+
+ def local_guardfile_path
+ File.join(Dir.pwd, "Guardfile")
+ end
+
+ def home_guardfile_path
+ File.expand_path(File.join("~", "Guardfile"))
+ end
+
end
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.to_s))
end
def guard(name, options = {}, &watch_definition)
View
91 lib/guard/listener.rb
@@ -1,4 +1,5 @@
require 'rbconfig'
+require 'digest/sha1'
module Guard
@@ -8,70 +9,122 @@ module Guard
autoload :Polling, 'guard/listeners/polling'
class Listener
- attr_reader :last_event, :sha1_checksums_hash
+ attr_reader :last_event, :sha1_checksums_hash, :directory, :callback
- def self.select_and_init
+ def self.select_and_init(*a)
if mac? && Darwin.usable?
- Darwin.new
+ Darwin.new(*a)
elsif linux? && Linux.usable?
- Linux.new
+ Linux.new(*a)
elsif windows? && Windows.usable?
- Windows.new
+ Windows.new(*a)
else
UI.info "Using polling (Please help us to support your system better than that.)"
- Polling.new
+ Polling.new(*a)
end
end
- def initialize
+ def initialize(directory=Dir.pwd, options={})
+ @directory = directory.to_s
@sha1_checksums_hash = {}
+ @relativate_paths = options.fetch(:relativate_paths, true)
update_last_event
end
+ def start
+ watch directory
+ end
+
+ def stop
+ end
+
+ def on_change(&callback)
+ @callback = callback
+ end
+
def update_last_event
@last_event = Time.now
end
def modified_files(dirs, options = {})
- files = potentially_modified_files(dirs, options).select { |path| File.file?(path) && file_modified?(path) && file_content_modified?(path) }
- files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
+ files = potentially_modified_files(dirs, options).select { |path| File.file?(path) && file_modified?(path) }
+ relativate_paths files
+ end
+
+ def worker
+ raise NotImplementedError, "should respond to #watch"
+ end
+
+ # register a directory to watch. must be implemented by the subclasses
+ def watch(directory)
+ raise NotImplementedError, "do whatever you want here, given the directory as only argument"
+ end
+
+ def all_files
+ potentially_modified_files [directory + '/'], :all => true
end
+ # scopes all given paths to the current #directory
+ def relativate_paths(paths)
+ return paths unless relativate_paths?
+ paths.map do |path|
+ path.gsub(%r~^#{directory}/~, '')
+ end
+ end
+
+ attr_writer :relativate_paths
+ def relativate_paths?
+ !!@relativate_paths
+ end
+
+
private
def potentially_modified_files(dirs, options = {})
match = options[:all] ? "**/*" : "*"
- Dir.glob(dirs.map { |dir| "#{dir}#{match}" })
+ Dir.glob(dirs.map { |dir| "#{dir}#{match}" }, File::FNM_DOTMATCH).select { |file| file !~ /\.\.?$/ }
end
+ # Depending on the filesystem, mtime is probably only precise to the second, so round
+ # both values down to the second for the comparison.
def file_modified?(path)
- # Depending on the filesystem, mtime is probably only precise to the second, so round
- # both values down to the second for the comparison.
- File.mtime(path).to_i >= last_event.to_i
+ if File.mtime(path).to_i == last_event.to_i
+ file_content_modified?(path, sha1_checksum(path))
+ elsif File.mtime(path).to_i > last_event.to_i
+ set_sha1_checksums_hash(path, sha1_checksum(path))
+ true
+ end
rescue
false
end
- def file_content_modified?(path)
- sha1_checksum = Digest::SHA1.file(path).to_s
+ def file_content_modified?(path, sha1_checksum)
if sha1_checksums_hash[path] != sha1_checksum
- @sha1_checksums_hash[path] = sha1_checksum
+ set_sha1_checksums_hash(path, sha1_checksum)
true
else
false
end
end
+ def set_sha1_checksums_hash(path, sha1_checksum)
+ @sha1_checksums_hash[path] = sha1_checksum
+ end
+
+ def sha1_checksum(path)
+ Digest::SHA1.file(path).to_s
+ end
+
def self.mac?
- Config::CONFIG['target_os'] =~ /darwin/i
+ RbConfig::CONFIG['target_os'] =~ /darwin/i
end
def self.linux?
- Config::CONFIG['target_os'] =~ /linux/i
+ RbConfig::CONFIG['target_os'] =~ /linux/i
end
def self.windows?
- Config::CONFIG['target_os'] =~ /mswin|mingw/i
+ RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
end
end
View
31 lib/guard/listeners/darwin.rb
@@ -2,31 +2,30 @@ module Guard
class Darwin < Listener
attr_reader :fsevent
- def initialize
+ def initialize(*)
super
@fsevent = FSEvent.new
end
- def on_change(&callback)
- @fsevent.watch Dir.pwd do |modified_dirs|
- files = modified_files(modified_dirs)
- update_last_event
- callback.call(files)
- end
+ def worker
+ @fsevent
end
def start
- @fsevent.run
+ super
+ fsevent.run
end
def stop
- @fsevent.stop
+ super
+ fsevent.stop
end
def self.usable?
require 'rb-fsevent'
- if !defined?(FSEvent::VERSION) || Gem::Version.new(FSEvent::VERSION) < Gem::Version.new('0.3.9')
- UI.info "Please update rb-fsevent (>= 0.3.9)"
+ if !defined?(FSEvent::VERSION) || (defined?(Gem::Version) &&
+ Gem::Version.new(FSEvent::VERSION) < Gem::Version.new('0.4.0'))
+ UI.info "Please update rb-fsevent (>= 0.4.0)"
false
else
true
@@ -36,5 +35,15 @@ def self.usable?
false
end
+ private
+
+ def watch(directory)
+ worker.watch directory do |modified_dirs|
+ files = modified_files(modified_dirs)
+ update_last_event
+ callback.call(files)
+ end
+ end
+
end
end
View
39 lib/guard/listeners/linux.rb
@@ -1,8 +1,8 @@
module Guard
class Linux < Listener
- attr_reader :inotify, :files, :latency, :callback
+ attr_reader :inotify, :files, :latency
- def initialize
+ def initialize(*)
super
@inotify = INotify::Notifier.new
@@ -12,28 +12,21 @@ def initialize
def start
@stop = false
+ super
watch_change unless watch_change?
end
def stop
+ super
@stop = true
sleep latency
end
- def on_change(&callback)
- @callback = callback
- inotify.watch(Dir.pwd, :recursive, :modify, :create, :delete, :move) do |event|
- unless event.name == "" # Event on root directory
- @files << event.absolute_name
- end
- end
- rescue Interrupt
- end
-
def self.usable?
require 'rb-inotify'
- if !defined?(INotify::VERSION) || Gem::Version.new(INotify::VERSION.join('.')) < Gem::Version.new('0.5.1')
- UI.info "Please update rb-inotify (>= 0.5.1)"
+ if !defined?(INotify::VERSION) || (defined?(Gem::Version) &&
+ Gem::Version.new(INotify::VERSION.join('.')) < Gem::Version.new('0.8.5'))
+ UI.info "Please update rb-inotify (>= 0.8.5)"
false
else
true
@@ -49,10 +42,23 @@ def watch_change?
private
+ def worker
+ @inotify
+ end
+
+ def watch(directory)
+ worker.watch(directory, :recursive, :modify, :create) do |event|
+ unless event.name == "" # Event on root directory
+ @files << event.absolute_name
+ end
+ end
+ rescue Interrupt
+ end
+
def watch_change
@watch_change = true
until @stop
- if Config::CONFIG['build'] =~ /java/ || IO.select([inotify.to_io], [], [], latency)
+ if RbConfig::CONFIG['build'] =~ /java/ || IO.select([inotify.to_io], [], [], latency)
break if @stop
sleep latency
@@ -61,8 +67,7 @@ def watch_change
unless files.empty?
files.uniq!
- files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
- callback.call(files)
+ callback.call( relativate_paths(files) )
files.clear
end
end
View
16 lib/guard/listeners/polling.rb
@@ -1,22 +1,20 @@
module Guard
class Polling < Listener
- attr_reader :callback, :latency
+ attr_reader :latency
- def initialize
+ def initialize(*)
super
@latency = 1.5
end
- def on_change(&callback)
- @callback = callback
- end
-
def start
@stop = false
+ super
watch_change
end
def stop
+ super
@stop = true
end
@@ -27,11 +25,15 @@ def watch_change
start = Time.now.to_f
files = modified_files([Dir.pwd + '/'], :all => true)
update_last_event
- callback.call(files) unless files.empty?
+ callback.call(relativate_paths(files)) unless files.empty?
nap_time = latency - (Time.now.to_f - start)
sleep(nap_time) if nap_time > 0
end
end
+ def watch(directory)
+ @existing = all_files
+ end
+
end
end
View
26 lib/guard/listeners/windows.rb
@@ -7,20 +7,13 @@ def initialize
@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
+ super
@fchange.run
end
def stop
+ super
@fchange.stop
end
@@ -32,5 +25,20 @@ def self.usable?
false
end
+ private
+
+ def worker
+ @fchange
+ end
+
+ def watch(directory)
+ worker.watch(directory, :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
+
end
end
View
41 lib/guard/notifier.rb
@@ -1,5 +1,6 @@
require 'rbconfig'
require 'pathname'
+require 'guard/ui'
module Guard
module Notifier
@@ -10,7 +11,7 @@ def self.turn_off
def self.turn_on
ENV["GUARD_NOTIFY"] = 'true'
- case Config::CONFIG['target_os']
+ case RbConfig::CONFIG['target_os']
when /darwin/i
require_growl
when /linux/i
@@ -22,18 +23,16 @@ def self.turn_on
def self.notify(message, options = {})
if enabled?
- image = options[:image] || :success
- title = options[:title] || "Guard"
- case Config::CONFIG['target_os']
+ image = options.delete(:image) || :success
+ title = options.delete(:title) || "Guard"
+
+ case RbConfig::CONFIG['target_os']
when /darwin/i
- require_growl # need for guard-rspec formatter that is called out of guard scope
- Growl.notify message, :title => title, :icon => image_path(image), :name => "Guard"
+ notify_mac(title, message, image, options)
when /linux/i
- require_libnotify # need for guard-rspec formatter that is called out of guard scope
- Libnotify.show :body => message, :summary => title, :icon_path => image_path(image)
+ notify_linux(title, message, image, options)
when /mswin|mingw/i
- require_rbnotifu
- Notifu.show :message => message, :title => title, :type => image_level(image), :time => 3
+ notify_windows(title, message, image, options)
end
end
end
@@ -44,6 +43,24 @@ def self.enabled?
private
+ def self.notify_mac(title, message, image, options)
+ require_growl # need for guard-rspec formatter that is called out of guard scope
+ default_options = { :title => title, :icon => image_path(image), :name => "Guard" }
+ Growl.notify message, default_options.merge(options) if enabled?
+ end
+
+ def self.notify_linux(title, message, image, options)
+ require_libnotify # need for guard-rspec formatter that is called out of guard scope
+ default_options = { :body => message, :summary => title, :icon_path => image_path(image) }
+ Libnotify.show default_options.merge(options) if enabled?
+ end
+
+ def self.notify_windows(title, message, image, options)
+ require_rbnotifu # need for guard-rspec formatter that is called out of guard scope
+ default_options = { :message => message, :title => title, :type => image_level(image), :time => 3 }
+ Notifu.show default_options.merge(options) if enabled?
+ end
+
def self.image_path(image)
images_path = Pathname.new(File.dirname(__FILE__)).join('../../images')
case image
@@ -71,7 +88,7 @@ def self.image_level(image)
:info
end
end
-
+
def self.require_growl
require 'growl'
rescue LoadError
@@ -93,4 +110,4 @@ def self.require_rbnotifu
UI.info "Please install rb-notifu gem for Windows notification support and add it to your Gemfile"
end
end
-end
+end
View
2  lib/guard/ui.rb
@@ -50,7 +50,7 @@ def color(text, color_code)
end
def color_enabled?
- @color_enabled ||= if Config::CONFIG['target_os'] =~ /mswin|mingw/i
+ @color_enabled ||= if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
unless ENV['ANSICON']
begin
require 'rubygems' unless ENV['NO_RUBYGEMS']
View
2  lib/guard/version.rb
@@ -1,3 +1,3 @@
module Guard
- VERSION = "0.3.4"
+ VERSION = "0.4.2" unless defined? Guard::VERSION
end
View
2  lib/guard/watcher.rb
@@ -59,4 +59,4 @@ def call_action(matches)
end
end
-end
+end
View
0  spec/fixtures/.dotfile
No changes.
View
283 spec/guard/dsl_spec.rb
@@ -1,123 +1,290 @@
require 'spec_helper'
describe Guard::Dsl do
- subject { Guard::Dsl }
-
+ subject { described_class }
before(:each) do
- ::Guard.stub!(:add_guard)
+ @local_guardfile_path = File.join(Dir.pwd, 'Guardfile')
+ @home_guardfile_path = File.expand_path(File.join("~", "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
+
+ 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(@local_guardfile_path, "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(@local_guardfile_path, "guard :bar")
- Guard::UI.should_receive(:error).with("No Guardfile in current folder, please create one.")
+ 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(@local_guardfile_path, "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 no Guardfile is found" do
+ subject.stub(:guardfile_default_path).and_return("no_guardfile_here")
+ Guard::UI.should_receive(:error).with("No Guardfile found, please create one with `guard init`.")
lambda { subject.evaluate_guardfile }.should raise_error
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
+
+ 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(@local_guardfile_path) { false }
+ File.stub!(:exist?).with(@home_guardfile_path) { false }
+
+ Guard::UI.should_receive(:error).with("No Guardfile found, please create one with `guard init`.")
+ 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_default_path" do
+ let(:local_path) { File.join(Dir.pwd, 'Guardfile') }
+ let(:user_path) { File.expand_path(File.join("~", 'Guardfile')) }
+
+ before do
+ File.stub(:exist? => false)
+ end
+
+ context "when there is a local Guardfile" do
+ it "returns the path to the local Guardfile" do
+ File.stub(:exist?).with(local_path).and_return(true)
+ subject.guardfile_default_path.should == local_path
+ end
+ end
+
+ context "when there is a Guardfile in the user's home directory" do
+ it "returns the path to the user Guardfile" do
+ File.stub(:exist?).with(user_path).and_return(true)
+ subject.guardfile_default_path.should == user_path
+ end
+ end
+
+ context "when there's both a local and user Guardfile" do
+ it "returns the path to the local Guardfile" do
+ File.stub(:exist?).with(local_path).and_return(true)
+ File.stub(:exist?).with(user_path).and_return(true)
+ subject.guardfile_default_path.should == local_path
+ end
+ end
- Guard::UI.should_receive(:error).with(/Invalid Guardfile, original error is:\n/)
- lambda { subject.evaluate_guardfile }.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")
+ it "should evaluates only the specified string group" do
+ ::Guard.should_receive(:add_guard).with('test', anything, {})
+ lambda { subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => ['w']) }.should_not raise_error
end
-
- it "evaluates only the specified group" do
+ it "should evaluates only the specified symbol 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
- end
- it "loads a Guard specified as a symbol from the DSL" do
- mock_guardfile_content("guard :test")
+ subject.evaluate_guardfile(:guardfile_contents => "guard 'test'")
+ end
+ 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 fake_guardfile(name, contents)
+ File.stub!(:exist?).with(name) { true }
+ File.stub!(:read).with(name) { contents }
+ end
+
+ def valid_guardfile_string
+ "group 'w' do
+ guard 'test' do
+ watch('c')
+ end
+ end
+
+ 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 mock_guardfile_content(content)
- File.stub!(:read).with(File.expand_path('../../../Guardfile', __FILE__)) { content }
+ File.stub!(:read).with(subject.guardfile_default_path) { content }
end
+ def invalid_guardfile_string
+ "Bad Guardfile"
+ end
end
View
81 spec/guard/listener_spec.rb
@@ -4,29 +4,62 @@
subject { Guard::Listener }
describe ".select_and_init" do
- before(:each) { @target_os = Config::CONFIG['target_os'] }
- after(:each) { Config::CONFIG['target_os'] = @target_os }
+ before(:each) { @target_os = RbConfig::CONFIG['target_os'] }
+ after(:each) { RbConfig::CONFIG['target_os'] = @target_os }
it "uses the Darwin listener on Mac OS X" do
- Config::CONFIG['target_os'] = 'darwin10.4.0'
+ RbConfig::CONFIG['target_os'] = 'darwin10.4.0'
Guard::Darwin.stub(:usable?).and_return(true)
Guard::Darwin.should_receive(:new)
subject.select_and_init
end
it "uses the Windows listener on Windows" do
- Config::CONFIG['target_os'] = 'mingw'
+ RbConfig::CONFIG['target_os'] = 'mingw'
Guard::Windows.stub(:usable?).and_return(true)
Guard::Windows.should_receive(:new)
subject.select_and_init
end
it "uses the Linux listener on Linux" do
- Config::CONFIG['target_os'] = 'linux'
+ RbConfig::CONFIG['target_os'] = 'linux'
Guard::Linux.stub(:usable?).and_return(true)
Guard::Linux.should_receive(:new)
subject.select_and_init
end
+
+ it "forwards its arguments to the constructor" do
+ subject.stub!(:mac?).and_return(true)
+ Guard::Darwin.stub!(:usable?).and_return(true)
+
+ path, opts = 'path', {:foo => 23}
+ Guard::Darwin.should_receive(:new).with(path, opts).and_return(true)
+ subject.select_and_init path, opts
+ end
+ end
+
+ describe "#all_files" do
+ subject { described_class.new(@fixture_path) }
+
+ it "should return all files" do
+ subject.all_files.should =~ Dir.glob("#{@fixture_path}/**/*", File::FNM_DOTMATCH).select { |file| file !~ /\.\.?$/ }
+ end
+ end
+
+ describe "#relativate_paths" do
+ subject { described_class.new('/tmp') }
+ before :each do
+ @paths = %w( /tmp/a /tmp/a/b /tmp/a.b/c.d )
+ end
+
+ it "should relativate paths to the configured directory" do
+ subject.relativate_paths(@paths).should =~ %w( a a/b a.b/c.d )
+ end
+
+ it "can be disabled" do
+ subject.relativate_paths = false
+ subject.relativate_paths(@paths).should == @paths
+ end
end
describe "#update_last_event" do
@@ -69,7 +102,7 @@
it "ignores the files for the second time" do
FileUtils.touch([file1, file2, file3])
subject.modified_files([@fixture_path.join("folder1/")], {}).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt"]
- sleep 1
+ subject.update_last_event
FileUtils.touch([file1, file2, file3])
subject.modified_files([@fixture_path.join("folder1/")], {}).should == []
sleep 1
@@ -82,7 +115,7 @@
it "identifies the files for the second time" do
FileUtils.touch([file1, file2, file3])
subject.modified_files([@fixture_path.join("folder1/")], {}).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt"]
- sleep 1
+ subject.update_last_event
FileUtils.touch([file2, file3])
File.open(file1, "w") { |f| f.write("changed content") }
subject.modified_files([@fixture_path.join("folder1/")], {}).should =~ ["spec/fixtures/folder1/file1.txt"]
@@ -91,4 +124,38 @@
end
end
+ describe "working directory" do
+
+ context "unspecified" do
+ subject { described_class.new }
+ it "defaults to Dir.pwd" do
+ subject.directory.should == Dir.pwd
+ end
+ it "can be not changed" do
+ subject.should_not respond_to(:directory=)
+ end
+ end
+
+ context "specified as first argument to ::new" do
+ before :each do
+ @wd = @fixture_path.join("folder1")
+ end
+ subject { described_class.new @wd }
+ it "can be inspected" do
+ subject.directory.should == @wd.to_s
+ end
+ it "can be not changed" do
+ subject.should_not respond_to(:directory=)
+ end
+
+ it "will be used to watch" do
+ subject.should_receive(:watch).with(@wd.to_s)
+ @listener = subject # indeed.
+ start
+ stop
+ end
+ end
+
+ end
+
end
View
62 spec/guard/listeners/darwin_spec.rb
@@ -4,6 +4,12 @@
describe Guard::Darwin do
subject { Guard::Darwin }
+ if windows?
+ it "isn't usable on windows" do
+ subject.should_not be_usable
+ end
+ end
+
if linux?
it "isn't usable on linux" do
subject.should_not be_usable
@@ -15,61 +21,9 @@
subject.should be_usable
end
- describe "#on_change" do
- before(:each) do
- @results = []
- @listener = Guard::Darwin.new
- @listener.on_change do |files|
- @results += files
- end
- end
-
- it "catches a new file" do
- file = @fixture_path.join("newfile.rb")
- File.exists?(file).should be_false
- start
- FileUtils.touch file
- stop
- File.delete file
- @results.should == ['spec/fixtures/newfile.rb']
- end
-
- it "catches a single 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 multiple file updates" 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 1
- @listener.update_last_event
- Thread.new { @listener.start }
- sleep 1
- end
+ it_should_behave_like "a listener that reacts to #on_change"
+ it_should_behave_like "a listener scoped to a specific directory"
- def stop
- sleep 1
- @listener.stop
- sleep 1
end
end
View
103 spec/guard/listeners/linux_spec.rb
@@ -11,12 +11,18 @@
end
end
+ if windows?
+ it "isn't usable on windows" do
+ subject.should_not be_usable
+ end
+ end
+
if linux? && Guard::Linux.usable?
it "is usable on linux" do
subject.should be_usable
end
- describe "#start" do
+ describe "#start", :long_running => true do
before(:each) do
@listener = Guard::Linux.new
end
@@ -37,95 +43,24 @@
stop
@listener.should_not be_watch_change
end
-
end
- describe "#on_change" do
- before(:each) do
- @results = []
- @listener = Guard::Linux.new
- @listener.on_change do |files|
- @results += files
- end
- end
-
- it "catches a new file" do
- file = @fixture_path.join("newfile.rb")
- File.exists?(file).should be_false
- start
- FileUtils.touch file
- stop
- File.delete file
- @results.should == ['spec/fixtures/newfile.rb']
- end
-
- it "catches a single file update" do
- file = @fixture_path.join("folder1/file1.txt")
- File.exists?(file).should be_true
- start
- File.open(file, 'w') {|f| f.write('') }
- stop
- @results.should == ['spec/fixtures/folder1/file1.txt']
- end
-
- it "catches multiple file updates" 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
- 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']
- end
+ it_should_behave_like "a listener that reacts to #on_change"
+ it_should_behave_like "a listener scoped to a specific directory"
- it "catches a deleted file" do
- file = @fixture_path.join("folder1/file1.txt")
- File.exists?(file).should be_true
- start
- File.delete file
- stop
- FileUtils.touch file
- @results.should == ['spec/fixtures/folder1/file1.txt']
- end
+ # Fun fact: File.touch seems not to be enough on Linux to trigger a modify event
- it "catches a moved file" do
- file1 = @fixture_path.join("folder1/file1.txt")
- file2 = @fixture_path.join("folder1/movedfile1.txt")
- File.exists?(file1).should be_true
- File.exists?(file2).should be_false
- start
- FileUtils.mv file1, file2
- stop
- FileUtils.mv file2, file1
- @results.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/movedfile1.txt']
- end
-
- it "doesn't process a change when it is stopped" do
- file = @fixture_path.join("folder1/file1.txt")
- File.exists?(file).should be_true
- start
- @listener.inotify.should_not_receive(:process)
- stop
- File.open(file, 'w') {|f| f.write('') }
- end
+ it "doesn't process a change when it is stopped" do
+ @listener = described_class.new
+ record_results
+ file = @fixture_path.join("folder1/file1.txt")
+ File.exists?(file).should be_true
+ start
+ @listener.inotify.should_not_receive(:process)
+ stop
+ File.open(file, 'w') {|f| f.write('') }
end
- end
-
-private
-
- def start
- sleep 1
- @listener.update_last_event
- Thread.new { @listener.start }
- sleep 1
- end
- def stop
- sleep 1
- @listener.stop
- sleep 1
end
end
View
57 spec/guard/listeners/polling_spec.rb
@@ -3,60 +3,9 @@
describe Guard::Polling do
- before(:each) do
- @results = []
- @listener = Guard::Polling.new
- @listener.on_change do |files|
- @results += files
- end
- end
+ subject { Guard::Polling }
- describe "#on_change" do
- it "catches a new file" do
- file = @fixture_path.join("newfile.rb")
- File.exists?(file).should be_false
- start
- FileUtils.touch file
- stop
- File.delete file
- @results.should == ['spec/fixtures/newfile.rb']
- end
-
- it "catches a single 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 multiple file updates" 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
-
-private
-
- def start
- sleep 1
- @listener.update_last_event
- Thread.new { @listener.start }
- sleep 1
- end
-
- def stop
- sleep 1
- @listener.stop
- sleep 1
- end
+ it_should_behave_like "a listener that reacts to #on_change"
+ it_should_behave_like "a listener scoped to a specific directory"
end
View
65 spec/guard/listeners/windows_spec.rb
@@ -21,70 +21,9 @@
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_should_behave_like "a listener that reacts to #on_change"
+ it_should_behave_like "a listener scoped to a specific directory"
- it "catches a 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 a single 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 multiple file updates" 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 1
- @listener.update_last_event
- Thread.new { @listener.start }
- sleep 1
- end
-
- def stop
- sleep 1
- @listener.stop
- sleep 1
end
end
View
263 spec/guard/notifier_spec.rb
@@ -3,71 +3,244 @@
describe Guard::Notifier do
subject { Guard::Notifier }
- describe ".notify" do
- before(:each) { subject.turn_on }
- after(:each) { subject.turn_off }
-
- if mac?
- if growl_installed?
- 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'
+ describe ".turn_off" do
+ before do
+ ENV["GUARD_NOTIFY"] = 'true'
+ subject.turn_off
+ end
+
+ it "disables the notifications" do
+ ENV["GUARD_NOTIFY"].should eql 'false'
+ end
+ end
+
+ describe ".turn_on" do
+ context "on Mac OS" do
+ before do
+ RbConfig::CONFIG.should_receive(:[]).with('target_os').and_return 'darwin'
+ end
+
+ context "with the Growl library available" do
+ it "loads the library and enables the notifications" do
+ subject.should_receive(:require).with('growl').and_return true
+ subject.turn_on
+ subject.should be_enabled
end
- else
- it { should_not be_enabled }
end
- end
- if linux?
- if libnotify_installed?
- 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'
+ context "without the Growl library available" do
+ it "disables the notifications" do
+ subject.should_receive(:require).with('growl').and_raise LoadError
+ subject.turn_on
+ subject.should_not be_enabled
end
- else
- it { should_not be_enabled }
end
end
-
- if windows?
- if rbnotifu_installed?
- it "uses rb-notifu on Windows" do
- @result = -1
- Notifu::show :message => "great", :title => 'Guard' do |status|
- @result = status
- end
- sleep 1.5
- Notifu::ERRORS.include?(@result).should be_false
+
+ context "on Linux" do
+ before do
+ RbConfig::CONFIG.should_receive(:[]).with('target_os').and_return 'linux'
+ end
+
+ context "with the Libnotify library available" do
+ it "loads the library and enables the notifications" do
+ subject.should_receive(:require).with('libnotify').and_return true
+ subject.turn_on
+ subject.should be_enabled
+ end
+ end
+
+ context "without the Libnotify library available" do
+ it "disables the notifications" do
+ subject.should_receive(:require).with('libnotify').and_raise LoadError
+ subject.turn_on
+ subject.should_not be_enabled
end
- else
- it { should_not be_enabled }
end
end
+ context "on Windows" do
+ before do
+ RbConfig::CONFIG.should_receive(:[]).with('target_os').and_return 'mswin'
+ end
+
+ context "with the rb-notifu library available" do
+ it "loads the library and enables the notifications" do
+ subject.should_receive(:require).with('rb-notifu').and_return true
+ subject.turn_on
+ subject.should be_enabled
+ end
+ end
+
+ context "without the rb-notify library available" do
+ it "disables the notifications" do
+ subject.should_receive(:require).with('rb-notifu').and_raise LoadError
+ subject.turn_on
+ subject.should_not be_enabled
+ end
+ end
+ end
end
- describe ".turn_off" do
- if mac? && growl_installed?
- it "prevents the notifications" do
+ describe ".notify" do
+ before { subject.stub(:enabled?).and_return(true) }
+
+ context "on Mac OS" do
+ before do
+ RbConfig::CONFIG.should_receive(:[]).with('target_os').and_return 'darwin'
+ subject.stub(:require_growl)
+ Object.send(:remove_const, :Growl) if defined?(Growl)
+ Growl = Object.new
+ end
+
+ after do
+ Object.send(:remove_const, :Growl)
+ end
+
+ it "passes the notification to Growl" 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'
+ end
+
+ it "don't passes the notification to Growl if library is not available" do
Growl.should_not_receive(:notify)
+ subject.should_receive(:enabled?).and_return(true, false)
subject.notify 'great', :title => 'Guard'
end
- elsif linux? && libnotify_installed?
- it "prevents the notifications" do
+
+ it "allows additional notification options" do
+ Growl.should_receive(:notify).with("great",
+ :title => "Guard",
+ :icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
+ :name => "Guard",
+ :priority => 1
+ )
+ subject.notify 'great', :title => 'Guard', :priority => 1
+ end
+
+ it "allows to overwrite a default notification option" do
+ Growl.should_receive(:notify).with("great",
+ :title => "Guard",
+ :icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
+ :name => "Guard-Cucumber"
+ )
+ subject.notify 'great', :title => 'Guard', :name => "Guard-Cucumber"
+ end
+ end