Compare commits
3 Commits
master
...
guard_opti
Author | SHA1 | Date | |
---|---|---|---|
|
56d4914ce1 | ||
|
c52139b396 | ||
|
973c6ea1b4 |
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,13 +1,6 @@
|
||||
pkg/*
|
||||
doc/*
|
||||
*.gem
|
||||
*.rbc
|
||||
.*.swp
|
||||
*.bak
|
||||
.bundle
|
||||
.yardoc
|
||||
.rbx
|
||||
.rvmrc
|
||||
Gemfile.lock
|
||||
|
||||
## MAC OS
|
||||
@ -16,4 +9,4 @@ Gemfile.lock
|
||||
.com.apple.timemachine.supported
|
||||
.fseventsd
|
||||
Desktop DB
|
||||
Desktop DF
|
||||
Desktop DF
|
19
.travis.yml
19
.travis.yml
@ -1,19 +0,0 @@
|
||||
rvm:
|
||||
- 1.8.7
|
||||
- 1.9.2
|
||||
- ree
|
||||
- jruby
|
||||
- rbx
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- guard_dependencies
|
||||
env:
|
||||
- GUARD_SLEEP=1
|
||||
notifications:
|
||||
recipients:
|
||||
- thibaud@thibaud.me
|
||||
- rymai@rymai.me
|
||||
- michi@netzpiraten.ch
|
||||
- yann.lugrin@sans-savoir.net
|
||||
irc: "irc.freenode.org#guard"
|
11
.yardopts
11
.yardopts
@ -1,11 +0,0 @@
|
||||
--title 'Guard Documentation'
|
||||
--readme README.md
|
||||
--markup markdown
|
||||
--markup-provider kramdown
|
||||
--private
|
||||
--protected
|
||||
--output-dir ./doc
|
||||
lib/**/*.rb
|
||||
-
|
||||
CHANGELOG.md
|
||||
LICENSE
|
347
CHANGELOG.md
347
CHANGELOG.md
@ -1,347 +0,0 @@
|
||||
## Master
|
||||
|
||||
### Improvements
|
||||
|
||||
- Add cli option (-i / --no-interactions) to turn off Guard terminal interactions. ([@thibaudgg][])
|
||||
- Add support for Growl Notification Transport Protocol. ([@netzpirat][])
|
||||
- [#157](https://github.com/guard/guard/pull/157): Allow any return from the Guard watchers. ([@earlonrails][])
|
||||
- [#156](https://github.com/guard/guard/pull/156): Log error and diagnostic messages to STDERR. ([@sunaku][])
|
||||
- [#152](https://github.com/guard/guard/pull/152): Growl Notify API update for a graceful fail. ([@scottdavis][])
|
||||
|
||||
### Bug fix
|
||||
|
||||
- [#149](https://github.com/guard/guard/issues/160): Avoid `Guard is not missing constant ...` exceptions. (reported by [@earlonrails][], fixed by [@netzpirat][])
|
||||
|
||||
## 0.8.4 - October 3, 2011
|
||||
|
||||
### Bug fix
|
||||
|
||||
- [#149](https://github.com/guard/guard/issues/149) & [#150](https://github.com/guard/guard/pull/150): Fix issue where interator thread was continuing to capture input from stdin while a guard is being executed. (reported by [@hardipe][], fixed by [@f1sherman][])
|
||||
|
||||
## 0.8.3 - October 1, 2011
|
||||
|
||||
### Bug fix
|
||||
|
||||
- [#145](https://github.com/guard/guard/pull/145): Fix over-utilization of CPU in Interactor. ([@johnbintz][])
|
||||
|
||||
### Improvements
|
||||
|
||||
- [#146](https://github.com/guard/guard/pull/146): Use a mutex instead of a lock for more efficient/simple locking. ([@f1sherman][])
|
||||
- Make Guard implementation of `:task_has_failed` simple. ([@netzpirat][])
|
||||
|
||||
## 0.8.2 - September 30, 2011
|
||||
|
||||
### Bug fix
|
||||
|
||||
- Fixed guard stop to prevent run_guard_task(:stop) to be skipped [guard-spork issue #28](https://github.com/guard/guard-spork/issues/28). ([@thibaudgg][])
|
||||
|
||||
### Improvement
|
||||
|
||||
- Update docs regarding :task_has_failed. ([@netzpirat][])
|
||||
|
||||
## 0.8.1 - September 29, 2011
|
||||
|
||||
### Bug fix
|
||||
|
||||
- [#144](https://github.com/guard/guard/pull/144): Fix `guard init`. (reported by [@fabioyamate][], fixed by [@rymai][])
|
||||
|
||||
## 0.8.0 - September 28, 2011
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [#137](https://github.com/guard/guard/pull/137): Fix interacting with tools like ruby-debug. ([@hron][] & [@netzpirat][])
|
||||
- [#138](https://github.com/guard/guard/pull/138): Fixed comments in example scaffold to reference interactions. ([@rmm5t][] & [@netzpirat][])
|
||||
|
||||
### New feature
|
||||
|
||||
- [#136](https://github.com/guard/guard/pull/136): New CLI `:watch_all_modifications`/`-A` option to watch for deleted and moved files too. ([@limeyd][] & [@netzpirat][])
|
||||
- [#97](https://github.com/guard/guard/issues/97): Guard dependencies. Task execution can now be halted if a Guard throws `:task_has_failed` and `Guard::Dsl#group` options include `:halt_on_fail => true`. ([@rymai][])
|
||||
- [#121](https://github.com/guard/guard/issues/121): `Guard.guards` and `Guard.groups` are now smart accessors. Filters can be passed to find a specific Guard/group or several Guards/groups that match (see YARDoc). ([@rymai][] & [@ches][])
|
||||
- New `Guard::Group` class to store groups defined in Guardfile (with `Guard::Dsl#group`). ([@rymai][])
|
||||
|
||||
### Improvements
|
||||
|
||||
- Specs refactoring. ([@netzpirat][])
|
||||
- Full YARD documentation. ([@netzpirat][] & a little of [@rymai][])
|
||||
|
||||
## 0.7.0 - September 14, 2011
|
||||
|
||||
## 0.7.0.rc1 - September 5, 2011
|
||||
|
||||
### Major Changes
|
||||
|
||||
- Posix Signals handlers (`Ctrl-C`, `Ctrl-\` and `Ctrl-Z`) are no more supported and replaced by `$stdin.gets`. Please refer to the "Interactions" section in the README for more information. ([@thibaudgg][])
|
||||
- JRuby & Rubinius support (beta). ([@thibaudgg][] & [@netzpirat][])
|
||||
|
||||
### New features
|
||||
|
||||
- [#42](https://github.com/guard/guard/pull/42): New DSL method: `callback` allows you to execute arbitrary code before or after any of the `start`, `stop`, `reload`, `run_all` and `run_on_change` guards' method. New [Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for documenting it. ([@monocle][] & [@rymai][])
|
||||
- Ability to 'pause' files modification listening. Please refer to the "Interactions" section in the README for more information. ([@thibaudgg][])
|
||||
|
||||
### Improvement
|
||||
|
||||
- Remove the need to scan the whole directory after guard's `run_on_change` method. ([@thibaudgg][])
|
||||
|
||||
## 0.6.3 - September 1, 2011
|
||||
|
||||
### New features
|
||||
|
||||
- [#130](https://github.com/guard/guard/pull/130): Adds `ignore_paths` method to DSL. ([@ianwhite][])
|
||||
- [#128](https://github.com/guard/guard/pull/128): Users can add additional settings to `~/.guard.rb` that augment the existing Guardfile. ([@tpope][])
|
||||
|
||||
## 0.6.2 - August 17, 2011
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Re-add the possibility to use the `growl` gem since the `growl_notify` gem this is currently known to not work in conjunction with Spork. ([@netzpirat][])
|
||||
- Ensure that scoped groups and group name are symbolized before checking for inclusion. ([@rymai][])
|
||||
|
||||
### New features
|
||||
|
||||
- Groups are now stored in a `@groups` variable (will be used for future features). ([@rymai][])
|
||||
- Guards will now receive their group in the options hash at initialization (will be used for future features). ([@rymai][])
|
||||
|
||||
### Improvement
|
||||
|
||||
- Explain the growl/growl_notify differences in the README. ([@netzpirat][])
|
||||
|
||||
## 0.6.1 - August 15, 2011
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [#120](https://github.com/guard/guard/pull/120): remove `guardfile_contents` when re-evaluating so that the Guardfile gets reloaded correctly. ([@mordaroso][])
|
||||
- [#119](https://github.com/guard/guard/pull/119): `Dsl.evaluate_guardfile` uses all groups if none specified. ([@ches][])
|
||||
|
||||
## 0.6.0 - August 13, 2011
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Pull request [#107](https://github.com/guard/guard/pull/107): Small spelling fix. ([@dnagir][])
|
||||
- `Dir.glob` now ignores files that don't need to be watched. ([@rymai][])
|
||||
|
||||
### New feature
|
||||
|
||||
- Pull request [#112](https://github.com/guard/guard/pull/112): Add `list` command to CLI. ([@docwhat][])
|
||||
|
||||
### Improvements
|
||||
|
||||
- [#99](https://github.com/guard/guard/pull/99): [OS X] Switch from growl gem to growl_notify gem. ([@johnbintz][])
|
||||
- [#115](https://github.com/guard/guard/pull/115): [Linux] Add `:transient => true` to default libnotify options. ([@zonque][])
|
||||
- [#95](https://github.com/guard/guard/pull/95): Output system commands and options to be executed when in debug mode. ([@uk-ar][] and [@netzpirat][])
|
||||
- `Guard::Dsl.revaluate_guardfile` has been renamed to `Guard::Dsl.reevaluate_guardfile`. ([@rymai][])
|
||||
- New CLI options: ([@nestegg][])
|
||||
- `watchdir`/`-w` to specify the directory in which Guard should watch for changes,
|
||||
- `guardfile`/`-G` to specify an alternate location for the Guardfile to use.
|
||||
- [#90](https://github.com/guard/guard/pull/90): Refactoring of color handling in the `Guard::UI`. ([@stereobooster][])
|
||||
|
||||
## 0.5.1 - July 2, 2011
|
||||
|
||||
### Bug fix
|
||||
|
||||
- Fixed `guard show` command. ([@bronson][] & [@thibaudgg][])
|
||||
|
||||
## 0.5.0 - July 2, 2011
|
||||
|
||||
### New features
|
||||
|
||||
- Guard::Ego is now part of Guard, so Guardfile is automagically re-evaluated when modified. ([@thibaudgg][])
|
||||
- [#91](https://github.com/guard/guard/pull/91): Show Guards in Guardfile with the `guard -T`. ([@johnbintz][])
|
||||
|
||||
### Improvements
|
||||
|
||||
- [#98](https://github.com/guard/guard/issues/98): Multiple calls per watch event on linux with rb-inotify. ([@jeffutter][] & [@netzpirat][])
|
||||
- [#94](https://github.com/guard/guard/pull/94): Show backtrace in terminal when a problem with a watch action occurs. ([@capotej][])
|
||||
- [#88](https://github.com/guard/guard/pull/88): Write exception trace in the terminal when a supervised task fail. ([@mcmire][])
|
||||
- Color in red the "ERROR:" flag when using `UI.error`. ([@rymai][])
|
||||
- [#79](https://github.com/guard/guard/issues/79) and Pull request [#82](https://github.com/guard/guard/pull/82): Improve INotify support on Linux. ([@Gazer][] & [@yannlugrin][])
|
||||
- [#12](https://github.com/guard/guard/issues/12) and Pull request [#86](https://github.com/guard/guard/pull/86): Eventually exits with SystemStackError. ([@stereobooster][])
|
||||
- [#84](https://github.com/guard/guard/pull/84): Use RbConfig instead of obsolete and deprecated Config. ([@etehtsea][])
|
||||
- [#80](https://github.com/guard/guard/pull/80): Watching dotfile (hidden files under unix). (reported by [@chrisberkhout][], fixed by [@yannlugrin][])
|
||||
- Clear the terminal on start when the `:clear` option is given. ([@rymai][])
|
||||
- Rename home directory Guardfile to `.Guardfile`. ([@tpope][])
|
||||
|
||||
## 0.4.2 - June 7, 2011
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Fixed Guard::Version in ruby 1.8.7 ([@thibaudgg][])
|
||||
- Fix ([@mislav][]) link in CHANGELOG (Note: this is a recursive CHANGELOG item). ([@fnichol][])
|
||||
|
||||
## 0.4.1 - June 7, 2011
|
||||
|
||||
### Improvements
|
||||
|
||||
- [#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
|
||||
|
||||
### Bug fix
|
||||
|
||||
- In Ruby < 1.9, `Symbol#downcase` doesn't exist! ([@rymai][])
|
||||
|
||||
### New features
|
||||
|
||||
- [#73](https://github.com/guard/guard/pull/73): Allow DSL's `group` method to accept a Symbol as group name. ([@johnbintz][])
|
||||
- [#51](https://github.com/guard/guard/pull/51): Allow options (like `:priority`) to be passed through to the Notifier. ([@indirect][] & [@netzpirat][])
|
||||
|
||||
### Improvement
|
||||
|
||||
- [#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
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [#69](https://github.com/guard/guard/pull/69): Fixed typo in README: `Ctr-/` => `Ctr-\`. ([@tinogomes][])
|
||||
- [#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][] & [@netzpirat][])
|
||||
- Fixed `Guard::Notifier` (when growl/libnotify not present). ([@thibaudgg][])
|
||||
- Fixed Rubygems deprecation messages. ([@thibaudgg][])
|
||||
|
||||
### New features
|
||||
|
||||
- [#67](https://github.com/guard/guard/pull/67): Allow Guardfile in `$HOME` folder. ([@hashrocketeer][])
|
||||
- [#64](https://github.com/guard/guard/pull/64): Windows notifications support. ([@stereobooster][])
|
||||
- [#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][])
|
||||
- [#60](https://github.com/guard/guard/pull/60): Added Windows support. ([@stereobooster][])
|
||||
- [#58](https://github.com/guard/guard/pull/58): Extract code from signal handlers into methods. ([@nicksieger][])
|
||||
- [#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
|
||||
|
||||
### Bug fix
|
||||
|
||||
- [#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][])
|
||||
- [#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
|
||||
|
||||
### Bug fix
|
||||
|
||||
- Fixed `new_modified_files` rerun conditions on `Guard.run_on_change_for_all_guards`. ([@thibaudgg][])
|
||||
|
||||
## 0.3.2 - April 17, 2011
|
||||
|
||||
### Bug fixe
|
||||
|
||||
- [#43](https://github.com/guard/guard/pull/43): Fixed `guard init` command. ([@brainopia][])
|
||||
|
||||
## 0.3.1 - April 14, 2011
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Return unique filenames from Linux listener. (Marian Schubert)
|
||||
- `Guard.get_guard_class` return wrong class when loaded nested class. ([@koshigoe][])
|
||||
- [#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][])
|
||||
- [#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 feature
|
||||
|
||||
- [#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
|
||||
|
||||
### Bug fix
|
||||
|
||||
- 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][])
|
||||
- [#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][])
|
||||
- [#13](https://github.com/guard/guard/pull/13): `Guard::Dsl`. ([@oliamb][])
|
||||
|
||||
## 0.2.2 - October 25, 2010
|
||||
|
||||
### Bug fix
|
||||
|
||||
- [#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
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [#7](https://github.com/guard/guard/pull/7): Fixes for Linux support. ([@yannlugrin][])
|
||||
- [#6](https://github.com/guard/guard/pull/6): Locate guard now chomp newline in result path. ([@yannlugrin][])
|
||||
|
||||
## 0.2.0 - October 21, 2010
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [#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][])
|
||||
- [#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
|
||||
[@bronson]: https://github.com/bronson
|
||||
[@capotej]: https://github.com/capotej
|
||||
[@ches]: https://github.com/ches
|
||||
[@chrisberkhout]: https://github.com/chrisberkhout
|
||||
[@dnagir]: https://github.com/dnagir
|
||||
[@docwhat]: https://github.com/docwhat
|
||||
[@earlonrails]: https://github.com/earlonrails
|
||||
[@etehtsea]: https://github.com/etehtsea
|
||||
[@f1sherman]: https://github.com/f1sherman
|
||||
[@fabioyamate]: https://github.com/fabioyamate
|
||||
[@fnichol]: https://github.com/fnichol
|
||||
[@Gazer]: https://github.com/Gazer
|
||||
[@gix]: https://github.com/gix
|
||||
[@hron]: https://github.com/hron
|
||||
[@hardipe]: https://github.com/hardipe
|
||||
[@hashrocketeer]: https://github.com/hashrocketeer
|
||||
[@ianwhite]: https://github.com/ianwhite
|
||||
[@indirect]: https://github.com/indirect
|
||||
[@jeffutter]: https://github.com/jeffutter
|
||||
[@johnbintz]: https://github.com/johnbintz
|
||||
[@jrsacks]: https://github.com/jrsacks
|
||||
[@koshigoe]: https://github.com/koshigoe
|
||||
[@limeyd]: https://github.com/limeyd
|
||||
[@mcmire]: https://github.com/mcmire
|
||||
[@mislav]: https://github.com/mislav
|
||||
[@monocle]: https://github.com/monocle
|
||||
[@mordaroso]: https://github.com/mordaroso
|
||||
[@nestegg]: https://github.com/nestegg
|
||||
[@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
|
||||
[@rmm5t]: https://github.com/rmm5t
|
||||
[@rymai]: https://github.com/rymai
|
||||
[@scottdavis]: https://github.com/scottdavis
|
||||
[@stereobooster]: https://github.com/stereobooster
|
||||
[@stouset]: https://github.com/stouset
|
||||
[@sunaku]: https://github.com/sunaku
|
||||
[@thibaudgg]: https://github.com/thibaudgg
|
||||
[@thierryhenrio]: https://github.com/thierryhenrio
|
||||
[@tinogomes]: https://github.com/tinogomes
|
||||
[@tpope]: https://github.com/tpope
|
||||
[@uk-ar]: https://github.com/uk-ar
|
||||
[@veged]: https://github.com/veged
|
||||
[@wereHamster]: https://github.com/wereHamster
|
||||
[@yannlugrin]: https://github.com/yannlugrin
|
||||
[@zonque]: https://github.com/zonque
|
26
CHANGELOG.rdoc
Normal file
26
CHANGELOG.rdoc
Normal file
@ -0,0 +1,26 @@
|
||||
== 0.2.2 (Oct 25, 2010)
|
||||
|
||||
Bugs fixes:
|
||||
|
||||
- Avoid creating new copy of fsevent_watch every time a file is changed. (issue #5)
|
||||
|
||||
== 0.2.1 (Oct 24, 2010)
|
||||
|
||||
Bugs fixes:
|
||||
|
||||
- Fixes for Linux support
|
||||
|
||||
== 0.2.0 (Oct 21, 2010)
|
||||
|
||||
Bugs fixes:
|
||||
|
||||
- Fixes for 1.8.6 compatibility (issue #2)
|
||||
- guard init <guard-name> no more need Gemfile presence but open_gem is required now (issue #3)
|
||||
- Removes growl & libnotify dependencies
|
||||
|
||||
== 0.2.0.beta.1 (Oct 17, 2010)
|
||||
|
||||
Features:
|
||||
|
||||
- Improved listeners support (rb-fsevent & rb-inotify)
|
||||
- Added polling listening fallback
|
25
Gemfile
25
Gemfile
@ -1,23 +1,14 @@
|
||||
source :rubygems
|
||||
source "http://rubygems.org"
|
||||
|
||||
gemspec
|
||||
|
||||
gem 'rake'
|
||||
|
||||
group :guard do
|
||||
gem 'guard-ronn'
|
||||
end
|
||||
|
||||
require 'rbconfig'
|
||||
|
||||
if RbConfig::CONFIG['target_os'] =~ /darwin/i
|
||||
gem 'rb-fsevent', '>= 0.4.0', :require => false
|
||||
gem 'growl', '~> 1.0.3', :require => false
|
||||
elsif RbConfig::CONFIG['target_os'] =~ /linux/i
|
||||
gem 'rb-inotify', '>= 0.8.5', :require => false
|
||||
gem 'libnotify', '~> 0.1.3', :require => false
|
||||
elsif 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
|
||||
if Config::CONFIG['target_os'] =~ /darwin/i
|
||||
gem 'rb-fsevent', '>= 0.3.5'
|
||||
gem 'growl', '~> 1.0.3'
|
||||
end
|
||||
if Config::CONFIG['target_os'] =~ /linux/i
|
||||
gem 'rb-inotify', '>= 0.5.1'
|
||||
gem 'libnotify', '~> 0.1.3'
|
||||
end
|
||||
|
33
Guardfile
33
Guardfile
@ -1,28 +1,5 @@
|
||||
group :specs do
|
||||
guard :rspec, :all_on_start => false, :all_after_pass => false, :cli => '--fail-fast --format doc' do
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
||||
watch('spec/support/listener_helper.rb') { Dir.glob("spec/guard/listeners/*") }
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
end
|
||||
end
|
||||
|
||||
group :docs do
|
||||
guard :ronn do
|
||||
watch(%r{^man/.+\.ronn?$})
|
||||
end
|
||||
end
|
||||
|
||||
# require 'guard/guard'
|
||||
#
|
||||
# module ::Guard
|
||||
# class Breaking < ::Guard::Guard
|
||||
# def run_all
|
||||
# raise "Fool !"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# group "exceptional" do
|
||||
# guard :breaking
|
||||
# end
|
||||
guard 'rspec', :version => 2 do
|
||||
watch('^spec/(.*)_spec.rb')
|
||||
watch('^lib/(.*).rb') { |m| "spec/#{m[1]}_spec.rb" }
|
||||
watch('^spec/spec_helper.rb') { "spec" }
|
||||
end
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2011 Thibaud Guillaume-Gentil
|
||||
Copyright (c) 2010 Thibaud Guillaume-Gentil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
459
README.md
459
README.md
@ -1,459 +0,0 @@
|
||||
Guard [![Build Status](https://secure.travis-ci.org/guard/guard.png)](http://travis-ci.org/guard/guard)
|
||||
=====
|
||||
|
||||
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
|
||||
--------
|
||||
|
||||
* [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).
|
||||
* [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).
|
||||
* Automatic & Super fast (when polling is not used) files modifications detection (even new files are detected).
|
||||
* Visual notifications on Mac OSX ([Growl](http://growl.info)), Linux ([Libnotify](http://developer.gnome.org/libnotify)) and Windows ([Notifu](http://www.paralint.com/projects/notifu)).
|
||||
* Tested against Ruby 1.8.7, 1.9.2, REE and the latest versions of JRuby & Rubinius.
|
||||
|
||||
Screencast
|
||||
----------
|
||||
|
||||
Ryan Bates made a Railscast on Guard, you can view it here: [http://railscasts.com/episodes/264-guard](http://railscasts.com/episodes/264-guard)
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
|
||||
Install the gem:
|
||||
|
||||
$ gem install guard
|
||||
|
||||
Or add it to your Gemfile (inside the `development` group):
|
||||
|
||||
gem 'guard'
|
||||
|
||||
and install it via Bundler:
|
||||
|
||||
$ bundle install
|
||||
|
||||
Generate an empty Guardfile with:
|
||||
|
||||
$ guard init
|
||||
|
||||
You may optionally place a .Guardfile in your home directory to use it across multiple projects.
|
||||
Also note that if a `.guard.rb` is found in your home directory, it will be appended to the Guardfile.
|
||||
|
||||
Add the guards you need to your Guardfile (see the existing guards below).
|
||||
|
||||
Now, be sure to read the particular instructions for your operating system: [Mac OS X](#mac) | [Linux](#linux) | [Windows](#win)
|
||||
|
||||
<a name="mac" />
|
||||
|
||||
### On Mac OS X
|
||||
|
||||
Install the rb-fsevent gem for [FSEvent](http://en.wikipedia.org/wiki/FSEvents) support:
|
||||
|
||||
$ gem install rb-fsevent
|
||||
|
||||
You have three possibilities for getting Growl support:
|
||||
|
||||
Use the [growl_notify gem](https://rubygems.org/gems/growl_notify):
|
||||
|
||||
$ gem install growl_notify
|
||||
|
||||
The `growl_notify` gem is compatible with Growl >= 1.3 and uses AppleScript to send Growl notifications.
|
||||
The gem needs a native C extension to make use of AppleScript and does not run on JRuby and MacRuby.
|
||||
|
||||
Use the [ruby_gntp gem](https://github.com/snaka/ruby_gntp):
|
||||
|
||||
$ gem install ruby_gntp
|
||||
|
||||
The `ruby_gntp` gem is compatible with Growl >= 1.3 and uses the Growl Notification Transport Protocol to send Growl
|
||||
notifications. Guard supports multiple notification channels for customizing each notification type, but it's limited
|
||||
to the local host currently.
|
||||
|
||||
Use the [growl gem](https://rubygems.org/gems/growl):
|
||||
|
||||
$ gem install growl
|
||||
|
||||
The `growl` gem is compatible with all versions of Growl and uses a command line tool [growlnotify](http://growl.info/extras.php#growlnotify)
|
||||
that must be separately downloaded and installed. You can also install it with HomeBrew:
|
||||
|
||||
$ brew install growlnotify
|
||||
|
||||
Finally you have to add your Growl library of choice to your Gemfile:
|
||||
|
||||
gem 'rb-fsevent'
|
||||
gem 'growl_notify' # or gem 'ruby_gntp' or gem 'growl'
|
||||
|
||||
Have a look at the [Guard Wiki](https://github.com/guard/guard/wiki/Which-Growl-library-should-I-use) for more information.
|
||||
|
||||
<a name="linux" />
|
||||
|
||||
### On Linux
|
||||
|
||||
Install the [rb-inotify gem](https://rubygems.org/gems/rb-inotify) for [inotify](http://en.wikipedia.org/wiki/Inotify) support:
|
||||
|
||||
$ gem install rb-inotify
|
||||
|
||||
Install the [libnotify gem](https://rubygems.org/gems/libnotify) if you want visual notification support:
|
||||
|
||||
$ gem install libnotify
|
||||
|
||||
And add them to your Gemfile:
|
||||
|
||||
gem 'rb-inotify'
|
||||
gem 'libnotify'
|
||||
|
||||
<a name="win" />
|
||||
|
||||
### On Windows
|
||||
|
||||
Install the [rb-fchange gem](https://rubygems.org/gems/rb-fchange) for [Directory Change Notification](http://msdn.microsoft.com/en-us/library/aa365261\(VS.85\).aspx) support:
|
||||
|
||||
$ gem install rb-fchange
|
||||
|
||||
Install the [win32console gem](https://rubygems.org/gems/win32console) if you want colors in your terminal:
|
||||
|
||||
$ gem install win32console
|
||||
|
||||
Install the [rb-notifu gem](https://rubygems.org/gems/rb-notifu) if you want visual notification support:
|
||||
|
||||
$ gem install rb-notifu
|
||||
|
||||
And add them to your Gemfile:
|
||||
|
||||
gem 'rb-fchange'
|
||||
gem 'rb-notifu'
|
||||
gem 'win32console'
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Just launch Guard inside your Ruby / Rails project with:
|
||||
|
||||
$ guard [start]
|
||||
|
||||
or if you use Bundler, to run the Guard executable specific to your bundle:
|
||||
|
||||
$ bundle exec guard [start]
|
||||
|
||||
Guard will look for a Guardfile in your current directory. If it does not find one, it will look in your `$HOME` directory for a .Guardfile.
|
||||
|
||||
Command line options
|
||||
--------------------
|
||||
|
||||
### `-c`/`--clear` option
|
||||
|
||||
Shell can be cleared after each change:
|
||||
|
||||
$ guard --clear
|
||||
$ guard -c # shortcut
|
||||
|
||||
### `-n`/`--notify` option
|
||||
|
||||
Notifications (growl/libnotify) can be disabled:
|
||||
|
||||
$ guard --notify false
|
||||
$ guard -n f # shortcut
|
||||
|
||||
Notifications can also be disabled globally by setting a `GUARD_NOTIFY` environment variable to `false`
|
||||
|
||||
### `-g`/`--group` option
|
||||
|
||||
Only certain guards groups can be run (see the Guardfile DSL below for creating groups):
|
||||
|
||||
$ guard --group group_name another_group_name
|
||||
$ guard -g group_name another_group_name # shortcut
|
||||
|
||||
### `-d`/`--debug` option
|
||||
|
||||
Guard can be run in debug mode:
|
||||
|
||||
$ guard --debug
|
||||
$ guard -d # shortcut
|
||||
|
||||
### `-w`/`--watchdir` option
|
||||
|
||||
Guard can watch in any directory (instead of the current directory):
|
||||
|
||||
$ guard --watchdir ~/your/fancy/project
|
||||
$ guard -w ~/your/fancy/project # shortcut
|
||||
|
||||
### `-G`/`--guardfile` option
|
||||
|
||||
Guard can use a Guardfile not located in the current directory:
|
||||
|
||||
$ guard --guardfile ~/.your_global_guardfile
|
||||
$ guard -G ~/.your_global_guardfile # shortcut
|
||||
|
||||
### `-A`/`--watch-all-modifications` option
|
||||
|
||||
Guard can optionally watch all file modifications like moves or deletions with:
|
||||
|
||||
$ guard start -A
|
||||
$ guard start --watch-all-modifications
|
||||
|
||||
### `-i`/`--no-interactions` option
|
||||
|
||||
Turn off completely any Guard terminal [interactions](#interactions) with:
|
||||
|
||||
$ guard start -A
|
||||
$ guard start --watch-all-modifications
|
||||
|
||||
An exhaustive list of options is available with:
|
||||
|
||||
$ guard help [TASK]
|
||||
|
||||
<a name="interactions" />
|
||||
|
||||
Interactions
|
||||
------------
|
||||
|
||||
**From version >= 0.7.0 Posix Signal handlers are no more used to interact with Guard. If you're using a version < 0.7, please refer to the [README in the v0.6 branch](https://github.com/guard/guard/blob/v0.6/README.md).**
|
||||
|
||||
When Guard do nothing you can interact with by entering a command + hitting enter:
|
||||
|
||||
* `stop|quit|exit|s|q|e + enter` - Calls each guard's `#stop` method, in the same order they are declared in the Guardfile, and then quits Guard itself.
|
||||
* `reload|r|z + enter` - Calls each guard's `#reload` method, in the same order they are declared in the Guardfile.
|
||||
* `pause|p + enter` - Toggle files modification listening. Useful when switching git branches.
|
||||
* `just enter (no commands)` - Calls each guard's `#run_all` method, in the same order they are declared in the Guardfile.
|
||||
|
||||
Available Guards
|
||||
----------------
|
||||
|
||||
A list of the available guards is present [in the wiki](https://github.com/guard/guard/wiki/List-of-available-Guards).
|
||||
|
||||
### Add a guard to your Guardfile
|
||||
|
||||
Add it to your Gemfile (inside the `development` group):
|
||||
|
||||
gem '<guard-name>'
|
||||
|
||||
You can list all guards installed on your system with:
|
||||
|
||||
$ guard list
|
||||
|
||||
Insert default guard's definition to your Guardfile by running this command:
|
||||
|
||||
$ guard init <guard-name>
|
||||
|
||||
You are good to go, or you can modify your guards' definition to suit your needs.
|
||||
|
||||
Guardfile DSL
|
||||
-------------
|
||||
|
||||
The Guardfile DSL consists of the following methods:
|
||||
|
||||
* `#guard` - Allows you to add a guard with an optional hash of options.
|
||||
* `#watch` - Allows you to define which files are supervised by a guard. An optional block can be added to overwrite the paths sent to the guard's `#run_on_change` method or to launch any arbitrary command.
|
||||
* `#group` - 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. Guards that don't belong to a group are considered global and are always run.
|
||||
* `#callback` - Allows you to execute arbitrary code before or after any of the `start`, `stop`, `reload`, `run_all` and `run_on_change` guards' method. You can even insert more hooks inside these methods. Please [checkout the Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for more details.
|
||||
* `#ignore_paths` - Allows you to ignore top level directories altogether. This comes is handy when you have large amounts of non-source data in you project. By default .bundle, .git, log, tmp, and vendor are ignored. Currently it is only possible to ignore the immediate descendants of the watched directory.
|
||||
|
||||
Example:
|
||||
|
||||
ignore_paths 'foo', 'bar'
|
||||
|
||||
group 'backend' do
|
||||
guard 'bundler' do
|
||||
watch('Gemfile')
|
||||
end
|
||||
|
||||
guard 'rspec', :cli => '--color --format doc' do
|
||||
# Regexp watch patterns are matched with Regexp#match
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||
watch(%r{^spec/models/.+\.rb$}) { ["spec/models", "spec/acceptance"] }
|
||||
watch(%r{^spec/.+\.rb$}) { `say hello` }
|
||||
|
||||
# String watch patterns are matched with simple '=='
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
end
|
||||
end
|
||||
|
||||
group 'frontend' do
|
||||
guard 'coffeescript', :output => 'public/javascripts/compiled' do
|
||||
watch(%r{^app/coffeescripts/.+\.coffee$})
|
||||
end
|
||||
|
||||
guard 'livereload' do
|
||||
watch(%r{^app/.+\.(erb|haml)$})
|
||||
end
|
||||
end
|
||||
|
||||
Using a Guardfile without the `guard` binary
|
||||
--------------------------------------------
|
||||
|
||||
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
|
||||
|
||||
Remember, without any options given, Guard will look for a Guardfile in your current directory and if it does not find one, it will look for it in your `$HOME` directory.
|
||||
|
||||
For instance, you could use it as follow:
|
||||
|
||||
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
|
||||
")
|
||||
|
||||
### Listing defined guards/groups for the current project
|
||||
|
||||
You can list the defined groups and guards for the current Guardfile from the command line using `guard show` or `guard -T`:
|
||||
|
||||
$ guard -T
|
||||
|
||||
(global):
|
||||
shell
|
||||
Group backend:
|
||||
bundler
|
||||
rspec: cli => "--color --format doc"
|
||||
Group frontend:
|
||||
coffeescript: output => "public/javascripts/compiled"
|
||||
livereload
|
||||
|
||||
User config file
|
||||
----------------
|
||||
|
||||
If a `.guard.rb` is found in your home directory, it will be appended to
|
||||
the Guardfile. This can be used for tasks you want guard to handle but
|
||||
other users probably don't. For example, indexing your source tree with
|
||||
[Ctags](http://ctags.sourceforge.net):
|
||||
|
||||
guard 'shell' do
|
||||
watch(%r{^(?:app|lib)/.+\.rb$}) { `ctags -R` }
|
||||
end
|
||||
|
||||
Create a new guard
|
||||
------------------
|
||||
|
||||
Creating a new guard is very easy, just create a new gem (`bundle gem` if you use Bundler) with this basic structure:
|
||||
|
||||
.travis.yml # bonus point!
|
||||
CHANGELOG.md # bonus point!
|
||||
Gemfile
|
||||
guard-name.gemspec
|
||||
Guardfile
|
||||
lib/
|
||||
guard/
|
||||
guard-name/
|
||||
templates/
|
||||
Guardfile # needed for `guard init <guard-name>`
|
||||
version.rb
|
||||
guard-name.rb
|
||||
test/ # or spec/
|
||||
README.md
|
||||
|
||||
`Guard::GuardName` (in `lib/guard/guard-name.rb`) must inherit from
|
||||
[Guard::Guard](http://rubydoc.info/github/guard/guard/master/Guard/Guard) and should overwrite at least one of
|
||||
the basic `Guard::Guard` task methods.
|
||||
|
||||
Here is an example scaffold for `lib/guard/guard-name.rb`:
|
||||
|
||||
require 'guard'
|
||||
require 'guard/guard'
|
||||
|
||||
module Guard
|
||||
class GuardName < Guard
|
||||
|
||||
# Initialize a Guard.
|
||||
# @param [Array<Guard::Watcher>] watchers the Guard file watchers
|
||||
# @param [Hash] options the custom Guard options
|
||||
def initialize(watchers = [], options = {})
|
||||
super
|
||||
end
|
||||
|
||||
# Call once when Guard starts. Please override initialize method to init stuff.
|
||||
# @raise [:task_has_failed] when start has failed
|
||||
def start
|
||||
end
|
||||
|
||||
# Called when `stop|quit|exit|s|q|e + enter` is pressed (when Guard quits).
|
||||
# @raise [:task_has_failed] when stop has failed
|
||||
def stop
|
||||
end
|
||||
|
||||
# Called when `reload|r|z + enter` is pressed.
|
||||
# This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
||||
# @raise [:task_has_failed] when reload has failed
|
||||
def reload
|
||||
end
|
||||
|
||||
# Called when just `enter` is pressed
|
||||
# This method should be principally used for long action like running all specs/tests/...
|
||||
# @raise [:task_has_failed] when run_all has failed
|
||||
def run_all
|
||||
end
|
||||
|
||||
# Called on file(s) modifications that the Guard watches.
|
||||
# @param [Array<String>] paths the changes files or paths
|
||||
# @raise [:task_has_failed] when run_on_change has failed
|
||||
def run_on_change(paths)
|
||||
end
|
||||
|
||||
# Called on file(s) deletions that the Guard watches.
|
||||
# @param [Array<String>] paths the deleted files or paths
|
||||
# @raise [:task_has_failed] when run_on_change has failed
|
||||
def run_on_deletion(paths)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
Please take a look at the [existing guards' source code](https://github.com/guard/guard/wiki/List-of-available-Guards)
|
||||
for more concrete example and inspiration.
|
||||
|
||||
Alternatively, a new guard can be added inline to a Guardfile with this basic structure:
|
||||
|
||||
require 'guard/guard'
|
||||
|
||||
module ::Guard
|
||||
class InlineGuard < ::Guard::Guard
|
||||
def run_all
|
||||
end
|
||||
|
||||
def run_on_change(paths)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Here is a very cool example by [@avdi](https://github.com/avdi) : [http://avdi.org/devblog/2011/06/15/a-guardfile-for-redis](http://avdi.org/devblog/2011/06/15/a-guardfile-for-redis)
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
* Documentation hosted at [RubyDoc](http://rubydoc.info/github/guard/guard/master/frames).
|
||||
* Source hosted at [GitHub](https://github.com/guard/guard).
|
||||
* Report issues and feature requests to [GitHub Issues](https://github.com/guard/guard/issues).
|
||||
|
||||
Pull requests are very welcome! Please try to follow these simple "rules", though:
|
||||
|
||||
- Please create a topic branch for every separate change you make;
|
||||
- Make sure your patches are well tested;
|
||||
- Update the README (if applicable);
|
||||
- Update the CHANGELOG (maybe not for a typo but don't hesitate!);
|
||||
- Please **do not change** the version number.
|
||||
|
||||
For questions please join us on our [Google group](http://groups.google.com/group/guard-dev) or on `#guard` (irc.freenode.net).
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
[Thibaud Guillaume-Gentil](https://github.com/thibaudgg)
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
[https://github.com/guard/guard/contributors](https://github.com/guard/guard/contributors)
|
204
README.rdoc
Normal file
204
README.rdoc
Normal file
@ -0,0 +1,204 @@
|
||||
= Guard
|
||||
|
||||
Guard is a command line tool to easly handle events on files modifications.
|
||||
|
||||
== 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)
|
||||
- {Inotify}[http://en.wikipedia.org/wiki/Inotify] support on Linux ({rb-inotify gem, >= 0.5.1}[https://rubygems.org/gems/rb-inotify] required)
|
||||
- Polling for others (help us to support more systems)
|
||||
- Super fast change detection (when polling not used)
|
||||
- Automatic files modifications detection (even new files are detected)
|
||||
- Growl notification ({growlnotify}[http://growl.info/documentation/growlnotify.php] & {growl gem}[https://rubygems.org/gems/growl] required)
|
||||
- Libnotify notification ({libnotify gem}[https://rubygems.org/gems/libnotify] required)
|
||||
- Tested on Ruby 1.8.6, 1.8.7 & 1.9.2
|
||||
|
||||
== Install
|
||||
|
||||
Install the gem:
|
||||
|
||||
gem install guard
|
||||
|
||||
Add it to your Gemfile (inside test group):
|
||||
|
||||
gem 'guard'
|
||||
|
||||
Generate an empty Guardfile with:
|
||||
|
||||
guard init
|
||||
|
||||
Add guard(s) you need (see available guards below)
|
||||
|
||||
=== On Mac OS X
|
||||
|
||||
Install rb-fsevent for {FSEvent}[http://en.wikipedia.org/wiki/FSEvents] support
|
||||
|
||||
gem install rb-fsevent
|
||||
|
||||
Install growl for Growl notification support
|
||||
|
||||
gem install growl
|
||||
|
||||
And add it to you Gemfile
|
||||
|
||||
gem 'growl'
|
||||
|
||||
=== On Linux
|
||||
|
||||
Install rb-inotify for {inotify}[http://en.wikipedia.org/wiki/Inotify] support
|
||||
|
||||
gem install rb-inotify
|
||||
|
||||
Install libnotify for libonity notification support
|
||||
|
||||
gem install libnotify
|
||||
|
||||
And add it to you Gemfile
|
||||
|
||||
gem 'libnotify'
|
||||
|
||||
== Usage
|
||||
|
||||
Just launch Guard inside your ruby/rails project with:
|
||||
|
||||
guard
|
||||
|
||||
Shell can be cleared after each change with:
|
||||
|
||||
guard -c
|
||||
|
||||
Options list is available with:
|
||||
|
||||
guard help [TASK]
|
||||
|
||||
Signal handlers are used to interact with Guard:
|
||||
|
||||
- Ctrl-C - Quit Guard (call stop guard(s) method before)
|
||||
- Ctrl-\ - Call run_all guard(s) method
|
||||
- Ctrl-Z - Call reload guard(s) method
|
||||
|
||||
== Available Guards
|
||||
|
||||
- {guard-bundler}[http://github.com/guard/guard-bundler] by {Yann Lugrin}[http://github.com/yannlugrin]
|
||||
- {guard-coffeescript}[http://github.com/guard/guard-coffeescript] by {Michael Kessler}[http://github.com/netzpirat]
|
||||
- {guard-compass}[http://github.com/guard/guard-compass] by {Olivier Amblet}[http://github.com/oliamb]
|
||||
- {guard-cucumber}[http://github.com/guard/guard-cucumber] by {Michael Kessler}[http://github.com/netzpirat]
|
||||
- {guard-livereload}[http://github.com/guard/guard-livereload] by {Thibaud Guillaume-Gentil}[http://github.com/thibaudgg]
|
||||
- {guard-minitest}[http://github.com/guard/guard-minitest] by {Yann Lugrin}[http://github.com/yannlugrin]
|
||||
- {guard-nanoc}[http://github.com/guard/guard-nanoc] by {Yann Lugrin}[http://github.com/yannlugrin]
|
||||
- {guard-passenger}[http://github.com/guard/guard-passenger] by {Fabio Kuhn}[http://github.com/mordaroso]
|
||||
- {guard-rspec}[http://github.com/guard/guard-rspec] by {Thibaud Guillaume-Gentil}[http://github.com/thibaudgg]
|
||||
- {guard-sass}[http://github.com/guard/guard-sass] by {Joshua Hawxwell}[http://github.com/hawx]
|
||||
- {guard-shell}[http://github.com/guard/guard-shell] by {Joshua Hawxwell}[http://github.com/hawx]
|
||||
- {guard-test}[http://github.com/guard/guard-test] by {Rémy Coutable}[http://github.com/rymai]
|
||||
|
||||
guard ideas:
|
||||
|
||||
- guard-spork
|
||||
- others ideas?
|
||||
|
||||
=== Add a guard to your Guardfile
|
||||
|
||||
Add it to your Gemfile (inside test group):
|
||||
|
||||
gem '<guard-name>'
|
||||
|
||||
Add guard definition to your Guardfile by running this command:
|
||||
|
||||
guard init <guard-name>
|
||||
|
||||
You are good to go!
|
||||
|
||||
== Create a guard
|
||||
|
||||
Create a new guard is very easy, just create a new gem with this basic structure:
|
||||
|
||||
lib/
|
||||
guard/
|
||||
guard-name/
|
||||
templates/
|
||||
Guardfile (needed for guard init <guard-name>)
|
||||
guard-name.rb
|
||||
|
||||
lib/guard/guard-name.rb inherit from guard/guard and should overwrite at least one of the five guard methods. Example:
|
||||
|
||||
require 'guard'
|
||||
require 'guard/guard'
|
||||
|
||||
module Guard
|
||||
class GuardName < Guard
|
||||
|
||||
def initialize(watchers = [], options = {})
|
||||
super
|
||||
# init stuff here, thx!
|
||||
end
|
||||
|
||||
# ================
|
||||
# = Guard method =
|
||||
# ================
|
||||
|
||||
# If one of those methods raise an exception, the Guard instance
|
||||
# will be removed from the active guard.
|
||||
|
||||
# Call once when guard starts
|
||||
# Please override initialize method to init stuff
|
||||
def start
|
||||
true
|
||||
end
|
||||
|
||||
# Call with Ctrl-C signal (when Guard quit)
|
||||
def stop
|
||||
true
|
||||
end
|
||||
|
||||
# Call with Ctrl-Z signal
|
||||
# This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
||||
def reload
|
||||
true
|
||||
end
|
||||
|
||||
# Call with Ctrl-/ signal
|
||||
# This method should be principally used for long action like running all specs/tests/...
|
||||
def run_all
|
||||
true
|
||||
end
|
||||
|
||||
# Call on file(s) modifications
|
||||
def run_on_change(paths)
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
Looks at available guards code for more concrete example.
|
||||
|
||||
== Guardfile DSL
|
||||
|
||||
Guardfile DSL consists of just two simple methods: guard & watch. Example:
|
||||
|
||||
guard 'rspec', :version => 2 do
|
||||
watch('^spec/(.*)_spec.rb')
|
||||
watch('^lib/(.*)\.rb') { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||
watch('^spec/spec_helper.rb') { "spec" }
|
||||
watch('^spec/spec_helper.rb') { `say hello` }
|
||||
end
|
||||
|
||||
- "guard" method allow to add a guard with an optional options hash
|
||||
- "watch" method allow to define which files are supervised per this guard. A optional block can be added to overwrite path sending to run_on_change guard method or launch simple command.
|
||||
|
||||
== TODO
|
||||
|
||||
- Add more specs! Shame on me :)
|
||||
|
||||
== Development
|
||||
|
||||
- Source hosted at {GitHub}[http://github.com/guard/guard]
|
||||
- Report issues/Questions/Feature requests on {GitHub Issues}[http://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.
|
||||
|
||||
== Authors
|
||||
|
||||
{Thibaud Guillaume-Gentil}[http://github.com/thibaudgg]
|
49
Rakefile
49
Rakefile
@ -5,44 +5,17 @@ require 'rspec/core/rake_task'
|
||||
RSpec::Core::RakeTask.new(:spec)
|
||||
task :default => :spec
|
||||
|
||||
require 'rbconfig'
|
||||
namespace(:spec) do
|
||||
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 & " +
|
||||
"bundle exec rspec spec"
|
||||
end
|
||||
end
|
||||
else
|
||||
desc "Run all specs on multiple ruby versions (requires rvm)"
|
||||
task(:portability) do
|
||||
travis_config_file = File.expand_path("../.travis.yml", __FILE__)
|
||||
begin
|
||||
travis_options ||= YAML::load_file(travis_config_file)
|
||||
rescue => ex
|
||||
puts "Travis config file '#{travis_config_file}' could not be found: #{ex.message}"
|
||||
return
|
||||
end
|
||||
|
||||
travis_options['rvm'].each do |version|
|
||||
system <<-BASH
|
||||
bash -c 'source ~/.rvm/scripts/rvm;
|
||||
rvm #{version};
|
||||
ruby_version_string_size=`ruby -v | wc -m`
|
||||
echo;
|
||||
for ((c=1; c<$ruby_version_string_size; c++)); do echo -n "="; done
|
||||
echo;
|
||||
echo "`ruby -v`";
|
||||
for ((c=1; c<$ruby_version_string_size; c++)); do echo -n "="; done
|
||||
echo;
|
||||
RBXOPT="-Xrbc.db" bundle install;
|
||||
RBXOPT="-Xrbc.db" bundle exec rspec spec -f doc 2>&1;'
|
||||
BASH
|
||||
end
|
||||
desc "Run all specs on multiple ruby versions (requires rvm)"
|
||||
task(:portability) do
|
||||
%w[1.8.6 1.8.7 1.9.2].each do |version|
|
||||
system <<-BASH
|
||||
bash -c 'source ~/.rvm/scripts/rvm;
|
||||
rvm #{version};
|
||||
echo "--------- version #{version} ----------\n";
|
||||
bundle install;
|
||||
rake spec'
|
||||
BASH
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -3,4 +3,4 @@
|
||||
require 'guard'
|
||||
require 'guard/cli'
|
||||
|
||||
Guard::CLI.start
|
||||
Guard::CLI.start
|
@ -1,5 +1,6 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
Kernel.load File.expand_path('../lib/guard/version.rb', __FILE__)
|
||||
$:.push File.expand_path('../lib', __FILE__)
|
||||
require 'guard/version'
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'guard'
|
||||
@ -7,22 +8,21 @@ Gem::Specification.new do |s|
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.authors = ['Thibaud Guillaume-Gentil']
|
||||
s.email = ['thibaud@thibaud.me']
|
||||
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.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.required_rubygems_version = '>= 1.3.6'
|
||||
s.rubyforge_project = 'guard'
|
||||
|
||||
s.add_dependency 'thor', '~> 0.14.6'
|
||||
|
||||
s.add_development_dependency 'bundler'
|
||||
s.add_development_dependency 'rspec', '~> 2.6.0'
|
||||
s.add_development_dependency 'guard-rspec', '~> 0.3.1'
|
||||
s.add_development_dependency 'yard', '~> 0.7.2'
|
||||
s.add_development_dependency 'kramdown', '~> 0.13.3'
|
||||
|
||||
s.files = Dir.glob('{bin,images,lib}/**/*') + %w[CHANGELOG.md LICENSE man/guard.1 man/guard.1.html README.md]
|
||||
|
||||
s.add_development_dependency 'bundler', '~> 1.0.3'
|
||||
s.add_development_dependency 'rspec', '~> 2.0.1'
|
||||
s.add_development_dependency 'guard-rspec', '~> 0.1.4'
|
||||
|
||||
s.add_dependency 'thor', '~> 0.14.3'
|
||||
s.add_dependency 'open_gem', '~> 1.4.2'
|
||||
|
||||
s.files = Dir.glob('{bin,images,lib}/**/*') + %w[LICENSE README.rdoc]
|
||||
s.executable = 'guard'
|
||||
s.require_path = 'lib'
|
||||
end
|
||||
end
|
501
lib/guard.rb
501
lib/guard.rb
@ -1,436 +1,101 @@
|
||||
require 'thread'
|
||||
require 'bundler'
|
||||
|
||||
# Guard is the main module for all Guard related modules and classes.
|
||||
# Also other Guard implementation should use this namespace.
|
||||
#
|
||||
module Guard
|
||||
|
||||
autoload :UI, 'guard/ui'
|
||||
autoload :Dsl, 'guard/dsl'
|
||||
autoload :DslDescriber, 'guard/dsl_describer'
|
||||
autoload :Group, 'guard/group'
|
||||
autoload :Interactor, 'guard/interactor'
|
||||
autoload :Listener, 'guard/listener'
|
||||
autoload :Watcher, 'guard/watcher'
|
||||
autoload :Notifier, 'guard/notifier'
|
||||
autoload :Hook, 'guard/hook'
|
||||
|
||||
|
||||
autoload :UI, 'guard/ui'
|
||||
autoload :Dsl, 'guard/dsl'
|
||||
autoload :Interactor, 'guard/interactor'
|
||||
autoload :Listener, 'guard/listener'
|
||||
autoload :Watcher, 'guard/watcher'
|
||||
autoload :Notifier, 'guard/notifier'
|
||||
|
||||
class << self
|
||||
attr_accessor :options, :interactor, :listener, :lock
|
||||
|
||||
# Creates the initial Guardfile template or add a Guard implementation
|
||||
# Guardfile template to an existing Guardfile.
|
||||
#
|
||||
# @see Guard::Guard.init
|
||||
#
|
||||
# @param [String] guard_name the name of the Guard to initialize
|
||||
#
|
||||
def initialize_template(guard_name = nil)
|
||||
if guard_name
|
||||
guard_class = ::Guard.get_guard_class(guard_name)
|
||||
guard_class.init(guard_name)
|
||||
else
|
||||
if !File.exist?('Guardfile')
|
||||
::Guard::UI.info "Writing new Guardfile to #{ Dir.pwd }/Guardfile"
|
||||
FileUtils.cp(File.expand_path('../templates/Guardfile', __FILE__), 'Guardfile')
|
||||
else
|
||||
::Guard::UI.error "Guardfile already exists at #{ Dir.pwd }/Guardfile"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
attr_accessor :options, :guards, :listener
|
||||
|
||||
# initialize this singleton
|
||||
def init(options = {})
|
||||
@options = options
|
||||
@listener = Listener.init
|
||||
@guards = []
|
||||
return self
|
||||
end
|
||||
|
||||
# Initialize the Guard singleton.
|
||||
#
|
||||
# @option options [Boolean] clear if auto clear the UI should be done
|
||||
# @option options [Boolean] notify if system notifications should be shown
|
||||
# @option options [Boolean] debug if debug output should be shown
|
||||
# @option options [Array<String>] group the list of groups to start
|
||||
# @option options [String] watchdir the director to watch
|
||||
# @option options [String] guardfile the path to the Guardfile
|
||||
# @option options [Boolean] watch_all_modifications watches all file modifications if true
|
||||
#
|
||||
def setup(options = {})
|
||||
@lock = Mutex.new
|
||||
|
||||
@options = options
|
||||
@guards = []
|
||||
@groups = [Group.new(:default)]
|
||||
@interactor = Interactor.new unless @options[:no_interactions]
|
||||
@listener = Listener.select_and_init(@options[:watchdir] ? File.expand_path(@options[:watchdir]) : Dir.pwd, options)
|
||||
|
||||
@options[:notify] && ENV['GUARD_NOTIFY'] != 'false' ? Notifier.turn_on : Notifier.turn_off
|
||||
|
||||
UI.clear if @options[:clear]
|
||||
|
||||
debug_command_execution if @options[:debug]
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
# Smart accessor for retrieving a specific guard or several guards at once.
|
||||
#
|
||||
# @param [String, Symbol] filter return the guard with the given name, or nil if not found
|
||||
# @param [Regexp] filter returns all guards matching the Regexp, or [] if no guard found
|
||||
# @param [Hash] filter returns all guards matching the given Hash.
|
||||
# Example: `{ :name => 'rspec', :group => 'backend' }`, or [] if no guard found
|
||||
# @param [NilClass] filter returns all guards
|
||||
#
|
||||
# @see Guard.groups
|
||||
#
|
||||
def guards(filter = nil)
|
||||
case filter
|
||||
when String, Symbol
|
||||
@guards.find { |guard| guard.class.to_s.downcase.sub('guard::', '') == filter.to_s.downcase.gsub('-', '') }
|
||||
when Regexp
|
||||
@guards.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') =~ filter }
|
||||
when Hash
|
||||
filter.inject(@guards) do |matches, (k, v)|
|
||||
if k.to_sym == :name
|
||||
matches.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') == v.to_s.downcase.gsub('-', '') }
|
||||
else
|
||||
matches.find_all { |guard| guard.send(k).to_sym == v.to_sym }
|
||||
end
|
||||
end
|
||||
else
|
||||
@guards
|
||||
end
|
||||
end
|
||||
|
||||
# Smart accessor for retrieving a specific group or several groups at once.
|
||||
#
|
||||
# @param [NilClass] filter returns all groups
|
||||
# @param [String, Symbol] filter return the group with the given name, or nil if not found
|
||||
# @param [Regexp] filter returns all groups matching the Regexp, or [] if no group found
|
||||
#
|
||||
# @see Guard.guards
|
||||
#
|
||||
def groups(filter = nil)
|
||||
case filter
|
||||
when String, Symbol
|
||||
@groups.find { |group| group.name == filter.to_sym }
|
||||
when Regexp
|
||||
@groups.find_all { |group| group.name.to_s =~ filter }
|
||||
else
|
||||
@groups
|
||||
end
|
||||
end
|
||||
|
||||
# Start Guard by evaluate the `Guardfile`, initialize the declared Guards
|
||||
# and start the available file change listener.
|
||||
#
|
||||
# @option options [Boolean] clear if auto clear the UI should be done
|
||||
# @option options [Boolean] notify if system notifications should be shown
|
||||
# @option options [Boolean] debug if debug output should be shown
|
||||
# @option options [Array<String>] group the list of groups to start
|
||||
# @option options [String] watchdir the director to watch
|
||||
# @option options [String] guardfile the path to the Guardfile
|
||||
#
|
||||
|
||||
def start(options = {})
|
||||
setup(options)
|
||||
|
||||
Dsl.evaluate_guardfile(options)
|
||||
|
||||
listener.on_change do |files|
|
||||
Dsl.reevaluate_guardfile if Watcher.match_guardfile?(files)
|
||||
listener.changed_files += files if Watcher.match_files?(guards, files)
|
||||
end
|
||||
|
||||
UI.info "Guard is now watching at '#{ listener.directory }'"
|
||||
|
||||
run_guard_task(:start)
|
||||
|
||||
interactor.start if interactor
|
||||
listener.start
|
||||
end
|
||||
|
||||
# Stop Guard listening to file changes
|
||||
#
|
||||
def stop
|
||||
UI.info 'Bye bye...', :reset => true
|
||||
|
||||
run_guard_task(:stop)
|
||||
|
||||
listener.stop
|
||||
abort
|
||||
end
|
||||
|
||||
# Reload all Guards currently enabled.
|
||||
#
|
||||
def reload
|
||||
run do
|
||||
run_guard_task(:reload)
|
||||
end
|
||||
end
|
||||
|
||||
# Trigger `run_all` on all Guards currently enabled.
|
||||
#
|
||||
def run_all
|
||||
run do
|
||||
run_guard_task(:run_all)
|
||||
end
|
||||
end
|
||||
|
||||
# Pause Guard listening to file changes.
|
||||
#
|
||||
def pause
|
||||
if listener.paused?
|
||||
UI.info 'Un-paused files modification listening', :reset => true
|
||||
listener.clear_changed_files
|
||||
listener.run
|
||||
init options
|
||||
|
||||
Dsl.evaluate_guardfile
|
||||
if guards.empty?
|
||||
UI.error "No guards found in Guardfile, please add at least one."
|
||||
else
|
||||
UI.info 'Paused files modification listening', :reset => true
|
||||
listener.pause
|
||||
end
|
||||
end
|
||||
|
||||
# Trigger `run_on_change` on all Guards currently enabled.
|
||||
#
|
||||
def run_on_change(paths)
|
||||
run do
|
||||
run_guard_task(:run_on_change, paths)
|
||||
end
|
||||
end
|
||||
|
||||
# Run a block where the listener and the interactor is
|
||||
# blocked.
|
||||
#
|
||||
# @yield the block to run
|
||||
#
|
||||
def run
|
||||
UI.clear if options[:clear]
|
||||
|
||||
lock.synchronize do
|
||||
begin
|
||||
interactor.stop_if_not_current if interactor
|
||||
yield
|
||||
rescue Interrupt
|
||||
end
|
||||
|
||||
interactor.start if interactor
|
||||
end
|
||||
end
|
||||
|
||||
# Loop through all groups and run the given task for each Guard.
|
||||
#
|
||||
# Stop the task run for the all Guards within a group if one Guard
|
||||
# throws `:task_has_failed`.
|
||||
#
|
||||
# @param [Symbol] task the task to run
|
||||
# @param [Array<String>] files the list of files to pass to the task
|
||||
#
|
||||
def run_guard_task(task, files = nil)
|
||||
groups.each do |group|
|
||||
catch :task_has_failed do
|
||||
guards(:group => group.name).each do |guard|
|
||||
if task == :run_on_change
|
||||
run_on_change_task(files, guard, task)
|
||||
else
|
||||
run_supervised_task(guard, task)
|
||||
Interactor.init_signal_traps
|
||||
|
||||
listener.on_change do |files|
|
||||
run do
|
||||
guards.each do |guard|
|
||||
paths = Watcher.match_files(guard, files)
|
||||
supervised_task(guard, :run_on_change, paths) unless paths.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Run the `:run_on_change` task. When the option `:watch_all_modifications` is set,
|
||||
# the task is split to run changed paths on {Guard::Guard#run_on_change}, whereas
|
||||
# deleted paths run on {Guard::Guard#run_on_deletion}.
|
||||
#
|
||||
# @param [Array<String>] files the list of files to pass to the task
|
||||
# @param [Guard::Guard] guard the guard to run
|
||||
# @param [Symbol] task the task to run
|
||||
# @raise [:task_has_failed] when task has failed
|
||||
#
|
||||
def run_on_change_task(files, guard, task)
|
||||
paths = Watcher.match_files(guard, files)
|
||||
changes = changed_paths(paths)
|
||||
deletions = deleted_paths(paths)
|
||||
|
||||
unless changes.empty?
|
||||
UI.debug "#{ guard.class.name }##{ task } with #{ changes.inspect }"
|
||||
run_supervised_task(guard, task, changes)
|
||||
end
|
||||
|
||||
unless deletions.empty?
|
||||
UI.debug "#{ guard.class.name }#run_on_deletion with #{ deletions.inspect }"
|
||||
run_supervised_task(guard, :run_on_deletion, deletions)
|
||||
end
|
||||
end
|
||||
|
||||
# Detects the paths that have changed.
|
||||
#
|
||||
# Deleted paths are prefixed by an exclamation point.
|
||||
# @see Guard::Listener#modified_files
|
||||
#
|
||||
# @param [Array<String>] paths the watched paths
|
||||
# @return [Array<String>] the changed paths
|
||||
#
|
||||
def changed_paths(paths)
|
||||
paths.select { |f| !f.start_with?('!') }
|
||||
end
|
||||
|
||||
# Detects the paths that have been deleted.
|
||||
#
|
||||
# Deleted paths are prefixed by an exclamation point.
|
||||
# @see Guard::Listener#modified_files
|
||||
#
|
||||
# @param [Array<String>] paths the watched paths
|
||||
# @return [Array<String>] the deleted paths
|
||||
#
|
||||
def deleted_paths(paths)
|
||||
paths.select { |f| f.start_with?('!') }.map { |f| f.slice(1..-1) }
|
||||
end
|
||||
|
||||
# Run a Guard task, but remove the Guard when his work leads to a system failure.
|
||||
#
|
||||
# When the Group has `:halt_on_fail` disabled, we've to catch `:task_has_failed`
|
||||
# here in order to avoid an uncaught throw error.
|
||||
#
|
||||
# @param [Guard::Guard] guard the Guard to execute
|
||||
# @param [Symbol] task the task to run
|
||||
# @param [Array] args the arguments for the task
|
||||
# @raise [:task_has_failed] when task has failed
|
||||
#
|
||||
def run_supervised_task(guard, task, *args)
|
||||
catch guard_symbol(guard) do
|
||||
guard.hook("#{ task }_begin", *args)
|
||||
result = guard.send(task, *args)
|
||||
guard.hook("#{ task }_end", result)
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
rescue Exception => ex
|
||||
UI.error("#{ guard.class.name } failed to achieve its <#{ task.to_s }>, exception was:" +
|
||||
"\n#{ ex.class }: #{ ex.message }\n#{ ex.backtrace.join("\n") }")
|
||||
|
||||
guards.delete guard
|
||||
UI.info("\n#{ guard.class.name } has just been fired")
|
||||
|
||||
ex
|
||||
end
|
||||
|
||||
# Get the symbol we have to catch when running a supervised task.
|
||||
# If we are within a Guard group that has the `:halt_on_fail`
|
||||
# option set, we do NOT catch it here, it will be catched at the
|
||||
# group level.
|
||||
#
|
||||
# @see .run_guard_task
|
||||
#
|
||||
# @param [Guard::Guard] guard the Guard to execute
|
||||
# @return [Symbol] the symbol to catch
|
||||
#
|
||||
def guard_symbol(guard)
|
||||
if guard.group.class == Symbol
|
||||
group = groups(guard.group)
|
||||
group.options[:halt_on_fail] ? :no_catch : :task_has_failed
|
||||
else
|
||||
:task_has_failed
|
||||
end
|
||||
end
|
||||
|
||||
# Add a Guard to use.
|
||||
#
|
||||
# @param [String] name the Guard name
|
||||
# @param [Array<Watcher>] watchers the list of declared watchers
|
||||
# @param [Array<Hash>] callbacks the list of callbacks
|
||||
# @param [Hash] options the Guard options (see the given Guard documentation)
|
||||
#
|
||||
def add_guard(name, watchers = [], callbacks = [], options = {})
|
||||
if name.to_sym == :ego
|
||||
UI.deprecation('Guard::Ego is now part of Guard. You can remove it from your Guardfile.')
|
||||
else
|
||||
guard_class = get_guard_class(name)
|
||||
callbacks.each { |callback| Hook.add_callback(callback[:listener], guard_class, callback[:events]) }
|
||||
@guards << guard_class.new(watchers, options)
|
||||
end
|
||||
end
|
||||
|
||||
# Add a Guard group.
|
||||
#
|
||||
# @param [String] name the group name
|
||||
# @option options [Boolean] halt_on_fail if a task execution
|
||||
# should be halted for all Guards in this group if one Guard throws `:task_has_failed`
|
||||
# @return [Guard::Group] the group added (or retrieved from the `@groups` variable if already present)
|
||||
#
|
||||
def add_group(name, options = {})
|
||||
group = groups(name)
|
||||
if group.nil?
|
||||
group = Group.new(name, options)
|
||||
@groups << group
|
||||
end
|
||||
group
|
||||
end
|
||||
|
||||
# Tries to load the Guard main class.
|
||||
#
|
||||
# @param [String] name the name of the Guard
|
||||
# @return [Class, nil] the loaded class
|
||||
#
|
||||
def get_guard_class(name)
|
||||
name = name.to_s
|
||||
try_require = false
|
||||
const_name = name.downcase.gsub('-', '')
|
||||
begin
|
||||
require "guard/#{ name.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 }"
|
||||
|
||||
UI.info "Guard is now watching at '#{Dir.pwd}'"
|
||||
guards.each do |guard|
|
||||
if supervised_task(guard, :start)
|
||||
%w[reload run_all].each do |m|
|
||||
guard.send(m) if guard.respond_to?(:"#{m}_at_start?") && guard.send(:"#{m}_at_start?")
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue LoadError => loadError
|
||||
UI.error "Could not load 'guard/#{ name.downcase }' or find class Guard::#{ const_name.capitalize }"
|
||||
UI.error loadError.to_s
|
||||
listener.start
|
||||
end
|
||||
end
|
||||
|
||||
# Locate a path to a Guard gem.
|
||||
#
|
||||
# @param [String] name the name of the Guard without the prefix `guard-`
|
||||
# @return [String] the full path to the Guard gem
|
||||
#
|
||||
|
||||
def add_guard(name, watchers = [], options = {})
|
||||
guard_class = get_guard_class(name)
|
||||
@guards << guard_class.new(watchers, options)
|
||||
end
|
||||
|
||||
def get_guard_class(name)
|
||||
require "guard/#{name.downcase}"
|
||||
klasses = []
|
||||
ObjectSpace.each_object(Class) do |klass|
|
||||
klasses << klass if klass.to_s.downcase.match "^guard::#{name.downcase}"
|
||||
end
|
||||
klasses.first
|
||||
rescue LoadError
|
||||
UI.error "Could not find gem 'guard-#{name}' in the current Gemfile."
|
||||
end
|
||||
|
||||
# Let a guard execute his task but
|
||||
# fire it if his work lead to system failure
|
||||
def supervised_task(guard, task_to_supervise, *args)
|
||||
if !guard.respond_to(:"#{task_to_supervise}?") || guard.send(:"#{task_to_supervise}?")
|
||||
guard.send(task_to_supervise, *args)
|
||||
else
|
||||
false
|
||||
end
|
||||
rescue Exception
|
||||
UI.error("#{guard.class.name} guard failed to achieve its <#{task_to_supervise.to_s}> command: #{$!}")
|
||||
::Guard.guards.delete guard
|
||||
UI.info("Guard #{guard.class.name} has just been fired")
|
||||
return $!
|
||||
end
|
||||
|
||||
def locate_guard(name)
|
||||
if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
|
||||
Gem::Specification.find_by_name("guard-#{ name }").full_gem_path
|
||||
else
|
||||
Gem.source_index.find_name("guard-#{ name }").last.full_gem_path
|
||||
end
|
||||
`gem open guard-#{name} --latest --command echo`.chomp
|
||||
rescue
|
||||
UI.error "Could not find 'guard-#{ name }' gem path."
|
||||
UI.error "Could not find 'guard-#{name}' gem path."
|
||||
end
|
||||
|
||||
# Returns a list of guard Gem names installed locally.
|
||||
#
|
||||
# @return [Array<String>] a list of guard gem names
|
||||
#
|
||||
def guard_gem_names
|
||||
if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
|
||||
Gem::Specification.find_all.select { |x| x.name =~ /^guard-/ }
|
||||
else
|
||||
Gem.source_index.find_name(/^guard-/)
|
||||
end.map { |x| x.name.sub /^guard-/, '' }
|
||||
end
|
||||
|
||||
# Adds a command logger in debug mode. This wraps common command
|
||||
# execution functions and logs the executed command before execution.
|
||||
#
|
||||
def debug_command_execution
|
||||
Kernel.send(:alias_method, :original_system, :system)
|
||||
Kernel.send(:define_method, :system) do |command, *args|
|
||||
::Guard::UI.debug "Command execution: #{ command } #{ args.join(' ') }"
|
||||
original_system command, *args
|
||||
end
|
||||
|
||||
Kernel.send(:alias_method, :original_backtick, :'`')
|
||||
Kernel.send(:define_method, :'`') do |command|
|
||||
::Guard::UI.debug "Command execution: #{ command }"
|
||||
original_backtick command
|
||||
|
||||
def run
|
||||
listener.stop
|
||||
UI.clear if options[:clear]
|
||||
begin
|
||||
yield
|
||||
rescue Interrupt
|
||||
end
|
||||
listener.start
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
129
lib/guard/cli.rb
129
lib/guard/cli.rb
@ -2,118 +2,37 @@ require 'thor'
|
||||
require 'guard/version'
|
||||
|
||||
module Guard
|
||||
|
||||
# Facade for the Guard command line interface managed by [Thor](https://github.com/wycats/thor).
|
||||
# This is the main interface to Guard that is called by the Guard binary `bin/guard`.
|
||||
# Do not put any logic in here, create a class and delegate instead.
|
||||
#
|
||||
class CLI < Thor
|
||||
|
||||
default_task :start
|
||||
|
||||
desc 'start', 'Starts Guard'
|
||||
|
||||
method_option :clear,
|
||||
:type => :boolean,
|
||||
:default => false,
|
||||
:aliases => '-c',
|
||||
:banner => 'Auto clear shell before each change/run_all/reload'
|
||||
|
||||
method_option :notify,
|
||||
:type => :boolean,
|
||||
:default => true,
|
||||
:aliases => '-n',
|
||||
:banner => 'Notifications feature (growl/libnotify)'
|
||||
|
||||
method_option :debug,
|
||||
:type => :boolean,
|
||||
:default => false,
|
||||
:aliases => '-d',
|
||||
:banner => 'Print debug messages'
|
||||
|
||||
method_option :group,
|
||||
:type => :array,
|
||||
:default => [],
|
||||
:aliases => '-g',
|
||||
:banner => 'Run only the passed groups'
|
||||
|
||||
method_option :watchdir,
|
||||
:type => :string,
|
||||
:aliases => '-w',
|
||||
:banner => 'Specify the directory to watch'
|
||||
|
||||
method_option :guardfile,
|
||||
:type => :string,
|
||||
:aliases => '-G',
|
||||
:banner => 'Specify a Guardfile'
|
||||
|
||||
method_option :watch_all_modifications,
|
||||
:type => :boolean,
|
||||
:default => false,
|
||||
:aliases => '-A',
|
||||
:banner => 'Watch for all file modifications including moves and deletions'
|
||||
|
||||
method_option :no_interactions,
|
||||
:type => :boolean,
|
||||
:default => false,
|
||||
:aliases => '-i',
|
||||
:banner => 'Turn off completely any guard terminal interactions'
|
||||
|
||||
# Start Guard by initialize the defined Guards and watch the file system.
|
||||
# This is the default task, so calling `guard` is the same as calling `guard start`.
|
||||
#
|
||||
# @see Guard.start
|
||||
#
|
||||
|
||||
desc "start", "Starts guard"
|
||||
method_option :clear, :type => :boolean, :default => false, :aliases => '-c', :banner => "Auto clear shell before each change/run_all/reload"
|
||||
method_option :debug, :type => :boolean, :default => false, :aliases => '-d', :banner => "Print debug messages"
|
||||
def start
|
||||
::Guard.start(options)
|
||||
end
|
||||
|
||||
desc 'list', 'Lists guards that can be used with init'
|
||||
|
||||
# List the Guards that are available for use in your system and marks
|
||||
# those that are currently used in your `Guardfile`.
|
||||
#
|
||||
# @see Guard::DslDescriber.list
|
||||
#
|
||||
def list
|
||||
::Guard::DslDescriber.list(options)
|
||||
end
|
||||
|
||||
desc 'version', 'Show the Guard version'
|
||||
map %w(-v --version) => :version
|
||||
|
||||
# Shows the current version of Guard.
|
||||
#
|
||||
# @see Guard::VERSION
|
||||
#
|
||||
|
||||
desc "version", "Prints the guard's version information"
|
||||
def version
|
||||
::Guard::UI.info "Guard version #{ Guard::VERSION }"
|
||||
::Guard::UI.info "Guard version #{Guard::VERSION}"
|
||||
end
|
||||
|
||||
desc 'init [GUARD]', 'Generates a Guardfile at the current working directory, or insert the given GUARD to an existing Guardfile'
|
||||
|
||||
# Appends the Guard template to the `Guardfile`, or creates an initial
|
||||
# `Guardfile` when no Guard name is passed.
|
||||
#
|
||||
# @see Guard.initialize_template
|
||||
#
|
||||
# @param [String] guard_name the name of the Guard to initialize
|
||||
#
|
||||
map %w(-v --version) => :version
|
||||
|
||||
desc "init [GUARD]", "Generates a Guardfile into the current working directory, or add it given guard"
|
||||
def init(guard_name = nil)
|
||||
::Guard.initialize_template(guard_name)
|
||||
if !File.exist?("Guardfile")
|
||||
puts "Writing new Guardfile to #{Dir.pwd}/Guardfile"
|
||||
FileUtils.cp(File.expand_path('../templates/Guardfile', __FILE__), 'Guardfile')
|
||||
elsif guard_name.nil?
|
||||
::Guard::UI.error "Guardfile already exists at #{Dir.pwd}/Guardfile"
|
||||
exit 1
|
||||
end
|
||||
|
||||
if guard_name
|
||||
guard_class = ::Guard.get_guard_class(guard_name)
|
||||
guard_class.init(guard_name)
|
||||
end
|
||||
end
|
||||
|
||||
desc 'show', 'Show all defined Guards and their options'
|
||||
map %w(-T) => :show
|
||||
|
||||
# Shows all Guards and their options that are defined in
|
||||
# the `Guardfile`.
|
||||
#
|
||||
# @see Guard::DslDescriber.show
|
||||
#
|
||||
def show
|
||||
::Guard::DslDescriber.show(options)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
381
lib/guard/dsl.rb
381
lib/guard/dsl.rb
@ -1,370 +1,35 @@
|
||||
module Guard
|
||||
|
||||
# The DSL class provides the methods that are used in each `Guardfile` to describe
|
||||
# the behaviour of Guard.
|
||||
#
|
||||
# The main keywords of the DSL are `guard` and `watch`. These are necessary to define
|
||||
# the used Guards and the file changes they are watching.
|
||||
#
|
||||
# You can optionally group the Guards with the `group` keyword and ignore certain paths
|
||||
# with the `ignore_paths` keyword.
|
||||
#
|
||||
# A more advanced DSL use is the `callback` keyword that allows you to execute arbitrary
|
||||
# code before or after any of the `start`, `stop`, `reload`, `run_all` and `run_on_change`
|
||||
# Guards' method. You can even insert more hooks inside these methods.
|
||||
# Please [checkout the Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for more details.
|
||||
#
|
||||
# The DSL will also evaluate normal Ruby code.
|
||||
#
|
||||
# There are two possible locations for the `Guardfile`:
|
||||
# - The `Guardfile` in the current directory where Guard has been started
|
||||
# - The `.Guardfile` in your home directory.
|
||||
#
|
||||
# In addition, if a user configuration `.guard.rb` in your home directory is found, it will
|
||||
# be appended to the current project `Guardfile`.
|
||||
#
|
||||
# @example A sample of a complex Guardfile
|
||||
#
|
||||
# group 'frontend' do
|
||||
# guard 'passenger', :ping => true do
|
||||
# watch('config/application.rb')
|
||||
# watch('config/environment.rb')
|
||||
# watch(%r{^config/environments/.+\.rb})
|
||||
# watch(%r{^config/initializers/.+\.rb})
|
||||
# end
|
||||
#
|
||||
# guard 'livereload', :apply_js_live => false do
|
||||
# watch(%r{^app/.+\.(erb|haml)})
|
||||
# watch(%r{^app/helpers/.+\.rb})
|
||||
# watch(%r{^public/javascripts/.+\.js})
|
||||
# watch(%r{^public/stylesheets/.+\.css})
|
||||
# watch(%r{^public/.+\.html})
|
||||
# watch(%r{^config/locales/.+\.yml})
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# group 'backend' do
|
||||
# # Reload the bundle when the Gemfile is modified
|
||||
# guard 'bundler' do
|
||||
# watch('Gemfile')
|
||||
# end
|
||||
#
|
||||
# # for big project you can fine tune the "timeout" before Spork's launch is considered failed
|
||||
# guard 'spork', :wait => 40 do
|
||||
# watch('Gemfile')
|
||||
# watch('config/application.rb')
|
||||
# watch('config/environment.rb')
|
||||
# watch(%r{^config/environments/.+\.rb})
|
||||
# watch(%r{^config/initializers/.+\.rb})
|
||||
# watch('spec/spec_helper.rb')
|
||||
# end
|
||||
#
|
||||
# # use RSpec 2, from the system's gem and with some direct RSpec CLI options
|
||||
# guard 'rspec', :version => 2, :cli => "--color --drb -f doc", :bundler => false do
|
||||
# watch('spec/spec_helper.rb') { "spec" }
|
||||
# watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
||||
# watch('config/routes.rb') { "spec/routing" }
|
||||
# watch(%r{^spec/support/(controllers|acceptance)_helpers\.rb}) { |m| "spec/#{m[1]}" }
|
||||
# watch(%r{^spec/.+_spec\.rb})
|
||||
#
|
||||
# watch(%r{^app/controllers/(.+)_(controller)\.rb}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
||||
#
|
||||
# watch(%r{^app/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
|
||||
# watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||
# end
|
||||
# end
|
||||
#
|
||||
class Dsl
|
||||
class << self
|
||||
|
||||
@@options = nil
|
||||
|
||||
# Evaluate the DSL methods in the `Guardfile`.
|
||||
#
|
||||
# @option options [Array<Symbol,String>] groups the groups to evaluate
|
||||
# @option options [String] guardfile the path to a valid Guardfile
|
||||
# @option options [String] guardfile_contents a string representing the content of a valid Guardfile
|
||||
# @raise [ArgumentError] when options are not a Hash
|
||||
#
|
||||
def evaluate_guardfile(options = {})
|
||||
raise ArgumentError.new('No option hash passed to evaluate_guardfile!') unless options.is_a?(Hash)
|
||||
|
||||
@@options = options.dup
|
||||
|
||||
fetch_guardfile_contents
|
||||
instance_eval_guardfile(guardfile_contents_with_user_config)
|
||||
|
||||
UI.error 'No guards found in Guardfile, please add at least one.' if !::Guard.guards.nil? && ::Guard.guards.empty?
|
||||
end
|
||||
|
||||
# Re-evaluate the `Guardfile` to update the current Guard configuration.
|
||||
#
|
||||
def reevaluate_guardfile
|
||||
::Guard.guards.clear
|
||||
::Guard.groups.clear
|
||||
@@options.delete(:guardfile_contents)
|
||||
Dsl.evaluate_guardfile(@@options)
|
||||
msg = 'Guardfile has been re-evaluated.'
|
||||
UI.info(msg)
|
||||
Notifier.notify(msg)
|
||||
end
|
||||
|
||||
# Evaluate the content of the `Guardfile`.
|
||||
#
|
||||
# @param [String] contents the content to evaluate.
|
||||
#
|
||||
def instance_eval_guardfile(contents)
|
||||
new.instance_eval(contents, @@options[:guardfile_path], 1)
|
||||
rescue
|
||||
UI.error "Invalid Guardfile, original error is:\n#{ $! }"
|
||||
exit 1
|
||||
end
|
||||
|
||||
# Test if the current `Guardfile` contains a specific Guard.
|
||||
#
|
||||
# @param [String] guard_name the name of the Guard
|
||||
# @return [Boolean] whether the Guard has been declared
|
||||
#
|
||||
def guardfile_include?(guard_name)
|
||||
guardfile_contents.match(/^guard\s*\(?\s*['":]#{ guard_name }['"]?/)
|
||||
end
|
||||
|
||||
# Read the current `Guardfile` content.
|
||||
#
|
||||
# @param [String] the path to the Guardfile
|
||||
#
|
||||
def read_guardfile(guardfile_path)
|
||||
@@options[:guardfile_path] = guardfile_path
|
||||
@@options[:guardfile_contents] = File.read(guardfile_path)
|
||||
rescue
|
||||
UI.error("Error reading file #{ guardfile_path }")
|
||||
exit 1
|
||||
end
|
||||
|
||||
# Get the content to evaluate and stores it into
|
||||
# the options as `:guardfile_contents`.
|
||||
#
|
||||
def fetch_guardfile_contents
|
||||
if @@options[:guardfile_contents]
|
||||
UI.info 'Using inline Guardfile.'
|
||||
@@options[:guardfile_path] = 'Inline Guardfile'
|
||||
|
||||
elsif @@options[: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
|
||||
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."
|
||||
|
||||
def self.evaluate_guardfile
|
||||
guardfile = "#{Dir.pwd}/Guardfile"
|
||||
if File.exists? guardfile
|
||||
begin
|
||||
dsl = new
|
||||
dsl.instance_eval(File.read(guardfile.to_s), guardfile.to_s, 1)
|
||||
rescue
|
||||
UI.error "Invalid Guardfile, original error is:\n#{$!}"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
# Get the content of the `Guardfile`.
|
||||
#
|
||||
# @return [String] the Guardfile content
|
||||
#
|
||||
def guardfile_contents
|
||||
@@options ? @@options[:guardfile_contents] : ''
|
||||
end
|
||||
|
||||
# Get the content of the `Guardfile` and the global
|
||||
# user configuration file.
|
||||
#
|
||||
# @see #user_config_path
|
||||
#
|
||||
# @return [String] the Guardfile content
|
||||
#
|
||||
def guardfile_contents_with_user_config
|
||||
config = File.read(user_config_path) if File.exist?(user_config_path)
|
||||
[guardfile_contents, config].join("\n")
|
||||
end
|
||||
|
||||
# Get the file path to the project `Guardfile`.
|
||||
#
|
||||
# @return [String] the path to the Guardfile
|
||||
#
|
||||
def guardfile_path
|
||||
@@options ? @@options[:guardfile_path] : ''
|
||||
end
|
||||
|
||||
# Tests if the current `Guardfile` content is usable.
|
||||
#
|
||||
# @return [Boolean] if the Guardfile is usable
|
||||
#
|
||||
def guardfile_contents_usable?
|
||||
guardfile_contents && guardfile_contents.size >= 'guard :a'.size # Smallest Guard definition
|
||||
end
|
||||
|
||||
# Gets the default path of the `Guardfile`. This returns the `Guardfile`
|
||||
# from the current directory when existing, or the global `.Guardfile`
|
||||
# at the home directory.
|
||||
#
|
||||
# @return [String] the path to the Guardfile
|
||||
#
|
||||
def guardfile_default_path
|
||||
File.exist?(local_guardfile_path) ? local_guardfile_path : home_guardfile_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# The path to the `Guardfile` that is located at
|
||||
# the directory, where Guard has been started from.
|
||||
#
|
||||
# @param [String] the path to the local Guardfile
|
||||
#
|
||||
def local_guardfile_path
|
||||
File.join(Dir.pwd, 'Guardfile')
|
||||
end
|
||||
|
||||
# The path to the `.Guardfile` that is located at
|
||||
# the users home directory.
|
||||
#
|
||||
# @param [String] the path to ~/.Guardfile
|
||||
#
|
||||
def home_guardfile_path
|
||||
File.expand_path(File.join('~', '.Guardfile'))
|
||||
end
|
||||
|
||||
# The path to the user configuration `.guard.rb`
|
||||
# that is located at the users home directory.
|
||||
#
|
||||
# @param [String] the path to ~/.guard.rb
|
||||
#
|
||||
def user_config_path
|
||||
File.expand_path(File.join('~', '.guard.rb'))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Declares a group of guards to be run with `guard start --group group_name`.
|
||||
#
|
||||
# @example Declare two groups of Guards
|
||||
#
|
||||
# group 'backend' do
|
||||
# guard 'spork'
|
||||
# guard 'rspec'
|
||||
# end
|
||||
#
|
||||
# group 'frontend' do
|
||||
# guard 'passenger'
|
||||
# guard 'livereload'
|
||||
# end
|
||||
#
|
||||
# @param [Symbol, String] name the group's name called from the CLI
|
||||
# @param [Hash] options the options accepted by the group
|
||||
# @yield a block where you can declare several guards
|
||||
#
|
||||
# @see Guard.add_group
|
||||
# @see Dsl#guard
|
||||
# @see Guard::DslDescriber
|
||||
#
|
||||
def group(name, options = {})
|
||||
@groups = @@options[:group] || []
|
||||
name = name.to_sym
|
||||
|
||||
if block_given? && (@groups.empty? || @groups.map(&:to_sym).include?(name))
|
||||
::Guard.add_group(name.to_s.downcase, options)
|
||||
@current_group = name
|
||||
|
||||
yield if block_given?
|
||||
|
||||
@current_group = nil
|
||||
else
|
||||
UI.error "No Guardfile in current folder, please create one."
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
# Declare a guard to be used when running `guard start`.
|
||||
#
|
||||
# The name parameter is usually the name of the gem without
|
||||
# the 'guard-' prefix.
|
||||
#
|
||||
# The available options are different for each Guard implementation.
|
||||
#
|
||||
# @example Declare a Guard
|
||||
#
|
||||
# guard 'rspec' do
|
||||
# end
|
||||
#
|
||||
# @param [String] name the Guard name
|
||||
# @param [Hash] options the options accepted by the Guard
|
||||
# @yield a block where you can declare several watch patterns and actions
|
||||
#
|
||||
# @see Guard.add_guard
|
||||
# @see Dsl#group
|
||||
# @see Dsl#watch
|
||||
# @see Guard::DslDescriber
|
||||
#
|
||||
def guard(name, options = {})
|
||||
@watchers = []
|
||||
@callbacks = []
|
||||
|
||||
yield if block_given?
|
||||
|
||||
options.update(:group => (@current_group || :default))
|
||||
::Guard.add_guard(name.to_s.downcase, @watchers, @callbacks, options)
|
||||
|
||||
def self.guardfile_included?(guard_name)
|
||||
File.read('Guardfile').include?("guard '#{guard_name}'")
|
||||
end
|
||||
|
||||
# Define a pattern to be watched in order to run actions on file modification.
|
||||
#
|
||||
# @example Declare watchers for a Guard
|
||||
#
|
||||
# guard 'rspec' do
|
||||
# watch('spec/spec_helper.rb')
|
||||
# watch(%r{^.+_spec.rb})
|
||||
# watch(%r{^app/controllers/(.+).rb}) { |m| 'spec/acceptance/#{m[1]}s_spec.rb' }
|
||||
# end
|
||||
#
|
||||
# @param [String, Regexp] pattern the pattern to be watched by the guard
|
||||
# @yield a block to be run when the pattern is matched
|
||||
# @yieldparam [MatchData] m matches of the pattern
|
||||
# @yieldreturn a directory, a filename, an array of directories / filenames, or nothing (can be an arbitrary command)
|
||||
#
|
||||
# @see Guard::Watcher
|
||||
# @see Dsl#guard
|
||||
#
|
||||
|
||||
def guard(name, options = {}, &definition)
|
||||
@watchers = []
|
||||
definition.call if definition
|
||||
::Guard.add_guard(name, @watchers, options)
|
||||
end
|
||||
|
||||
def watch(pattern, &action)
|
||||
@watchers << ::Guard::Watcher.new(pattern, action)
|
||||
end
|
||||
|
||||
# Define a callback to execute arbitrary code before or after any of
|
||||
# the `start`, `stop`, `reload`, `run_all` and `run_on_change` guards' method.
|
||||
#
|
||||
# @param [Array] args the callback arguments
|
||||
# @yield a block with listeners
|
||||
#
|
||||
# @see Guard::Hook
|
||||
#
|
||||
def callback(*args, &listener)
|
||||
listener, events = args.size > 1 ? args : [listener, args[0]]
|
||||
@callbacks << { :events => events, :listener => listener }
|
||||
end
|
||||
|
||||
# Ignore certain paths globally.
|
||||
#
|
||||
# @example Ignore some paths
|
||||
# ignore_paths ".git", ".svn"
|
||||
#
|
||||
# @param [Array] paths the list of paths to ignore
|
||||
#
|
||||
# @see Guard::Listener
|
||||
#
|
||||
def ignore_paths(*paths)
|
||||
UI.info "Ignoring paths: #{ paths.join(', ') }"
|
||||
::Guard.listener.ignore_paths.push(*paths)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -1,150 +0,0 @@
|
||||
require 'guard/dsl'
|
||||
|
||||
module Guard
|
||||
|
||||
autoload :UI, 'guard/ui'
|
||||
|
||||
# The DslDescriber overrides methods to create an internal structure
|
||||
# of the Guardfile that is used in some inspection utility methods
|
||||
# like the CLI commands `show` and `list`.
|
||||
#
|
||||
# @see Guard::Dsl
|
||||
# @see Guard::CLI
|
||||
#
|
||||
class DslDescriber < Dsl
|
||||
|
||||
class << self
|
||||
|
||||
# Evaluate the DSL methods in the `Guardfile`.
|
||||
#
|
||||
# @option options [Array<Symbol,String>] groups the groups to evaluate
|
||||
# @option options [String] guardfile the path to a valid Guardfile
|
||||
# @option options [String] guardfile_contents a string representing the content of a valid Guardfile
|
||||
# @raise [ArgumentError] when options are not a Hash
|
||||
#
|
||||
def evaluate_guardfile(options = {})
|
||||
@@guardfile_structure = [{ :guards => [] }]
|
||||
super options
|
||||
end
|
||||
|
||||
# List the Guards that are available for use in your system and marks
|
||||
# those that are currently used in your `Guardfile`.
|
||||
#
|
||||
# @example Guard list output
|
||||
#
|
||||
# Available guards:
|
||||
# bundler *
|
||||
# livereload
|
||||
# ronn
|
||||
# rspec *
|
||||
# spork
|
||||
#
|
||||
# See also https://github.com/guard/guard/wiki/List-of-available-Guards
|
||||
# * denotes ones already in your Guardfile
|
||||
#
|
||||
# @param [Hash] options the Guard options
|
||||
#
|
||||
def list(options)
|
||||
evaluate_guardfile(options)
|
||||
|
||||
installed = guardfile_structure.inject([]) do |installed, group|
|
||||
group[:guards].each { |guard| installed << guard[:name] } if group[:guards]
|
||||
installed
|
||||
end
|
||||
|
||||
UI.info 'Available guards:'
|
||||
|
||||
::Guard.guard_gem_names.sort.uniq.each do |name|
|
||||
UI.info " #{ name }#{ installed.include?(name) ? '*' : '' }"
|
||||
end
|
||||
|
||||
UI.info ''
|
||||
UI.info 'See also https://github.com/guard/guard/wiki/List-of-available-Guards'
|
||||
UI.info '* denotes ones already in your Guardfile'
|
||||
end
|
||||
|
||||
# Shows all Guards and their options that are defined in
|
||||
# the `Guardfile`.
|
||||
#
|
||||
# @example guard show output
|
||||
#
|
||||
# (global):
|
||||
# bundler
|
||||
# coffeescript: input => "app/assets/javascripts", noop => true
|
||||
# jasmine
|
||||
# rspec: cli => "--fail-fast --format Fuubar
|
||||
#
|
||||
# @param [Hash] options the Guard options
|
||||
#
|
||||
def show(options)
|
||||
evaluate_guardfile(options)
|
||||
|
||||
guardfile_structure.each do |group|
|
||||
unless group[:guards].empty?
|
||||
if group[:group]
|
||||
UI.info "Group #{ group[:group] }:"
|
||||
else
|
||||
UI.info '(global):'
|
||||
end
|
||||
|
||||
group[:guards].each do |guard|
|
||||
line = " #{ guard[:name] }"
|
||||
|
||||
unless guard[:options].empty?
|
||||
line += ": #{ guard[:options].sort.collect { |k, v| "#{ k } => #{ v.inspect }" }.join(', ') }"
|
||||
end
|
||||
|
||||
UI.info line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
UI.info ''
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Get the Guardfile structure.
|
||||
#
|
||||
# @return [Array<Hash>] the structure
|
||||
#
|
||||
def guardfile_structure
|
||||
@@guardfile_structure
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Declares a group of guards.
|
||||
#
|
||||
# @param [String] name the group's name called from the CLI
|
||||
# @yield a block where you can declare several guards
|
||||
#
|
||||
# @see Guard::Dsl#group
|
||||
#
|
||||
def group(name)
|
||||
@@guardfile_structure << { :group => name.to_sym, :guards => [] }
|
||||
@group = true
|
||||
|
||||
yield if block_given?
|
||||
|
||||
@group = false
|
||||
end
|
||||
|
||||
# Declares a Guard.
|
||||
#
|
||||
# @param [String] name the Guard name
|
||||
# @param [Hash] options the options accepted by the Guard
|
||||
# @yield a block where you can declare several watch patterns and actions
|
||||
#
|
||||
# @see Guard::Dsl#guard
|
||||
#
|
||||
def guard(name, options = { })
|
||||
node = (@group ? @@guardfile_structure.last : @@guardfile_structure.first)
|
||||
|
||||
node[:guards] << { :name => name, :options => options }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,37 +0,0 @@
|
||||
module Guard
|
||||
|
||||
# A group of Guards. There are two reasons why you want to group your guards:
|
||||
#
|
||||
# - You can start only certain Groups from the command line by passing the `--group` option.
|
||||
# - Abort task execution chain on failure within a group.
|
||||
#
|
||||
# @example Group that aborts on failure
|
||||
#
|
||||
# group :frontend, :halt_on_fail => true do
|
||||
# guard 'coffeescript', :input => 'spec/coffeescripts', :output => 'spec/javascripts'
|
||||
# guard 'jasmine-headless-webkit' do
|
||||
# watch(%r{^spec/javascripts/(.*)\..*}) { |m| newest_js_file("spec/javascripts/#{m[1]}_spec") }
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# @see Guard::CLI
|
||||
#
|
||||
class Group
|
||||
|
||||
attr_accessor :name, :options
|
||||
|
||||
# Initialize a Group.
|
||||
#
|
||||
# @param [String] name the name of the group
|
||||
# @param [Hash] options the group options
|
||||
# @option options [Boolean] halt_on_fail if a task execution
|
||||
# should be halted for all Guards in this group if one Guard throws `:task_has_failed`
|
||||
#
|
||||
def initialize(name, options = {})
|
||||
@name = name.to_sym
|
||||
@options = options
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
@ -1,129 +1,80 @@
|
||||
module Guard
|
||||
|
||||
# Main class that every Guard implementation must subclass.
|
||||
#
|
||||
# Guard will trigger the `start`, `stop`, `reload`, `run_all`, `run_on_change` and
|
||||
# `run_on_deletion` task methods depending on user interaction and file modification.
|
||||
#
|
||||
# In each of these Guard task methods you have to implement some work when you want to
|
||||
# support this kind of task. The return value of each Guard task method is not evaluated
|
||||
# by Guard, but I'll be passed to the "_end" hook for further evaluation. You can
|
||||
# throw `:task_has_failed` to indicate that your Guard method was not successful,
|
||||
# and successive guard tasks will be aborted when the group has set the `:halt_on_fail`
|
||||
# option.
|
||||
#
|
||||
# @see Guard::Hook
|
||||
# @see Guard::Group
|
||||
#
|
||||
# @example Throw :task_has_failed
|
||||
#
|
||||
# def run_all
|
||||
# if !runner.run(['all'])
|
||||
# throw :task_has_failed
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Each Guard should provide a template Guardfile located within the Gem
|
||||
# at `lib/guard/guard-name/templates/Guardfile`.
|
||||
#
|
||||
# By default all watchers for a Guard are returning strings of paths to the
|
||||
# Guard, but if your Guard want to allow any return value from a watcher,
|
||||
# you can set the `any_return` option to true.
|
||||
#
|
||||
# If one of those methods raise an exception other than `:task_has_failed`,
|
||||
# the Guard::GuardName instance will be removed from the active guards.
|
||||
#
|
||||
class Guard
|
||||
include Hook
|
||||
attr_accessor :watchers, :options
|
||||
|
||||
attr_accessor :watchers, :options, :group
|
||||
|
||||
# Initialize a Guard.
|
||||
#
|
||||
# @param [Array<Guard::Watcher>] watchers the Guard file watchers
|
||||
# @param [Hash] options the custom Guard options
|
||||
# @options [Symbol] group the group this Guard belongs to
|
||||
# @options [Boolean] any_return allow any object to be returned from a watcher
|
||||
#
|
||||
DEFAULT_OPTIONS = {
|
||||
:reload => {:at_start => false, :disable => false},
|
||||
:run_all => {:at_start => false, :disable => false}
|
||||
}.freeze
|
||||
|
||||
def initialize(watchers = [], options = {})
|
||||
@group = options[:group] ? options.delete(:group).to_sym : :default
|
||||
@watchers, @options = watchers, options
|
||||
@watchers, @options = watchers, DEFAULT_OPTIONS.merge(options)
|
||||
end
|
||||
|
||||
# Initialize the Guard. This will copy the Guardfile template inside the Guard gem.
|
||||
# The template Guardfile must be located within the Gem at `lib/guard/guard-name/templates/Guardfile`.
|
||||
#
|
||||
# @param [String] name the name of the Guard
|
||||
#
|
||||
|
||||
# Guardfile template needed inside guard gem
|
||||
def self.init(name)
|
||||
if ::Guard::Dsl.guardfile_include?(name)
|
||||
::Guard::UI.info "Guardfile already includes #{ name } guard"
|
||||
if ::Guard::Dsl.guardfile_included?(name)
|
||||
::Guard::UI.info "Guardfile already include #{name} guard"
|
||||
else
|
||||
content = File.read('Guardfile')
|
||||
guard = File.read("#{ ::Guard.locate_guard(name) }/lib/guard/#{ name }/templates/Guardfile")
|
||||
|
||||
guard = File.read("#{::Guard.locate_guard(name)}/lib/guard/#{name}/templates/Guardfile")
|
||||
File.open('Guardfile', 'wb') do |f|
|
||||
f.puts(content)
|
||||
f.puts("")
|
||||
f.puts(guard)
|
||||
f.puts content
|
||||
f.puts ""
|
||||
f.puts guard
|
||||
end
|
||||
|
||||
::Guard::UI.info "#{ name } guard added to Guardfile, feel free to edit it"
|
||||
::Guard::UI.info "#{name} guard added to Guardfile, feel free to edit it"
|
||||
end
|
||||
end
|
||||
|
||||
# Call once when Guard starts. Please override initialize method to init stuff.
|
||||
#
|
||||
# @raise [:task_has_failed] when start has failed
|
||||
# @return [Object] the task result
|
||||
#
|
||||
|
||||
# ================
|
||||
# = Guard method =
|
||||
# ================
|
||||
|
||||
# Call once when guard starts
|
||||
# Please override initialize method to init stuff
|
||||
def start
|
||||
true
|
||||
end
|
||||
|
||||
# Called when `stop|quit|exit|s|q|e + enter` is pressed (when Guard quits).
|
||||
#
|
||||
# @raise [:task_has_failed] when stop has failed
|
||||
# @return [Object] the task result
|
||||
#
|
||||
|
||||
# Call once when guard quit
|
||||
def stop
|
||||
true
|
||||
end
|
||||
|
||||
# Called when `reload|r|z + enter` is pressed.
|
||||
# This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
||||
#
|
||||
# @raise [:task_has_failed] when reload has failed
|
||||
# @return [Object] the task result
|
||||
#
|
||||
|
||||
# Should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
||||
def reload
|
||||
true
|
||||
end
|
||||
|
||||
# Called when just `enter` is pressed
|
||||
# This method should be principally used for long action like running all specs/tests/...
|
||||
#
|
||||
# @raise [:task_has_failed] when run_all has failed
|
||||
# @return [Object] the task result
|
||||
#
|
||||
# disable reload method if return true. When is overwriten, must return false if `super' return false
|
||||
def reload?
|
||||
options[:reload] == true || (options[:reload].is_a?(Hash) && !options[:reload][:disable])
|
||||
end
|
||||
|
||||
# enable call of reload method after start method if return true. When is overwriten, must return false if `super' return false
|
||||
def reload_at_start?
|
||||
options[:reload].is_a?(Hash) && !!options[:reload][:at_start]
|
||||
end
|
||||
|
||||
# Should be principally used for long action like running all specs/tests/...
|
||||
def run_all
|
||||
true
|
||||
end
|
||||
|
||||
# Called on file(s) modifications that the Guard watches.
|
||||
#
|
||||
# @param [Array<String>] paths the changes files or paths
|
||||
# @raise [:task_has_failed] when run_on_change has failed
|
||||
# @return [Object] the task result
|
||||
#
|
||||
# disable run_all method if return true. When is overwriten, must return false if `super' return false
|
||||
def run_all?
|
||||
options[:run_all] == true || (options[:run_all].is_a?(Hash) && !options[:run_all][:disable])
|
||||
end
|
||||
|
||||
# enable call of run_all method after start method if return true. When is overwriten, must return false if `super' return false
|
||||
def run_all_at_start?
|
||||
options[:run_all].is_a?(Hash) && !!options[:run_all][:at_start]
|
||||
end
|
||||
|
||||
def run_on_change(paths)
|
||||
true
|
||||
end
|
||||
|
||||
# Called on file(s) deletions that the Guard watches.
|
||||
#
|
||||
# @param [Array<String>] paths the deleted files or paths
|
||||
# @raise [:task_has_failed] when run_on_change has failed
|
||||
# @return [Object] the task result
|
||||
#
|
||||
def run_on_deletion(paths)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,118 +0,0 @@
|
||||
module Guard
|
||||
|
||||
# Guard has a hook mechanism that allows you to insert callbacks for individual Guards.
|
||||
# By default, each of the Guard instance methods has a "_begin" and an "_end" hook.
|
||||
# For example, the Guard::Guard#start method has a :start_begin hook that is runs immediately
|
||||
# before Guard::Guard#start, and a :start_end hook that runs immediately after Guard::Guard#start.
|
||||
#
|
||||
# Read more about [hooks and callbacks on the wiki](https://github.com/guard/guard/wiki/Hooks-and-callbacks).
|
||||
#
|
||||
module Hook
|
||||
|
||||
# The Hook module gets included.
|
||||
#
|
||||
# @param [Class] base the class that includes the module
|
||||
#
|
||||
def self.included(base)
|
||||
base.send :include, InstanceMethods
|
||||
end
|
||||
|
||||
# Instance methods that gets included in the base class.
|
||||
#
|
||||
module InstanceMethods
|
||||
|
||||
# When event is a Symbol, {#hook} will generate a hook name
|
||||
# by concatenating the method name from where {#hook} is called
|
||||
# with the given Symbol.
|
||||
#
|
||||
# @example Add a hook with a Symbol
|
||||
#
|
||||
# def run_all
|
||||
# hook :foo
|
||||
# end
|
||||
#
|
||||
# Here, when {Guard::Guard#run_all} is called, {#hook} will notify callbacks
|
||||
# registered for the "run_all_foo" event.
|
||||
#
|
||||
# When event is a String, {#hook} will directly turn the String
|
||||
# into a Symbol.
|
||||
#
|
||||
# @example Add a hook with a String
|
||||
#
|
||||
# def run_all
|
||||
# hook "foo_bar"
|
||||
# end
|
||||
#
|
||||
# When {Guard::Guard#run_all} is called, {#hook} will notify callbacks
|
||||
# registered for the "foo_bar" event.
|
||||
#
|
||||
# @param [Symbol, String] event the name of the Guard event
|
||||
# @param [Array] args the parameters are passed as is to the callbacks registered for the given event.
|
||||
#
|
||||
def hook(event, *args)
|
||||
hook_name = if event.is_a? Symbol
|
||||
calling_method = caller[0][/`([^']*)'/, 1]
|
||||
"#{ calling_method }_#{ event }"
|
||||
else
|
||||
event
|
||||
end.to_sym
|
||||
|
||||
UI.debug "Hook :#{ hook_name } executed for #{ self.class }"
|
||||
|
||||
Hook.notify(self.class, hook_name, *args)
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
|
||||
# Get all callbacks.
|
||||
#
|
||||
def callbacks
|
||||
@callbacks ||= Hash.new { |hash, key| hash[key] = [] }
|
||||
end
|
||||
|
||||
# Add a callback.
|
||||
#
|
||||
# @param [Block] listener the listener to notify
|
||||
# @param [Guard::Guard] guard_class the Guard class to add the callback
|
||||
# @param [Array<Symbol>] events the events to register
|
||||
#
|
||||
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
|
||||
|
||||
# Checks if a callback has been registered.
|
||||
#
|
||||
# @param [Block] listener the listener to notify
|
||||
# @param [Guard::Guard] guard_class the Guard class to add the callback
|
||||
# @param [Symbol] event the event to look for
|
||||
#
|
||||
def has_callback?(listener, guard_class, event)
|
||||
callbacks[[guard_class, event]].include?(listener)
|
||||
end
|
||||
|
||||
# Notify a callback.
|
||||
#
|
||||
# @param [Guard::Guard] guard_class the Guard class to add the callback
|
||||
# @param [Symbol] event the event to trigger
|
||||
# @param [Array] args the arguments for the listener
|
||||
#
|
||||
def notify(guard_class, event, *args)
|
||||
callbacks[[guard_class, event]].each do |listener|
|
||||
listener.call(guard_class, event, *args)
|
||||
end
|
||||
end
|
||||
|
||||
# Reset all callbacks.
|
||||
#
|
||||
def reset_callbacks!
|
||||
@callbacks = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,44 +1,29 @@
|
||||
module Guard
|
||||
|
||||
# The interactor reads user input and triggers
|
||||
# specific action upon them unless its locked.
|
||||
#
|
||||
# Currently the following actions are implemented:
|
||||
#
|
||||
# - stop, quit, exit, s, q, e => Exit Guard
|
||||
# - reload, r, z => Reload Guard
|
||||
# - pause, p => Pause Guard
|
||||
# - Everything else => Run all
|
||||
#
|
||||
class Interactor
|
||||
# Start the interactor in its own thread.
|
||||
#
|
||||
def start
|
||||
return if ENV["GUARD_ENV"] == 'test'
|
||||
|
||||
if !@thread || @thread.stop?
|
||||
@thread = Thread.new do
|
||||
while entry = $stdin.gets.chomp
|
||||
case entry
|
||||
when 'stop', 'quit', 'exit', 's', 'q', 'e'
|
||||
::Guard.stop
|
||||
when 'reload', 'r', 'z'
|
||||
::Guard::Dsl.reevaluate_guardfile
|
||||
::Guard.reload
|
||||
when 'pause', 'p'
|
||||
::Guard.pause
|
||||
else
|
||||
::Guard.run_all
|
||||
end
|
||||
end
|
||||
module Interactor
|
||||
|
||||
def self.init_signal_traps
|
||||
# Run all (Ctrl-\)
|
||||
Signal.trap('QUIT') do
|
||||
::Guard.run do
|
||||
::Guard.guards.each { |g| ::Guard.supervised_task g, :run_all }
|
||||
end
|
||||
end
|
||||
|
||||
# Stop (Ctrl-C)
|
||||
Signal.trap('INT') do
|
||||
::Guard.listener.stop
|
||||
::Guard.guards.each { |g| ::Guard.supervised_task g, :stop }
|
||||
UI.info "Bye bye...", :reset => true
|
||||
abort("\n")
|
||||
end
|
||||
|
||||
# Reload (Ctrl-Z)
|
||||
Signal.trap('TSTP') do
|
||||
::Guard.run do
|
||||
::Guard.guards.each { |g| ::Guard.supervised_task g, :reload }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def stop_if_not_current
|
||||
unless Thread.current == @thread
|
||||
@thread.kill
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,350 +1,58 @@
|
||||
require 'rbconfig'
|
||||
require 'digest/sha1'
|
||||
|
||||
module Guard
|
||||
|
||||
|
||||
autoload :Darwin, 'guard/listeners/darwin'
|
||||
autoload :Linux, 'guard/listeners/linux'
|
||||
autoload :Windows, 'guard/listeners/windows'
|
||||
autoload :Polling, 'guard/listeners/polling'
|
||||
|
||||
# The Listener is the base class for all listener
|
||||
# implementations.
|
||||
#
|
||||
# @abstract
|
||||
#
|
||||
|
||||
class Listener
|
||||
|
||||
# Default paths that gets ignored by the listener
|
||||
DEFAULT_IGNORE_PATHS = %w[. .. .bundle .git log tmp vendor]
|
||||
|
||||
attr_accessor :changed_files
|
||||
attr_reader :directory, :ignore_paths
|
||||
|
||||
def paused?
|
||||
@paused
|
||||
end
|
||||
|
||||
# Select the appropriate listener implementation for the
|
||||
# current OS and initializes it.
|
||||
#
|
||||
# @param [Array] args the arguments for the listener
|
||||
# @return [Guard::Listener] the chosen listener
|
||||
#
|
||||
def self.select_and_init(*args)
|
||||
attr_reader :last_event
|
||||
|
||||
def self.init
|
||||
if mac? && Darwin.usable?
|
||||
Darwin.new(*args)
|
||||
Darwin.new
|
||||
elsif linux? && Linux.usable?
|
||||
Linux.new(*args)
|
||||
elsif windows? && Windows.usable?
|
||||
Windows.new(*args)
|
||||
Linux.new
|
||||
else
|
||||
UI.info 'Using polling (Please help us to support your system better than that).'
|
||||
Polling.new(*args)
|
||||
UI.info "Using polling (Please help us to support your system better than that.)"
|
||||
Polling.new
|
||||
end
|
||||
end
|
||||
|
||||
# Initialize the listener.
|
||||
#
|
||||
# @param [String] directory the root directory to listen to
|
||||
# @option options [Boolean] relativize_paths use only relative paths
|
||||
# @option options [Array<String>] ignore_paths the paths to ignore by the listener
|
||||
#
|
||||
def initialize(directory = Dir.pwd, options = {})
|
||||
@directory = directory.to_s
|
||||
@sha1_checksums_hash = {}
|
||||
@file_timestamp_hash = {}
|
||||
@relativize_paths = options.fetch(:relativize_paths, true)
|
||||
@changed_files = []
|
||||
@paused = false
|
||||
@ignore_paths = DEFAULT_IGNORE_PATHS
|
||||
@ignore_paths |= options[:ignore_paths] if options[:ignore_paths]
|
||||
@watch_all_modifications = options.fetch(:watch_all_modifications, false)
|
||||
|
||||
|
||||
def initialize
|
||||
update_last_event
|
||||
start_reactor
|
||||
end
|
||||
|
||||
# Start the listener thread.
|
||||
#
|
||||
def start_reactor
|
||||
return if ENV["GUARD_ENV"] == 'test'
|
||||
|
||||
Thread.new do
|
||||
loop do
|
||||
if @changed_files != [] && !@paused
|
||||
changed_files = @changed_files.dup
|
||||
clear_changed_files
|
||||
::Guard.run_on_change(changed_files)
|
||||
else
|
||||
sleep 0.1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Start watching the root directory.
|
||||
#
|
||||
def start
|
||||
watch(@directory)
|
||||
timestamp_files
|
||||
end
|
||||
|
||||
# Stop listening for events.
|
||||
#
|
||||
def stop
|
||||
end
|
||||
|
||||
# Pause the listener to ignore change events.
|
||||
#
|
||||
def pause
|
||||
@paused = true
|
||||
end
|
||||
|
||||
# Unpause the listener to listen again to change events.
|
||||
#
|
||||
def run
|
||||
@paused = false
|
||||
end
|
||||
|
||||
# Clear the list of changed files.
|
||||
#
|
||||
def clear_changed_files
|
||||
@changed_files.clear
|
||||
end
|
||||
|
||||
# Store a listener callback.
|
||||
#
|
||||
# @param [Block] callback the callback to store
|
||||
#
|
||||
def on_change(&callback)
|
||||
@callback = callback
|
||||
end
|
||||
|
||||
# Updates the timestamp of the last event.
|
||||
#
|
||||
def update_last_event
|
||||
@last_event = Time.now
|
||||
end
|
||||
|
||||
# Get the modified files.
|
||||
#
|
||||
# If the `:watch_all_modifications` option is true, then moved and
|
||||
# deleted files are also reported, but prefixed by an exclamation point.
|
||||
#
|
||||
# @example Deleted or moved file
|
||||
# !/home/user/dir/file.rb
|
||||
#
|
||||
# @param [Array<String>] dirs the watched directories
|
||||
# @param [Hash] options the listener options
|
||||
# @option options [Symbol] all whether to files in sub directories
|
||||
# @return [Array<String>] paths of files that have been modified
|
||||
#
|
||||
|
||||
private
|
||||
|
||||
def modified_files(dirs, options = {})
|
||||
last_event = @last_event
|
||||
files = []
|
||||
if @watch_all_modifications
|
||||
deleted_files = @file_timestamp_hash.collect do |path, ts|
|
||||
unless File.exists?(path)
|
||||
@sha1_checksums_hash.delete(path)
|
||||
@file_timestamp_hash.delete(path)
|
||||
"!#{path}"
|
||||
end
|
||||
end
|
||||
files.concat(deleted_files.compact)
|
||||
end
|
||||
update_last_event
|
||||
files.concat(potentially_modified_files(dirs, options).select { |path| file_modified?(path, last_event) })
|
||||
|
||||
relativize_paths(files)
|
||||
files = potentially_modified_files(dirs, options).select { |path| File.file?(path) && recent_file?(path) }
|
||||
files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
|
||||
end
|
||||
|
||||
# Register a directory to watch.
|
||||
# Must be implemented by the subclasses.
|
||||
#
|
||||
# @param [String] directory the directory to watch
|
||||
#
|
||||
def watch(directory)
|
||||
raise NotImplementedError, "do whatever you want here, given the directory as only argument"
|
||||
end
|
||||
|
||||
# Get all files that are in the watched directory.
|
||||
#
|
||||
# @return [Array<String>] the list of files
|
||||
#
|
||||
def all_files
|
||||
potentially_modified_files([@directory], :all => true)
|
||||
end
|
||||
|
||||
# Scopes all given paths to the current directory.
|
||||
#
|
||||
# @param [Array<String>] paths the paths to change
|
||||
# @return [Array<String>] all paths now relative to the current dir
|
||||
#
|
||||
def relativize_paths(paths)
|
||||
return paths unless relativize_paths?
|
||||
paths.map do |path|
|
||||
path.gsub(%r{^(!)?#{ @directory }/},'\1')
|
||||
end
|
||||
end
|
||||
|
||||
# Use paths relative to the current directory.
|
||||
#
|
||||
# @return [Boolean] whether to use relative or absolute paths
|
||||
#
|
||||
def relativize_paths?
|
||||
!!@relativize_paths
|
||||
end
|
||||
|
||||
# Populate initial timestamp file hash to watch for deleted or moved files.
|
||||
#
|
||||
def timestamp_files
|
||||
all_files.each {|path| set_file_timestamp_hash(path, file_timestamp(path)) } if @watch_all_modifications
|
||||
end
|
||||
|
||||
# Removes the ignored paths from the directory list.
|
||||
#
|
||||
# @param [Array<String>] dirs the directory to listen to
|
||||
# @param [Array<String>] ignore_paths the paths to ignore
|
||||
# @return children of the passed dirs that are not in the ignore_paths list
|
||||
#
|
||||
def exclude_ignored_paths(dirs, ignore_paths = self.ignore_paths)
|
||||
Dir.glob(dirs.map { |d| "#{d.sub(%r{/+$}, '')}/*" }, File::FNM_DOTMATCH).reject do |path|
|
||||
ignore_paths.include?(File.basename(path))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Gets a list of files that are in the modified directories.
|
||||
#
|
||||
# @param [Array<String>] dirs the list of directories
|
||||
# @param [Hash] options the find file option
|
||||
# @option options [Symbol] all whether to files in sub directories
|
||||
#
|
||||
|
||||
def potentially_modified_files(dirs, options = {})
|
||||
paths = exclude_ignored_paths(dirs)
|
||||
|
||||
if options[:all]
|
||||
paths.inject([]) do |array, path|
|
||||
if File.file?(path)
|
||||
array << path
|
||||
else
|
||||
array += Dir.glob("#{ path }/**/*", File::FNM_DOTMATCH).select { |p| File.file?(p) }
|
||||
end
|
||||
array
|
||||
end
|
||||
else
|
||||
paths.select { |path| File.file?(path) }
|
||||
end
|
||||
match = options[:all] ? "**/*" : "*"
|
||||
Dir.glob(dirs.map { |dir| "#{dir}#{match}" })
|
||||
end
|
||||
|
||||
# Test if the file content has changed.
|
||||
#
|
||||
# Depending on the filesystem, mtime/ctime is probably only precise to the second, so round
|
||||
# both values down to the second for the comparison.
|
||||
#
|
||||
# ctime is used only on == comparison to always catches Rails 3.1 Assets pipelined on Mac OSX
|
||||
#
|
||||
# @param [String] path the file path
|
||||
# @param [Time] last_event the time of the last event
|
||||
# @return [Boolean] Whether the file content has changed or not.
|
||||
#
|
||||
def file_modified?(path, last_event)
|
||||
ctime = File.ctime(path).to_i
|
||||
mtime = File.mtime(path).to_i
|
||||
if [mtime, ctime].max == last_event.to_i
|
||||
file_content_modified?(path, sha1_checksum(path))
|
||||
elsif mtime > last_event.to_i
|
||||
set_sha1_checksums_hash(path, sha1_checksum(path))
|
||||
true
|
||||
elsif @watch_all_modifications
|
||||
ts = file_timestamp(path)
|
||||
if ts != @file_timestamp_hash[path]
|
||||
set_file_timestamp_hash(path, ts)
|
||||
true
|
||||
end
|
||||
else
|
||||
false
|
||||
end
|
||||
|
||||
def recent_file?(file)
|
||||
File.mtime(file) >= last_event
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
# Tests if the file content has been modified by
|
||||
# comparing the SHA1 checksum.
|
||||
#
|
||||
# @param [String] path the file path
|
||||
# @param [String] sha1_checksum the checksum of the file
|
||||
#
|
||||
def file_content_modified?(path, sha1_checksum)
|
||||
if @sha1_checksums_hash[path] != sha1_checksum
|
||||
set_sha1_checksums_hash(path, sha1_checksum)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
|
||||
def update_last_event
|
||||
@last_event = Time.now
|
||||
end
|
||||
|
||||
# Set save a files current timestamp
|
||||
#
|
||||
# @param [String] path the file path
|
||||
# @param [Int] file_timestamp the files modified timestamp
|
||||
#
|
||||
def set_file_timestamp_hash(path, file_timestamp)
|
||||
@file_timestamp_hash[path] = file_timestamp
|
||||
end
|
||||
|
||||
# Set the current checksum of a file.
|
||||
#
|
||||
# @param [String] path the file path
|
||||
# @param [String] sha1_checksum the checksum of the file
|
||||
#
|
||||
def set_sha1_checksums_hash(path, sha1_checksum)
|
||||
@sha1_checksums_hash[path] = sha1_checksum
|
||||
end
|
||||
|
||||
# Gets a files modified timestamp
|
||||
#
|
||||
# @path [String] path the file path
|
||||
# @return [Int] file modified timestamp
|
||||
#
|
||||
def file_timestamp(path)
|
||||
File.mtime(path).to_i
|
||||
end
|
||||
|
||||
# Calculates the SHA1 checksum of a file.
|
||||
#
|
||||
# @param [String] path the path to the file
|
||||
# @return [String] the SHA1 checksum
|
||||
#
|
||||
def sha1_checksum(path)
|
||||
Digest::SHA1.file(path).to_s
|
||||
end
|
||||
|
||||
# Test if the OS is Mac OS X.
|
||||
#
|
||||
# @return [Boolean] Whether the OS is Mac OS X
|
||||
#
|
||||
|
||||
def self.mac?
|
||||
RbConfig::CONFIG['target_os'] =~ /darwin/i
|
||||
Config::CONFIG['target_os'] =~ /darwin/i
|
||||
end
|
||||
|
||||
# Test if the OS is Linux.
|
||||
#
|
||||
# @return [Boolean] Whether the OS is Linux
|
||||
#
|
||||
|
||||
def self.linux?
|
||||
RbConfig::CONFIG['target_os'] =~ /linux/i
|
||||
Config::CONFIG['target_os'] =~ /linux/i
|
||||
end
|
||||
|
||||
# Test if the OS is Windows.
|
||||
#
|
||||
# @return [Boolean] Whether the OS is Windows
|
||||
#
|
||||
def self.windows?
|
||||
RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,66 +1,40 @@
|
||||
module Guard
|
||||
|
||||
# Listener implementation for Mac OS X `FSEvents`.
|
||||
#
|
||||
class Darwin < Listener
|
||||
|
||||
# Initialize the Listener.
|
||||
#
|
||||
def initialize(*)
|
||||
attr_reader :fsevent
|
||||
|
||||
def initialize
|
||||
super
|
||||
@fsevent = FSEvent.new
|
||||
end
|
||||
|
||||
# Start the listener.
|
||||
#
|
||||
|
||||
def on_change(&callback)
|
||||
@fsevent.watch Dir.pwd do |modified_dirs|
|
||||
files = modified_files(modified_dirs)
|
||||
update_last_event
|
||||
callback.call(files)
|
||||
end
|
||||
end
|
||||
|
||||
def start
|
||||
super
|
||||
worker.run
|
||||
@fsevent.run
|
||||
end
|
||||
|
||||
# Stop the listener.
|
||||
#
|
||||
|
||||
def stop
|
||||
super
|
||||
worker.stop
|
||||
@fsevent.stop
|
||||
end
|
||||
|
||||
# Check if the listener is usable on the current OS.
|
||||
#
|
||||
# @return [Boolean] whether usable or not
|
||||
#
|
||||
|
||||
def self.usable?
|
||||
require 'rb-fsevent'
|
||||
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)'
|
||||
if !defined?(FSEvent::VERSION) || Gem::Version.new(FSEvent::VERSION) < Gem::Version.new('0.3.5')
|
||||
UI.info "Please update rb-fsevent (>= 0.3.5)"
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
rescue LoadError
|
||||
UI.info 'Please install rb-fsevent gem for Mac OSX FSEvents support'
|
||||
UI.info "Please install rb-fsevent gem for Mac OSX FSEvents support"
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Get the listener worker.
|
||||
#
|
||||
def worker
|
||||
@fsevent
|
||||
end
|
||||
|
||||
# Watch the given directory for file changes.
|
||||
#
|
||||
# @param [String] directory the directory to watch
|
||||
#
|
||||
def watch(directory)
|
||||
worker.watch(directory) do |modified_dirs|
|
||||
files = modified_files(modified_dirs)
|
||||
@callback.call(files) unless files.empty?
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,65 +1,28 @@
|
||||
module Guard
|
||||
|
||||
# Listener implementation for Linux `inotify`.
|
||||
#
|
||||
class Linux < Listener
|
||||
attr_reader :inotify, :files, :latency, :callback
|
||||
|
||||
# Initialize the Listener.
|
||||
#
|
||||
def initialize(*)
|
||||
def initialize
|
||||
super
|
||||
|
||||
@inotify = INotify::Notifier.new
|
||||
@files = []
|
||||
@latency = 0.5
|
||||
end
|
||||
|
||||
# Start the listener.
|
||||
#
|
||||
def start
|
||||
@stop = false
|
||||
super
|
||||
watch_change unless watch_change?
|
||||
end
|
||||
|
||||
# Stop the listener.
|
||||
#
|
||||
def stop
|
||||
super
|
||||
@stop = true
|
||||
sleep latency
|
||||
end
|
||||
|
||||
# Check if the listener is usable on the current OS.
|
||||
#
|
||||
# @return [Boolean] whether usable or not
|
||||
#
|
||||
def self.usable?
|
||||
require 'rb-inotify'
|
||||
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
|
||||
end
|
||||
rescue LoadError
|
||||
UI.info 'Please install rb-inotify gem for Linux inotify support'
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Get the listener worker.
|
||||
#
|
||||
def worker
|
||||
@inotify
|
||||
end
|
||||
|
||||
# Watch the given directory for file changes.
|
||||
#
|
||||
# @param [String] directory the directory to watch
|
||||
#
|
||||
def watch(directory)
|
||||
worker.watch(directory, :recursive, :attrib, :create, :move_self, :close_write) do |event|
|
||||
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
|
||||
@ -67,31 +30,44 @@ module Guard
|
||||
rescue Interrupt
|
||||
end
|
||||
|
||||
# Test if inotify is watching for changes.
|
||||
#
|
||||
# @return [Boolean] whether inotify is active or not
|
||||
#
|
||||
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)"
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
rescue LoadError
|
||||
UI.info "Please install rb-inotify gem for Linux inotify support"
|
||||
false
|
||||
end
|
||||
|
||||
def watch_change?
|
||||
!!@watch_change
|
||||
end
|
||||
|
||||
# Watch for file system changes.
|
||||
#
|
||||
private
|
||||
|
||||
def watch_change
|
||||
@watch_change = true
|
||||
until @stop
|
||||
if RbConfig::CONFIG['build'] =~ /java/ || IO.select([worker.to_io], [], [], @latency)
|
||||
while !@stop
|
||||
if Config::CONFIG['build'] =~ /java/ || IO.select([inotify.to_io], [], [], latency)
|
||||
break if @stop
|
||||
|
||||
sleep(@latency)
|
||||
worker.process
|
||||
sleep latency
|
||||
inotify.process
|
||||
update_last_event
|
||||
|
||||
files = modified_files(@files.shift(@files.size).map { |f| File.dirname(f) }.uniq)
|
||||
@callback.call(files) unless files.empty?
|
||||
unless files.empty?
|
||||
files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
|
||||
callback.call(files)
|
||||
files.clear
|
||||
end
|
||||
end
|
||||
end
|
||||
@watch_change = false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,55 +1,37 @@
|
||||
module Guard
|
||||
|
||||
# Polling listener that works cross-platform and
|
||||
# has no dependencies. This is the listener that
|
||||
# uses the most CPU processing power and has higher
|
||||
# file IO that the other implementations.
|
||||
#
|
||||
class Polling < Listener
|
||||
|
||||
# Initialize the Listener.
|
||||
#
|
||||
def initialize(*)
|
||||
attr_reader :callback, :latency
|
||||
|
||||
def initialize
|
||||
super
|
||||
@latency = 1.5
|
||||
end
|
||||
|
||||
# Start the listener.
|
||||
#
|
||||
|
||||
def on_change(&callback)
|
||||
@callback = callback
|
||||
end
|
||||
|
||||
def start
|
||||
@stop = false
|
||||
super
|
||||
watch_change
|
||||
end
|
||||
|
||||
# Stop the listener.
|
||||
#
|
||||
|
||||
def stop
|
||||
super
|
||||
@stop = true
|
||||
end
|
||||
|
||||
# Watch the given directory for file changes.
|
||||
#
|
||||
# @param [String] directory the directory to watch
|
||||
#
|
||||
def watch(directory)
|
||||
@existing = all_files
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Watch for file system changes.
|
||||
#
|
||||
|
||||
private
|
||||
|
||||
def watch_change
|
||||
until @stop
|
||||
while !@stop
|
||||
start = Time.now.to_f
|
||||
files = modified_files([@directory], :all => true)
|
||||
@callback.call(files) unless files.empty?
|
||||
nap_time = @latency - (Time.now.to_f - start)
|
||||
files = modified_files([Dir.pwd + '/'], :all => true)
|
||||
update_last_event
|
||||
callback.call(files) unless files.empty?
|
||||
nap_time = latency - (Time.now.to_f - start)
|
||||
sleep(nap_time) if nap_time > 0
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,61 +0,0 @@
|
||||
module Guard
|
||||
|
||||
# Listener implementation for Windows `fchange`.
|
||||
#
|
||||
class Windows < Listener
|
||||
|
||||
# Initialize the Listener.
|
||||
#
|
||||
def initialize(*)
|
||||
super
|
||||
@fchange = FChange::Notifier.new
|
||||
end
|
||||
|
||||
# Start the listener.
|
||||
#
|
||||
def start
|
||||
super
|
||||
worker.run
|
||||
end
|
||||
|
||||
# Stop the listener.
|
||||
#
|
||||
def stop
|
||||
super
|
||||
worker.stop
|
||||
end
|
||||
|
||||
# Check if the listener is usable on the current OS.
|
||||
#
|
||||
# @return [Boolean] whether usable or not
|
||||
#
|
||||
def self.usable?
|
||||
require 'rb-fchange'
|
||||
true
|
||||
rescue LoadError
|
||||
UI.info 'Please install rb-fchange gem for Windows file events support'
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Watch the given directory for file changes.
|
||||
#
|
||||
# @param [String] directory the directory to watch
|
||||
#
|
||||
def watch(directory)
|
||||
worker.watch(directory, :all_events, :recursive) do |event|
|
||||
paths = [File.expand_path(event.watcher.path)]
|
||||
files = modified_files(paths, :all => true)
|
||||
@callback.call(files) unless files.empty?
|
||||
end
|
||||
end
|
||||
|
||||
# Get the listener worker.
|
||||
#
|
||||
def worker
|
||||
@fchange
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,290 +1,62 @@
|
||||
require 'rbconfig'
|
||||
require 'pathname'
|
||||
require 'guard/ui'
|
||||
|
||||
module Guard
|
||||
|
||||
# The notifier class handles cross-platform system notifications that supports:
|
||||
#
|
||||
# - Growl on Mac OS X
|
||||
# - Libnotify on Linux
|
||||
# - Notifu on Windows
|
||||
#
|
||||
module Notifier
|
||||
|
||||
# Application name as shown in the specific notification settings
|
||||
APPLICATION_NAME = "Guard"
|
||||
|
||||
class << self
|
||||
|
||||
attr_accessor :growl_library, :gntp
|
||||
|
||||
# Turn notifications off.
|
||||
#
|
||||
def turn_off
|
||||
ENV["GUARD_NOTIFY"] = 'false'
|
||||
end
|
||||
|
||||
# Turn notifications on. This tries to load the platform
|
||||
# specific notification library.
|
||||
#
|
||||
# @return [Boolean] whether the notification could be enabled.
|
||||
#
|
||||
def turn_on
|
||||
ENV["GUARD_NOTIFY"] = 'true'
|
||||
case RbConfig::CONFIG['target_os']
|
||||
when /darwin/i
|
||||
require_growl
|
||||
when /linux/i
|
||||
require_libnotify
|
||||
when /mswin|mingw/i
|
||||
require_rbnotifu
|
||||
end
|
||||
end
|
||||
|
||||
# Show a message with the system notification.
|
||||
#
|
||||
# @see .image_path
|
||||
#
|
||||
# @param [String] the message to show
|
||||
# @option options [Symbol, String] image the image symbol or path to an image
|
||||
# @option options [String] title the notification title
|
||||
#
|
||||
def notify(message, options = { })
|
||||
if enabled?
|
||||
image = options.delete(:image) || :success
|
||||
title = options.delete(:title) || "Guard"
|
||||
|
||||
case RbConfig::CONFIG['target_os']
|
||||
when /darwin/i
|
||||
notify_mac(title, message, image, options)
|
||||
when /linux/i
|
||||
notify_linux(title, message, image, options)
|
||||
when /mswin|mingw/i
|
||||
notify_windows(title, message, image, options)
|
||||
|
||||
def self.notify(message, options = {})
|
||||
unless ENV["GUARD_ENV"] == "test"
|
||||
image = options[:image] || :success
|
||||
title = options[:title] || "Guard"
|
||||
case Config::CONFIG['target_os']
|
||||
when /darwin/i
|
||||
if growl_installed?
|
||||
Growl.notify message, :title => title, :icon => image_path(image), :name => "Guard"
|
||||
end
|
||||
when /linux/i
|
||||
if libnotify_installed?
|
||||
Libnotify.show :body => message, :summary => title, :icon_path => image_path(image)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Test if the notifications are enabled and available.
|
||||
#
|
||||
# @return [Boolean] whether the notifications are available
|
||||
#
|
||||
def enabled?
|
||||
ENV["GUARD_NOTIFY"] == 'true'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Send a message to Growl either with the `growl` gem or the `growl_notify` gem.
|
||||
#
|
||||
# @param [String] title the notification title
|
||||
# @param [String] message the message to show
|
||||
# @param [Symbol, String] the image to user
|
||||
# @param [Hash] options the growl options
|
||||
#
|
||||
def notify_mac(title, message, image, options = { })
|
||||
require_growl # need for guard-rspec formatter that is called out of guard scope
|
||||
|
||||
notification = { :title => title, :icon => image_path(image) }.merge(options)
|
||||
|
||||
case self.growl_library
|
||||
when :growl_notify
|
||||
notification.delete(:name)
|
||||
|
||||
GrowlNotify.send_notification({
|
||||
:description => message,
|
||||
:application_name => APPLICATION_NAME
|
||||
}.merge(notification))
|
||||
|
||||
when :ruby_gntp
|
||||
icon = "file://#{ notification.delete(:icon) }"
|
||||
|
||||
self.gntp.notify({
|
||||
:name => [:pending, :success, :failed].include?(image) ? image.to_s : 'notify',
|
||||
:text => message,
|
||||
:icon => icon
|
||||
}.merge(notification))
|
||||
|
||||
when :growl
|
||||
Growl.notify(message, {
|
||||
:name => APPLICATION_NAME
|
||||
}.merge(notification))
|
||||
end
|
||||
end
|
||||
|
||||
# Send a message to libnotify.
|
||||
#
|
||||
# @param [String] title the notification title
|
||||
# @param [String] message the message to show
|
||||
# @param [Symbol, String] the image to user
|
||||
# @param [Hash] options the libnotify options
|
||||
#
|
||||
def notify_linux(title, message, image, options = { })
|
||||
require_libnotify # need for guard-rspec formatter that is called out of guard scope
|
||||
|
||||
notification = { :body => message, :summary => title, :icon_path => image_path(image), :transient => true }
|
||||
Libnotify.show notification.merge(options)
|
||||
end
|
||||
|
||||
# Send a message to notifu.
|
||||
#
|
||||
# @param [String] title the notification title
|
||||
# @param [String] message the message to show
|
||||
# @param [Symbol, String] the image to user
|
||||
# @param [Hash] options the notifu options
|
||||
#
|
||||
def notify_windows(title, message, image, options = { })
|
||||
require_rbnotifu # need for guard-rspec formatter that is called out of guard scope
|
||||
|
||||
notification = { :message => message, :title => title, :type => image_level(image), :time => 3 }
|
||||
Notifu.show notification.merge(options)
|
||||
end
|
||||
|
||||
# Get the image path for an image symbol.
|
||||
#
|
||||
# Known symbols are:
|
||||
#
|
||||
# - failed
|
||||
# - pending
|
||||
# - success
|
||||
#
|
||||
# @param [Symbol] image the image name
|
||||
# @return [String] the image path
|
||||
#
|
||||
def image_path(image)
|
||||
images_path = Pathname.new(File.dirname(__FILE__)).join('../../images')
|
||||
case image
|
||||
when :failed
|
||||
images_path.join("failed.png").to_s
|
||||
when :pending
|
||||
images_path.join("pending.png").to_s
|
||||
when :success
|
||||
images_path.join("success.png").to_s
|
||||
else
|
||||
# path given
|
||||
image
|
||||
end
|
||||
end
|
||||
|
||||
# The notification level type for the given image.
|
||||
#
|
||||
# @param [Symbol] image the image
|
||||
# @return [Symbol] the level
|
||||
#
|
||||
def image_level(image)
|
||||
case image
|
||||
when :failed
|
||||
:error
|
||||
when :pending
|
||||
:warn
|
||||
when :success
|
||||
:info
|
||||
else
|
||||
:info
|
||||
end
|
||||
end
|
||||
|
||||
# Try to safely load growl and turns notifications off on load failure.
|
||||
# The Guard notifier knows three different library to handle sending
|
||||
# Growl messages and tries to loading them in the given order:
|
||||
#
|
||||
# - [Growl Notify](https://github.com/scottdavis/growl_notify)
|
||||
# - [Ruby GNTP](https://github.com/snaka/ruby_gntp)
|
||||
# - [Growl](https://github.com/visionmedia/growl)
|
||||
#
|
||||
# On successful loading of any of the libraries, the active library name is
|
||||
# accessible through `.growl_library`.
|
||||
#
|
||||
def require_growl
|
||||
self.growl_library = try_growl_notify || try_ruby_gntp || try_growl
|
||||
|
||||
unless self.growl_library
|
||||
turn_off
|
||||
UI.info "Please install growl_notify or growl gem for Mac OS X notification support and add it to your Gemfile"
|
||||
end
|
||||
end
|
||||
|
||||
# Try to load the `growl_notify` gem.
|
||||
#
|
||||
# @return [Symbol, nil] A symbol with the name of the loaded library
|
||||
#
|
||||
def try_growl_notify
|
||||
require 'growl_notify'
|
||||
|
||||
begin
|
||||
if GrowlNotify.application_name != APPLICATION_NAME
|
||||
GrowlNotify.config do |c|
|
||||
c.notifications = c.default_notifications = [APPLICATION_NAME]
|
||||
c.application_name = c.notifications.first
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::GrowlNotify::GrowlNotFound
|
||||
turn_off
|
||||
UI.info "Please install Growl from http://growl.info"
|
||||
end
|
||||
|
||||
:growl_notify
|
||||
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
# Try to load the `ruby_gntp` gem and register the available
|
||||
# notification channels.
|
||||
#
|
||||
# @return [Symbol, nil] A symbol with the name of the loaded library
|
||||
#
|
||||
def try_ruby_gntp
|
||||
require 'ruby_gntp'
|
||||
|
||||
self.gntp = GNTP.new(APPLICATION_NAME)
|
||||
self.gntp.register(:notifications => [
|
||||
{ :name => 'notify', :enabled => true },
|
||||
{ :name => 'failed', :enabled => true },
|
||||
{ :name => 'pending', :enabled => true },
|
||||
{ :name => 'success', :enabled => true }
|
||||
])
|
||||
|
||||
:ruby_gntp
|
||||
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
# Try to load the `growl_notify` gem.
|
||||
#
|
||||
# @return [Symbol, nil] A symbol with the name of the loaded library
|
||||
#
|
||||
def try_growl
|
||||
require 'growl'
|
||||
|
||||
:growl
|
||||
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
# Try to safely load libnotify and turns notifications
|
||||
# off on load failure.
|
||||
#
|
||||
def require_libnotify
|
||||
require 'libnotify'
|
||||
|
||||
rescue LoadError
|
||||
turn_off
|
||||
UI.info "Please install libnotify gem for Linux notification support and add it to your Gemfile"
|
||||
end
|
||||
|
||||
# Try to safely load rb-notifu and turns notifications
|
||||
# off on load failure.
|
||||
#
|
||||
def require_rbnotifu
|
||||
require 'rb-notifu'
|
||||
|
||||
rescue LoadError
|
||||
turn_off
|
||||
UI.info "Please install rb-notifu gem for Windows notification support and add it to your Gemfile"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.image_path(image)
|
||||
images_path = Pathname.new(File.dirname(__FILE__)).join('../../images')
|
||||
case image
|
||||
when :failed
|
||||
images_path.join("failed.png").to_s
|
||||
when :pending
|
||||
images_path.join("pending.png").to_s
|
||||
when :success
|
||||
images_path.join("success.png").to_s
|
||||
else
|
||||
# path given
|
||||
image
|
||||
end
|
||||
end
|
||||
|
||||
def self.growl_installed?
|
||||
@installed ||= begin
|
||||
require 'growl'
|
||||
true
|
||||
rescue LoadError
|
||||
UI.info "Please install growl gem for Mac OS X notification support and add it to your Gemfile"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def self.libnotify_installed?
|
||||
@installed ||= begin
|
||||
require 'libnotify'
|
||||
true
|
||||
rescue LoadError
|
||||
UI.info "Please install libnotify gem for Linux notification support and add it to your Gemfile"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,2 +1,2 @@
|
||||
# A sample Guardfile
|
||||
# More info at https://github.com/guard/guard#readme
|
||||
# More info at http://github.com/guard/guard#readme
|
||||
|
199
lib/guard/ui.rb
199
lib/guard/ui.rb
@ -1,193 +1,42 @@
|
||||
module Guard
|
||||
|
||||
# The UI class helps to format messages for the user. Everything that is logged
|
||||
# through this class is considered either as an error message or a diagnostic
|
||||
# message and is written to standard error (STDERR).
|
||||
#
|
||||
# If your Guard does some output that is piped into another process for further
|
||||
# processing, please just write it to STDOUT with `puts`.
|
||||
#
|
||||
module UI
|
||||
class << self
|
||||
|
||||
color_enabled = nil
|
||||
|
||||
# Show an info message.
|
||||
#
|
||||
# @param [String] message the message to show
|
||||
# @option options [Boolean] reset whether to clean the output before
|
||||
#
|
||||
def info(message, options = { })
|
||||
unless ENV['GUARD_ENV'] == 'test'
|
||||
|
||||
def info(message, options = {})
|
||||
unless ENV["GUARD_ENV"] == "test"
|
||||
reset_line if options[:reset]
|
||||
STDERR.puts color(message) if message != ''
|
||||
puts reset_color(message) if message != ''
|
||||
end
|
||||
end
|
||||
|
||||
# Show a red error message that is prefixed with ERROR.
|
||||
#
|
||||
# @param [String] message the message to show
|
||||
# @option options [Boolean] reset whether to clean the output before
|
||||
#
|
||||
def error(message, options = { })
|
||||
unless ENV['GUARD_ENV'] == 'test'
|
||||
reset_line if options[:reset]
|
||||
STDERR.puts color('ERROR: ', :red) + message
|
||||
|
||||
def error(message)
|
||||
puts "ERROR: #{message}"
|
||||
end
|
||||
|
||||
def debug(message)
|
||||
unless ENV["GUARD_ENV"] == "test"
|
||||
puts "DEBUG: #{message}" if ::Guard.options && ::Guard.options[:debug]
|
||||
end
|
||||
end
|
||||
|
||||
# Show a red deprecation message that is prefixed with DEPRECATION.
|
||||
#
|
||||
# @param [String] message the message to show
|
||||
# @option options [Boolean] reset whether to clean the output before
|
||||
#
|
||||
def deprecation(message, options = { })
|
||||
unless ENV['GUARD_ENV'] == 'test'
|
||||
reset_line if options[:reset]
|
||||
STDERR.puts color('DEPRECATION: ', :red) + message
|
||||
end
|
||||
end
|
||||
|
||||
# Show a debug message that is prefixed with DEBUG and a timestamp.
|
||||
#
|
||||
# @param [String] message the message to show
|
||||
# @option options [Boolean] reset whether to clean the output before
|
||||
#
|
||||
def debug(message, options = { })
|
||||
unless ENV['GUARD_ENV'] == 'test'
|
||||
reset_line if options[:reset]
|
||||
STDERR.puts color("DEBUG (#{Time.now.strftime('%T')}): ", :yellow) + message if ::Guard.options && ::Guard.options[:debug]
|
||||
end
|
||||
end
|
||||
|
||||
# Reset a line.
|
||||
#
|
||||
|
||||
def reset_line
|
||||
STDERR.print(color_enabled? ? "\r\e[0m" : "\r\n")
|
||||
print "\r\e "
|
||||
end
|
||||
|
||||
# Clear the output.
|
||||
#
|
||||
|
||||
def clear
|
||||
system('clear;')
|
||||
system("clear;")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Reset a color sequence.
|
||||
#
|
||||
# @deprecated
|
||||
# @param [String] text the text
|
||||
#
|
||||
|
||||
private
|
||||
|
||||
def reset_color(text)
|
||||
deprecation('UI.reset_color(text) is deprecated, please use color(text, ' ') instead.')
|
||||
color(text, '')
|
||||
color(text, "\e[0m")
|
||||
end
|
||||
|
||||
# Checks if color output can be enabled.
|
||||
#
|
||||
# @return [Boolean] whether color is enabled or not
|
||||
#
|
||||
def color_enabled?
|
||||
if @color_enabled.nil?
|
||||
if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
||||
if ENV['ANSICON']
|
||||
@color_enabled = true
|
||||
else
|
||||
begin
|
||||
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
||||
require 'Win32/Console/ANSI'
|
||||
@color_enabled = true
|
||||
rescue LoadError
|
||||
@color_enabled = false
|
||||
info "You must 'gem install win32console' to use color on Windows"
|
||||
end
|
||||
end
|
||||
else
|
||||
@color_enabled = true
|
||||
end
|
||||
end
|
||||
|
||||
@color_enabled
|
||||
|
||||
def color(text, color_code)
|
||||
"#{color_code}#{text}\e[0m"
|
||||
end
|
||||
|
||||
# Colorizes a text message. See the constant in the UI class for possible
|
||||
# color_options parameters. You can pass optionally :bright, a foreground
|
||||
# color and a background color.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# color('Hello World', :red, :bright)
|
||||
#
|
||||
# @param [String] the text to colorize
|
||||
# @param [Array] color_options the color options
|
||||
#
|
||||
def color(text, *color_options)
|
||||
color_code = ''
|
||||
color_options.each do |color_option|
|
||||
color_option = color_option.to_s
|
||||
if color_option != ''
|
||||
if !(color_option =~ /\d+/)
|
||||
color_option = const_get("ANSI_ESCAPE_#{ color_option.upcase }")
|
||||
end
|
||||
color_code += ';' + color_option
|
||||
end
|
||||
end
|
||||
color_enabled? ? "\e[0#{ color_code }m#{ text }\e[0m" : text
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
# Brighten the color
|
||||
ANSI_ESCAPE_BRIGHT = '1'
|
||||
|
||||
# Black foreground color
|
||||
ANSI_ESCAPE_BLACK = '30'
|
||||
|
||||
# Red foreground color
|
||||
ANSI_ESCAPE_RED = '31'
|
||||
|
||||
# Green foreground color
|
||||
ANSI_ESCAPE_GREEN = '32'
|
||||
|
||||
# Yellow foreground color
|
||||
ANSI_ESCAPE_YELLOW = '33'
|
||||
|
||||
# Blue foreground color
|
||||
ANSI_ESCAPE_BLUE = '34'
|
||||
|
||||
# Magenta foreground color
|
||||
ANSI_ESCAPE_MAGENTA = '35'
|
||||
|
||||
# Cyan foreground color
|
||||
ANSI_ESCAPE_CYAN = '36'
|
||||
|
||||
# White foreground color
|
||||
ANSI_ESCAPE_WHITE = '37'
|
||||
|
||||
# Black background color
|
||||
ANSI_ESCAPE_BGBLACK = '40'
|
||||
|
||||
# Red background color
|
||||
ANSI_ESCAPE_BGRED = '41'
|
||||
|
||||
# Green background color
|
||||
ANSI_ESCAPE_BGGREEN = '42'
|
||||
|
||||
# Yellow background color
|
||||
ANSI_ESCAPE_BGYELLOW = '43'
|
||||
|
||||
# Blue background color
|
||||
ANSI_ESCAPE_BGBLUE = '44'
|
||||
|
||||
# Magenta background color
|
||||
ANSI_ESCAPE_BGMAGENTA = '45'
|
||||
|
||||
# Cyan background color
|
||||
ANSI_ESCAPE_BGCYAN = '46'
|
||||
|
||||
# White background color
|
||||
ANSI_ESCAPE_BGWHITE = '47'
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -1,6 +1,3 @@
|
||||
module Guard
|
||||
unless defined? Guard::VERSION
|
||||
# The current gem version of Guard
|
||||
VERSION = '0.8.4'
|
||||
end
|
||||
end
|
||||
VERSION = "0.2.2"
|
||||
end
|
@ -1,114 +1,34 @@
|
||||
module Guard
|
||||
|
||||
# The watcher defines a RegExp that will be matched against file system modifications.
|
||||
# When a watcher matches a change, an optional action block is executed to enable
|
||||
# processing the file system change result.
|
||||
#
|
||||
class Watcher
|
||||
|
||||
attr_accessor :pattern, :action
|
||||
|
||||
# Initialize a file watcher.
|
||||
#
|
||||
# @param [String, Regexp] pattern the pattern to be watched by the guard
|
||||
# @param [Block] action the action to execute before passing the result to the Guard
|
||||
#
|
||||
|
||||
def initialize(pattern, action = nil)
|
||||
@pattern, @action = pattern, action
|
||||
@@warning_printed ||= false
|
||||
|
||||
# deprecation warning
|
||||
if @pattern.is_a?(String) && @pattern =~ /(^(\^))|(>?(\\\.)|(\.\*))|(\(.*\))|(\[.*\])|(\$$)/
|
||||
unless @@warning_printed
|
||||
UI.info "*"*20 + "\nDEPRECATION WARNING!\n" + "*"*20
|
||||
UI.info <<-MSG
|
||||
You have a string in your Guardfile watch patterns that seem to represent a Regexp.
|
||||
Guard matches String with == and Regexp with Regexp#match.
|
||||
You should either use plain String (without Regexp special characters) or real Regexp.
|
||||
MSG
|
||||
@@warning_printed = true
|
||||
end
|
||||
|
||||
UI.info "\"#{@pattern}\" has been converted to #{ Regexp.new(@pattern).inspect }\n"
|
||||
@pattern = Regexp.new(@pattern)
|
||||
end
|
||||
end
|
||||
|
||||
# Finds the files that matches a Guard.
|
||||
#
|
||||
# @param [Guard::Guard] guard the guard which watchers are used
|
||||
# @param [Array<String>] files the changed files
|
||||
# @return [Array<Object>] the matched watcher response
|
||||
#
|
||||
|
||||
def self.match_files(guard, files)
|
||||
guard.watchers.inject([]) do |paths, watcher|
|
||||
files.each do |file|
|
||||
if matches = watcher.match_file?(file)
|
||||
if matches = file.match(watcher.pattern)
|
||||
if watcher.action
|
||||
result = watcher.call_action(matches)
|
||||
if guard.options[:any_return]
|
||||
paths << result
|
||||
elsif result.respond_to?(:empty?) && !result.empty?
|
||||
paths << Array(result)
|
||||
begin
|
||||
if watcher.action.arity == 1
|
||||
result = watcher.action.call(matches)
|
||||
else
|
||||
result = watcher.action.call
|
||||
end
|
||||
rescue
|
||||
UI.info "Problem with watch action"
|
||||
end
|
||||
paths << result if result.is_a?(String) && result != ''
|
||||
else
|
||||
paths << matches[0]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
guard.options[:any_return] ? paths : paths.flatten.map { |p| p.to_s }
|
||||
paths
|
||||
end
|
||||
end
|
||||
|
||||
# Test if a file would be matched by any of the Guards watchers.
|
||||
#
|
||||
# @param [Array<Guard::Guard>] guards the guards to use the watchers from
|
||||
# @param [Array<String>] files the files to test
|
||||
# @return [Boolean] Whether a file matches
|
||||
#
|
||||
def self.match_files?(guards, files)
|
||||
guards.any? do |guard|
|
||||
guard.watchers.any? do |watcher|
|
||||
files.any? { |file| watcher.match_file?(file) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Test the watchers pattern against a file.
|
||||
#
|
||||
# @param [String] file the file to test
|
||||
# @return [Boolean] whether the given file is matched
|
||||
#
|
||||
def match_file?(file)
|
||||
if @pattern.is_a?(Regexp)
|
||||
file.match(@pattern)
|
||||
else
|
||||
file == @pattern ? [file] : nil
|
||||
end
|
||||
end
|
||||
|
||||
# Test if any of the files is the Guardfile.
|
||||
#
|
||||
# @param [Array<String>] the files to test
|
||||
# @return [Boolean] whether one of these files is the Guardfile
|
||||
#
|
||||
def self.match_guardfile?(files)
|
||||
files.any? { |file| "#{ Dir.pwd }/#{ file }" == Dsl.guardfile_path }
|
||||
end
|
||||
|
||||
# Executes a watcher action.
|
||||
#
|
||||
# @param [String, MatchData] the matched path or the match from the Regex
|
||||
# @return [String] the final paths
|
||||
#
|
||||
def call_action(matches)
|
||||
begin
|
||||
@action.arity > 0 ? @action.call(matches) : @action.call
|
||||
rescue Exception => e
|
||||
UI.error "Problem with watch action!\n#{ e.message }\n\n#{ e.backtrace.join("\n") }"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
93
man/guard.1
93
man/guard.1
@ -1,93 +0,0 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "GUARD" "1" "September 2011" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBguard\fR \- Guard keeps an eye on your file modifications\.
|
||||
.
|
||||
.SH "DESCRIPTION"
|
||||
Guard is a command line tool that easily handle events on files modifications\.
|
||||
.
|
||||
.SH "SYNOPSIS"
|
||||
\fBguard <COMMAND> <OPTIONS>\fR
|
||||
.
|
||||
.SH "COMMANDS"
|
||||
.
|
||||
.SS "start"
|
||||
Starts Guard\. This is the default command if none is provided\.
|
||||
.
|
||||
.P
|
||||
The following options are available:
|
||||
.
|
||||
.P
|
||||
\fB\-c\fR, \fB\-\-clear\fR Clears the Shell after each change\.
|
||||
.
|
||||
.P
|
||||
\fB\-n\fR, \fB\-\-notify\fR \fIFLAG\fR Disable notifications (Growl or Libnotify depending on your system)\. Notifications can be disabled globally by setting a GUARD_NOTIFY environment variable to false\. FLAG can be \fBtrue\fR/\fBfalse\fR or \fBt\fR/\fBf\fR\.
|
||||
.
|
||||
.P
|
||||
\fB\-d\fR, \fB\-\-debug\fR Runs Guard in debug mode\.
|
||||
.
|
||||
.P
|
||||
\fB\-g\fR, \fB\-\-group\fR \fIGROUP1\fR \fIGROUP2\fR\.\.\. Runs only the groups specified by GROUP1, GROUP2 etc\. Groups name should be separated by spaces\. Guards that don\'t belong to a group are considered global and are always run\.
|
||||
.
|
||||
.P
|
||||
\fB\-w\fR, \fB\-\-watchdir\fR \fIPATH\fR
|
||||
.
|
||||
.P
|
||||
Tells Guard to watch PATH instead of \fB\./\fR\.
|
||||
.
|
||||
.P
|
||||
\fB\-G\fR, \fB\-\-guardfile\fR \fIFILE\fR Tells Guard to use FILE as its Guardfile instead of \fB\./Guardfile\fR or \fB~/\.Guardfile\fR\.
|
||||
.
|
||||
.SS "init [GUARD]"
|
||||
If no Guardfile is present in the current directory, creates an empty Guardfile\.
|
||||
.
|
||||
.P
|
||||
If \fIGUARD\fR is present, add its default Guardfile configuration to the current Guardfile\. Note that \fIGUARD\fR is the guard\'s name without the \fBguard\-\fR prefix\. For instance to initialize guard\-rspec, run \fBguard init rspec\fR\.
|
||||
.
|
||||
.SS "list"
|
||||
Lists guards that can be used with the \fBinit\fR command\.
|
||||
.
|
||||
.SS "\-T, show"
|
||||
List defined groups and guards for the current Guardfile\.
|
||||
.
|
||||
.SS "\-h, help [COMMAND]"
|
||||
List all of Guard\'s available commands\.
|
||||
.
|
||||
.P
|
||||
If \fICOMMAND\fR is given, displays a specific help for \fITASK\fR\.
|
||||
.
|
||||
.SH "EXAMPLES"
|
||||
Initialize Guard and a specific guard at the same time:
|
||||
.
|
||||
.P
|
||||
\fB[bundle exec] guard init [rspec]\fR
|
||||
.
|
||||
.P
|
||||
Run Guard:
|
||||
.
|
||||
.P
|
||||
\fB[bundle exec] guard [start] \-\-watchdir ~/dev \-\-guardfile ~/env/Guardfile \-\-clear \-\-group backend frontend \-\-notify false \-\-debug\fR
|
||||
.
|
||||
.P
|
||||
or in a more concise way:
|
||||
.
|
||||
.P
|
||||
\fB[bundle exec] guard [start] \-w ~/dev \-G ~/env/Guardfile \-c \-g backend frontend \-n f \-d\fR
|
||||
.
|
||||
.SH "AUTHORS / CONTRIBUTORS"
|
||||
Thibaud Guillaume\-Gentil is the main author\.
|
||||
.
|
||||
.P
|
||||
A list of contributors based on all commits can be found here: https://github\.com/guard/guard/contributors
|
||||
.
|
||||
.P
|
||||
For an exhaustive list of all the contributors, please see the CHANGELOG: https://github\.com/guard/guard/blob/master/CHANGELOG\.md
|
||||
.
|
||||
.P
|
||||
This manual has been written by Remy Coutable\.
|
||||
.
|
||||
.SH "WWW"
|
||||
https://github\.com/guard/guard
|
176
man/guard.1.html
176
man/guard.1.html
@ -1,176 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='content-type' value='text/html;charset=utf8'>
|
||||
<meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
|
||||
<title>guard(1) - Guard keeps an eye on your file modifications.</title>
|
||||
<style type='text/css' media='all'>
|
||||
/* style: man */
|
||||
body#manpage {margin:0}
|
||||
.mp {max-width:100ex;padding:0 9ex 1ex 4ex}
|
||||
.mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
|
||||
.mp h2 {margin:10px 0 0 0}
|
||||
.mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
|
||||
.mp h3 {margin:0 0 0 4ex}
|
||||
.mp dt {margin:0;clear:left}
|
||||
.mp dt.flush {float:left;width:8ex}
|
||||
.mp dd {margin:0 0 0 9ex}
|
||||
.mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
|
||||
.mp pre {margin-bottom:20px}
|
||||
.mp pre+h2,.mp pre+h3 {margin-top:22px}
|
||||
.mp h2+pre,.mp h3+pre {margin-top:5px}
|
||||
.mp img {display:block;margin:auto}
|
||||
.mp h1.man-title {display:none}
|
||||
.mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
|
||||
.mp h2 {font-size:16px;line-height:1.25}
|
||||
.mp h1 {font-size:20px;line-height:2}
|
||||
.mp {text-align:justify;background:#fff}
|
||||
.mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
|
||||
.mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
|
||||
.mp u {text-decoration:underline}
|
||||
.mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
|
||||
.mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
|
||||
.mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
|
||||
.mp b.man-ref {font-weight:normal;color:#434241}
|
||||
.mp pre {padding:0 4ex}
|
||||
.mp pre code {font-weight:normal;color:#434241}
|
||||
.mp h2+pre,h3+pre {padding-left:0}
|
||||
ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
|
||||
ol.man-decor {width:100%}
|
||||
ol.man-decor li.tl {text-align:left}
|
||||
ol.man-decor li.tc {text-align:center;letter-spacing:4px}
|
||||
ol.man-decor li.tr {text-align:right;float:right}
|
||||
</style>
|
||||
</head>
|
||||
<!--
|
||||
The following styles are deprecated and will be removed at some point:
|
||||
div#man, div#man ol.man, div#man ol.head, div#man ol.man.
|
||||
|
||||
The .man-page, .man-decor, .man-head, .man-foot, .man-title, and
|
||||
.man-navigation should be used instead.
|
||||
-->
|
||||
<body id='manpage'>
|
||||
<div class='mp' id='man'>
|
||||
|
||||
<div class='man-navigation' style='display:none'>
|
||||
<a href="#NAME">NAME</a>
|
||||
<a href="#DESCRIPTION">DESCRIPTION</a>
|
||||
<a href="#SYNOPSIS">SYNOPSIS</a>
|
||||
<a href="#COMMANDS">COMMANDS</a>
|
||||
<a href="#EXAMPLES">EXAMPLES</a>
|
||||
<a href="#AUTHORS-CONTRIBUTORS">AUTHORS / CONTRIBUTORS</a>
|
||||
<a href="#WWW">WWW</a>
|
||||
</div>
|
||||
|
||||
<ol class='man-decor man-head man head'>
|
||||
<li class='tl'>guard(1)</li>
|
||||
<li class='tc'></li>
|
||||
<li class='tr'>guard(1)</li>
|
||||
</ol>
|
||||
|
||||
<h2 id="NAME">NAME</h2>
|
||||
<p class="man-name">
|
||||
<code>guard</code> - <span class="man-whatis">Guard keeps an eye on your file modifications.</span>
|
||||
</p>
|
||||
|
||||
<h2 id="DESCRIPTION">DESCRIPTION</h2>
|
||||
|
||||
<p>Guard is a command line tool that easily handle events on files modifications.</p>
|
||||
|
||||
<h2 id="SYNOPSIS">SYNOPSIS</h2>
|
||||
|
||||
<p><code>guard <COMMAND> <OPTIONS></code></p>
|
||||
|
||||
<h2 id="COMMANDS">COMMANDS</h2>
|
||||
|
||||
<h3 id="start">start</h3>
|
||||
|
||||
<p>Starts Guard. This is the default command if none is provided.</p>
|
||||
|
||||
<p>The following options are available:</p>
|
||||
|
||||
<p><code>-c</code>, <code>--clear</code>
|
||||
Clears the Shell after each change.</p>
|
||||
|
||||
<p><code>-n</code>, <code>--notify</code> <var>FLAG</var>
|
||||
Disable notifications (Growl or Libnotify depending on your system).
|
||||
Notifications can be disabled globally by setting a GUARD_NOTIFY environment variable to false.
|
||||
FLAG can be <code>true</code>/<code>false</code> or <code>t</code>/<code>f</code>.</p>
|
||||
|
||||
<p><code>-d</code>, <code>--debug</code>
|
||||
Runs Guard in debug mode.</p>
|
||||
|
||||
<p><code>-g</code>, <code>--group</code> <var>GROUP1</var> <var>GROUP2</var>...
|
||||
Runs only the groups specified by GROUP1, GROUP2 etc.
|
||||
Groups name should be separated by spaces.
|
||||
Guards that don't belong to a group are considered global and are always run.</p>
|
||||
|
||||
<p><code>-w</code>, <code>--watchdir</code> <var>PATH</var></p>
|
||||
|
||||
<p>Tells Guard to watch PATH instead of <code>./</code>.</p>
|
||||
|
||||
<p><code>-G</code>, <code>--guardfile</code> <var>FILE</var>
|
||||
Tells Guard to use FILE as its Guardfile instead of <code>./Guardfile</code> or <code>~/.Guardfile</code>.</p>
|
||||
|
||||
<h3 id="init-GUARD-">init [GUARD]</h3>
|
||||
|
||||
<p>If no Guardfile is present in the current directory, creates an empty Guardfile.</p>
|
||||
|
||||
<p>If <var>GUARD</var> is present, add its default Guardfile configuration to the current Guardfile.
|
||||
Note that <var>GUARD</var> is the guard's name without the <code>guard-</code> prefix.
|
||||
For instance to initialize guard-rspec, run <code>guard init rspec</code>.</p>
|
||||
|
||||
<h3 id="list">list</h3>
|
||||
|
||||
<p>Lists guards that can be used with the <code>init</code> command.</p>
|
||||
|
||||
<h3 id="-T-show">-T, show</h3>
|
||||
|
||||
<p>List defined groups and guards for the current Guardfile.</p>
|
||||
|
||||
<h3 id="-h-help-COMMAND-">-h, help [COMMAND]</h3>
|
||||
|
||||
<p>List all of Guard's available commands.</p>
|
||||
|
||||
<p>If <var>COMMAND</var> is given, displays a specific help for <var>TASK</var>.</p>
|
||||
|
||||
<h2 id="EXAMPLES">EXAMPLES</h2>
|
||||
|
||||
<p>Initialize Guard and a specific guard at the same time:</p>
|
||||
|
||||
<p><code>[bundle exec] guard init [rspec]</code></p>
|
||||
|
||||
<p>Run Guard:</p>
|
||||
|
||||
<p><code>[bundle exec] guard [start] --watchdir ~/dev --guardfile ~/env/Guardfile --clear --group backend frontend --notify false --debug</code></p>
|
||||
|
||||
<p>or in a more concise way:</p>
|
||||
|
||||
<p><code>[bundle exec] guard [start] -w ~/dev -G ~/env/Guardfile -c -g backend frontend -n f -d</code></p>
|
||||
|
||||
<h2 id="AUTHORS-CONTRIBUTORS">AUTHORS / CONTRIBUTORS</h2>
|
||||
|
||||
<p>Thibaud Guillaume-Gentil is the main author.</p>
|
||||
|
||||
<p>A list of contributors based on all commits can be found here:
|
||||
https://github.com/guard/guard/contributors</p>
|
||||
|
||||
<p>For an exhaustive list of all the contributors, please see the CHANGELOG:
|
||||
https://github.com/guard/guard/blob/master/CHANGELOG.md</p>
|
||||
|
||||
<p>This manual has been written by Remy Coutable.</p>
|
||||
|
||||
<h2 id="WWW">WWW</h2>
|
||||
|
||||
<p>https://github.com/guard/guard</p>
|
||||
|
||||
|
||||
<ol class='man-decor man-foot man foot'>
|
||||
<li class='tl'></li>
|
||||
<li class='tc'>September 2011</li>
|
||||
<li class='tr'>guard(1)</li>
|
||||
</ol>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,93 +0,0 @@
|
||||
guard(1) -- Guard keeps an eye on your file modifications.
|
||||
========================================================
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
Guard is a command line tool that easily handle events on files modifications.
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`guard <COMMAND> <OPTIONS>`
|
||||
|
||||
## COMMANDS
|
||||
|
||||
### start
|
||||
|
||||
Starts Guard. This is the default command if none is provided.
|
||||
|
||||
The following options are available:
|
||||
|
||||
`-c`, `--clear`
|
||||
Clears the Shell after each change.
|
||||
|
||||
`-n`, `--notify` <FLAG>
|
||||
Disable notifications (Growl or Libnotify depending on your system).
|
||||
Notifications can be disabled globally by setting a GUARD_NOTIFY environment variable to false.
|
||||
FLAG can be `true`/`false` or `t`/`f`.
|
||||
|
||||
`-d`, `--debug`
|
||||
Runs Guard in debug mode.
|
||||
|
||||
`-g`, `--group` <GROUP1> <GROUP2>...
|
||||
Runs only the groups specified by GROUP1, GROUP2 etc.
|
||||
Groups name should be separated by spaces.
|
||||
Guards that don't belong to a group are considered global and are always run.
|
||||
|
||||
`-w`, `--watchdir` <PATH>
|
||||
|
||||
Tells Guard to watch PATH instead of `./`.
|
||||
|
||||
`-G`, `--guardfile` <FILE>
|
||||
Tells Guard to use FILE as its Guardfile instead of `./Guardfile` or `~/.Guardfile`.
|
||||
|
||||
### init [GUARD]
|
||||
|
||||
If no Guardfile is present in the current directory, creates an empty Guardfile.
|
||||
|
||||
If <GUARD> is present, add its default Guardfile configuration to the current Guardfile.
|
||||
Note that <GUARD> is the guard's name without the `guard-` prefix.
|
||||
For instance to initialize guard-rspec, run `guard init rspec`.
|
||||
|
||||
### list
|
||||
|
||||
Lists guards that can be used with the `init` command.
|
||||
|
||||
### -T, show
|
||||
|
||||
List defined groups and guards for the current Guardfile.
|
||||
|
||||
### -h, help [COMMAND]
|
||||
|
||||
List all of Guard's available commands.
|
||||
|
||||
If <COMMAND> is given, displays a specific help for <TASK>.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
Initialize Guard and a specific guard at the same time:
|
||||
|
||||
`[bundle exec] guard init [rspec]`
|
||||
|
||||
Run Guard:
|
||||
|
||||
`[bundle exec] guard [start] --watchdir ~/dev --guardfile ~/env/Guardfile --clear --group backend frontend --notify false --debug`
|
||||
|
||||
or in a more concise way:
|
||||
|
||||
`[bundle exec] guard [start] -w ~/dev -G ~/env/Guardfile -c -g backend frontend -n f -d`
|
||||
|
||||
## AUTHORS / CONTRIBUTORS
|
||||
|
||||
Thibaud Guillaume-Gentil is the main author.
|
||||
|
||||
A list of contributors based on all commits can be found here:
|
||||
https://github.com/guard/guard/contributors
|
||||
|
||||
For an exhaustive list of all the contributors, please see the CHANGELOG:
|
||||
https://github.com/guard/guard/blob/master/CHANGELOG.md
|
||||
|
||||
This manual has been written by Remy Coutable.
|
||||
|
||||
## WWW
|
||||
|
||||
https://github.com/guard/guard
|
0
spec/fixtures/.dotfile
vendored
0
spec/fixtures/.dotfile
vendored
1
spec/fixtures/Guardfile
vendored
1
spec/fixtures/Guardfile
vendored
@ -1 +0,0 @@
|
||||
# nothing here, it's just for feeding the specs! :)
|
0
spec/fixtures/folder1/file1.txt
vendored
Executable file → Normal file
0
spec/fixtures/folder1/file1.txt
vendored
Executable file → Normal file
@ -1,70 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Guard::DslDescriber do
|
||||
|
||||
let(:describer) { ::Guard::DslDescriber }
|
||||
|
||||
let(:guardfile) do
|
||||
<<-GUARD
|
||||
guard 'test', :a => :b do
|
||||
watch('c')
|
||||
end
|
||||
|
||||
group :a do
|
||||
guard 'test', :x => 1 do
|
||||
watch('c')
|
||||
end
|
||||
end
|
||||
|
||||
group "b" do
|
||||
guard 'another' do
|
||||
watch('c')
|
||||
end
|
||||
end
|
||||
GUARD
|
||||
end
|
||||
|
||||
before do
|
||||
@output = ''
|
||||
Guard::UI.stub(:info) { |msg| @output << msg + "\n" }
|
||||
end
|
||||
|
||||
after do
|
||||
Guard::UI.unstub(:info)
|
||||
end
|
||||
|
||||
describe '.list' do
|
||||
it 'lists the available Guards' do
|
||||
Guard.stub(:guard_gem_names).and_return ['test', 'another', 'even', 'more']
|
||||
describer.list(:guardfile_contents => guardfile)
|
||||
@output.should eql <<OUTPUT
|
||||
Using inline Guardfile.
|
||||
Available guards:
|
||||
another*
|
||||
even
|
||||
more
|
||||
test*
|
||||
|
||||
See also https://github.com/guard/guard/wiki/List-of-available-Guards
|
||||
* denotes ones already in your Guardfile
|
||||
OUTPUT
|
||||
end
|
||||
end
|
||||
|
||||
describe '.show' do
|
||||
it 'shows the Guards and their options' do
|
||||
describer.show(:guardfile_contents => guardfile)
|
||||
@output.should eql <<OUTPUT
|
||||
Using inline Guardfile.
|
||||
(global):
|
||||
test: a => :b
|
||||
Group a:
|
||||
test: x => 1
|
||||
Group b:
|
||||
another
|
||||
|
||||
OUTPUT
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,383 +1,62 @@
|
||||
require 'spec_helper'
|
||||
require 'guard/guard'
|
||||
require 'guard/dsl'
|
||||
|
||||
describe Guard::Dsl do
|
||||
|
||||
class Guard::Dummy < Guard::Guard; end
|
||||
|
||||
subject { Guard::Dsl }
|
||||
|
||||
before(:each) do
|
||||
@local_guardfile_path = File.join(Dir.pwd, 'Guardfile')
|
||||
@home_guardfile_path = File.expand_path(File.join("~", ".Guardfile"))
|
||||
@user_config_path = File.expand_path(File.join("~", ".guard.rb"))
|
||||
::Guard.setup
|
||||
::Guard.stub!(:options).and_return(:debug => true)
|
||||
::Guard.stub!(:guards).and_return([mock('Guard')])
|
||||
::Guard.stub!(:add_guard)
|
||||
end
|
||||
|
||||
def self.disable_user_config
|
||||
before(:each) { File.stub(:exist?).with(@user_config_path) { false } }
|
||||
|
||||
it "write an error message when no Guardfile is found" do
|
||||
Dir.stub!(:pwd).and_return("no_guardfile_here")
|
||||
|
||||
Guard::UI.should_receive(:error).with("No Guardfile in current folder, please create one.")
|
||||
lambda { subject.evaluate_guardfile }.should raise_error
|
||||
end
|
||||
|
||||
describe "it should select the correct data source for Guardfile" do
|
||||
before(:each) { ::Guard::Dsl.stub!(:instance_eval_guardfile) }
|
||||
disable_user_config
|
||||
|
||||
it "should use a string for initializing" do
|
||||
Guard::UI.should_not_receive(:error)
|
||||
lambda { described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string) }.should_not raise_error
|
||||
described_class.guardfile_contents.should == valid_guardfile_string
|
||||
end
|
||||
|
||||
it "should use a given file over the default loc" do
|
||||
fake_guardfile('/abc/Guardfile', "guard :foo")
|
||||
|
||||
Guard::UI.should_not_receive(:error)
|
||||
lambda { described_class.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
|
||||
described_class.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 { described_class.evaluate_guardfile }.should_not raise_error
|
||||
described_class.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_not_receive(:error)
|
||||
lambda { described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string) }.should_not raise_error
|
||||
described_class.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 { described_class.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
|
||||
described_class.guardfile_contents.should == "guard :foo"
|
||||
end
|
||||
|
||||
it 'should append the user config file if present' do
|
||||
fake_guardfile('/abc/Guardfile', "guard :foo")
|
||||
fake_guardfile(@user_config_path, "guard :bar")
|
||||
Guard::UI.should_not_receive(:error)
|
||||
lambda { described_class.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
|
||||
described_class.guardfile_contents_with_user_config.should == "guard :foo\nguard :bar"
|
||||
end
|
||||
|
||||
|
||||
it "write an error message when Guardfile is not valid" do
|
||||
mock_guardfile_content("This Guardfile is invalid!")
|
||||
|
||||
Guard::UI.should_receive(:error).with(/Invalid Guardfile, original error is:\n/)
|
||||
lambda { subject.evaluate_guardfile }.should raise_error
|
||||
end
|
||||
|
||||
it "displays an error message when no Guardfile is found" do
|
||||
described_class.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 { described_class.evaluate_guardfile }.should raise_error
|
||||
|
||||
it "load a guard from the DSL" do
|
||||
mock_guardfile_content("guard 'test'")
|
||||
|
||||
::Guard.should_receive(:add_guard).with('test', [], {})
|
||||
subject.evaluate_guardfile
|
||||
end
|
||||
|
||||
it "displays an error message when no guard are defined in Guardfile" do
|
||||
::Guard::Dsl.stub!(:instance_eval_guardfile)
|
||||
::Guard.stub!(:guards).and_return([])
|
||||
Guard::UI.should_receive(:error)
|
||||
described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string)
|
||||
end
|
||||
|
||||
describe "correctly reads data from its valid data source" do
|
||||
before(:each) { ::Guard::Dsl.stub!(:instance_eval_guardfile) }
|
||||
disable_user_config
|
||||
|
||||
it "reads correctly from a string" do
|
||||
lambda { described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string) }.should_not raise_error
|
||||
described_class.guardfile_contents.should == valid_guardfile_string
|
||||
end
|
||||
|
||||
it "reads correctly from a Guardfile" do
|
||||
fake_guardfile('/abc/Guardfile', "guard :foo" )
|
||||
|
||||
lambda { described_class.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
|
||||
described_class.guardfile_contents.should == "guard :foo"
|
||||
end
|
||||
|
||||
it "reads correctly from a Guardfile" do
|
||||
fake_guardfile(File.join(Dir.pwd, 'Guardfile'), valid_guardfile_string)
|
||||
|
||||
lambda { described_class.evaluate_guardfile }.should_not raise_error
|
||||
described_class.guardfile_contents.should == valid_guardfile_string
|
||||
end
|
||||
end
|
||||
|
||||
describe "correctly throws errors when initializing with invalid data" do
|
||||
before(:each) { ::Guard::Dsl.stub!(:instance_eval_guardfile) }
|
||||
|
||||
it "raises 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 { described_class.evaluate_guardfile(:guardfile => '/def/Guardfile') }.should raise_error
|
||||
end
|
||||
|
||||
it "raises error when given Guardfile doesn't exist" do
|
||||
File.stub!(:exist?).with('/def/Guardfile') { false }
|
||||
|
||||
Guard::UI.should_receive(:error).with(/No Guardfile exists at/)
|
||||
lambda { described_class.evaluate_guardfile(:guardfile => '/def/Guardfile') }.should raise_error
|
||||
end
|
||||
|
||||
it "raises 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 { described_class.evaluate_guardfile }.should raise_error
|
||||
end
|
||||
|
||||
it "raises error when guardfile_content ends up empty or nil" do
|
||||
Guard::UI.should_receive(:error).with(/The command file/)
|
||||
lambda { described_class.evaluate_guardfile(:guardfile_contents => "") }.should raise_error
|
||||
end
|
||||
|
||||
it "doesn't raise error when guardfile_content is nil (skipped)" do
|
||||
Guard::UI.should_not_receive(:error)
|
||||
lambda { described_class.evaluate_guardfile(:guardfile_contents => nil) }.should_not 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 { described_class.evaluate_guardfile(:guardfile_contents => invalid_guardfile_string ) }.should raise_error
|
||||
end
|
||||
|
||||
describe ".reevaluate_guardfile" do
|
||||
before(:each) { ::Guard::Dsl.stub!(:instance_eval_guardfile) }
|
||||
|
||||
it "resets already definded guards before calling evaluate_guardfile" do
|
||||
Guard::Notifier.turn_off
|
||||
described_class.evaluate_guardfile(:guardfile_contents => invalid_guardfile_string)
|
||||
::Guard.guards.should_not be_empty
|
||||
::Guard::Dsl.should_receive(:evaluate_guardfile)
|
||||
described_class.reevaluate_guardfile
|
||||
::Guard.guards.should be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe ".guardfile_default_path" do
|
||||
let(:local_path) { File.join(Dir.pwd, 'Guardfile') }
|
||||
let(:user_path) { File.expand_path(File.join("~", '.Guardfile')) }
|
||||
before(:each) { File.stub(:exist? => false) }
|
||||
|
||||
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)
|
||||
described_class.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)
|
||||
described_class.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)
|
||||
described_class.guardfile_default_path.should == local_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".guardfile_include?" do
|
||||
it "detects a guard specified by a string with double quotes" do
|
||||
described_class.stub(:guardfile_contents => 'guard "test" {watch("c")}')
|
||||
|
||||
described_class.guardfile_include?('test').should be_true
|
||||
end
|
||||
|
||||
it "detects a guard specified by a string with single quote" do
|
||||
described_class.stub(:guardfile_contents => 'guard \'test\' {watch("c")}')
|
||||
|
||||
described_class.guardfile_include?('test').should be_true
|
||||
end
|
||||
|
||||
it "detects a guard specified by a symbol" do
|
||||
described_class.stub(:guardfile_contents => 'guard :test {watch("c")}')
|
||||
|
||||
described_class.guardfile_include?('test').should be_true
|
||||
end
|
||||
|
||||
it "detects a guard wrapped in parentheses" do
|
||||
described_class.stub(:guardfile_contents => 'guard(:test) {watch("c")}')
|
||||
|
||||
described_class.guardfile_include?('test').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "#ignore_paths" do
|
||||
disable_user_config
|
||||
|
||||
it "adds the paths to the listener's ignore_paths" do
|
||||
::Guard.stub!(:listener).and_return(mock('Listener'))
|
||||
::Guard.listener.should_receive(:ignore_paths).and_return(ignore_paths = ['faz'])
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => "ignore_paths 'foo', 'bar'")
|
||||
ignore_paths.should == ['faz', 'foo', 'bar']
|
||||
end
|
||||
end
|
||||
|
||||
describe "#group" do
|
||||
disable_user_config
|
||||
|
||||
it "evaluates only the specified string group" do
|
||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :w })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:w])
|
||||
end
|
||||
|
||||
it "evaluates only the specified symbol group" do
|
||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :w })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:w])
|
||||
end
|
||||
|
||||
it "evaluates only the specified groups (with their options)" do
|
||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('rspec', [], [], { :group => :x })
|
||||
::Guard.should_receive(:add_guard).with('ronn', [], [], { :group => :x })
|
||||
::Guard.should_receive(:add_guard).with('less', [], [], { :group => :y })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:x, :y])
|
||||
end
|
||||
|
||||
it "evaluates always guard outside any group (even when a group is given)" do
|
||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :w })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:w])
|
||||
end
|
||||
|
||||
it "evaluates all groups when no group option is specified (with their options)" do
|
||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :w })
|
||||
::Guard.should_receive(:add_guard).with('rspec', [], [], { :group => :x })
|
||||
::Guard.should_receive(:add_guard).with('ronn', [], [], { :group => :x })
|
||||
::Guard.should_receive(:add_guard).with('less', [], [], { :group => :y })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#guard" do
|
||||
disable_user_config
|
||||
|
||||
it "loads a guard specified as a quoted string from the DSL" do
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :default })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => "guard 'test'")
|
||||
end
|
||||
|
||||
it "loads a guard specified as a double quoted string from the DSL" do
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :default })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => 'guard "test"')
|
||||
end
|
||||
|
||||
it "loads a guard specified as a symbol from the DSL" do
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :default })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => "guard :test")
|
||||
end
|
||||
|
||||
it "loads a guard specified as a symbol and called with parens from the DSL" do
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :default })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => "guard(:test)")
|
||||
end
|
||||
|
||||
it "receives options when specified, from normal arg" do
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :opt_a => 1, :opt_b => 'fancy', :group => :default })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => "guard 'test', :opt_a => 1, :opt_b => 'fancy'")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#watch" do
|
||||
disable_user_config
|
||||
|
||||
it "should receive watchers when specified" do
|
||||
::Guard.should_receive(:add_guard).with('dummy', anything, anything, { :group => :default }) do |name, watchers, callbacks, 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 == nil
|
||||
end
|
||||
described_class.evaluate_guardfile(:guardfile_contents => "
|
||||
guard :dummy do
|
||||
watch('a') { 'b' }
|
||||
watch('c')
|
||||
|
||||
it "receive watchers when specified" 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
|
||||
end
|
||||
subject.evaluate_guardfile
|
||||
end
|
||||
|
||||
describe "#callback" do
|
||||
it "creates callbacks for the guard" do
|
||||
class MyCustomCallback
|
||||
def self.call(guard_class, event, args)
|
||||
# do nothing
|
||||
end
|
||||
end
|
||||
|
||||
::Guard.should_receive(:add_guard).with('dummy', anything, anything, { :group => :default }) do |name, watchers, callbacks, options|
|
||||
callbacks.should have(2).items
|
||||
callbacks[0][:events].should == :start_end
|
||||
callbacks[0][:listener].call(Guard::Dummy, :start_end, 'foo').should == "Guard::Dummy executed 'start_end' hook with foo!"
|
||||
callbacks[1][:events].should == [:start_begin, :run_all_begin]
|
||||
callbacks[1][:listener].should == MyCustomCallback
|
||||
end
|
||||
described_class.evaluate_guardfile(:guardfile_contents => '
|
||||
guard :dummy do
|
||||
callback(:start_end) { |guard_class, event, args| "#{guard_class} executed \'#{event}\' hook with #{args}!" }
|
||||
callback(MyCustomCallback, [:start_begin, :run_all_begin])
|
||||
end')
|
||||
end
|
||||
|
||||
it "receive options when specified" do
|
||||
mock_guardfile_content("guard 'test', :opt_a => 1, :opt_b => 'fancy'")
|
||||
|
||||
::Guard.should_receive(:add_guard).with('test', anything, { :opt_a => 1, :opt_b => 'fancy' })
|
||||
subject.evaluate_guardfile
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def fake_guardfile(name, contents)
|
||||
File.stub!(:exist?).with(name) { true }
|
||||
File.stub!(:read).with(name) { contents }
|
||||
end
|
||||
|
||||
def valid_guardfile_string
|
||||
"
|
||||
guard :pow
|
||||
|
||||
group :w do
|
||||
guard :test
|
||||
end
|
||||
|
||||
group :x, :halt_on_fail => true do
|
||||
guard :rspec
|
||||
guard :ronn
|
||||
end
|
||||
|
||||
group :y do
|
||||
guard :less
|
||||
end
|
||||
"
|
||||
end
|
||||
|
||||
def invalid_guardfile_string
|
||||
"Bad Guardfile"
|
||||
|
||||
def mock_guardfile_content(content)
|
||||
File.stub!(:read).with(File.expand_path('../../../Guardfile', __FILE__)) { content }
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,19 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Guard::Group do
|
||||
|
||||
describe ".initialize" do
|
||||
it "accepts a name as a string and provides an accessor for it (returning a symbol)" do
|
||||
described_class.new('foo').name.should eql :foo
|
||||
end
|
||||
|
||||
it "accepts a name as a symbol and provides an accessor for it (returning a symbol)" do
|
||||
described_class.new(:foo).name.should eql :foo
|
||||
end
|
||||
|
||||
it "accepts options and provides an accessor for it" do
|
||||
described_class.new('foo', :halt_on_fail => true).options.should == { :halt_on_fail => true }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,60 +1,152 @@
|
||||
require 'spec_helper'
|
||||
require 'guard/guard'
|
||||
|
||||
describe Guard::Guard do
|
||||
subject { Guard::Guard }
|
||||
|
||||
describe '#initialize' do
|
||||
|
||||
it 'assigns the defined watchers' do
|
||||
watchers = [ Guard::Watcher.new('*') ]
|
||||
guard = Guard::Guard.new(watchers)
|
||||
guard.watchers.should eql watchers
|
||||
end
|
||||
|
||||
it 'assigns the defined options' do
|
||||
options = { :a => 1, :b => 2 }
|
||||
guard = Guard::Guard.new([], options)
|
||||
guard.options.should eql options
|
||||
end
|
||||
|
||||
context 'with a group in the options' do
|
||||
it 'assigns the given group' do
|
||||
options = { :group => :test }
|
||||
guard = Guard::Guard.new([], options)
|
||||
guard.group.should eql :test
|
||||
end
|
||||
end
|
||||
|
||||
context 'without a group in the options' do
|
||||
it 'assigns a default group' do
|
||||
options = { }
|
||||
guard = Guard::Guard.new([], options)
|
||||
guard.group.should eql :default
|
||||
end
|
||||
end
|
||||
it "should be initialized with watchers" do
|
||||
watcher = mock(Guard::Watcher)
|
||||
guard = subject.new([watcher])
|
||||
guard.watchers.should == [watcher]
|
||||
end
|
||||
|
||||
describe '#init' do
|
||||
context 'when the Guard is already in the Guardfile' do
|
||||
before { ::Guard::Dsl.stub(:guardfile_include?).and_return true }
|
||||
|
||||
it 'shows an info message' do
|
||||
::Guard::UI.should_receive(:info).with 'Guardfile already includes myguard guard'
|
||||
Guard::Guard.init('myguard')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the Guard is not in the Guardfile' do
|
||||
before { ::Guard::Dsl.stub(:guardfile_include?).and_return false }
|
||||
|
||||
it 'appends the template to the Guardfile' do
|
||||
File.should_receive(:read).with('Guardfile').and_return 'Guardfile content'
|
||||
::Guard.should_receive(:locate_guard).with('myguard').and_return '/Users/me/projects/guard-myguard'
|
||||
File.should_receive(:read).with('/Users/me/projects/guard-myguard/lib/guard/myguard/templates/Guardfile').and_return('Template content')
|
||||
io = StringIO.new
|
||||
File.should_receive(:open).with('Guardfile', 'wb').and_yield io
|
||||
Guard::Guard.init('myguard')
|
||||
io.string.should eql "Guardfile content\n\nTemplate content\n"
|
||||
end
|
||||
end
|
||||
it "should be initialized with options" do
|
||||
watcher = mock(Guard::Watcher)
|
||||
guard = subject.new([], {:test => true})
|
||||
guard.options[:test].should be_true
|
||||
guard.options[:fake].should be_nil
|
||||
end
|
||||
|
||||
context "#reload?" do
|
||||
|
||||
it "should return true by default" do
|
||||
subject.new.reload?.should be_true
|
||||
end
|
||||
|
||||
it "should return true with option `:reload => {:disable => false}'" do
|
||||
guard = subject.new([], :reload => {:disable => false})
|
||||
guard.reload?.should be_true
|
||||
end
|
||||
|
||||
it "should return true with option `:reload => {}'" do
|
||||
guard = subject.new([], :reload => {})
|
||||
guard.reload?.should be_true
|
||||
end
|
||||
|
||||
it "should return true with option `:reload => true'" do
|
||||
guard = subject.new([], :reload => true)
|
||||
guard.reload?.should be_true
|
||||
end
|
||||
|
||||
it "should return false with option `:reload => {:disable => true}'" do
|
||||
guard = subject.new([], :reload => {:disable => true})
|
||||
guard.reload?.should be_false
|
||||
end
|
||||
|
||||
it "should return false with option `:reload => false'" do
|
||||
guard = subject.new([], :reload => false)
|
||||
guard.reload?.should be_false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "#reload_at_start?" do
|
||||
|
||||
it "should return false by default" do
|
||||
subject.new.reload_at_start?.should be_false
|
||||
end
|
||||
|
||||
it "should return false with option `:reload => {:at_start => false}'" do
|
||||
guard = subject.new([], :reload => {:at_start => false})
|
||||
guard.reload_at_start?.should be_false
|
||||
end
|
||||
|
||||
it "should return false with option `:reload => {}'" do
|
||||
guard = subject.new([], :reload => {})
|
||||
guard.reload_at_start?.should be_false
|
||||
end
|
||||
|
||||
it "should return false with option `:reload => true'" do
|
||||
guard = subject.new([], :reload => true)
|
||||
guard.reload_at_start?.should be_false
|
||||
end
|
||||
|
||||
it "should return false with option `:reload => false'" do
|
||||
guard = subject.new([], :reload => false)
|
||||
guard.reload_at_start?.should be_false
|
||||
end
|
||||
|
||||
it "should return true with option `:reload => {:at_start => true}'" do
|
||||
guard = subject.new([], :reload => {:at_start => true})
|
||||
guard.reload_at_start?.should be_true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "#run_all?" do
|
||||
|
||||
it "should return true by default" do
|
||||
subject.new.run_all?.should be_true
|
||||
end
|
||||
|
||||
it "should return true with option `:reload => {:disable => false}'" do
|
||||
guard = subject.new([], :run_all => {:disable => false})
|
||||
guard.run_all?.should be_true
|
||||
end
|
||||
|
||||
it "should return true with option `:reload => {}'" do
|
||||
guard = subject.new([], :run_all => {})
|
||||
guard.run_all?.should be_true
|
||||
end
|
||||
|
||||
it "should return true with option `:reload => true'" do
|
||||
guard = subject.new([], :run_all => true)
|
||||
guard.run_all?.should be_true
|
||||
end
|
||||
|
||||
it "should return false with option `:reload => {:disable => true}'" do
|
||||
guard = subject.new([], :run_all => {:disable => true})
|
||||
guard.run_all?.should be_false
|
||||
end
|
||||
|
||||
it "should return false with option `:reload => false'" do
|
||||
guard = subject.new([], :run_all => false)
|
||||
guard.run_all?.should be_false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "#run_all_at_start?" do
|
||||
|
||||
it "should return false by default" do
|
||||
subject.new.run_all_at_start?.should be_false
|
||||
end
|
||||
|
||||
it "should return false with option `:reload => {:at_start => false}'" do
|
||||
guard = subject.new([], :run_all => {:at_start => false})
|
||||
guard.run_all_at_start?.should be_false
|
||||
end
|
||||
|
||||
it "should return false with option `:reload => {}'" do
|
||||
guard = subject.new([], :run_all => {})
|
||||
guard.run_all_at_start?.should be_false
|
||||
end
|
||||
|
||||
it "should return false with option `:reload => true'" do
|
||||
guard = subject.new([], :run_all => true)
|
||||
guard.run_all_at_start?.should be_false
|
||||
end
|
||||
|
||||
it "should return false with option `:reload => false'" do
|
||||
guard = subject.new([], :run_all => false)
|
||||
guard.run_all_at_start?.should be_false
|
||||
end
|
||||
|
||||
it "should return true with option `:reload => {:at_start => true}'" do
|
||||
guard = subject.new([], :run_all => {:at_start => true})
|
||||
guard.run_all_at_start?.should be_true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,89 +0,0 @@
|
||||
require 'spec_helper'
|
||||
require 'guard/guard'
|
||||
|
||||
describe Guard::Hook do
|
||||
|
||||
class Guard::Dummy < Guard::Guard; end
|
||||
|
||||
let(:guard_class) { ::Guard::Dummy }
|
||||
let(:listener) { double('listener').as_null_object }
|
||||
|
||||
after { described_class.reset_callbacks! }
|
||||
|
||||
describe "--module methods--" do
|
||||
before { described_class.add_callback(listener, guard_class, :start_begin) }
|
||||
|
||||
describe ".add_callback" do
|
||||
it "can add a single callback" do
|
||||
described_class.has_callback?(listener, guard_class, :start_begin).should be_true
|
||||
end
|
||||
|
||||
it "can add multiple callbacks" do
|
||||
described_class.add_callback(listener, guard_class, [:event1, :event2])
|
||||
described_class.has_callback?(listener, guard_class, :event1).should be_true
|
||||
described_class.has_callback?(listener, guard_class, :event2).should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe ".notify" do
|
||||
it "sends :call to the given Guard class's callbacks" do
|
||||
listener.should_receive(:call).with(guard_class, :start_begin, "args")
|
||||
described_class.notify(guard_class, :start_begin, "args")
|
||||
end
|
||||
|
||||
it "runs only the given callbacks" do
|
||||
listener2 = double('listener2')
|
||||
described_class.add_callback(listener2, guard_class, :start_end)
|
||||
listener2.should_not_receive(:call).with(guard_class, :start_end)
|
||||
described_class.notify(guard_class, :start_begin)
|
||||
end
|
||||
|
||||
it "runs callbacks only for the guard given" do
|
||||
guard2_class = double('Guard::Dummy2').class
|
||||
described_class.add_callback(listener, guard2_class, :start_begin)
|
||||
listener.should_not_receive(:call).with(guard2_class, :start_begin)
|
||||
described_class.notify(guard_class, :start_begin)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#hook" do
|
||||
before(:all) do
|
||||
guard_class.class_eval do
|
||||
def start
|
||||
hook "my_hook"
|
||||
end
|
||||
|
||||
def run_all
|
||||
hook :begin
|
||||
hook :end
|
||||
end
|
||||
|
||||
def stop
|
||||
hook :begin, 'args'
|
||||
hook 'special_sauce', 'first_arg', 'second_arg'
|
||||
end
|
||||
end
|
||||
|
||||
@guard = guard_class.new
|
||||
end
|
||||
|
||||
it "calls Guard::Hook.notify" do
|
||||
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
|
||||
|
||||
it "if passed a string parameter, will use that for the hook name" do
|
||||
Guard::Hook.should_receive(:notify).with(guard_class, :my_hook)
|
||||
@guard.start
|
||||
end
|
||||
|
||||
it "accepts extra args" do
|
||||
Guard::Hook.should_receive(:notify).with(guard_class, :stop_begin, 'args')
|
||||
Guard::Hook.should_receive(:notify).with(guard_class, :special_sauce, 'first_arg', 'second_arg')
|
||||
@guard.stop
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,6 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Guard::Interactor do
|
||||
subject { Guard::Interactor.new }
|
||||
|
||||
end
|
@ -1,291 +1,34 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Guard::Listener do
|
||||
|
||||
describe '.select_and_init' do
|
||||
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
|
||||
RbConfig::CONFIG['target_os'] = 'darwin10.4.0'
|
||||
subject { described_class }
|
||||
|
||||
describe "init" do
|
||||
|
||||
before(:each) { @target_os = Config::CONFIG['target_os'] }
|
||||
after(:each) { Config::CONFIG['target_os'] = @target_os }
|
||||
|
||||
it "should use darwin listener on Mac OS X" do
|
||||
Config::CONFIG['target_os'] = 'darwin10.4.0'
|
||||
Guard::Darwin.stub(:usable?).and_return(true)
|
||||
Guard::Darwin.should_receive(:new)
|
||||
described_class.select_and_init
|
||||
subject.init
|
||||
end
|
||||
|
||||
it 'uses the Windows listener on Windows' do
|
||||
RbConfig::CONFIG['target_os'] = 'mingw'
|
||||
Guard::Windows.stub(:usable?).and_return(true)
|
||||
Guard::Windows.should_receive(:new)
|
||||
described_class.select_and_init
|
||||
|
||||
it "should use polling listener on Windows" do
|
||||
Config::CONFIG['target_os'] = 'win32'
|
||||
Guard::Polling.stub(:usable?).and_return(true)
|
||||
Guard::Polling.should_receive(:new)
|
||||
subject.init
|
||||
end
|
||||
|
||||
it 'uses the Linux listener on Linux' do
|
||||
RbConfig::CONFIG['target_os'] = 'linux'
|
||||
|
||||
it "should use linux listener on Linux" do
|
||||
Config::CONFIG['target_os'] = 'linux'
|
||||
Guard::Linux.stub(:usable?).and_return(true)
|
||||
Guard::Linux.should_receive(:new)
|
||||
described_class.select_and_init
|
||||
end
|
||||
|
||||
it 'forwards its arguments to the constructor' do
|
||||
described_class.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)
|
||||
described_class.select_and_init(path, opts)
|
||||
subject.init
|
||||
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.file?(file) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#relativize_paths' do
|
||||
subject { described_class.new('/tmp') }
|
||||
|
||||
let(:paths) { %w( /tmp/a /tmp/a/b /tmp/a.b/c.d ) }
|
||||
|
||||
it 'should relativize paths to the configured directory' do
|
||||
subject.relativize_paths(paths).should =~ %w( a a/b a.b/c.d )
|
||||
end
|
||||
|
||||
context 'when set to false' do
|
||||
subject { described_class.new('/tmp', :relativize_paths => false) }
|
||||
|
||||
it 'can be disabled' do
|
||||
subject.relativize_paths(paths).should eql paths
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update_last_event' do
|
||||
subject { described_class.new }
|
||||
|
||||
it 'updates the last event to the current time' do
|
||||
time = Time.now
|
||||
subject.update_last_event
|
||||
subject.instance_variable_get(:@last_event).to_i.should >= time.to_i
|
||||
end
|
||||
end
|
||||
|
||||
describe '#modified_files' do
|
||||
subject { described_class.new }
|
||||
|
||||
let(:file1) { fixture('folder1', 'file1.txt') }
|
||||
let(:file2) { fixture('folder1', 'folder2', 'file2.txt') }
|
||||
let(:file3) { fixture('folder1', 'deletedfile1.txt') }
|
||||
let(:file4) { fixture('folder1', 'movedfile1.txt') }
|
||||
let(:file5) { fixture('folder1', 'folder2', 'movedfile1.txt') }
|
||||
|
||||
before { listen_to subject }
|
||||
|
||||
context 'without the :all option' do
|
||||
it 'finds modified files only in the directory supplied' do
|
||||
watch do
|
||||
FileUtils.touch([file1, file2, file3])
|
||||
subject.modified_files([fixture('folder1')], {}).should =~
|
||||
['spec/fixtures/folder1/deletedfile1.txt', 'spec/fixtures/folder1/file1.txt']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with the :all options' do
|
||||
it 'finds modified files within subdirectories' do
|
||||
watch do
|
||||
FileUtils.touch([file1, file2, file3])
|
||||
subject.modified_files([fixture('folder1')], { :all => true }).should =~
|
||||
['spec/fixtures/folder1/deletedfile1.txt',
|
||||
'spec/fixtures/folder1/file1.txt',
|
||||
'spec/fixtures/folder1/folder2/file2.txt']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without updating the content' do
|
||||
it 'ignores the files for the second time' do
|
||||
watch do
|
||||
FileUtils.touch([file1, file2, file3])
|
||||
subject.modified_files([fixture('folder1')], {}).should =~
|
||||
['spec/fixtures/folder1/deletedfile1.txt', 'spec/fixtures/folder1/file1.txt']
|
||||
|
||||
subject.update_last_event
|
||||
|
||||
FileUtils.touch([file1, file2, file3])
|
||||
subject.modified_files([fixture('folder1')], {}).should be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with content that has changed' do
|
||||
after { File.open(file1, 'w') { |f| f.write('') } }
|
||||
|
||||
it 'identifies the files for the second time' do
|
||||
watch do
|
||||
FileUtils.touch([file1, file2, file3])
|
||||
subject.modified_files([fixture('folder1')], {}).should =~
|
||||
['spec/fixtures/folder1/deletedfile1.txt', 'spec/fixtures/folder1/file1.txt']
|
||||
|
||||
subject.update_last_event
|
||||
|
||||
FileUtils.touch([file2, file3])
|
||||
File.open(file1, 'w') { |f| f.write('changed content') }
|
||||
subject.modified_files([fixture('folder1')], {}).should =~
|
||||
['spec/fixtures/folder1/file1.txt']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without the :watch_all_modifications option' do
|
||||
after { FileUtils.touch(file3) }
|
||||
|
||||
it 'defaults to false' do
|
||||
subject.instance_variable_get(:@watch_all_modifications).should eql false
|
||||
end
|
||||
|
||||
context 'for a deleted file' do
|
||||
after { FileUtils.touch(file3) }
|
||||
|
||||
it 'does not catch the deletion' do
|
||||
File.exists?(file3).should be_true
|
||||
|
||||
watch do
|
||||
FileUtils.remove_file(file3)
|
||||
end
|
||||
|
||||
subject.modified_files([fixture('folder1')], {}).should =~ []
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a moved file' do
|
||||
after { FileUtils.move(file4, file1) }
|
||||
|
||||
it 'does not catch the move' do
|
||||
File.exists?(file1).should be_true
|
||||
File.exists?(file4).should be_false
|
||||
|
||||
watch do
|
||||
FileUtils.move(file1, file4)
|
||||
end
|
||||
|
||||
subject.modified_files([@fixture_path.join('folder1')], {}).should =~ []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with the :watch_all_modifications option' do
|
||||
subject { described_class.new(Dir.pwd, :watch_all_modifications => true) }
|
||||
|
||||
before do
|
||||
subject.timestamp_files
|
||||
subject.update_last_event
|
||||
end
|
||||
|
||||
it 'should be true when set' do
|
||||
subject.instance_variable_get(:@watch_all_modifications).should eql true
|
||||
end
|
||||
|
||||
context 'for a deleted file' do
|
||||
after { FileUtils.touch(file3) }
|
||||
|
||||
it 'catches the deletion' do
|
||||
File.exists?(file3).should be_true
|
||||
|
||||
watch do
|
||||
FileUtils.remove_file(file3)
|
||||
end
|
||||
|
||||
subject.modified_files([fixture('folder1')], {}).should =~
|
||||
['!spec/fixtures/folder1/deletedfile1.txt']
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a moved file' do
|
||||
after { FileUtils.move(file4, file1) }
|
||||
|
||||
it 'catches the move' do
|
||||
File.exists?(file1).should be_true
|
||||
File.exists?(file4).should be_false
|
||||
|
||||
watch do
|
||||
FileUtils.move(file1, file4)
|
||||
end
|
||||
|
||||
subject.modified_files([@fixture_path.join('folder1')], {}).should =~
|
||||
['!spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/movedfile1.txt']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'working directory' do
|
||||
context 'unspecified' do
|
||||
subject { described_class.new }
|
||||
|
||||
it 'defaults to Dir.pwd' do
|
||||
subject.instance_variable_get(:@directory).should eql 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
|
||||
let(:working_directory) { fixture('folder1') }
|
||||
|
||||
subject { described_class.new working_directory }
|
||||
|
||||
before { listen_to subject }
|
||||
|
||||
it 'can be inspected' do
|
||||
subject.instance_variable_get(:@directory).should eql working_directory.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(working_directory.to_s)
|
||||
start
|
||||
stop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ignore_paths' do
|
||||
it 'defaults to the default ignore paths' do
|
||||
described_class.new.ignore_paths.should == Guard::Listener::DEFAULT_IGNORE_PATHS
|
||||
end
|
||||
|
||||
it 'can be added to via :ignore_paths option' do
|
||||
listener = described_class.new 'path', :ignore_paths => ['foo', 'bar']
|
||||
listener.ignore_paths.should include('foo', 'bar')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#exclude_ignored_paths [<dirs>]' do
|
||||
let(:ignore_paths) { nil }
|
||||
subject { described_class.new(@fixture_path, { :ignore_paths => ignore_paths }) }
|
||||
|
||||
it 'returns children of <dirs>' do
|
||||
subject.exclude_ignored_paths(['spec/fixtures']).should =~
|
||||
['spec/fixtures/.dotfile', 'spec/fixtures/folder1', 'spec/fixtures/Guardfile']
|
||||
end
|
||||
|
||||
describe 'when ignore_paths set to some of <dirs> children' do
|
||||
let(:ignore_paths) { ['Guardfile', '.dotfile'] }
|
||||
|
||||
it 'excludes the ignored paths' do
|
||||
subject.exclude_ignored_paths(['spec/fixtures']).should =~ ['spec/fixtures/folder1']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
@ -2,26 +2,72 @@ require 'spec_helper'
|
||||
require 'guard/listeners/darwin'
|
||||
|
||||
describe Guard::Darwin do
|
||||
|
||||
if windows?
|
||||
it "isn't usable on windows" do
|
||||
described_class.should_not be_usable
|
||||
end
|
||||
end
|
||||
|
||||
subject { Guard::Darwin }
|
||||
|
||||
if linux?
|
||||
it "isn't usable on linux" do
|
||||
described_class.should_not be_usable
|
||||
it "should not be usable on linux" do
|
||||
subject.should_not be_usable
|
||||
end
|
||||
end
|
||||
|
||||
if mac? && Guard::Darwin.usable?
|
||||
it "is usable on 10.6" do
|
||||
described_class.should be_usable
|
||||
|
||||
if mac?
|
||||
it "should be usable on 10.6" do
|
||||
subject.should be_usable
|
||||
end
|
||||
|
||||
describe "watch" do
|
||||
before(:each) do
|
||||
@results = []
|
||||
@listener = Guard::Darwin.new
|
||||
@listener.on_change do |files|
|
||||
@results += files
|
||||
end
|
||||
end
|
||||
|
||||
it "should catch 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 "should catch 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 "should catch 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
|
||||
|
||||
it_should_behave_like "a listener that reacts to #on_change"
|
||||
it_should_behave_like "a listener scoped to a specific directory"
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def start
|
||||
sleep 1
|
||||
Thread.new { @listener.start }
|
||||
sleep 1
|
||||
end
|
||||
|
||||
def stop
|
||||
sleep 1
|
||||
@listener.stop
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -3,35 +3,30 @@ require 'fileutils'
|
||||
require 'guard/listeners/linux'
|
||||
|
||||
describe Guard::Linux do
|
||||
|
||||
subject { Guard::Linux }
|
||||
|
||||
if mac?
|
||||
it "isn't usable on 10.6" do
|
||||
described_class.should_not be_usable
|
||||
it "should not be usable on 10.6" do
|
||||
subject.should_not be_usable
|
||||
end
|
||||
end
|
||||
|
||||
if windows?
|
||||
it "isn't usable on windows" do
|
||||
described_class.should_not be_usable
|
||||
end
|
||||
end
|
||||
|
||||
if linux? && Guard::Linux.usable?
|
||||
it "is usable on linux" do
|
||||
described_class.should be_usable
|
||||
|
||||
if linux?
|
||||
it "should be usable on linux" do
|
||||
subject.should be_usable
|
||||
end
|
||||
|
||||
describe "#start", :long_running => true do
|
||||
describe "start" do
|
||||
before(:each) do
|
||||
@listener = Guard::Linux.new
|
||||
end
|
||||
|
||||
it "calls watch_change on the first start" do
|
||||
|
||||
it "should call watch_change if first start" do
|
||||
@listener.should_receive(:watch_change)
|
||||
start
|
||||
end
|
||||
|
||||
it "doesn't call watch_change on subsequent starts after a stop" do
|
||||
it "should not call watch_change if start after stop" do
|
||||
@listener.stub!(:stop)
|
||||
start
|
||||
stop
|
||||
@ -42,35 +37,94 @@ describe Guard::Linux do
|
||||
stop
|
||||
@listener.should_not be_watch_change
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "watch" do
|
||||
before(:each) do
|
||||
@results = []
|
||||
@listener = Guard::Linux.new
|
||||
@listener.on_change do |files|
|
||||
@results += files
|
||||
end
|
||||
end
|
||||
|
||||
it "should catch 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 "should catch 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 "should catch 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
|
||||
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 catch 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
|
||||
|
||||
it "should catch 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_should_behave_like "a listener that reacts to #on_change"
|
||||
it_should_behave_like "a listener scoped to a specific directory"
|
||||
|
||||
# Fun fact: FileUtils.touch seems not to be enough on Linux to trigger a modify event
|
||||
|
||||
it "catches modified files with glib saving routine (like Vim, Emacs or Gedit)" do
|
||||
@listener = described_class.new
|
||||
record_results
|
||||
file = @fixture_path.join("folder1/file1.txt")
|
||||
File.exists?(file).should be_true
|
||||
start
|
||||
File.open(file, 'r+').close
|
||||
FileUtils.touch(file)
|
||||
stop
|
||||
results.should =~ ['spec/fixtures/folder1/file1.txt']
|
||||
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.instance_variable_get(:@inotify).should_not_receive(:process)
|
||||
stop
|
||||
File.open(file, 'w') {|f| f.write('') }
|
||||
it "should not process change if 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
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def start
|
||||
sleep 1
|
||||
Thread.new { @listener.start }
|
||||
sleep 1
|
||||
end
|
||||
|
||||
def stop
|
||||
sleep 1
|
||||
@listener.stop
|
||||
sleep 1
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -2,8 +2,56 @@ require 'spec_helper'
|
||||
require 'guard/listeners/polling'
|
||||
|
||||
describe Guard::Polling do
|
||||
|
||||
it_should_behave_like "a listener that reacts to #on_change"
|
||||
it_should_behave_like "a listener scoped to a specific directory"
|
||||
|
||||
|
||||
before(:each) do
|
||||
@results = []
|
||||
@listener = Guard::Polling.new
|
||||
@listener.on_change do |files|
|
||||
@results += files
|
||||
end
|
||||
end
|
||||
|
||||
it "should catch 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 "should catch 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 "should catch 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.sort.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def start
|
||||
Thread.new { @listener.start }
|
||||
sleep 1
|
||||
end
|
||||
|
||||
def stop
|
||||
sleep 1
|
||||
@listener.stop
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,27 +0,0 @@
|
||||
require 'spec_helper'
|
||||
require 'guard/listeners/windows'
|
||||
|
||||
describe Guard::Windows do
|
||||
|
||||
if linux?
|
||||
it "isn't usable on linux" do
|
||||
described_class.should_not be_usable
|
||||
end
|
||||
end
|
||||
|
||||
if mac?
|
||||
it "isn't usable on Mac" do
|
||||
described_class.should_not be_usable
|
||||
end
|
||||
end
|
||||
|
||||
if windows?
|
||||
it "is usable on Windows 2000 and later" do
|
||||
described_class.should be_usable
|
||||
end
|
||||
|
||||
it_should_behave_like "a listener that reacts to #on_change"
|
||||
it_should_behave_like "a listener scoped to a specific directory"
|
||||
end
|
||||
|
||||
end
|
@ -1,428 +1,36 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Guard::Notifier do
|
||||
|
||||
describe ".turn_off" do
|
||||
before do
|
||||
ENV["GUARD_NOTIFY"] = 'true'
|
||||
described_class.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 GrowlNotify library available" do
|
||||
before do
|
||||
class ::GrowlNotify
|
||||
class GrowlNotFound < Exception; end
|
||||
def self.config ; end
|
||||
end
|
||||
end
|
||||
|
||||
it "loads the library and enables the notifications" do
|
||||
described_class.should_receive(:require).with('growl_notify').and_return true
|
||||
GrowlNotify.should_receive(:application_name).and_return ''
|
||||
described_class.turn_on
|
||||
described_class.should be_enabled
|
||||
end
|
||||
|
||||
it "should respond properly to a GrowlNotify exception" do
|
||||
::GrowlNotify.should_receive(:config).and_raise ::GrowlNotify::GrowlNotFound
|
||||
::GrowlNotify.should_receive(:application_name).and_return ''
|
||||
::Guard::UI.should_receive(:info)
|
||||
described_class.should_receive(:require).with('growl_notify').and_return true
|
||||
described_class.turn_on
|
||||
described_class.should_not be_enabled
|
||||
described_class.growl_library.should eql :growl_notify
|
||||
end
|
||||
|
||||
after do
|
||||
Object.send(:remove_const, :GrowlNotify)
|
||||
end
|
||||
end
|
||||
|
||||
context "with the GNTP library available" do
|
||||
before do
|
||||
class ::GNTP
|
||||
def initialize(app); end
|
||||
def register(config) ; end
|
||||
end
|
||||
end
|
||||
|
||||
it "loads the library and enables the notifications" do
|
||||
described_class.should_receive(:require).with('growl_notify').and_raise LoadError
|
||||
described_class.should_receive(:require).with('ruby_gntp').and_return true
|
||||
described_class.turn_on
|
||||
described_class.should be_enabled
|
||||
described_class.growl_library.should eql :ruby_gntp
|
||||
end
|
||||
|
||||
after do
|
||||
Object.send(:remove_const, :GNTP)
|
||||
end
|
||||
end
|
||||
|
||||
context "with the Growl library available" do
|
||||
it "loads the library and enables the notifications" do
|
||||
described_class.should_receive(:require).with('growl_notify').and_raise LoadError
|
||||
described_class.should_receive(:require).with('ruby_gntp').and_raise LoadError
|
||||
described_class.should_receive(:require).with('growl').and_return true
|
||||
described_class.turn_on
|
||||
described_class.should be_enabled
|
||||
described_class.growl_library.should eql :growl
|
||||
end
|
||||
end
|
||||
|
||||
context "without a Growl library available" do
|
||||
it "disables the notifications" do
|
||||
described_class.should_receive(:require).with('growl_notify').and_raise LoadError
|
||||
described_class.should_receive(:require).with('ruby_gntp').and_raise LoadError
|
||||
described_class.should_receive(:require).with('growl').and_raise LoadError
|
||||
described_class.turn_on
|
||||
described_class.should_not be_enabled
|
||||
described_class.growl_library.should be nil
|
||||
end
|
||||
subject { Guard::Notifier }
|
||||
|
||||
describe "notify" do
|
||||
before(:each) { ENV["GUARD_ENV"] = 'special_test' }
|
||||
|
||||
if mac?
|
||||
require 'growl'
|
||||
it "should use 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'
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
described_class.should_receive(:require).with('libnotify').and_return true
|
||||
described_class.turn_on
|
||||
described_class.should be_enabled
|
||||
end
|
||||
end
|
||||
|
||||
context "without the Libnotify library available" do
|
||||
it "disables the notifications" do
|
||||
described_class.should_receive(:require).with('libnotify').and_raise LoadError
|
||||
described_class.turn_on
|
||||
described_class.should_not be_enabled
|
||||
end
|
||||
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
|
||||
described_class.should_receive(:require).with('rb-notifu').and_return true
|
||||
described_class.turn_on
|
||||
described_class.should be_enabled
|
||||
end
|
||||
end
|
||||
|
||||
context "without the rb-notify library available" do
|
||||
it "disables the notifications" do
|
||||
described_class.should_receive(:require).with('rb-notifu').and_raise LoadError
|
||||
described_class.turn_on
|
||||
described_class.should_not be_enabled
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".notify" do
|
||||
before { described_class.stub(:enabled?).and_return(true) }
|
||||
|
||||
context "on Mac OS" do
|
||||
before do
|
||||
RbConfig::CONFIG.stub(:[]).and_return 'darwin'
|
||||
described_class.stub(:require_growl)
|
||||
end
|
||||
|
||||
context 'with growl gem' do
|
||||
before do
|
||||
Object.send(:remove_const, :Growl) if defined?(Growl)
|
||||
Growl = Object.new
|
||||
described_class.growl_library = :growl
|
||||
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"
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "don't passes the notification to Growl if library is not available" do
|
||||
Growl.should_not_receive(:notify)
|
||||
described_class.growl_library = nil
|
||||
described_class.should_receive(:enabled?).and_return(false)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
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
|
||||
)
|
||||
described_class.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"
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :name => "Guard-Cucumber"
|
||||
end
|
||||
end
|
||||
|
||||
context 'with growl_notify gem' do
|
||||
before do
|
||||
Object.send(:remove_const, :GrowlNotify) if defined?(GrowlNotify)
|
||||
GrowlNotify = Object.new
|
||||
described_class.growl_library = :growl_notify
|
||||
end
|
||||
|
||||
after do
|
||||
Object.send(:remove_const, :GrowlNotify)
|
||||
end
|
||||
|
||||
it "passes the notification to Growl" do
|
||||
GrowlNotify.should_receive(:send_notification).with(
|
||||
:title => "Guard",
|
||||
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
||||
:application_name => "Guard",
|
||||
:description => 'great'
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "don't passes the notification to Growl if library is not available" do
|
||||
GrowlNotify.should_not_receive(:send_notification)
|
||||
described_class.growl_library = nil
|
||||
described_class.should_receive(:enabled?).and_return(false)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "allows additional notification options" do
|
||||
GrowlNotify.should_receive(:send_notification).with(
|
||||
:title => "Guard",
|
||||
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
||||
:application_name => "Guard",
|
||||
:description => 'great',
|
||||
:priority => 1
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :priority => 1
|
||||
end
|
||||
|
||||
it "throws out the application name since Guard should only use one Growl App Name while running" do
|
||||
GrowlNotify.should_receive(:send_notification).with(
|
||||
:title => "Guard",
|
||||
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
||||
:application_name => "Guard",
|
||||
:description => 'great'
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :name => "Guard-Cucumber"
|
||||
end
|
||||
end
|
||||
|
||||
context 'with ruby_gntp gem' do
|
||||
before do
|
||||
described_class.growl_library = :ruby_gntp
|
||||
described_class.gntp = Object.new
|
||||
end
|
||||
|
||||
it "passes a success notification to Ruby GNTP" do
|
||||
described_class.gntp.should_receive(:notify).with(
|
||||
:name => "success",
|
||||
:text => 'great',
|
||||
:title => "Guard",
|
||||
:icon => 'file://' + Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "passes a pending notification to Ruby GNTP" do
|
||||
described_class.gntp.should_receive(:notify).with(
|
||||
:name => "pending",
|
||||
:text => 'great',
|
||||
:title => "Guard",
|
||||
:icon => 'file://' + Pathname.new(File.dirname(__FILE__)).join('../../images/pending.png').to_s
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :image => :pending
|
||||
end
|
||||
|
||||
it "passes a failure notification to Ruby GNTP" do
|
||||
described_class.gntp.should_receive(:notify).with(
|
||||
:name => "failed",
|
||||
:text => 'great',
|
||||
:title => "Guard",
|
||||
:icon => 'file://' + Pathname.new(File.dirname(__FILE__)).join('../../images/failed.png').to_s
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :image => :failed
|
||||
end
|
||||
|
||||
it "passes a general notification to Ruby GNTP" do
|
||||
described_class.gntp.should_receive(:notify).with(
|
||||
:name => "notify",
|
||||
:text => 'great',
|
||||
:title => "Guard",
|
||||
:icon => 'file:///path/to/custom.png'
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :image => '/path/to/custom.png'
|
||||
end
|
||||
|
||||
it "don't passes the notification to Ruby GNTP if library is not available" do
|
||||
described_class.gntp.should_not_receive(:notify)
|
||||
described_class.growl_library = nil
|
||||
described_class.should_receive(:enabled?).and_return(false)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "allows additional notification options" do
|
||||
described_class.gntp.should_receive(:notify).with(
|
||||
:name => "success",
|
||||
:text => 'great',
|
||||
:title => "Guard",
|
||||
:icon => 'file://' + Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
||||
:sticky => true
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :sticky => true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "on Linux" do
|
||||
before do
|
||||
RbConfig::CONFIG.stub(:[]).and_return 'linux'
|
||||
described_class.stub(:require_libnotify)
|
||||
Object.send(:remove_const, :Libnotify) if defined?(Libnotify)
|
||||
Libnotify = Object.new
|
||||
end
|
||||
|
||||
after do
|
||||
Object.send(:remove_const, :Libnotify)
|
||||
end
|
||||
|
||||
it "passes the notification to Libnotify" do
|
||||
|
||||
if linux?
|
||||
require 'libnotify'
|
||||
it "should use 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,
|
||||
:transient => true
|
||||
:icon_path => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "don't passes the notification to Libnotify if library is not available" do
|
||||
Libnotify.should_not_receive(:show)
|
||||
described_class.should_receive(:enabled?).and_return(false)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "allows additional notification options" do
|
||||
Libnotify.should_receive(:show).with(
|
||||
:body => "great",
|
||||
:summary => 'Guard',
|
||||
:icon_path => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
||||
:transient => true,
|
||||
:urgency => :critical
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :urgency => :critical
|
||||
end
|
||||
|
||||
it "allows to overwrite a default notification option" do
|
||||
Libnotify.should_receive(:show).with(
|
||||
:body => "great",
|
||||
:summary => 'Guard',
|
||||
:icon_path => '~/.guard/success.png',
|
||||
:transient => true
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :icon_path => '~/.guard/success.png'
|
||||
end
|
||||
end
|
||||
|
||||
context "on Windows" do
|
||||
before do
|
||||
RbConfig::CONFIG.stub(:[]).and_return 'mswin'
|
||||
described_class.stub(:require_rbnotifu)
|
||||
Object.send(:remove_const, :Notifu) if defined?(Notifu)
|
||||
Notifu = Object.new
|
||||
end
|
||||
|
||||
after do
|
||||
Object.send(:remove_const, :Notifu)
|
||||
end
|
||||
|
||||
it "passes the notification to rb-notifu" do
|
||||
Notifu.should_receive(:show).with(
|
||||
:message => "great",
|
||||
:title => 'Guard',
|
||||
:type => :info,
|
||||
:time => 3
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "don't passes the notification to rb-notifu if library is not available" do
|
||||
Notifu.should_not_receive(:show)
|
||||
described_class.should_receive(:enabled?).and_return(false)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "allows additional notification options" do
|
||||
Notifu.should_receive(:show).with(
|
||||
:message => "great",
|
||||
:title => 'Guard',
|
||||
:type => :info,
|
||||
:time => 3,
|
||||
:nosound => true
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :nosound => true
|
||||
end
|
||||
|
||||
it "allows to overwrite a default notification option" do
|
||||
Notifu.should_receive(:show).with(
|
||||
:message => "great",
|
||||
:title => 'Guard',
|
||||
:type => :info,
|
||||
:time => 10
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :time => 10
|
||||
subject.notify 'great', :title => 'Guard'
|
||||
end
|
||||
end
|
||||
|
||||
after(:each) { ENV["GUARD_ENV"] = 'test' }
|
||||
end
|
||||
|
||||
describe ".enabled?" do
|
||||
context "when enabled" do
|
||||
before { ENV["GUARD_NOTIFY"] = 'true' }
|
||||
|
||||
it { should be_enabled }
|
||||
end
|
||||
|
||||
context "when disabled" do
|
||||
before { ENV["GUARD_NOTIFY"] = 'false' }
|
||||
|
||||
it { should_not be_enabled }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
@ -1,309 +0,0 @@
|
||||
require 'spec_helper'
|
||||
require 'guard/guard'
|
||||
|
||||
describe Guard::Watcher do
|
||||
|
||||
describe "#initialize" do
|
||||
it "requires a pattern parameter" do
|
||||
expect { described_class.new }.to raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
context "with a pattern parameter" do
|
||||
context "that is a string" do
|
||||
it "keeps the string pattern unmodified" do
|
||||
described_class.new('spec_helper.rb').pattern.should == 'spec_helper.rb'
|
||||
end
|
||||
end
|
||||
|
||||
context "that is a regexp" do
|
||||
it "keeps the regex pattern unmodified" do
|
||||
described_class.new(/spec_helper\.rb/).pattern.should == /spec_helper\.rb/
|
||||
end
|
||||
end
|
||||
|
||||
context "that is a string looking like a regex (deprecated)" do
|
||||
before(:each) { Guard::UI.should_receive(:info).any_number_of_times }
|
||||
|
||||
it "converts the string automatically to a regex" do
|
||||
described_class.new('^spec_helper.rb').pattern.should == /^spec_helper.rb/
|
||||
described_class.new('spec_helper.rb$').pattern.should == /spec_helper.rb$/
|
||||
described_class.new('spec_helper\.rb').pattern.should == /spec_helper\.rb/
|
||||
described_class.new('.*_spec.rb').pattern.should == /.*_spec.rb/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#action" do
|
||||
it "sets the action to nothing by default" do
|
||||
described_class.new(/spec_helper\.rb/).action.should be_nil
|
||||
end
|
||||
|
||||
it "sets the action to the supplied block" do
|
||||
action = lambda { |m| "spec/#{m[1]}_spec.rb" }
|
||||
described_class.new(%r{^lib/(.*).rb}, action).action.should == action
|
||||
end
|
||||
end
|
||||
|
||||
describe ".match_files" do
|
||||
before(:all) do
|
||||
@guard = Guard::Guard.new
|
||||
@guard_any_return = Guard::Guard.new
|
||||
@guard_any_return.options[:any_return] = true
|
||||
end
|
||||
|
||||
context "with a watcher without action" do
|
||||
context "that is a regex pattern" do
|
||||
before(:all) { @guard.watchers = [described_class.new(/.*_spec\.rb/)] }
|
||||
|
||||
it "returns the paths that matches the regex" do
|
||||
described_class.match_files(@guard, ['guard_rocks_spec.rb', 'guard_rocks.rb']).should == ['guard_rocks_spec.rb']
|
||||
end
|
||||
end
|
||||
|
||||
context "that is a string pattern" do
|
||||
before(:all) { @guard.watchers = [described_class.new('guard_rocks_spec.rb')] }
|
||||
|
||||
it "returns the path that matches the string" do
|
||||
described_class.match_files(@guard, ['guard_rocks_spec.rb', 'guard_rocks.rb']).should == ['guard_rocks_spec.rb']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a watcher action without parameter" do
|
||||
context "for a watcher that matches file strings" do
|
||||
before(:all) do
|
||||
@guard.watchers = [
|
||||
described_class.new('spec_helper.rb', lambda { 'spec' }),
|
||||
described_class.new('addition.rb', lambda { 1 + 1 }),
|
||||
described_class.new('hash.rb', lambda { Hash[:foo, 'bar'] }),
|
||||
described_class.new('array.rb', lambda { ['foo', 'bar'] }),
|
||||
described_class.new('blank.rb', lambda { '' }),
|
||||
described_class.new(/^uptime\.rb/, lambda { `uptime > /dev/null` })
|
||||
]
|
||||
end
|
||||
|
||||
it "returns a single file specified within the action" do
|
||||
described_class.match_files(@guard, ['spec_helper.rb']).should == ['spec']
|
||||
end
|
||||
|
||||
it "returns multiple files specified within the action" do
|
||||
described_class.match_files(@guard, ['hash.rb']).should == ['foo', 'bar']
|
||||
end
|
||||
|
||||
it "returns multiple files by combining the results of different actions" do
|
||||
described_class.match_files(@guard, ['spec_helper.rb', 'array.rb']).should == ['spec', 'foo', 'bar']
|
||||
end
|
||||
|
||||
it "returns nothing if the action returns something other than a string or an array of strings" do
|
||||
described_class.match_files(@guard, ['addition.rb']).should == []
|
||||
end
|
||||
|
||||
it "returns nothing if the action response is empty" do
|
||||
described_class.match_files(@guard, ['blank.rb']).should == []
|
||||
end
|
||||
|
||||
it "returns nothing if the action returns nothing" do
|
||||
described_class.match_files(@guard, ['uptime.rb']).should == []
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a watcher that matches information objects' do
|
||||
before(:all) do
|
||||
@guard_any_return.watchers = [
|
||||
described_class.new('spec_helper.rb', lambda { 'spec' }),
|
||||
described_class.new('addition.rb', lambda { 1 + 1 }),
|
||||
described_class.new('hash.rb', lambda { Hash[:foo, 'bar'] }),
|
||||
described_class.new('array.rb', lambda { ['foo', 'bar'] }),
|
||||
described_class.new('blank.rb', lambda { '' }),
|
||||
described_class.new(/^uptime\.rb/, lambda { `uptime > /dev/null` })
|
||||
]
|
||||
end
|
||||
|
||||
it "returns a single file specified within the action" do
|
||||
described_class.match_files(@guard_any_return, ['spec_helper.rb']).class.should == Array
|
||||
described_class.match_files(@guard_any_return, ['spec_helper.rb']).empty?.should == false
|
||||
end
|
||||
|
||||
it "returns multiple files specified within the action" do
|
||||
described_class.match_files(@guard_any_return, ['hash.rb']).should == [{:foo => 'bar'}]
|
||||
end
|
||||
|
||||
it "returns multiple files by combining the results of different actions" do
|
||||
described_class.match_files(@guard_any_return, ['spec_helper.rb', 'array.rb']).should == ['spec', ['foo', 'bar']]
|
||||
end
|
||||
|
||||
it "returns the evaluated addition argument in an array" do
|
||||
described_class.match_files(@guard_any_return, ['addition.rb']).class.should == Array
|
||||
described_class.match_files(@guard_any_return, ['addition.rb'])[0].should == 2
|
||||
end
|
||||
|
||||
it "returns nothing if the action response is empty string" do
|
||||
described_class.match_files(@guard_any_return, ['blank.rb']).should == ['']
|
||||
end
|
||||
|
||||
it "returns nothing if the action returns empty string" do
|
||||
described_class.match_files(@guard_any_return, ['uptime.rb']).should == ['']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a watcher action that takes a parameter" do
|
||||
context "for a watcher that matches file strings" do
|
||||
before(:all) do
|
||||
@guard.watchers = [
|
||||
described_class.new(%r{lib/(.*)\.rb}, lambda { |m| "spec/#{m[1]}_spec.rb" }),
|
||||
described_class.new(/addition(.*)\.rb/, lambda { |m| 1 + 1 }),
|
||||
described_class.new('hash.rb', lambda { |m| Hash[:foo, 'bar'] }),
|
||||
described_class.new(/array(.*)\.rb/, lambda { |m| ['foo', 'bar'] }),
|
||||
described_class.new(/blank(.*)\.rb/, lambda { |m| '' }),
|
||||
described_class.new(/uptime(.*)\.rb/, lambda { |m| `uptime > /dev/null` })
|
||||
]
|
||||
end
|
||||
|
||||
it "returns a substituted single file specified within the action" do
|
||||
described_class.match_files(@guard, ['lib/my_wonderful_lib.rb']).should == ['spec/my_wonderful_lib_spec.rb']
|
||||
end
|
||||
|
||||
it "returns multiple files specified within the action" do
|
||||
described_class.match_files(@guard, ['hash.rb']).should == ['foo', 'bar']
|
||||
end
|
||||
|
||||
it "returns multiple files by combining the results of different actions" do
|
||||
described_class.match_files(@guard, ['lib/my_wonderful_lib.rb', 'array.rb']).should == ['spec/my_wonderful_lib_spec.rb', 'foo', 'bar']
|
||||
end
|
||||
|
||||
it "returns nothing if the action returns something other than a string or an array of strings" do
|
||||
described_class.match_files(@guard, ['addition.rb']).should == []
|
||||
end
|
||||
|
||||
it "returns nothing if the action response is empty" do
|
||||
described_class.match_files(@guard, ['blank.rb']).should == []
|
||||
end
|
||||
|
||||
it "returns nothing if the action returns nothing" do
|
||||
described_class.match_files(@guard, ['uptime.rb']).should == []
|
||||
end
|
||||
end
|
||||
|
||||
context "for a watcher that matches information objects" do
|
||||
before(:all) do
|
||||
@guard_any_return.watchers = [
|
||||
described_class.new(%r{lib/(.*)\.rb}, lambda { |m| "spec/#{m[1]}_spec.rb" }),
|
||||
described_class.new(/addition(.*)\.rb/, lambda { |m| (1 + 1).to_s + m[0] }),
|
||||
described_class.new('hash.rb', lambda { |m| Hash[:foo, 'bar', :file_name, m[0]] }),
|
||||
described_class.new(/array(.*)\.rb/, lambda { |m| ['foo', 'bar', m[0]] }),
|
||||
described_class.new(/blank(.*)\.rb/, lambda { |m| '' }),
|
||||
described_class.new(/uptime(.*)\.rb/, lambda { |m| `uptime > /dev/null` })
|
||||
]
|
||||
end
|
||||
|
||||
it "returns a substituted single file specified within the action" do
|
||||
described_class.match_files(@guard_any_return, ['lib/my_wonderful_lib.rb']).should == ['spec/my_wonderful_lib_spec.rb']
|
||||
end
|
||||
|
||||
it "returns a hash specified within the action" do
|
||||
described_class.match_files(@guard_any_return, ['hash.rb']).should == [{:foo => 'bar', :file_name => 'hash.rb'}]
|
||||
end
|
||||
|
||||
it "returns multiple files by combining the results of different actions" do
|
||||
described_class.match_files(@guard_any_return, ['lib/my_wonderful_lib.rb', 'array.rb']).should == ['spec/my_wonderful_lib_spec.rb', ['foo', 'bar', "array.rb"]]
|
||||
end
|
||||
|
||||
it "returns the evaluated addition argument + the path" do
|
||||
described_class.match_files(@guard_any_return, ['addition.rb']).should == ["2addition.rb"]
|
||||
end
|
||||
|
||||
it "returns nothing if the action response is empty string" do
|
||||
described_class.match_files(@guard_any_return, ['blank.rb']).should == ['']
|
||||
end
|
||||
|
||||
it "returns nothing if the action returns empty string" do
|
||||
described_class.match_files(@guard_any_return, ['uptime.rb']).should == ['']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with an exception that is raised" do
|
||||
before(:all) { @guard.watchers = [described_class.new('evil.rb', lambda { raise "EVIL" })] }
|
||||
|
||||
it "displays the error and backtrace" do
|
||||
Guard::UI.should_receive(:error) do |msg|
|
||||
msg.should include("Problem with watch action!")
|
||||
msg.should include("EVIL")
|
||||
end
|
||||
|
||||
described_class.match_files(@guard, ['evil.rb'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".match_files?" do
|
||||
before(:all) do
|
||||
@guard1 = Guard::Guard.new([described_class.new(/.*_spec\.rb/)])
|
||||
@guard2 = Guard::Guard.new([described_class.new('spec_helper.rb', 'spec')])
|
||||
@guards = [@guard1, @guard2]
|
||||
end
|
||||
|
||||
context "with a watcher that matches a file" do
|
||||
specify { described_class.match_files?(@guards, ['lib/my_wonderful_lib.rb', 'guard_rocks_spec.rb']).should be_true }
|
||||
end
|
||||
|
||||
context "with no watcher that matches a file" do
|
||||
specify { described_class.match_files?(@guards, ['lib/my_wonderful_lib.rb']).should be_false }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#match_file?" do
|
||||
context "with a string pattern" do
|
||||
context "that is a normal string" do
|
||||
subject { described_class.new('guard_rocks_spec.rb') }
|
||||
|
||||
context "with a watcher that matches a file" do
|
||||
specify { subject.match_file?('guard_rocks_spec.rb').should be_true }
|
||||
end
|
||||
|
||||
context "with no watcher that matches a file" do
|
||||
specify { subject.match_file?('lib/my_wonderful_lib.rb').should be_false }
|
||||
end
|
||||
end
|
||||
|
||||
context "that is a string representing a regexp (deprecated)" do
|
||||
subject { described_class.new('^guard_rocks_spec\.rb$') }
|
||||
|
||||
context "with a watcher that matches a file" do
|
||||
specify { subject.match_file?('guard_rocks_spec.rb').should be_true }
|
||||
end
|
||||
|
||||
context "with no watcher that matches a file" do
|
||||
specify { subject.match_file?('lib/my_wonderful_lib.rb').should be_false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "that is a regexp pattern" do
|
||||
subject { described_class.new(/.*_spec\.rb/) }
|
||||
|
||||
context "with a watcher that matches a file" do
|
||||
specify { subject.match_file?('guard_rocks_spec.rb').should be_true }
|
||||
end
|
||||
|
||||
context "with no watcher that matches a file" do
|
||||
specify { subject.match_file?('lib/my_wonderful_lib.rb').should be_false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".match_guardfile?" do
|
||||
before(:all) { Guard::Dsl.stub(:guardfile_path) { Dir.pwd + '/Guardfile' } }
|
||||
|
||||
context "with files that match the Guardfile" do
|
||||
specify { described_class.match_guardfile?(['Guardfile', 'guard_rocks_spec.rb']).should be_true }
|
||||
end
|
||||
|
||||
context "with no files that match the Guardfile" do
|
||||
specify { described_class.match_guardfile?(['guard_rocks.rb', 'guard_rocks_spec.rb']).should be_false }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,638 +1,191 @@
|
||||
require 'spec_helper'
|
||||
require 'guard/guard'
|
||||
|
||||
# mute UI
|
||||
module Guard::UI
|
||||
class << self
|
||||
def info(message, options = {})
|
||||
end
|
||||
|
||||
def error(message)
|
||||
end
|
||||
|
||||
def debug(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe Guard do
|
||||
|
||||
describe ".initialize_template" do
|
||||
context "with a Guard name" do
|
||||
it "initializes a the Guard" do
|
||||
class Guard::TestGuard < Guard::Guard
|
||||
end
|
||||
Guard::TestGuard.should_receive(:init)
|
||||
Guard.initialize_template('test-guard')
|
||||
end
|
||||
end
|
||||
|
||||
context "without a Guard name" do
|
||||
context "with an existing Guardfile" do
|
||||
before do
|
||||
File.stub(:exist?).and_return true
|
||||
Dir.stub(:pwd).and_return "/home/user"
|
||||
end
|
||||
|
||||
it "shows an error" do
|
||||
Guard.should_receive(:exit).with 1
|
||||
::Guard::UI.should_receive(:error).with("Guardfile already exists at /home/user/Guardfile")
|
||||
Guard.initialize_template()
|
||||
end
|
||||
end
|
||||
|
||||
context "without an existing Guardfile" do
|
||||
before do
|
||||
File.stub(:exist?).and_return false
|
||||
Dir.stub(:pwd).and_return "/home/user"
|
||||
end
|
||||
|
||||
it "copies the Guardfile template" do
|
||||
::Guard::UI.should_receive(:info).with("Writing new Guardfile to /home/user/Guardfile")
|
||||
FileUtils.should_receive(:cp).with(an_instance_of(String), 'Guardfile')
|
||||
Guard.initialize_template()
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_guard_class" do
|
||||
|
||||
it "should return Guard::RSpec" do
|
||||
Guard.get_guard_class('rspec').should == Guard::RSpec
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe ".setup" do
|
||||
subject { ::Guard.setup }
|
||||
|
||||
it "returns itself for chaining" do
|
||||
subject.should be ::Guard
|
||||
|
||||
describe "locate_guard" do
|
||||
|
||||
it "should return guard-rspec gem path" do
|
||||
guard_path = Guard.locate_guard('rspec')
|
||||
guard_path.should match(/^.*\/guard-rspec-.*$/)
|
||||
guard_path.should == guard_path.chomp
|
||||
end
|
||||
|
||||
it "initializes @guards" do
|
||||
subject.guards.should eql []
|
||||
|
||||
end
|
||||
|
||||
describe "init" do
|
||||
subject { ::Guard.init }
|
||||
|
||||
it "Should retrieve itself for chaining" do
|
||||
subject.should be_kind_of(Module)
|
||||
end
|
||||
|
||||
it "initializes @groups" do
|
||||
described_class.groups[0].name.should eql :default
|
||||
described_class.groups[0].options.should == {}
|
||||
|
||||
it "Should init guards array" do
|
||||
::Guard.guards.should be_kind_of(Array)
|
||||
end
|
||||
|
||||
it "initializes the options" do
|
||||
opts = { :my_opts => true }
|
||||
Guard.setup(opts).options.should include(:my_opts)
|
||||
|
||||
it "Should init options" do
|
||||
opts = {:my_opts => true}
|
||||
::Guard.init(opts).options.should be_include(:my_opts)
|
||||
end
|
||||
|
||||
it "initializes the listener" do
|
||||
|
||||
it "Should init listeners" do
|
||||
::Guard.listener.should be_kind_of(Guard::Listener)
|
||||
end
|
||||
|
||||
it "respect the watchdir option" do
|
||||
::Guard.setup(:watchdir => "/foo/bar")
|
||||
::Guard.listener.directory.should eql "/foo/bar"
|
||||
end
|
||||
|
||||
it "turns on the notifier by default" do
|
||||
ENV["GUARD_NOTIFY"] = nil
|
||||
::Guard::Notifier.should_receive(:turn_on)
|
||||
::Guard.setup(:notify => true)
|
||||
end
|
||||
|
||||
it "turns off the notifier if the notify option is false" do
|
||||
::Guard::Notifier.should_receive(:turn_off)
|
||||
::Guard.setup(:notify => false)
|
||||
end
|
||||
|
||||
it "turns off the notifier if environment variable GUARD_NOTIFY is false" do
|
||||
ENV["GUARD_NOTIFY"] = 'false'
|
||||
::Guard::Notifier.should_receive(:turn_off)
|
||||
::Guard.setup(:notify => true)
|
||||
end
|
||||
|
||||
it "logs command execution if the debug option is true" do
|
||||
::Guard.should_receive(:debug_command_execution)
|
||||
::Guard.setup(:debug => true)
|
||||
end
|
||||
|
||||
it "initializes the interactor" do
|
||||
::Guard.setup
|
||||
::Guard.interactor.should be_kind_of(Guard::Interactor)
|
||||
end
|
||||
|
||||
it "skips the interactor initalization if no-interactions is true" do
|
||||
::Guard.interactor = nil
|
||||
::Guard.setup(:no_interactions => true)
|
||||
::Guard.interactor.should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe ".guards" do
|
||||
describe "start" do
|
||||
|
||||
class Guard::FooBar < Guard::Guard; end
|
||||
class Guard::FooBaz < Guard::Guard; end
|
||||
|
||||
subject do
|
||||
guard = ::Guard.setup
|
||||
@guard_foo_bar_backend = Guard::FooBar.new([], { :group => 'backend' })
|
||||
@guard_foo_bar_frontend = Guard::FooBar.new([], { :group => 'frontend' })
|
||||
@guard_foo_baz_backend = Guard::FooBaz.new([], { :group => 'backend' })
|
||||
@guard_foo_baz_frontend = Guard::FooBaz.new([], { :group => 'frontend' })
|
||||
guard.instance_variable_get("@guards").push(@guard_foo_bar_backend)
|
||||
guard.instance_variable_get("@guards").push(@guard_foo_bar_frontend)
|
||||
guard.instance_variable_get("@guards").push(@guard_foo_baz_backend)
|
||||
guard.instance_variable_get("@guards").push(@guard_foo_baz_frontend)
|
||||
guard
|
||||
end
|
||||
|
||||
it "return @guards without any argument" do
|
||||
subject.guards.should eql subject.instance_variable_get("@guards")
|
||||
end
|
||||
|
||||
describe "find a guard by as string/symbol" do
|
||||
it "find a guard by a string" do
|
||||
subject.guards('foo-bar').should eql @guard_foo_bar_backend
|
||||
end
|
||||
|
||||
it "find a guard by a symbol" do
|
||||
subject.guards(:'foo-bar').should eql @guard_foo_bar_backend
|
||||
end
|
||||
|
||||
it "returns nil if guard is not found" do
|
||||
subject.guards('foo-foo').should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "find guards matching a regexp" do
|
||||
it "with matches" do
|
||||
subject.guards(/^foobar/).should eql [@guard_foo_bar_backend, @guard_foo_bar_frontend]
|
||||
end
|
||||
|
||||
it "without matches" do
|
||||
subject.guards(/foo$/).should eql []
|
||||
end
|
||||
end
|
||||
|
||||
describe "find guards by their group" do
|
||||
it "group name is a string" do
|
||||
subject.guards(:group => 'backend').should eql [@guard_foo_bar_backend, @guard_foo_baz_backend]
|
||||
end
|
||||
|
||||
it "group name is a symbol" do
|
||||
subject.guards(:group => :frontend).should eql [@guard_foo_bar_frontend, @guard_foo_baz_frontend]
|
||||
end
|
||||
|
||||
it "returns [] if guard is not found" do
|
||||
subject.guards(:group => :unknown).should eql []
|
||||
end
|
||||
end
|
||||
|
||||
describe "find guards by their group & name" do
|
||||
it "group name is a string" do
|
||||
subject.guards(:group => 'backend', :name => 'foo-bar').should eql [@guard_foo_bar_backend]
|
||||
end
|
||||
|
||||
it "group name is a symbol" do
|
||||
subject.guards(:group => :frontend, :name => :'foo-baz').should eql [@guard_foo_baz_frontend]
|
||||
end
|
||||
|
||||
it "returns [] if guard is not found" do
|
||||
subject.guards(:group => :unknown, :name => :'foo-baz').should eql []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".groups" do
|
||||
subject do
|
||||
guard = ::Guard.setup
|
||||
@group_backend = guard.add_group(:backend)
|
||||
@group_backflip = guard.add_group(:backflip)
|
||||
guard
|
||||
end
|
||||
|
||||
it "return @groups without any argument" do
|
||||
subject.groups.should eql subject.instance_variable_get("@groups")
|
||||
end
|
||||
|
||||
describe "find a group by as string/symbol" do
|
||||
it "find a group by a string" do
|
||||
subject.groups('backend').should eql @group_backend
|
||||
end
|
||||
|
||||
it "find a group by a symbol" do
|
||||
subject.groups(:backend).should eql @group_backend
|
||||
end
|
||||
|
||||
it "returns nil if group is not found" do
|
||||
subject.groups(:foo).should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "find groups matching a regexp" do
|
||||
it "with matches" do
|
||||
subject.groups(/^back/).should eql [@group_backend, @group_backflip]
|
||||
end
|
||||
|
||||
it "without matches" do
|
||||
subject.groups(/back$/).should eql []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".start" do
|
||||
it "basic check that core methods are called" do
|
||||
opts = { :my_opts => true, :guardfile => File.join(@fixture_path, "Guardfile") }
|
||||
::Guard.should_receive(:setup).with(opts)
|
||||
::Guard::Dsl.should_receive(:evaluate_guardfile).with(opts)
|
||||
::Guard.listener.should_receive(:start)
|
||||
|
||||
::Guard.start(opts)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".add_guard" do
|
||||
before(:each) do
|
||||
@guard_rspec_class = double('Guard::RSpec')
|
||||
@guard_rspec = double('Guard::RSpec')
|
||||
@guard = mock(::Guard::Guard)
|
||||
@guard.stub!(:reload_at_start?).and_return(false)
|
||||
@guard.stub!(:run_all_at_start?).and_return(false)
|
||||
@guard.stub!(:respond_to?)
|
||||
|
||||
Guard.stub!(:get_guard_class) { @guard_rspec_class }
|
||||
@listener = mock(::Guard::Polling)
|
||||
@listener.stub!(:on_change)
|
||||
@listener.stub!(:start).and_return(true)
|
||||
|
||||
Guard.setup
|
||||
::Guard::Listener.stub!(:init).and_return(@listener)
|
||||
::Guard::Dsl.stub!(:evaluate_guardfile)
|
||||
::Guard::Interactor.stub!(:init_signal_traps)
|
||||
subject.stub!(:guards).and_return([@guard])
|
||||
subject.stub!(:supervised_task).with(anything(), :start).and_return(true)
|
||||
end
|
||||
|
||||
it "accepts guard name as string" do
|
||||
@guard_rspec_class.should_receive(:new).and_return(@guard_rspec)
|
||||
|
||||
Guard.add_guard('rspec')
|
||||
it 'should evaluate Guardfile' do
|
||||
::Guard::Dsl.should_receive(:evaluate_guardfile)
|
||||
subject.start
|
||||
end
|
||||
|
||||
it "accepts guard name as symbol" do
|
||||
@guard_rspec_class.should_receive(:new).and_return(@guard_rspec)
|
||||
|
||||
Guard.add_guard(:rspec)
|
||||
it 'should init signal traps' do
|
||||
::Guard::Interactor.should_receive(:init_signal_traps)
|
||||
subject.start
|
||||
end
|
||||
|
||||
it "adds guard to the @guards array" do
|
||||
@guard_rspec_class.should_receive(:new).and_return(@guard_rspec)
|
||||
|
||||
Guard.add_guard(:rspec)
|
||||
|
||||
Guard.guards.should eql [@guard_rspec]
|
||||
it 'should define listener on_change' do
|
||||
@listener.should_receive(:on_change)
|
||||
subject.start
|
||||
end
|
||||
|
||||
context "with no watchers given" do
|
||||
it "gives an empty array of watchers" do
|
||||
@guard_rspec_class.should_receive(:new).with([], {}).and_return(@guard_rspec)
|
||||
|
||||
Guard.add_guard(:rspec, [])
|
||||
end
|
||||
it 'should start guards' do
|
||||
subject.should_receive(:supervised_task).with(@guard, :start).and_return(true)
|
||||
subject.start
|
||||
end
|
||||
|
||||
context "with watchers given" do
|
||||
it "give the watchers array" do
|
||||
@guard_rspec_class.should_receive(:new).with([:foo], {}).and_return(@guard_rspec)
|
||||
it 'should call reload at start if needed' do
|
||||
@guard.should_receive(:respond_to?).with(:reload_at_start?).and_return(true)
|
||||
@guard.should_receive(:reload_at_start?).and_return(true)
|
||||
@guard.should_receive(:reload).and_return(true)
|
||||
|
||||
Guard.add_guard(:rspec, [:foo])
|
||||
end
|
||||
subject.start
|
||||
end
|
||||
|
||||
context "with no options given" do
|
||||
it "gives an empty hash of options" do
|
||||
@guard_rspec_class.should_receive(:new).with([], {}).and_return(@guard_rspec)
|
||||
it 'should call run_all at start if needed' do
|
||||
@guard.should_receive(:respond_to?).with(:run_all_at_start?).and_return(true)
|
||||
@guard.should_receive(:run_all_at_start?).and_return(true)
|
||||
@guard.should_receive(:run_all).and_return(true)
|
||||
|
||||
Guard.add_guard(:rspec, [], [], {})
|
||||
end
|
||||
subject.start
|
||||
end
|
||||
|
||||
context "with options given" do
|
||||
it "give the options hash" do
|
||||
@guard_rspec_class.should_receive(:new).with([], { :foo => true, :group => :backend }).and_return(@guard_rspec)
|
||||
it 'should not call reload and run_all at start if not needed' do
|
||||
@guard.should_receive(:respond_to?).with(:reload_at_start?).and_return(true)
|
||||
@guard.should_receive(:reload_at_start?).and_return(false)
|
||||
@guard.should_not_receive(:reload)
|
||||
|
||||
Guard.add_guard(:rspec, [], [], { :foo => true, :group => :backend })
|
||||
end
|
||||
@guard.should_receive(:respond_to?).with(:run_all_at_start?).and_return(true)
|
||||
@guard.should_receive(:run_all_at_start?).and_return(false)
|
||||
@guard.should_not_receive(:run_all)
|
||||
|
||||
subject.start
|
||||
end
|
||||
|
||||
it 'should start listener' do
|
||||
@listener.should_receive(:start)
|
||||
subject.start
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe ".add_group" do
|
||||
subject { ::Guard.setup }
|
||||
|
||||
it "accepts group name as string" do
|
||||
subject.add_group('backend')
|
||||
|
||||
subject.groups[0].name.should eql :default
|
||||
subject.groups[1].name.should eql :backend
|
||||
|
||||
describe "supervised_task" do
|
||||
subject { ::Guard.init }
|
||||
|
||||
before :each do
|
||||
@g = mock(Guard::Guard)
|
||||
@g.stub!(:respond_to).and_return { false }
|
||||
@g.stub!(:regular).and_return { true }
|
||||
@g.stub!(:spy).and_return { raise "I break your system" }
|
||||
@g.stub!(:pirate).and_raise Exception.new("I blow your system up")
|
||||
@g.stub!(:regular_arg).with("given_path").and_return { "given_path" }
|
||||
@g.stub!(:enable_method).and_return { true }
|
||||
@g.stub!(:enable_method?).and_return { true }
|
||||
@g.stub!(:respond_to).with(:enable_method?).and_return { true }
|
||||
@g.stub!(:disable_method).and_return { true }
|
||||
@g.stub!(:disable_method?).and_return { false }
|
||||
@g.stub!(:respond_to).with(:disable_method?).and_return { true }
|
||||
subject.guards.push @g
|
||||
end
|
||||
|
||||
it "should let it go when nothing special occurs" do
|
||||
subject.guards.should be_include(@g)
|
||||
subject.supervised_task(@g, :regular).should be_true
|
||||
subject.guards.should be_include(@g)
|
||||
end
|
||||
|
||||
it "should let it work with some tools" do
|
||||
subject.guards.should be_include(@g)
|
||||
subject.supervised_task(@g, :regular).should be_true
|
||||
subject.guards.should be_include(@g)
|
||||
end
|
||||
|
||||
it "should fire the guard on spy act discovery" do
|
||||
subject.guards.should be_include(@g)
|
||||
::Guard.supervised_task(@g, :spy).should be_kind_of(Exception)
|
||||
subject.guards.should_not be_include(@g)
|
||||
::Guard.supervised_task(@g, :spy).message.should == 'I break your system'
|
||||
end
|
||||
|
||||
it "should fire the guard on pirate act discovery" do
|
||||
subject.guards.should be_include(@g)
|
||||
::Guard.supervised_task(@g, :regular_arg, "given_path").should be_kind_of(String)
|
||||
subject.guards.should be_include(@g)
|
||||
::Guard.supervised_task(@g, :regular_arg, "given_path").should == "given_path"
|
||||
end
|
||||
|
||||
it "accepts group name as symbol" do
|
||||
subject.add_group(:backend)
|
||||
|
||||
subject.groups[0].name.should eql :default
|
||||
subject.groups[1].name.should eql :backend
|
||||
it 'should let it go when method is enable by guard options' do
|
||||
subject.guards.should be_include(@g)
|
||||
subject.supervised_task(@g, :enable_method).should be_true
|
||||
subject.guards.should be_include(@g)
|
||||
end
|
||||
|
||||
it "accepts options" do
|
||||
subject.add_group(:backend, { :halt_on_fail => true })
|
||||
|
||||
subject.groups[0].options.should == {}
|
||||
subject.groups[1].options.should == { :halt_on_fail => true }
|
||||
end
|
||||
end
|
||||
|
||||
describe ".get_guard_class" do
|
||||
after do
|
||||
[:Classname, :DashedClassName, :Inline].each do |const|
|
||||
Guard.send(:remove_const, const) rescue nil
|
||||
end
|
||||
end
|
||||
|
||||
it "reports an error if the class is not found" do
|
||||
::Guard::UI.should_receive(:error).twice
|
||||
Guard.get_guard_class('notAGuardClass')
|
||||
end
|
||||
|
||||
context 'with a nested Guard class' do
|
||||
it "resolves the Guard class from string" do
|
||||
Guard.should_receive(:require) { |classname|
|
||||
classname.should == 'guard/classname'
|
||||
class Guard::Classname
|
||||
end
|
||||
}
|
||||
Guard.get_guard_class('classname').should == Guard::Classname
|
||||
end
|
||||
|
||||
it "resolves the Guard class from symbol" do
|
||||
Guard.should_receive(:require) { |classname|
|
||||
classname.should == 'guard/classname'
|
||||
class Guard::Classname
|
||||
end
|
||||
}
|
||||
Guard.get_guard_class(:classname).should == Guard::Classname
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a name with dashes' do
|
||||
it "returns the Guard class" do
|
||||
Guard.should_receive(:require) { |classname|
|
||||
classname.should == 'guard/dashed-class-name'
|
||||
class Guard::DashedClassName
|
||||
end
|
||||
}
|
||||
Guard.get_guard_class('dashed-class-name').should == Guard::DashedClassName
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an inline Guard class' do
|
||||
it 'returns the Guard class' do
|
||||
module Guard
|
||||
class Inline < Guard
|
||||
end
|
||||
end
|
||||
|
||||
Guard.should_not_receive(:require)
|
||||
Guard.get_guard_class('inline').should == Guard::Inline
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".locate_guard" do
|
||||
it "returns the path of a Guard gem" do
|
||||
if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
|
||||
gem_location = Gem::Specification.find_by_name("guard-rspec").full_gem_path
|
||||
else
|
||||
gem_location = Gem.source_index.find_name("guard-rspec").last.full_gem_path
|
||||
end
|
||||
|
||||
Guard.locate_guard('rspec').should == gem_location
|
||||
end
|
||||
end
|
||||
|
||||
describe ".guard_gem_names" do
|
||||
it "returns the list of guard gems" do
|
||||
gems = Guard.guard_gem_names
|
||||
gems.should include("rspec")
|
||||
end
|
||||
end
|
||||
|
||||
describe ".run_guard_task" do
|
||||
subject { ::Guard.setup }
|
||||
|
||||
before do
|
||||
class Guard::Dummy < Guard::Guard; end
|
||||
|
||||
subject.add_group(:foo, { :halt_on_fail => true })
|
||||
subject.add_group(:bar)
|
||||
subject.add_guard(:dummy, [], [], { :group => :foo })
|
||||
subject.add_guard(:dummy, [], [], { :group => :foo })
|
||||
subject.add_guard(:dummy, [], [], { :group => :bar })
|
||||
subject.add_guard(:dummy, [], [], { :group => :bar })
|
||||
@sum = { :foo => 0, :bar => 0}
|
||||
end
|
||||
|
||||
context "all tasks succeed" do
|
||||
before do
|
||||
subject.guards.each { |guard| guard.stub!(:task) { @sum[guard.group] += 1; true } }
|
||||
end
|
||||
|
||||
it "executes the task for each guard in each group" do
|
||||
subject.run_guard_task(:task)
|
||||
|
||||
@sum.all? { |k, v| v == 2 }.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context "one guard fails" do
|
||||
before do
|
||||
subject.guards.each_with_index do |g, i|
|
||||
g.stub!(:task) do
|
||||
@sum[g.group] += i+1
|
||||
if i % 2 == 0
|
||||
throw :task_has_failed
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "executes the task only for guards that didn't fail for group with :halt_on_fail == true" do
|
||||
subject.run_guard_task(:task)
|
||||
|
||||
@sum[:foo].should eql 1
|
||||
@sum[:bar].should eql 7
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".run_on_change_task" do
|
||||
let(:guard) do
|
||||
class Guard::Dummy < Guard::Guard
|
||||
def watchers
|
||||
[Guard::Watcher.new(/.+\.rb/)]
|
||||
end
|
||||
end
|
||||
|
||||
Guard::Dummy.new
|
||||
end
|
||||
|
||||
it 'runs the :run_on_change task with the watched file changes' do
|
||||
Guard.should_receive(:run_supervised_task).with(guard, :run_on_change, ['a.rb', 'b.rb'])
|
||||
Guard.run_on_change_task(['a.rb', 'b.rb', 'templates/d.haml'], guard, :run_on_change)
|
||||
end
|
||||
|
||||
it 'runs the :run_on_deletion task with the watched file deletions' do
|
||||
Guard.should_receive(:run_supervised_task).with(guard, :run_on_deletion, ['c.rb'])
|
||||
Guard.run_on_change_task(['!c.rb', '!templates/e.haml'], guard, :run_on_change)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".changed_paths" do
|
||||
let(:paths) { ['a.rb', 'b.rb', '!c.rb', 'templates/d.haml', '!templates/e.haml'] }
|
||||
|
||||
it 'returns the changed paths' do
|
||||
Guard.changed_paths(paths).should =~ ['a.rb', 'b.rb', 'templates/d.haml']
|
||||
end
|
||||
end
|
||||
|
||||
describe ".deleted_paths" do
|
||||
let(:paths) { ['a.rb', 'b.rb', '!c.rb', 'templates/d.haml', '!templates/e.haml'] }
|
||||
|
||||
it 'returns the deleted paths' do
|
||||
Guard.deleted_paths(paths).should =~ ['c.rb', 'templates/e.haml']
|
||||
end
|
||||
end
|
||||
|
||||
describe ".run_supervised_task" do
|
||||
subject { ::Guard.setup }
|
||||
|
||||
before do
|
||||
@g = mock(Guard::Guard).as_null_object
|
||||
subject.guards.push(@g)
|
||||
subject.add_group(:foo, { :halt_on_fail => true })
|
||||
subject.add_group(:bar, { :halt_on_fail => false })
|
||||
end
|
||||
|
||||
context "with a task that succeed" do
|
||||
context 'without any arguments' do
|
||||
before(:each) do
|
||||
@g.stub!(:regular_without_arg) { true }
|
||||
end
|
||||
|
||||
it "doesn't fire the Guard" do
|
||||
lambda { subject.run_supervised_task(@g, :regular_without_arg) }.should_not change(subject.guards, :size)
|
||||
end
|
||||
|
||||
it "returns the result of the task" do
|
||||
::Guard.run_supervised_task(@g, :regular_without_arg).should be_true
|
||||
end
|
||||
|
||||
it "passes the args to the :begin hook" do
|
||||
@g.should_receive(:hook).with("regular_without_arg_begin", "given_path")
|
||||
::Guard.run_supervised_task(@g, :regular_without_arg, "given_path")
|
||||
end
|
||||
|
||||
it "passes the result of the supervised method to the :end hook" do
|
||||
@g.should_receive(:hook).with("regular_without_arg_begin", "given_path")
|
||||
@g.should_receive(:hook).with("regular_without_arg_end", true)
|
||||
::Guard.run_supervised_task(@g, :regular_without_arg, "given_path")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with arguments' do
|
||||
before(:each) do
|
||||
@g.stub!(:regular_with_arg).with("given_path") { "I'm a success" }
|
||||
end
|
||||
|
||||
it "doesn't fire the Guard" do
|
||||
lambda { subject.run_supervised_task(@g, :regular_with_arg, "given_path") }.should_not change(subject.guards, :size)
|
||||
end
|
||||
|
||||
it "returns the result of the task" do
|
||||
::Guard.run_supervised_task(@g, :regular_with_arg, "given_path").should eql "I'm a success"
|
||||
end
|
||||
|
||||
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.run_supervised_task(@g, :failing)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a task that throw :task_has_failed" do
|
||||
context "for a guard's group has the :halt_on_fail option == true" do
|
||||
before(:each) { @g.stub!(:group) { :foo }; @g.stub!(:failing) { throw :task_has_failed } }
|
||||
|
||||
it "throws :task_has_failed" do
|
||||
expect { subject.run_supervised_task(@g, :failing) }.to throw_symbol(:task_has_failed)
|
||||
end
|
||||
end
|
||||
|
||||
context "for a guard's group has the :halt_on_fail option == false" do
|
||||
before(:each) { @g.stub!(:group) { :bar }; @g.stub!(:failing) { throw :task_has_failed } }
|
||||
|
||||
it "catches :task_has_failed" do
|
||||
expect { subject.run_supervised_task(@g, :failing) }.to_not throw_symbol(:task_has_failed)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a task that raises an exception" do
|
||||
before(:each) { @g.stub!(:group) { :foo }; @g.stub!(:failing) { raise "I break your system" } }
|
||||
|
||||
it "fires the Guard" do
|
||||
lambda { subject.run_supervised_task(@g, :failing) }.should change(subject.guards, :size).by(-1)
|
||||
subject.guards.should_not include(@g)
|
||||
end
|
||||
|
||||
it "returns the exception" do
|
||||
failing_result = ::Guard.run_supervised_task(@g, :failing)
|
||||
failing_result.should be_kind_of(Exception)
|
||||
failing_result.message.should == 'I break your system'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.guard_symbol' do
|
||||
let(:guard) { mock(Guard::Guard).as_null_object }
|
||||
|
||||
it 'returns :task_has_failed when the group is missing' do
|
||||
subject.guard_symbol(guard).should eql :task_has_failed
|
||||
end
|
||||
|
||||
context 'for a group with :halt_on_fail' do
|
||||
let(:group) { mock(Guard::Group) }
|
||||
|
||||
before do
|
||||
guard.stub(:group).and_return :foo
|
||||
group.stub(:options).and_return({ :halt_on_fail => true })
|
||||
end
|
||||
|
||||
it 'returns :no_catch' do
|
||||
subject.should_receive(:groups).with(:foo).and_return group
|
||||
subject.guard_symbol(guard).should eql :no_catch
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a group without :halt_on_fail' do
|
||||
let(:group) { mock(Guard::Group) }
|
||||
|
||||
before do
|
||||
guard.stub(:group).and_return :foo
|
||||
group.stub(:options).and_return({ :halt_on_fail => false })
|
||||
end
|
||||
|
||||
it 'returns :task_has_failed' do
|
||||
subject.should_receive(:groups).with(:foo).and_return group
|
||||
subject.guard_symbol(guard).should eql :task_has_failed
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".debug_command_execution" do
|
||||
subject { ::Guard.setup }
|
||||
|
||||
before do
|
||||
@original_system = Kernel.method(:system)
|
||||
@original_command = Kernel.method(:"`")
|
||||
end
|
||||
|
||||
after do
|
||||
Kernel.send(:define_method, :system, @original_system.to_proc )
|
||||
Kernel.send(:define_method, :"`", @original_command.to_proc )
|
||||
end
|
||||
|
||||
it "outputs Kernel.#system method parameters" do
|
||||
::Guard.setup(:debug => true)
|
||||
::Guard::UI.should_receive(:debug).with("Command execution: echo test")
|
||||
system("echo", "test").should be_true
|
||||
end
|
||||
|
||||
it "outputs Kernel.#` method parameters" do
|
||||
::Guard.setup(:debug => true)
|
||||
::Guard::UI.should_receive(:debug).twice.with("Command execution: echo test")
|
||||
`echo test`.should eql "test\n"
|
||||
%x{echo test}.should eql "test\n"
|
||||
it 'should not let it go when method is disable by guard options' do
|
||||
subject.guards.should be_include(@g)
|
||||
subject.supervised_task(@g, :disable_method).should be_false
|
||||
subject.guards.should be_include(@g)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
@ -2,21 +2,20 @@ require 'rubygems'
|
||||
require 'guard'
|
||||
require 'rspec'
|
||||
|
||||
ENV["GUARD_ENV"] = 'test'
|
||||
|
||||
Dir["#{File.expand_path('..', __FILE__)}/support/**/*.rb"].each { |f| require f }
|
||||
|
||||
puts "Please do not update/create files while tests are running."
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.color_enabled = true
|
||||
|
||||
config.filter_run :focus => true
|
||||
config.treat_symbols_as_metadata_keys_with_true_values = true
|
||||
config.run_all_when_everything_filtered = true
|
||||
|
||||
|
||||
config.before(:each) do
|
||||
ENV["GUARD_ENV"] = 'test'
|
||||
@fixture_path = Pathname.new(File.expand_path('../fixtures/', __FILE__))
|
||||
end
|
||||
|
||||
end
|
||||
config.after(:each) do
|
||||
ENV["GUARD_ENV"] = nil
|
||||
end
|
||||
|
||||
end
|
@ -1,20 +0,0 @@
|
||||
def growl_installed?
|
||||
require 'growl'
|
||||
true
|
||||
rescue LoadError
|
||||
false
|
||||
end
|
||||
|
||||
def libnotify_installed?
|
||||
require 'libnotify'
|
||||
true
|
||||
rescue LoadError
|
||||
false
|
||||
end
|
||||
|
||||
def rbnotifu_installed?
|
||||
require 'rb-notifu'
|
||||
true
|
||||
rescue LoadError
|
||||
false
|
||||
end
|
@ -1,225 +0,0 @@
|
||||
private
|
||||
|
||||
# Set the sleep time around start/stop the listener. This defaults
|
||||
# to one second but can be overridden by setting the environment
|
||||
# variable `GUARD_SLEEP`.
|
||||
#
|
||||
def sleep_time
|
||||
@sleep_time ||= ENV['GUARD_SLEEP'] ? ENV['GUARD_SLEEP'].to_f : 1
|
||||
end
|
||||
|
||||
# Make the spec listen to a specific listener.
|
||||
# This automatically starts to record results for the supplied listener.
|
||||
#
|
||||
# @param [Guard::Listener] listener the Guard listener
|
||||
#
|
||||
def listen_to(listener)
|
||||
@listener = listener
|
||||
record_results
|
||||
end
|
||||
|
||||
# Start the listener. Normally you use {#watch} to wrap
|
||||
# the code block that should be listen to instead of starting
|
||||
# it manually.
|
||||
#
|
||||
def start
|
||||
sleep(sleep_time)
|
||||
@listener.update_last_event
|
||||
Thread.new { @listener.start }
|
||||
sleep(sleep_time)
|
||||
end
|
||||
|
||||
# Stop the listener. Normally you use {#watch} to wrap
|
||||
# the code block that should be listen to instead of stopping
|
||||
# it manually.
|
||||
#
|
||||
def stop
|
||||
sleep(sleep_time)
|
||||
@listener.stop
|
||||
sleep(sleep_time)
|
||||
end
|
||||
|
||||
# Watch file changes in a code block.
|
||||
#
|
||||
# @example Watch file changes
|
||||
# watch do
|
||||
# File.mv file1, file2
|
||||
# end
|
||||
#
|
||||
# @yield The block to listen for file changes
|
||||
#
|
||||
def watch
|
||||
start
|
||||
yield if block_given?
|
||||
stop
|
||||
end
|
||||
|
||||
# Start recording results from the current listener.
|
||||
# You may want to use {#listen_to} to set a listener
|
||||
# instead of set it up manually.
|
||||
#
|
||||
def record_results
|
||||
# Don't fail specs due to editor swap files, etc.
|
||||
noise = %r|\.sw.$|
|
||||
@results = []
|
||||
|
||||
@listener.on_change do |files|
|
||||
@results += files.reject { |f| f =~ noise }
|
||||
end
|
||||
end
|
||||
|
||||
# Get the recorded result from the listener.
|
||||
#
|
||||
# @return [Array<String>] the result files
|
||||
#
|
||||
def results
|
||||
@results.flatten
|
||||
end
|
||||
|
||||
# Define a file absolute to the fixture path.
|
||||
#
|
||||
# @param [String, Array<String>] file the relative file name, separated by segment
|
||||
# @return [String] the absolute file
|
||||
#
|
||||
def fixture(*file)
|
||||
@fixture_path.join(*file)
|
||||
end
|
||||
|
||||
shared_examples_for 'a listener that reacts to #on_change' do
|
||||
before do
|
||||
listen_to described_class.new
|
||||
end
|
||||
|
||||
context 'for a new file' do
|
||||
let(:file) { fixture('newfile.rb') }
|
||||
|
||||
before { File.delete(file) if File.exists?(file) }
|
||||
after { File.delete file }
|
||||
|
||||
it 'catches the new file' do
|
||||
File.exists?(file).should be_false
|
||||
|
||||
watch do
|
||||
FileUtils.touch file
|
||||
end
|
||||
|
||||
results.should =~ ['spec/fixtures/newfile.rb']
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a single file update' do
|
||||
let(:file) { fixture('folder1', 'file1.txt') }
|
||||
|
||||
it 'catches the update' do
|
||||
File.exists?(file).should be_true
|
||||
|
||||
watch do
|
||||
File.open(file, 'w') { |f| f.write('') }
|
||||
end
|
||||
|
||||
results.should =~ ['spec/fixtures/folder1/file1.txt']
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a single file chmod update' do
|
||||
let(:file) { fixture('folder1/file1.txt') }
|
||||
|
||||
it 'does not catch the update' do
|
||||
File.exists?(file).should be_true
|
||||
|
||||
watch do
|
||||
File.chmod(0777, file)
|
||||
end
|
||||
|
||||
results.should =~ []
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a dotfile update' do
|
||||
let(:file) { fixture('.dotfile') }
|
||||
|
||||
it "catches the update" do
|
||||
File.exists?(file).should be_true
|
||||
|
||||
watch do
|
||||
File.open(file, 'w') { |f| f.write('') }
|
||||
end
|
||||
|
||||
results.should =~ ['spec/fixtures/.dotfile']
|
||||
end
|
||||
end
|
||||
|
||||
context 'for multiple file updates' do
|
||||
let(:file1) { fixture('folder1', 'file1.txt') }
|
||||
let(:file2) { fixture('folder1', 'folder2', 'file2.txt') }
|
||||
|
||||
it 'catches the updates' do
|
||||
File.exists?(file1).should be_true
|
||||
File.exists?(file2).should be_true
|
||||
|
||||
watch do
|
||||
File.open(file1, 'w') { |f| f.write('') }
|
||||
File.open(file2, 'w') { |f| f.write('') }
|
||||
end
|
||||
|
||||
results.should =~ ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a deleted file' do
|
||||
let(:file) { fixture('folder1', 'file1.txt') }
|
||||
|
||||
after { FileUtils.touch file }
|
||||
|
||||
it 'does not catch the deletion' do
|
||||
File.exists?(file).should be_true
|
||||
|
||||
watch do
|
||||
File.delete file
|
||||
end
|
||||
|
||||
results.should =~ []
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a moved file' do
|
||||
let(:file1) { fixture('folder1', 'file1.txt') }
|
||||
let(:file2) { fixture('folder1', 'movedfile1.txt') }
|
||||
|
||||
after { FileUtils.mv file2, file1 }
|
||||
|
||||
it 'does not catch the move' do
|
||||
File.exists?(file1).should be_true
|
||||
File.exists?(file2).should be_false
|
||||
|
||||
watch do
|
||||
FileUtils.mv file1, file2
|
||||
end
|
||||
|
||||
results.should =~ []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for "a listener scoped to a specific directory" do
|
||||
|
||||
let(:work_directory) { fixture('folder1') }
|
||||
|
||||
let(:new_file) { work_directory.join('folder2', 'newfile.rb') }
|
||||
let(:modified) { work_directory.join('file1.txt') }
|
||||
|
||||
before { listen_to described_class.new(work_directory) }
|
||||
after { File.delete new_file }
|
||||
|
||||
it 'should base paths within this directory' do
|
||||
File.exists?(modified).should be_true
|
||||
File.exists?(new_file).should be_false
|
||||
|
||||
watch do
|
||||
FileUtils.touch new_file
|
||||
File.open(modified, 'w') { |f| f.write('') }
|
||||
end
|
||||
|
||||
results.should =~ ['folder2/newfile.rb', 'file1.txt']
|
||||
end
|
||||
end
|
@ -1,11 +1,7 @@
|
||||
def mac?
|
||||
RbConfig::CONFIG['target_os'] =~ /darwin/i
|
||||
Config::CONFIG['target_os'] =~ /darwin/i
|
||||
end
|
||||
|
||||
def linux?
|
||||
RbConfig::CONFIG['target_os'] =~ /linux/i
|
||||
end
|
||||
|
||||
def windows?
|
||||
RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
||||
end
|
||||
Config::CONFIG['target_os'] =~ /linux/i
|
||||
end
|
Loading…
Reference in New Issue
Block a user