Compare commits
No commits in common. "master" and "hook" have entirely different histories.
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,13 +1,9 @@
|
||||
pkg/*
|
||||
doc/*
|
||||
*.gem
|
||||
*.rbc
|
||||
.*.swp
|
||||
*.bak
|
||||
.bundle
|
||||
.yardoc
|
||||
.rbx
|
||||
.rvmrc
|
||||
Gemfile.lock
|
||||
|
||||
## MAC OS
|
||||
@ -16,4 +12,4 @@ Gemfile.lock
|
||||
.com.apple.timemachine.supported
|
||||
.fseventsd
|
||||
Desktop DB
|
||||
Desktop DF
|
||||
Desktop DF
|
11
.travis.yml
11
.travis.yml
@ -7,13 +7,8 @@ rvm:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- guard_dependencies
|
||||
env:
|
||||
- GUARD_SLEEP=1
|
||||
- hook
|
||||
- stdin
|
||||
- jruby_on_travis
|
||||
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
|
239
CHANGELOG.md
239
CHANGELOG.md
@ -1,296 +1,238 @@
|
||||
## 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][])
|
||||
- JRuby support (beta). ([@thibaudgg][])
|
||||
- Rubinius support (beta). ([@thibaudgg][])
|
||||
|
||||
### New features
|
||||
### New feature:
|
||||
|
||||
- [#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][])
|
||||
- Pull request [#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
|
||||
### 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
|
||||
### 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][])
|
||||
- Pull request [#130](https://github.com/guard/guard/pull/130): Adds `ignore_paths` method to DSL. ([@ianwhite][])
|
||||
- Pull request [#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
|
||||
### Bugs 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
|
||||
### 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
|
||||
### Improvement:
|
||||
|
||||
- Explain the growl/growl_notify differences in the README. ([@netzpirat][])
|
||||
|
||||
## 0.6.1 - August 15, 2011
|
||||
|
||||
### Bug fixes
|
||||
### Bugs 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][])
|
||||
- Pull request [#120](https://github.com/guard/guard/pull/120): remove `guardfile_contents` when re-evaluating so that the Guardfile gets reloaded correctly. ([@mordaroso][])
|
||||
- Pull request [#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
|
||||
### Bugs 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
|
||||
### New features:
|
||||
|
||||
- Pull request [#112](https://github.com/guard/guard/pull/112): Add `list` command to CLI. ([@docwhat][])
|
||||
|
||||
### Improvements
|
||||
### 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][])
|
||||
- Pull request [#99](https://github.com/guard/guard/pull/99): [OS X] Switch from growl gem to growl_notify gem. ([@johnbintz][])
|
||||
- Pull request [#115](https://github.com/guard/guard/pull/115): [Linux] Add `:transient => true` to default libnotify options. ([@zonque][])
|
||||
- Pull request [#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][])
|
||||
- Pull request [#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
|
||||
### Bugs fixes:
|
||||
|
||||
- Fixed `guard show` command. ([@bronson][] & [@thibaudgg][])
|
||||
|
||||
## 0.5.0 - July 2, 2011
|
||||
|
||||
### New features
|
||||
### 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][])
|
||||
- Pull request [#91](https://github.com/guard/guard/pull/91): Show Guards in Guardfile with the `guard -T`. ([@johnbintz][])
|
||||
|
||||
### Improvements
|
||||
### 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][])
|
||||
- Issue [#98](https://github.com/guard/guard/issues/98): Multiple calls per watch event on linux with rb-inotify. ([@jeffutter][] & [@netzpirat][])
|
||||
- Pull request [#94](https://github.com/guard/guard/pull/94): Show backtrace in terminal when a problem with a watch action occurs. ([@capotej][])
|
||||
- Pull request [#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][])
|
||||
- Issue [#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][])
|
||||
- Issue [#12](https://github.com/guard/guard/issues/12) and Pull request [#86](https://github.com/guard/guard/pull/86): Eventually exits with SystemStackError. ([@stereobooster][])
|
||||
- Pull request [#84](https://github.com/guard/guard/pull/84): Use RbConfig instead of obsolete and deprecated Config. ([@etehtsea][])
|
||||
- Pull request [#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
|
||||
### Bugs 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
|
||||
### 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][])
|
||||
- Pull request [#77](https://github.com/guard/guard/pull/77): Refactor `get_guard_class` to first try the constant and fallback to require + various tweaks. ([@mislav][])
|
||||
- Notifier improvement, don't use system notification library if could not be required. ([@yannlugrin][])
|
||||
|
||||
## 0.4.0 - June 5, 2011
|
||||
|
||||
### Bug fix
|
||||
### Bugs fixes:
|
||||
|
||||
- In Ruby < 1.9, `Symbol#downcase` doesn't exist! ([@rymai][])
|
||||
|
||||
### New features
|
||||
### 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][])
|
||||
- Pull request [#73](https://github.com/guard/guard/pull/73): Allow DSL's `group` method to accept a Symbol as group name. ([@johnbintz][])
|
||||
- Pull request [#51](https://github.com/guard/guard/pull/51): Allow options (like `:priority`) to be passed through to the Notifier. ([@indirect][] & [@netzpirat][])
|
||||
|
||||
### Improvement
|
||||
### Improvements:
|
||||
|
||||
- [#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][])
|
||||
- Pull request [#74](https://github.com/guard/guard/pull/74): Added link definitions to make the CHANGELOG more DRY! That's for sure now, we have the cleanest CHANGELOG ever! (even the link definitions are sorted alphabetically!) ([@pcreux][])
|
||||
|
||||
## 0.4.0.rc - May 28, 2011
|
||||
|
||||
### Bug fixes
|
||||
### Bugs 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][])
|
||||
- Pull request [#69](https://github.com/guard/guard/pull/69): Fixed typo in README: `Ctr-/` => `Ctr-\`. ([@tinogomes][])
|
||||
- Pull request [#66](https://github.com/guard/guard/pull/66): Support for dashes in guard names. ([@johnbintz][])
|
||||
- Require `guard/ui` because `Guard::Notifier` can be required without full Guard. ([@yannlugrin][])
|
||||
- Handled quick file (<1s) modification. Avoid to catch modified files without content modification (sha1 checksum). ([@thibaudgg][] & [@netzpirat][])
|
||||
- Fixed `Guard::Notifier` (when growl/libnotify not present). ([@thibaudgg][])
|
||||
- Fixed Rubygems deprecation messages. ([@thibaudgg][])
|
||||
|
||||
### New features
|
||||
### 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][])
|
||||
- Pull request [#67](https://github.com/guard/guard/pull/67): Allow Guardfile in `$HOME` folder. ([@hashrocketeer][])
|
||||
- Pull request [#64](https://github.com/guard/guard/pull/64): Windows notifications support. ([@stereobooster][])
|
||||
- Pull request [#63](https://github.com/guard/guard/pull/63): Refactor listeners to work as a library. ([@niklas][])
|
||||
- Use `ENV["GUARD_NOTIFY"]` to disable notifications. ([@thibaudgg][])
|
||||
- Cleaning up all specs. ([@netzpirat][])
|
||||
- [#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][])
|
||||
- Pull request [#60](https://github.com/guard/guard/pull/60): Added Windows support. ([@stereobooster][])
|
||||
- Pull request [#58](https://github.com/guard/guard/pull/58): Extract code from signal handlers into methods. ([@nicksieger][])
|
||||
- Pull request [#55](https://github.com/guard/guard/pull/55): It is now possible to pass `:guardfile` (a Guardfile path) or `:guardfile_contents` (the content of a Guardfile) to `Guard::Dsl.evaluate_guardfile`. Hence this allows the use of `Guard::Dsl.evaluate_guardfile` in a programmatic manner. ([@anithri][], improved by [@rymai][])
|
||||
|
||||
|
||||
## 0.3.4 - April 24, 2011
|
||||
|
||||
### Bug fix
|
||||
### Bugs fixes:
|
||||
|
||||
- [#41](https://github.com/guard/guard/issues/41): Removed useless Bundler requirement. ([@thibaudgg][])
|
||||
- Issue [#41](https://github.com/guard/guard/issues/41): Removed useless Bundler requirement. ([@thibaudgg][])
|
||||
|
||||
### New features
|
||||
### 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][])
|
||||
- Issue [#48](https://github.com/guard/guard/issues/48): Adding support for inline Guard classes rather than requiring a gem. ([@jrsacks][])
|
||||
|
||||
|
||||
## 0.3.3 - April 18, 2011
|
||||
|
||||
### Bug fix
|
||||
### Bugs fixes:
|
||||
|
||||
- Fixed `new_modified_files` rerun conditions on `Guard.run_on_change_for_all_guards`. ([@thibaudgg][])
|
||||
|
||||
|
||||
## 0.3.2 - April 17, 2011
|
||||
|
||||
### Bug fixe
|
||||
### Bugs fixes:
|
||||
|
||||
- Pull request [#43](https://github.com/guard/guard/pull/43): Fixed `guard init` command. ([@brainopia][])
|
||||
|
||||
- [#43](https://github.com/guard/guard/pull/43): Fixed `guard init` command. ([@brainopia][])
|
||||
|
||||
## 0.3.1 - April 14, 2011
|
||||
|
||||
### Bug fixes
|
||||
### Bugs 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][])
|
||||
- Issue [#35](https://github.com/guard/guard/issues/35): Fixed open-gem/gem_open dependency problem by using `gem which` to locate guards gem path. (reported by [@thierryhenrio][], fixed by [@thibaudgg][])
|
||||
- Issue [#38](https://github.com/guard/guard/issues/38) & Pull request [#39](https://github.com/guard/guard/issues/39): Fixed an invalid ANSI escape code in `Guard::UI.reset_line`. ([@gix][])
|
||||
|
||||
### New feature
|
||||
### New features:
|
||||
|
||||
- Issue [#28](https://github.com/guard/guard/issues/28): New `-n` command line option to disable notifications (Growl / Libnotify). ([@thibaudgg][])
|
||||
|
||||
- [#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
|
||||
### Bugs fixes:
|
||||
|
||||
- Avoid launching `run_on_change` guards method when no files matched. `--clear` guard argument is now usable. ([@thibaudgg][])
|
||||
|
||||
### New features
|
||||
### 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][])
|
||||
- Pull request [#26](https://github.com/guard/guard/pull/26): New DSL method: `group` allows you to group several guards. New CLI option: `--group group_name` to specify certain groups of guards to start. ([@netzpirat][])
|
||||
- `watch` patterns are now more strict: strings are matched with `String#==`, `Regexp` are matched with `Regexp#match`. ([@rymai][])
|
||||
- A deprecation warning is displayed if your `Guardfile` contains `String` that look like `Regexp` (bad!). ([@rymai][])
|
||||
- It's now possible to return an `Enumerable` in the `watch` optional blocks in the `Guardfile`. ([@rymai][])
|
||||
|
||||
### New specs
|
||||
### New specs:
|
||||
|
||||
- `Guard::Watcher`. ([@rymai][])
|
||||
- [#13](https://github.com/guard/guard/pull/13): `Guard::Dsl`. ([@oliamb][])
|
||||
- Pull request [#13](https://github.com/guard/guard/pull/13): `Guard::Dsl`. ([@oliamb][])
|
||||
|
||||
|
||||
## 0.2.2 - October 25, 2010
|
||||
|
||||
### Bug fix
|
||||
### Bugs fixes:
|
||||
|
||||
- Issue [#5](https://github.com/guard/guard/issues/5): avoid creating new copy of `fsevent_watch` every time a file is changed. (reported by [@stouset][], fixed by [@thibaudgg][])
|
||||
|
||||
- [#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
|
||||
### Bugs fixes:
|
||||
|
||||
- Pull request [#7](https://github.com/guard/guard/pull/7): Fixes for Linux support. ([@yannlugrin][])
|
||||
- Pull request [#6](https://github.com/guard/guard/pull/6): Locate guard now chomp newline in result path. ([@yannlugrin][])
|
||||
|
||||
- [#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
|
||||
### Bugs 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][])
|
||||
- Issue [#3](https://github.com/guard/guard/issues/3): `guard init <guard-name>` no more need `Gemfile` but `open_gem` is required now. (reported by [@wereHamster][], fixed by [@thibaudgg][])
|
||||
- Issue [#2](https://github.com/guard/guard/issues/2): 1.8.6 compatibility. (reported by [@veged][], fixed by [@thibaudgg][])
|
||||
- Removes Growl & Libnotify dependencies. ([@thibaudgg][])
|
||||
|
||||
|
||||
## 0.2.0.beta.1 - October 17, 2010
|
||||
|
||||
### New features
|
||||
### New features:
|
||||
|
||||
- Improved listeners support (`rb-fsevent` & `rb-inotify`). ([@thibaudgg][])
|
||||
- Added polling listening fallback. ([@thibaudgg][])
|
||||
@ -303,15 +245,10 @@
|
||||
[@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
|
||||
@ -319,7 +256,6 @@
|
||||
[@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
|
||||
@ -330,12 +266,9 @@
|
||||
[@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
|
||||
|
6
Gemfile
6
Gemfile
@ -13,10 +13,12 @@ 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
|
||||
end
|
||||
if 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
|
||||
end
|
||||
if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
||||
gem 'win32console', :require => false
|
||||
gem 'rb-fchange', '>= 0.0.2', :require => false
|
||||
gem 'rb-notifu', '>= 0.0.4', :require => false
|
||||
|
17
Guardfile
17
Guardfile
@ -1,16 +1,11 @@
|
||||
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
|
||||
guard :rspec, :version => 2, :all_on_start => false, :all_after_pass => false, :keep_failed => false, :cli => '--format doc' do
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
end
|
||||
|
||||
group :docs do
|
||||
guard :ronn do
|
||||
watch(%r{^man/.+\.ronn?$})
|
||||
end
|
||||
guard :ronn do
|
||||
watch(%r{^man/.+\.ronn?$})
|
||||
end
|
||||
|
||||
# require 'guard/guard'
|
||||
|
454
README.md
454
README.md
@ -1,4 +1,4 @@
|
||||
Guard [![Build Status](https://secure.travis-ci.org/guard/guard.png)](http://travis-ci.org/guard/guard)
|
||||
Guard [![Build Status](http://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.
|
||||
@ -14,130 +14,143 @@ Features
|
||||
* 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.
|
||||
* Tested against Ruby 1.8.7, 1.9.2 and REE.
|
||||
|
||||
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)
|
||||
Ryan Bates made a Railscast on Guard, you can view it here: http://railscasts.com/episodes/264-guard
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
|
||||
Install the gem:
|
||||
|
||||
$ gem install guard
|
||||
``` bash
|
||||
$ gem install guard
|
||||
```
|
||||
|
||||
Or add it to your Gemfile (inside the `development` group):
|
||||
|
||||
gem 'guard'
|
||||
``` ruby
|
||||
gem 'guard'
|
||||
```
|
||||
|
||||
and install it via Bundler:
|
||||
|
||||
$ bundle install
|
||||
``` bash
|
||||
$ bundle install
|
||||
```
|
||||
|
||||
Generate an empty Guardfile with:
|
||||
|
||||
$ guard init
|
||||
``` bash
|
||||
$ 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
|
||||
``` bash
|
||||
$ gem install rb-fsevent
|
||||
```
|
||||
|
||||
You have three possibilities for getting Growl support:
|
||||
You have two possibilities:
|
||||
|
||||
Use the [growl_notify gem](https://rubygems.org/gems/growl_notify):
|
||||
Use the [growl_notify gem](https://rubygems.org/gems/growl_notify) (recommended):
|
||||
|
||||
$ gem install growl_notify
|
||||
``` bash
|
||||
$ 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 [growlnotify](http://growl.info/extras.php#growlnotify) (cli tool for growl) + the [growl gem](https://rubygems.org/gems/growl).
|
||||
|
||||
Use the [ruby_gntp gem](https://github.com/snaka/ruby_gntp):
|
||||
``` bash
|
||||
$ brew install growlnotify
|
||||
$ gem install growl
|
||||
```
|
||||
|
||||
$ gem install ruby_gntp
|
||||
And add them to your Gemfile:
|
||||
|
||||
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.
|
||||
``` ruby
|
||||
gem 'rb-fsevent'
|
||||
gem 'growl_notify' # or gem 'growl'
|
||||
```
|
||||
|
||||
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" />
|
||||
The difference between growl and growl_notify is that growl_notify uses AppleScript to
|
||||
display a message, whereas growl uses the `growlnotify` command. In general the AppleScript
|
||||
approach is preferred, but you may also use the older growl gem. Have a look at the
|
||||
[Guard Wiki](https://github.com/guard/guard/wiki/Use-growl_notify-or-growl-gem) for more information.
|
||||
|
||||
### 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
|
||||
``` bash
|
||||
$ gem install rb-inotify
|
||||
```
|
||||
|
||||
Install the [libnotify gem](https://rubygems.org/gems/libnotify) if you want visual notification support:
|
||||
|
||||
$ gem install libnotify
|
||||
``` bash
|
||||
$ gem install libnotify
|
||||
```
|
||||
|
||||
And add them to your Gemfile:
|
||||
|
||||
gem 'rb-inotify'
|
||||
gem 'libnotify'
|
||||
|
||||
<a name="win" />
|
||||
``` ruby
|
||||
gem 'rb-inotify'
|
||||
gem 'libnotify'
|
||||
```
|
||||
|
||||
### 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
|
||||
``` bash
|
||||
$ gem install rb-fchange
|
||||
```
|
||||
|
||||
Install the [win32console gem](https://rubygems.org/gems/win32console) if you want colors in your terminal:
|
||||
|
||||
$ gem install win32console
|
||||
``` bash
|
||||
$ gem install win32console
|
||||
```
|
||||
|
||||
Install the [rb-notifu gem](https://rubygems.org/gems/rb-notifu) if you want visual notification support:
|
||||
|
||||
$ gem install rb-notifu
|
||||
``` bash
|
||||
$ gem install rb-notifu
|
||||
```
|
||||
|
||||
And add them to your Gemfile:
|
||||
|
||||
gem 'rb-fchange'
|
||||
gem 'rb-notifu'
|
||||
gem 'win32console'
|
||||
``` ruby
|
||||
gem 'rb-fchange'
|
||||
gem 'rb-notifu'
|
||||
gem 'win32console'
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Just launch Guard inside your Ruby / Rails project with:
|
||||
|
||||
$ guard [start]
|
||||
``` bash
|
||||
$ guard [start]
|
||||
```
|
||||
|
||||
or if you use Bundler, to run the Guard executable specific to your bundle:
|
||||
|
||||
$ bundle exec guard [start]
|
||||
``` bash
|
||||
$ 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.
|
||||
|
||||
@ -148,15 +161,19 @@ Command line options
|
||||
|
||||
Shell can be cleared after each change:
|
||||
|
||||
$ guard --clear
|
||||
$ guard -c # shortcut
|
||||
``` bash
|
||||
$ guard --clear
|
||||
$ guard -c # shortcut
|
||||
```
|
||||
|
||||
### `-n`/`--notify` option
|
||||
|
||||
Notifications (growl/libnotify) can be disabled:
|
||||
|
||||
$ guard --notify false
|
||||
$ guard -n f # shortcut
|
||||
``` bash
|
||||
$ guard --notify false
|
||||
$ guard -n f # shortcut
|
||||
```
|
||||
|
||||
Notifications can also be disabled globally by setting a `GUARD_NOTIFY` environment variable to `false`
|
||||
|
||||
@ -164,49 +181,43 @@ Notifications can also be disabled globally by setting a `GUARD_NOTIFY` environm
|
||||
|
||||
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
|
||||
``` bash
|
||||
$ 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
|
||||
``` bash
|
||||
$ 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
|
||||
``` bash
|
||||
$ 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
|
||||
``` bash
|
||||
$ guard --guardfile ~/.your_global_guardfile
|
||||
$ guard -G ~/.your_global_guardfile # shortcut
|
||||
```
|
||||
|
||||
An exhaustive list of options is available with:
|
||||
|
||||
$ guard help [TASK]
|
||||
|
||||
<a name="interactions" />
|
||||
``` bash
|
||||
$ guard help [TASK]
|
||||
```
|
||||
|
||||
Interactions
|
||||
------------
|
||||
@ -229,15 +240,21 @@ A list of the available guards is present [in the wiki](https://github.com/guard
|
||||
|
||||
Add it to your Gemfile (inside the `development` group):
|
||||
|
||||
gem '<guard-name>'
|
||||
``` ruby
|
||||
gem '<guard-name>'
|
||||
```
|
||||
|
||||
You can list all guards installed on your system with:
|
||||
|
||||
$ guard list
|
||||
``` bash
|
||||
$ guard list
|
||||
```
|
||||
|
||||
Insert default guard's definition to your Guardfile by running this command:
|
||||
|
||||
$ guard init <guard-name>
|
||||
``` bash
|
||||
$ guard init <guard-name>
|
||||
```
|
||||
|
||||
You are good to go, or you can modify your guards' definition to suit your needs.
|
||||
|
||||
@ -246,42 +263,44 @@ 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.
|
||||
* `#guard`: allows you to add a guard with an optional hash of options.
|
||||
* `#watch`: allows you to define which files are supervised by this 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'
|
||||
``` ruby
|
||||
ignore_paths 'foo', 'bar'
|
||||
|
||||
group 'backend' do
|
||||
guard 'bundler' do
|
||||
watch('Gemfile')
|
||||
end
|
||||
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` }
|
||||
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
|
||||
# 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
|
||||
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
|
||||
guard 'livereload' do
|
||||
watch(%r{^app/.+\.(erb|haml)$})
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Using a Guardfile without the `guard` binary
|
||||
--------------------------------------------
|
||||
@ -296,33 +315,37 @@ Remember, without any options given, Guard will look for a Guardfile in your cur
|
||||
|
||||
For instance, you could use it as follow:
|
||||
|
||||
gem 'guard'
|
||||
require 'guard'
|
||||
``` ruby
|
||||
gem 'guard'
|
||||
require 'guard'
|
||||
|
||||
Guard.setup
|
||||
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
|
||||
")
|
||||
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
|
||||
``` bash
|
||||
# guard -T
|
||||
|
||||
(global):
|
||||
shell
|
||||
Group backend:
|
||||
bundler
|
||||
rspec: cli => "--color --format doc"
|
||||
Group frontend:
|
||||
coffeescript: output => "public/javascripts/compiled"
|
||||
livereload
|
||||
(global):
|
||||
shell
|
||||
Group backend:
|
||||
bundler
|
||||
rspec: cli => "--color --format doc"
|
||||
Group frontend:
|
||||
coffeescript: output => "public/javascripts/compiled"
|
||||
livereload
|
||||
```
|
||||
|
||||
User config file
|
||||
----------------
|
||||
@ -332,109 +355,114 @@ 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
|
||||
``` ruby
|
||||
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
|
||||
```
|
||||
.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.
|
||||
`Guard::GuardName` (in `lib/guard/guard-name.rb`) must inherit from `Guard::Guard` and should overwrite at least one of the five basic `Guard::Guard` instance methods.
|
||||
|
||||
Here is an example scaffold for `lib/guard/guard-name.rb`:
|
||||
|
||||
require 'guard'
|
||||
require 'guard/guard'
|
||||
``` ruby
|
||||
require 'guard'
|
||||
require 'guard/guard'
|
||||
|
||||
module Guard
|
||||
class GuardName < 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
|
||||
def initialize(watchers=[], options={})
|
||||
super
|
||||
# init stuff here, thx!
|
||||
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.
|
||||
# =================
|
||||
# = Guard methods =
|
||||
# =================
|
||||
|
||||
# If one of those methods raise an exception, the Guard::GuardName instance
|
||||
# will be removed from the active guards.
|
||||
|
||||
# Called once when Guard starts
|
||||
# Please override initialize method to init stuff
|
||||
def start
|
||||
true
|
||||
end
|
||||
|
||||
# Called on Ctrl-C signal (when Guard quits)
|
||||
def stop
|
||||
true
|
||||
end
|
||||
|
||||
# Called on Ctrl-Z signal
|
||||
# This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
||||
def reload
|
||||
true
|
||||
end
|
||||
|
||||
# Called on Ctrl-\ signal
|
||||
# This method should be principally used for long action like running all specs/tests/...
|
||||
def run_all
|
||||
true
|
||||
end
|
||||
|
||||
# Called on file(s) modifications
|
||||
def run_on_change(paths)
|
||||
true
|
||||
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'
|
||||
``` ruby
|
||||
require 'guard/guard'
|
||||
|
||||
module ::Guard
|
||||
class InlineGuard < ::Guard::Guard
|
||||
def run_all
|
||||
end
|
||||
|
||||
def run_on_change(paths)
|
||||
end
|
||||
end
|
||||
module ::Guard
|
||||
class InlineGuard < ::Guard::Guard
|
||||
def run_all
|
||||
true
|
||||
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)
|
||||
def run_on_change(paths)
|
||||
true
|
||||
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
|
||||
|
||||
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).
|
||||
|
||||
@ -456,4 +484,4 @@ Author
|
||||
Contributors
|
||||
------------
|
||||
|
||||
[https://github.com/guard/guard/contributors](https://github.com/guard/guard/contributors)
|
||||
https://github.com/guard/guard/contributors
|
||||
|
4
Rakefile
4
Rakefile
@ -39,8 +39,8 @@ namespace(:spec) do
|
||||
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;'
|
||||
bundle install;
|
||||
bundle exec rspec spec -f doc 2>&1;'
|
||||
BASH
|
||||
end
|
||||
end
|
||||
|
@ -3,4 +3,4 @@
|
||||
require 'guard'
|
||||
require 'guard/cli'
|
||||
|
||||
Guard::CLI.start
|
||||
Guard::CLI.start
|
@ -14,13 +14,11 @@ Gem::Specification.new do |s|
|
||||
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.add_dependency 'thor', '~> 0.14.6'
|
||||
|
||||
s.files = Dir.glob('{bin,images,lib}/**/*') + %w[CHANGELOG.md LICENSE man/guard.1 man/guard.1.html README.md]
|
||||
s.executable = 'guard'
|
||||
|
366
lib/guard.rb
366
lib/guard.rb
@ -1,14 +1,8 @@
|
||||
require 'thread'
|
||||
|
||||
# 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'
|
||||
@ -16,116 +10,24 @@ module Guard
|
||||
autoload :Hook, 'guard/hook'
|
||||
|
||||
class << self
|
||||
attr_accessor :options, :interactor, :listener, :lock
|
||||
attr_accessor :options, :guards, :groups, :interactor, :listener
|
||||
|
||||
# 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
|
||||
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
|
||||
#
|
||||
# initialize this singleton
|
||||
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)
|
||||
@groups = [:default]
|
||||
@interactor = Interactor.new
|
||||
@listener = Listener.select_and_init(@options[:watchdir] ? File.expand_path(@options[:watchdir]) : Dir.pwd)
|
||||
|
||||
@options[:notify] && ENV['GUARD_NOTIFY'] != 'false' ? Notifier.turn_on : Notifier.turn_off
|
||||
@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)
|
||||
|
||||
@ -136,210 +38,85 @@ module Guard
|
||||
listener.changed_files += files if Watcher.match_files?(guards, files)
|
||||
end
|
||||
|
||||
UI.info "Guard is now watching at '#{ listener.directory }'"
|
||||
UI.info "Guard is now watching at '#{listener.directory}'"
|
||||
guards.each { |guard| supervised_task(guard, :start) }
|
||||
|
||||
run_guard_task(:start)
|
||||
|
||||
interactor.start if interactor
|
||||
interactor.start
|
||||
listener.start
|
||||
end
|
||||
|
||||
# Stop Guard listening to file changes
|
||||
#
|
||||
def stop
|
||||
UI.info 'Bye bye...', :reset => true
|
||||
|
||||
run_guard_task(:stop)
|
||||
|
||||
UI.info "Bye bye...", :reset => true
|
||||
listener.stop
|
||||
guards.each { |guard| supervised_task(guard, :stop) }
|
||||
abort
|
||||
end
|
||||
|
||||
# Reload all Guards currently enabled.
|
||||
#
|
||||
def reload
|
||||
run do
|
||||
run_guard_task(:reload)
|
||||
guards.each { |guard| supervised_task(guard, :reload) }
|
||||
end
|
||||
end
|
||||
|
||||
# Trigger `run_all` on all Guards currently enabled.
|
||||
#
|
||||
def run_all
|
||||
run do
|
||||
run_guard_task(:run_all)
|
||||
guards.each { |guard| supervised_task(guard, :run_all) }
|
||||
end
|
||||
end
|
||||
|
||||
# Pause Guard listening to file changes.
|
||||
#
|
||||
def pause
|
||||
if listener.paused?
|
||||
UI.info 'Un-paused files modification listening', :reset => true
|
||||
if listener.locked
|
||||
UI.info "Un-paused files modification listening", :reset => true
|
||||
listener.clear_changed_files
|
||||
listener.run
|
||||
listener.unlock
|
||||
else
|
||||
UI.info 'Paused files modification listening', :reset => true
|
||||
listener.pause
|
||||
UI.info "Paused files modification listening", :reset => true
|
||||
listener.lock
|
||||
end
|
||||
end
|
||||
|
||||
# Trigger `run_on_change` on all Guards currently enabled.
|
||||
#
|
||||
def run_on_change(paths)
|
||||
def run_on_change(files)
|
||||
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)
|
||||
end
|
||||
guards.each do |guard|
|
||||
paths = Watcher.match_files(guard, files)
|
||||
unless paths.empty?
|
||||
UI.debug "#{guard.class.name}#run_on_change with #{paths.inspect}"
|
||||
supervised_task(guard, :run_on_change, paths)
|
||||
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)
|
||||
def run
|
||||
listener.lock
|
||||
interactor.lock
|
||||
UI.clear if options[:clear]
|
||||
begin
|
||||
yield
|
||||
rescue Interrupt
|
||||
end
|
||||
interactor.unlock
|
||||
listener.unlock
|
||||
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
|
||||
|
||||
# Let a guard execute its task but
|
||||
# fire it if his work leads to a system failure
|
||||
def supervised_task(guard, task_to_supervise, *args)
|
||||
guard.hook("#{task_to_supervise}_begin", *args)
|
||||
result = guard.send(task_to_supervise, *args)
|
||||
guard.hook("#{task_to_supervise}_end", result)
|
||||
result
|
||||
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") }")
|
||||
|
||||
UI.error("#{guard.class.name} failed to achieve its <#{task_to_supervise.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
|
||||
UI.info("\n#{guard.class.name} has just been fired")
|
||||
return 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.')
|
||||
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]) }
|
||||
@ -347,66 +124,42 @@ module Guard
|
||||
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
|
||||
def add_group(name)
|
||||
@groups << name.to_sym unless name.nil?
|
||||
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
|
||||
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.error "Could not find class Guard::#{const_name.capitalize}"
|
||||
end
|
||||
rescue LoadError => loadError
|
||||
UI.error "Could not load 'guard/#{ name.downcase }' or find class Guard::#{ const_name.capitalize }"
|
||||
UI.error "Could not load 'guard/#{name.downcase}' or find class Guard::#{const_name.capitalize}"
|
||||
UI.error loadError.to_s
|
||||
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 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
|
||||
Gem::Specification.find_by_name("guard-#{name}").full_gem_path
|
||||
else
|
||||
Gem.source_index.find_name("guard-#{ name }").last.full_gem_path
|
||||
Gem.source_index.find_name("guard-#{name}").last.full_gem_path
|
||||
end
|
||||
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-/ }
|
||||
@ -415,19 +168,16 @@ module 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(' ') }"
|
||||
::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 }"
|
||||
Kernel.send(:alias_method, :original_backtick, :"`")
|
||||
Kernel.send(:define_method, :"`") do |command|
|
||||
::Guard::UI.debug "Command execution: #{command}"
|
||||
original_backtick command
|
||||
end
|
||||
end
|
||||
|
161
lib/guard/cli.rb
161
lib/guard/cli.rb
@ -2,118 +2,89 @@ 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 :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"
|
||||
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
|
||||
#
|
||||
desc "list", "Lists guards that can be used with init"
|
||||
def list
|
||||
::Guard::DslDescriber.list(options)
|
||||
::Guard::DslDescriber.evaluate_guardfile(options)
|
||||
installed = []
|
||||
::Guard::DslDescriber.guardfile_structure.each do |group|
|
||||
group[:guards].each {|x| installed << x[:name]} if group[:guards]
|
||||
end
|
||||
|
||||
::Guard::UI.info "Available guards:"
|
||||
::Guard::guard_gem_names.sort.each do |name|
|
||||
if installed.include? name
|
||||
::Guard::UI.info " #{name} *"
|
||||
else
|
||||
::Guard::UI.info " #{name}"
|
||||
end
|
||||
end
|
||||
::Guard::UI.info ' '
|
||||
::Guard::UI.info "See also https://github.com/guard/guard/wiki/List-of-available-Guards"
|
||||
::Guard::UI.info "* denotes ones already in your Guardfile"
|
||||
end
|
||||
|
||||
desc 'version', 'Show the Guard version'
|
||||
desc "version", "Prints Guard's version"
|
||||
def version
|
||||
::Guard::UI.info "Guard version #{Guard::VERSION}"
|
||||
end
|
||||
map %w(-v --version) => :version
|
||||
|
||||
# Shows the current version of Guard.
|
||||
#
|
||||
# @see Guard::VERSION
|
||||
#
|
||||
def 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
|
||||
#
|
||||
desc "init [GUARD]", "Generates a Guardfile into the current working directory, or insert the given GUARD in an existing Guardfile"
|
||||
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
|
||||
#
|
||||
desc "show", "Show all defined Guards and their options"
|
||||
def show
|
||||
::Guard::DslDescriber.show(options)
|
||||
end
|
||||
::Guard::DslDescriber.evaluate_guardfile(options)
|
||||
|
||||
::Guard::DslDescriber.guardfile_structure.each do |group|
|
||||
if !group[:guards].empty?
|
||||
if group[:group]
|
||||
::Guard::UI.info "Group #{group[:group]}:"
|
||||
else
|
||||
::Guard::UI.info "(global):"
|
||||
end
|
||||
|
||||
group[:guards].each do |guard|
|
||||
line = " #{guard[:name]}"
|
||||
|
||||
if !guard[:options].empty?
|
||||
line += ": #{guard[:options].collect { |k, v| "#{k} => #{v.inspect}" }.join(", ")}"
|
||||
end
|
||||
::Guard::UI.info line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
::Guard::UI.info ''
|
||||
end
|
||||
map %w(-T) => :show
|
||||
end
|
||||
end
|
||||
|
298
lib/guard/dsl.rb
298
lib/guard/dsl.rb
@ -1,160 +1,62 @@
|
||||
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.is_a?(Hash) or raise ArgumentError.new("evaluate_guardfile not passed 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?
|
||||
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.'
|
||||
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
|
||||
begin
|
||||
new.instance_eval(contents, @@options[:guardfile_path], 1)
|
||||
rescue
|
||||
UI.error "Invalid Guardfile, original error is:\n#{$!}"
|
||||
exit 1
|
||||
end
|
||||
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 }['"]?/)
|
||||
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
|
||||
begin
|
||||
@@options[:guardfile_path] = guardfile_path
|
||||
@@options[:guardfile_contents] = File.read(guardfile_path)
|
||||
rescue
|
||||
UI.error("Error reading file #{guardfile_path}")
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
# Get the content to evaluate and stores it into
|
||||
# the options as `:guardfile_contents`.
|
||||
#
|
||||
def fetch_guardfile_contents
|
||||
# TODO: do we need .rc file interaction?
|
||||
if @@options[:guardfile_contents]
|
||||
UI.info 'Using inline Guardfile.'
|
||||
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] }."
|
||||
UI.info "Using Guardfile at #{@@options[:guardfile]}."
|
||||
else
|
||||
UI.error "No Guardfile exists at #{ @@options[:guardfile] }."
|
||||
UI.error "No Guardfile exists at #{@@options[:guardfile]}."
|
||||
exit 1
|
||||
end
|
||||
|
||||
@ -162,209 +64,85 @@ module Guard
|
||||
if File.exist?(guardfile_default_path)
|
||||
read_guardfile(guardfile_default_path)
|
||||
else
|
||||
UI.error 'No Guardfile found, please create one with `guard init`.'
|
||||
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."
|
||||
UI.error "The command file(#{@@options[:guardfile]}) seems to be empty."
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
# Get the content of the `Guardfile`.
|
||||
#
|
||||
# @return [String] the Guardfile content
|
||||
#
|
||||
def guardfile_contents
|
||||
@@options ? @@options[: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] : ''
|
||||
@@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
|
||||
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
|
||||
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')
|
||||
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'))
|
||||
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'))
|
||||
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 = {})
|
||||
def group(name, &guard_definition)
|
||||
@groups = @@options[:group] || []
|
||||
name = name.to_sym
|
||||
name = name.to_sym
|
||||
|
||||
if block_given? && (@groups.empty? || @groups.map(&:to_sym).include?(name))
|
||||
::Guard.add_group(name.to_s.downcase, options)
|
||||
if guard_definition && (@groups.empty? || @groups.map(&:to_sym).include?(name))
|
||||
@current_group = name
|
||||
|
||||
yield if block_given?
|
||||
|
||||
guard_definition.call
|
||||
@current_group = nil
|
||||
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 = {})
|
||||
def guard(name, options = {}, &watch_and_callback_definition)
|
||||
@watchers = []
|
||||
@callbacks = []
|
||||
|
||||
yield if block_given?
|
||||
|
||||
watch_and_callback_definition.call if watch_and_callback_definition
|
||||
options.update(:group => (@current_group || :default))
|
||||
::Guard.add_guard(name.to_s.downcase, @watchers, @callbacks, options)
|
||||
::Guard.add_guard(name.to_s.downcase.to_sym, @watchers, @callbacks, options)
|
||||
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 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(', ') }"
|
||||
UI.info "Ignoring paths: #{paths.join(', ')}"
|
||||
::Guard.listener.ignore_paths.push(*paths)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -1,150 +1,28 @@
|
||||
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
|
||||
@@guardfile_structure = [ { :guards => [] } ]
|
||||
|
||||
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)
|
||||
def group(name, &guard_definition)
|
||||
@@guardfile_structure << { :group => name.to_sym, :guards => [] }
|
||||
|
||||
@group = true
|
||||
|
||||
yield if block_given?
|
||||
|
||||
guard_definition.call
|
||||
@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 = { })
|
||||
def guard(name, options = {}, &watch_definition)
|
||||
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,58 @@
|
||||
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, :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
|
||||
#
|
||||
def initialize(watchers = [], options = {})
|
||||
@group = options[:group] ? options.delete(:group).to_sym : :default
|
||||
@group = options.delete(:group) || :default
|
||||
@watchers, @options = watchers, 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"
|
||||
::Guard::UI.info "Guardfile already includes #{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)
|
||||
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
|
||||
#
|
||||
# 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
|
||||
#
|
||||
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
|
||||
# @return [Object] the task result
|
||||
#
|
||||
def run_on_deletion(paths)
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,82 +1,51 @@
|
||||
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
|
||||
# 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
|
||||
#
|
||||
# Example:
|
||||
# def run_all
|
||||
# hook :foo
|
||||
# end
|
||||
#
|
||||
# Here, when {Guard::Guard#run_all} is called, {#hook} will notify callbacks
|
||||
# Here, when #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
|
||||
# When +event+ is a String, #hook will directly turn the String
|
||||
# into a Symbol.
|
||||
#
|
||||
# @example Add a hook with a String
|
||||
#
|
||||
# Example:
|
||||
# def run_all
|
||||
# hook "foo_bar"
|
||||
# end
|
||||
#
|
||||
# When {Guard::Guard#run_all} is called, {#hook} will notify callbacks
|
||||
# Here, when #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.
|
||||
#
|
||||
# +args+ parameter is 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
|
||||
calling_method = caller[0][/`([^']*)'/, 1]
|
||||
"#{calling_method}_#{event}"
|
||||
else
|
||||
event
|
||||
end.to_sym
|
||||
|
||||
UI.debug "Hook :#{ hook_name } executed for #{ self.class }"
|
||||
UI.debug "\nHook :#{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|
|
||||
@ -84,34 +53,19 @@ module Guard
|
||||
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
|
||||
|
@ -1,44 +1,40 @@
|
||||
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.
|
||||
#
|
||||
|
||||
attr_reader :locked
|
||||
|
||||
def initialize
|
||||
@locked = false
|
||||
end
|
||||
|
||||
def start
|
||||
return if ENV["GUARD_ENV"] == 'test'
|
||||
|
||||
if !@thread || @thread.stop?
|
||||
@thread = Thread.new do
|
||||
while entry = $stdin.gets.chomp
|
||||
Thread.new do
|
||||
loop do
|
||||
if (entry = $stdin.gets) && !@locked
|
||||
entry.gsub! /\n/, ''
|
||||
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
|
||||
when 'stop', 'quit', 'exit', 's', 'q', 'e'
|
||||
::Guard.stop
|
||||
when 'reload', 'r', 'z'
|
||||
::Guard.reload
|
||||
when 'pause', 'p'
|
||||
::Guard.pause
|
||||
else
|
||||
::Guard.run_all
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def stop_if_not_current
|
||||
unless Thread.current == @thread
|
||||
@thread.kill
|
||||
end
|
||||
def lock
|
||||
@locked = true
|
||||
end
|
||||
|
||||
def unlock
|
||||
@locked = false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -8,71 +8,43 @@ module Guard
|
||||
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]
|
||||
|
||||
DefaultIgnorePaths = %w[. .. .bundle .git log tmp vendor]
|
||||
attr_accessor :changed_files
|
||||
attr_reader :directory, :ignore_paths
|
||||
attr_reader :directory, :ignore_paths, :locked
|
||||
|
||||
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)
|
||||
def self.select_and_init(*a)
|
||||
if mac? && Darwin.usable?
|
||||
Darwin.new(*args)
|
||||
Darwin.new(*a)
|
||||
elsif linux? && Linux.usable?
|
||||
Linux.new(*args)
|
||||
Linux.new(*a)
|
||||
elsif windows? && Windows.usable?
|
||||
Windows.new(*args)
|
||||
Windows.new(*a)
|
||||
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(*a)
|
||||
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)
|
||||
@directory = directory.to_s
|
||||
@sha1_checksums_hash = {}
|
||||
@relativize_paths = options.fetch(:relativize_paths, true)
|
||||
@changed_files = []
|
||||
@locked = false
|
||||
@ignore_paths = DefaultIgnorePaths
|
||||
@ignore_paths |= options[:ignore_paths] if options[:ignore_paths]
|
||||
|
||||
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
|
||||
if @changed_files != [] && !@locked
|
||||
changed_files = @changed_files.dup
|
||||
clear_changed_files
|
||||
::Guard.run_on_change(changed_files)
|
||||
@ -83,146 +55,75 @@ module Guard
|
||||
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
|
||||
def lock
|
||||
@locked = true
|
||||
end
|
||||
|
||||
# Unpause the listener to listen again to change events.
|
||||
#
|
||||
def run
|
||||
@paused = false
|
||||
def unlock
|
||||
@locked = 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
|
||||
#
|
||||
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) })
|
||||
|
||||
files = potentially_modified_files(dirs, options).select { |path| file_modified?(path, last_event) }
|
||||
relativize_paths(files)
|
||||
end
|
||||
|
||||
# Register a directory to watch.
|
||||
# Must be implemented by the subclasses.
|
||||
#
|
||||
# @param [String] directory the directory to watch
|
||||
#
|
||||
def worker
|
||||
raise NotImplementedError, "should respond to #watch"
|
||||
end
|
||||
|
||||
# register a directory to watch. must be implemented by the subclasses
|
||||
def watch(directory)
|
||||
raise NotImplementedError, "do whatever you want here, given the directory as only argument"
|
||||
end
|
||||
|
||||
# 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
|
||||
#
|
||||
# scopes all given paths to the current #directory
|
||||
def relativize_paths(paths)
|
||||
return paths unless relativize_paths?
|
||||
paths.map do |path|
|
||||
path.gsub(%r{^(!)?#{ @directory }/},'\1')
|
||||
path.gsub(%r{^#{@directory}/}, '')
|
||||
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
|
||||
#
|
||||
# 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
|
||||
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 = {})
|
||||
def potentially_modified_files(dirs, options={})
|
||||
paths = exclude_ignored_paths(dirs)
|
||||
|
||||
if options[:all]
|
||||
@ -230,7 +131,7 @@ module Guard
|
||||
if File.file?(path)
|
||||
array << path
|
||||
else
|
||||
array += Dir.glob("#{ path }/**/*", File::FNM_DOTMATCH).select { |p| File.file?(p) }
|
||||
array += Dir.glob("#{path}/**/*", File::FNM_DOTMATCH).select { |p| File.file?(p) }
|
||||
end
|
||||
array
|
||||
end
|
||||
@ -239,31 +140,15 @@ module Guard
|
||||
end
|
||||
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.
|
||||
#
|
||||
# ctime is used only on == comparaison to always catches Rails 3.1 Assets pipelined on Mac OSX
|
||||
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
|
||||
if File.ctime(path).to_i == last_event.to_i
|
||||
file_content_modified?(path, sha1_checksum(path))
|
||||
elsif mtime > last_event.to_i
|
||||
elsif File.mtime(path).to_i > 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
|
||||
@ -271,12 +156,6 @@ module Guard
|
||||
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)
|
||||
@ -286,62 +165,22 @@ module Guard
|
||||
end
|
||||
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
|
||||
end
|
||||
|
||||
# Test if the OS is Linux.
|
||||
#
|
||||
# @return [Boolean] Whether the OS is Linux
|
||||
#
|
||||
def self.linux?
|
||||
RbConfig::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
|
||||
|
@ -1,60 +1,41 @@
|
||||
module Guard
|
||||
|
||||
# Listener implementation for Mac OS X `FSEvents`.
|
||||
#
|
||||
class Darwin < Listener
|
||||
|
||||
# Initialize the Listener.
|
||||
#
|
||||
def initialize(*)
|
||||
super
|
||||
@fsevent = FSEvent.new
|
||||
end
|
||||
|
||||
# Start the listener.
|
||||
#
|
||||
def worker
|
||||
@fsevent
|
||||
end
|
||||
|
||||
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-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)'
|
||||
UI.info "Please update rb-fsevent (>= 0.4.0)"
|
||||
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
|
||||
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)
|
||||
|
@ -1,11 +1,6 @@
|
||||
module Guard
|
||||
|
||||
# Listener implementation for Linux `inotify`.
|
||||
#
|
||||
class Linux < Listener
|
||||
|
||||
# Initialize the Listener.
|
||||
#
|
||||
def initialize(*)
|
||||
super
|
||||
@inotify = INotify::Notifier.new
|
||||
@ -13,52 +8,44 @@ module Guard
|
||||
@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)'
|
||||
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'
|
||||
UI.info "Please install rb-inotify gem for Linux inotify support"
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
def watch_change?
|
||||
!!@watch_change
|
||||
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)
|
||||
# The event selection is based on https://github.com/guard/guard/wiki/Analysis-of-inotify-events-for-different-editors
|
||||
worker.watch(directory, :recursive, :attrib, :create, :move_self, :close_write) do |event|
|
||||
unless event.name == "" # Event on root directory
|
||||
@files << event.absolute_name
|
||||
@ -67,16 +54,6 @@ module Guard
|
||||
rescue Interrupt
|
||||
end
|
||||
|
||||
# Test if inotify is watching for changes.
|
||||
#
|
||||
# @return [Boolean] whether inotify is active or not
|
||||
#
|
||||
def watch_change?
|
||||
!!@watch_change
|
||||
end
|
||||
|
||||
# Watch for file system changes.
|
||||
#
|
||||
def watch_change
|
||||
@watch_change = true
|
||||
until @stop
|
||||
|
@ -1,46 +1,24 @@
|
||||
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(*)
|
||||
super
|
||||
@latency = 1.5
|
||||
end
|
||||
|
||||
# Start the listener.
|
||||
#
|
||||
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
|
||||
|
||||
private
|
||||
|
||||
# Watch for file system changes.
|
||||
#
|
||||
def watch_change
|
||||
until @stop
|
||||
start = Time.now.to_f
|
||||
@ -51,5 +29,9 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
def watch(directory)
|
||||
@existing = all_files
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -1,48 +1,35 @@
|
||||
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'
|
||||
UI.info "Please install rb-fchange gem for Windows file events support"
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def worker
|
||||
@fchange
|
||||
end
|
||||
|
||||
# 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)]
|
||||
@ -51,11 +38,5 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# Get the listener worker.
|
||||
#
|
||||
def worker
|
||||
@fchange
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -3,288 +3,134 @@ 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
|
||||
def self.turn_off
|
||||
ENV["GUARD_NOTIFY"] = 'false'
|
||||
end
|
||||
|
||||
attr_accessor :growl_library, :gntp
|
||||
|
||||
# Turn notifications off.
|
||||
#
|
||||
def turn_off
|
||||
ENV["GUARD_NOTIFY"] = 'false'
|
||||
def self.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
|
||||
|
||||
def self.notify(message, options = {})
|
||||
if enabled?
|
||||
image = options.delete(:image) || :success
|
||||
title = options.delete(:title) || "Guard"
|
||||
|
||||
# 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
|
||||
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)
|
||||
end
|
||||
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"
|
||||
def self.enabled?
|
||||
ENV["GUARD_NOTIFY"] == 'true'
|
||||
end
|
||||
|
||||
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)
|
||||
end
|
||||
end
|
||||
private
|
||||
|
||||
def self.notify_mac(title, message, image, options)
|
||||
require_growl # need for guard-rspec formatter that is called out of guard scope
|
||||
|
||||
default_options = { :title => title, :icon => image_path(image), :name => APPLICATION_NAME }
|
||||
default_options.merge!(options)
|
||||
|
||||
if defined?(GrowlNotify)
|
||||
default_options[:description] = message
|
||||
default_options[:application_name] = APPLICATION_NAME
|
||||
default_options.delete(:name)
|
||||
|
||||
GrowlNotify.send_notification(default_options) if enabled?
|
||||
else
|
||||
Growl.notify message, default_options.merge(options) if enabled?
|
||||
end
|
||||
end
|
||||
|
||||
# Test if the notifications are enabled and available.
|
||||
#
|
||||
# @return [Boolean] whether the notifications are available
|
||||
#
|
||||
def enabled?
|
||||
ENV["GUARD_NOTIFY"] == 'true'
|
||||
def self.notify_linux(title, message, image, options)
|
||||
require_libnotify # need for guard-rspec formatter that is called out of guard scope
|
||||
default_options = { :body => message, :summary => title, :icon_path => image_path(image), :transient => true }
|
||||
Libnotify.show default_options.merge(options) if enabled?
|
||||
end
|
||||
|
||||
def self.notify_windows(title, message, image, options)
|
||||
require_rbnotifu # need for guard-rspec formatter that is called out of guard scope
|
||||
default_options = { :message => message, :title => title, :type => image_level(image), :time => 3 }
|
||||
Notifu.show default_options.merge(options) if enabled?
|
||||
end
|
||||
|
||||
def self.image_path(image)
|
||||
images_path = Pathname.new(File.dirname(__FILE__)).join('../../images')
|
||||
case image
|
||||
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
|
||||
|
||||
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
|
||||
def self.image_level(image)
|
||||
case image
|
||||
when :failed
|
||||
:error
|
||||
when :pending
|
||||
:warn
|
||||
when :success
|
||||
:info
|
||||
else
|
||||
:info
|
||||
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
|
||||
def self.require_growl
|
||||
begin
|
||||
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
|
||||
if GrowlNotify.application_name != APPLICATION_NAME
|
||||
GrowlNotify.config do |c|
|
||||
c.notifications = c.default_notifications = [ APPLICATION_NAME ]
|
||||
c.application_name = c.notifications.first
|
||||
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
|
||||
rescue LoadError
|
||||
turn_off
|
||||
UI.info "Please install growl_notify or growl gem for Mac OS X notification support and add it to your Gemfile"
|
||||
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
|
||||
def self.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
|
||||
|
||||
def self.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
|
||||
end
|
||||
|
187
lib/guard/ui.rb
187
lib/guard/ui.rb
@ -1,93 +1,88 @@
|
||||
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
|
||||
|
||||
ANSI_ESCAPE_BRIGHT = "1"
|
||||
|
||||
ANSI_ESCAPE_BLACK = "30"
|
||||
ANSI_ESCAPE_RED = "31"
|
||||
ANSI_ESCAPE_GREEN = "32"
|
||||
ANSI_ESCAPE_YELLOW = "33"
|
||||
ANSI_ESCAPE_BLUE = "34"
|
||||
ANSI_ESCAPE_MAGENTA = "35"
|
||||
ANSI_ESCAPE_CYAN = "36"
|
||||
ANSI_ESCAPE_WHITE = "37"
|
||||
|
||||
ANSI_ESCAPE_BGBLACK = "40"
|
||||
ANSI_ESCAPE_BGRED = "41"
|
||||
ANSI_ESCAPE_BGGREEN = "42"
|
||||
ANSI_ESCAPE_BGYELLOW = "43"
|
||||
ANSI_ESCAPE_BGBLUE = "44"
|
||||
ANSI_ESCAPE_BGMAGENTA = "45"
|
||||
ANSI_ESCAPE_BGCYAN = "46"
|
||||
ANSI_ESCAPE_BGWHITE = "47"
|
||||
|
||||
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 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'
|
||||
def error(message, options={})
|
||||
unless ENV["GUARD_ENV"] == "test"
|
||||
reset_line if options[:reset]
|
||||
STDERR.puts color('ERROR: ', :red) + message
|
||||
puts color('ERROR: ', :red) + message
|
||||
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'
|
||||
def deprecation(message, options = {})
|
||||
unless ENV["GUARD_ENV"] == "test"
|
||||
reset_line if options[:reset]
|
||||
STDERR.puts color('DEPRECATION: ', :red) + message
|
||||
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'
|
||||
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]
|
||||
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(color_enabled? ? "\r\e[0m" : "\r\n")
|
||||
end
|
||||
|
||||
# Clear the output.
|
||||
#
|
||||
def clear
|
||||
system('clear;')
|
||||
system("clear;")
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
# Reset a color sequence.
|
||||
#
|
||||
# @deprecated
|
||||
# @param [String] text the text
|
||||
#
|
||||
def reset_color(text)
|
||||
deprecation('UI.reset_color(text) is deprecated, please use color(text, ' ') instead.')
|
||||
color(text, '')
|
||||
deprecation('UI.reset_color(text) is deprecated, please use color(text, "") instead.')
|
||||
color(text, "")
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
# 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
|
||||
@ -107,87 +102,9 @@ module Guard
|
||||
@color_enabled = true
|
||||
end
|
||||
end
|
||||
|
||||
@color_enabled
|
||||
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
|
||||
VERSION = "0.6.3" unless defined? Guard::VERSION
|
||||
end
|
||||
|
@ -1,18 +1,7 @@
|
||||
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
|
||||
@ -21,52 +10,30 @@ module Guard
|
||||
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
|
||||
UI.info "You have strings in your Guardfile's watch patterns that seem to represent regexps.\nGuard matchs String with == and Regexp with Regexp#match.\nYou should either use plain String (without Regexp special characters) or real Regexp.\n"
|
||||
@@warning_printed = true
|
||||
end
|
||||
|
||||
UI.info "\"#{@pattern}\" has been converted to #{ Regexp.new(@pattern).inspect }\n"
|
||||
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 watcher.action
|
||||
result = watcher.call_action(matches)
|
||||
if guard.options[:any_return]
|
||||
paths << result
|
||||
elsif result.respond_to?(:empty?) && !result.empty?
|
||||
paths << Array(result)
|
||||
end
|
||||
paths << Array(result) if result.respond_to?(:empty?) && !result.empty?
|
||||
else
|
||||
paths << matches[0]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
guard.options[:any_return] ? paths : paths.flatten.map { |p| p.to_s }
|
||||
paths.flatten.map { |p| p.to_s }
|
||||
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|
|
||||
@ -75,11 +42,6 @@ module Guard
|
||||
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)
|
||||
@ -88,25 +50,15 @@ module Guard
|
||||
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 }
|
||||
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") }"
|
||||
UI.error "Problem with watch action!\n#{e.message}\n\n#{e.backtrace.join("\n")}"
|
||||
end
|
||||
end
|
||||
|
||||
|
69
man/guard
Normal file
69
man/guard
Normal file
@ -0,0 +1,69 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "GUARD" "" "September 2011" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBguard\fR
|
||||
.
|
||||
.P
|
||||
\fI!DOCTYPE html\fR
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
<a href="#NAME">NAME</a>
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
<li class=\'tl\'>guard</li>
|
||||
<li class=\'tc\'></li>
|
||||
<li class=\'tr\'>guard</li>
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
\fBguard\fR
|
||||
.
|
||||
.P
|
||||
\&\.\e" generated with Ronn/v0\.7\.3 \.\e" http://github\.com/rtomayko/ronn/tree/0\.7\.3 \. \.TH "GUARD" "1" "August 2011" "" "" \. \.SH "NAME" \efBguard\efR \- 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" \efBguard \fICOMMAND\fR \fIOPTIONS\fR\efR \. \.SH "COMMANDS" \. \.SS "start" Starts Guard\. This is the default command if none is provided\. \. \.P The following options are available: \. \.P \efB\-c\efR, \efB\-\-clear\efR Clears the Shell after each change\. \. \.P \efB\-n\efR, \efB\-\-notify\efR \efIFLAG\efR 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 \efBtrue\efR/\efBfalse\efR or \efBt\efR/\efBf\efR\. \. \.P \efB\-d\efR, \efB\-\-debug\efR Runs Guard in debug mode\. \. \.P \efB\-g\efR, \efB\-\-group\efR \efIGROUP1\efR \efIGROUP2\efR\.\.\. Runs only the groups specified by GROUP1, GROUP2 etc\. Groups name should be separated by spaces\. Guards that don\e\'t belong to a group are considered global and are always run\. \. \.P \efB\-w\efR, \efB\-\-watchdir\efR \efIPATH\efR \. \.P Tells Guard to watch PATH instead of \efB\./\efR\. \. \.P \efB\-G\efR, \efB\-\-guardfile\efR \efIFILE\efR Tells Guard to use FILE as its Guardfile instead of \efB\./Guardfile\efR or \efB~/\.Guardfile\efR\. \. \.SS "init [GUARD]" If no Guardfile is present in the current directory, creates an empty Guardfile\. \. \.P If \efIGUARD\efR is present, add its default Guardfile configuration to the current Guardfile\. Note that \efIGUARD\efR is the guard\e\'s name without the \efBguard\-\efR prefix\. For instance to initialize guard\-rspec, run \efBguard init rspec\efR\. \. \.SS "list" Lists guards that can be used with the \efBinit\efR command\. \. \.SS "\-T, show" List defined groups and guards for the current Guardfile\. \. \.SS "\-h, help [COMMAND]" List all of Guard\e\'s available commands\. \. \.P If \efICOMMAND\efR is given, displays a specific help for \efITASK\efR\. \. \.SH "EXAMPLES" Initialize Guard and a specific guard at the same time: \. \.P \efB[bundle exec] guard init [rspec]\efR \. \.P Run Guard: \. \.P \efB[bundle exec] guard [start] \-\-watchdir ~/dev \-\-guardfile ~/env/Guardfile \-\-clear \-\-group backend frontend \-\-notify false \-\-debug\efR \. \.P or in a more concise way: \. \.P \efB[bundle exec] guard [start] \-w ~/dev \-G ~/env/Guardfile \-c \-g backend frontend \-n f \-d\efR \. \.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
|
||||
.
|
||||
.P
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
<li class=\'tl\'></li>
|
||||
<li class=\'tc\'>September 2011</li>
|
||||
<li class=\'tr\'>guard</li>
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
|
@ -41,7 +41,7 @@ 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]"
|
||||
.SS "init <a href=\"guard\.html\">GUARD</a>"
|
||||
If no Guardfile is present in the current directory, creates an empty Guardfile\.
|
||||
.
|
||||
.P
|
||||
|
@ -112,7 +112,7 @@
|
||||
<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>
|
||||
<h3 id="init-GUARD">init <a href="guard.html">GUARD</a></h3>
|
||||
|
||||
<p>If no Guardfile is present in the current directory, creates an empty Guardfile.</p>
|
||||
|
||||
|
267
man/guard.html
Normal file
267
man/guard.html
Normal file
@ -0,0 +1,267 @@
|
||||
<!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</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="#NAME">NAME</a>
|
||||
</div>
|
||||
|
||||
<ol class='man-decor man-head man head'>
|
||||
<li class='tl'>guard</li>
|
||||
<li class='tc'></li>
|
||||
<li class='tr'>guard</li>
|
||||
</ol>
|
||||
|
||||
<h2 id="NAME">NAME</h2>
|
||||
<p class="man-name">
|
||||
<code>guard</code>
|
||||
</p>
|
||||
<p><var>!DOCTYPE html</var>
|
||||
<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</title>
|
||||
<style type="text/css" media="all">
|
||||
/<em> style: man </em>/
|
||||
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></html></p>
|
||||
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
|
||||
<p><body id="manpage">
|
||||
<div class="mp" id="man" /></body></p>
|
||||
|
||||
<p> <div class="man-navigation" style="display:none" /></p>
|
||||
|
||||
<pre><code><a href="#NAME">NAME</a>
|
||||
</code></pre>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<p> <ol class="man-decor man-head man head" /></p>
|
||||
|
||||
<pre><code><li class='tl'>guard</li>
|
||||
<li class='tc'></li>
|
||||
<li class='tr'>guard</li>
|
||||
</code></pre>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<p> <h2 id="NAME">NAME</h2></p>
|
||||
|
||||
<p class="man-name">
|
||||
<code>guard</code>
|
||||
</p>
|
||||
|
||||
|
||||
<p>.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "GUARD" "1" "August 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 <var>COMMAND</var> <var>OPTIONS</var>\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</p>
|
||||
|
||||
|
||||
<p> <ol class="man-decor man-foot man foot" /></p>
|
||||
|
||||
<pre><code><li class='tl'></li>
|
||||
<li class='tc'>September 2011</li>
|
||||
<li class='tr'>guard</li>
|
||||
</code></pre>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
<ol class='man-decor man-foot man foot'>
|
||||
<li class='tl'></li>
|
||||
<li class='tc'>September 2011</li>
|
||||
<li class='tr'>guard</li>
|
||||
</ol>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
0
spec/fixtures/folder1/file1.txt
vendored
Executable file → Normal file
0
spec/fixtures/folder1/file1.txt
vendored
Executable file → Normal file
@ -1,70 +1,40 @@
|
||||
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
|
||||
before(:each) do
|
||||
::Guard.stub!(:guards).and_return([mock('Guard')])
|
||||
user_config_path = File.expand_path(File.join('~', '.guard.rb'))
|
||||
File.stub(:exist?).with(user_config_path) { false }
|
||||
end
|
||||
subject { described_class }
|
||||
|
||||
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
|
||||
|
||||
it 'should evaluate a Guardfile and create the right structure' do
|
||||
mixed_guardfile_string = <<-GUARD
|
||||
guard 'test', :a => :b do
|
||||
watch('c')
|
||||
end
|
||||
|
||||
group :a do
|
||||
guard 'test' do
|
||||
watch('c')
|
||||
end
|
||||
end
|
||||
|
||||
group "b" do
|
||||
guard 'another' do
|
||||
watch('c')
|
||||
end
|
||||
end
|
||||
GUARD
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => mixed_guardfile_string)
|
||||
|
||||
subject.guardfile_structure.should == [
|
||||
{ :guards => [ { :name => 'test', :options => { :a => :b } } ] },
|
||||
{ :group => :a, :guards => [ { :name => 'test', :options => {} } ] },
|
||||
{ :group => :b, :guards => [ { :name => 'another', :options => {} } ] }
|
||||
]
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -2,14 +2,13 @@ require 'spec_helper'
|
||||
require 'guard/guard'
|
||||
|
||||
describe Guard::Dsl do
|
||||
|
||||
subject { described_class }
|
||||
class Guard::Dummy < Guard::Guard; end
|
||||
|
||||
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')])
|
||||
end
|
||||
@ -24,24 +23,24 @@ describe Guard::Dsl do
|
||||
|
||||
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
|
||||
lambda { subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string) }.should_not raise_error
|
||||
subject.guardfile_contents.should == valid_guardfile_string
|
||||
end
|
||||
|
||||
it "should use a 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"
|
||||
lambda { subject.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
|
||||
subject.guardfile_contents.should == "guard :foo"
|
||||
end
|
||||
|
||||
it "should use a default file if no other options are given" do
|
||||
fake_guardfile(@local_guardfile_path, "guard :bar")
|
||||
|
||||
Guard::UI.should_not_receive(:error)
|
||||
lambda { described_class.evaluate_guardfile }.should_not raise_error
|
||||
described_class.guardfile_contents.should == "guard :bar"
|
||||
lambda { subject.evaluate_guardfile }.should_not raise_error
|
||||
subject.guardfile_contents.should == "guard :bar"
|
||||
end
|
||||
|
||||
it "should use a string over any other method" do
|
||||
@ -49,8 +48,8 @@ describe Guard::Dsl do
|
||||
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
|
||||
lambda { subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string) }.should_not raise_error
|
||||
subject.guardfile_contents.should == valid_guardfile_string
|
||||
end
|
||||
|
||||
it "should use the given Guardfile over default Guardfile" do
|
||||
@ -58,31 +57,31 @@ describe Guard::Dsl do
|
||||
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"
|
||||
lambda { subject.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
|
||||
subject.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"
|
||||
lambda { subject.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
|
||||
subject.guardfile_contents_with_user_config.should == "guard :foo\nguard :bar"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it "displays an error message when no Guardfile is found" do
|
||||
described_class.stub(:guardfile_default_path).and_return("no_guardfile_here")
|
||||
subject.stub(:guardfile_default_path).and_return("no_guardfile_here")
|
||||
Guard::UI.should_receive(:error).with("No Guardfile found, please create one with `guard init`.")
|
||||
lambda { described_class.evaluate_guardfile }.should raise_error
|
||||
lambda { subject.evaluate_guardfile }.should raise_error
|
||||
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)
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string)
|
||||
end
|
||||
|
||||
describe "correctly reads data from its valid data source" do
|
||||
@ -90,22 +89,22 @@ describe Guard::Dsl do
|
||||
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
|
||||
lambda { subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string) }.should_not raise_error
|
||||
subject.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"
|
||||
lambda { subject.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
|
||||
subject.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
|
||||
lambda { subject.evaluate_guardfile }.should_not raise_error
|
||||
subject.guardfile_contents.should == valid_guardfile_string
|
||||
end
|
||||
end
|
||||
|
||||
@ -117,14 +116,14 @@ describe Guard::Dsl do
|
||||
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
|
||||
lambda { subject.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
|
||||
lambda { subject.evaluate_guardfile(:guardfile => '/def/Guardfile') }.should raise_error
|
||||
end
|
||||
|
||||
it "raises error when resorting to use default, finds no default" do
|
||||
@ -132,24 +131,24 @@ describe Guard::Dsl do
|
||||
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
|
||||
lambda { subject.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
|
||||
lambda { subject.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
|
||||
lambda { subject.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
|
||||
lambda { subject.evaluate_guardfile(:guardfile_contents => invalid_guardfile_string ) }.should raise_error
|
||||
end
|
||||
|
||||
describe ".reevaluate_guardfile" do
|
||||
@ -157,10 +156,10 @@ describe Guard::Dsl do
|
||||
|
||||
it "resets already definded guards before calling evaluate_guardfile" do
|
||||
Guard::Notifier.turn_off
|
||||
described_class.evaluate_guardfile(:guardfile_contents => invalid_guardfile_string)
|
||||
subject.evaluate_guardfile(:guardfile_contents => invalid_guardfile_string)
|
||||
::Guard.guards.should_not be_empty
|
||||
::Guard::Dsl.should_receive(:evaluate_guardfile)
|
||||
described_class.reevaluate_guardfile
|
||||
subject.reevaluate_guardfile
|
||||
::Guard.guards.should be_empty
|
||||
end
|
||||
end
|
||||
@ -173,14 +172,14 @@ describe Guard::Dsl do
|
||||
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
|
||||
subject.guardfile_default_path.should == local_path
|
||||
end
|
||||
end
|
||||
|
||||
context "when there is a Guardfile in the user's home directory" do
|
||||
it "returns the path to the user Guardfile" do
|
||||
File.stub(:exist?).with(user_path).and_return(true)
|
||||
described_class.guardfile_default_path.should == user_path
|
||||
subject.guardfile_default_path.should == user_path
|
||||
end
|
||||
end
|
||||
|
||||
@ -188,90 +187,90 @@ describe Guard::Dsl 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
|
||||
subject.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")}')
|
||||
subject.stub(:guardfile_contents => 'guard "test" {watch("c")}')
|
||||
|
||||
described_class.guardfile_include?('test').should be_true
|
||||
subject.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")}')
|
||||
subject.stub(:guardfile_contents => 'guard \'test\' {watch("c")}')
|
||||
|
||||
described_class.guardfile_include?('test').should be_true
|
||||
subject.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")}')
|
||||
subject.stub(:guardfile_contents => 'guard :test {watch("c")}')
|
||||
|
||||
described_class.guardfile_include?('test').should be_true
|
||||
subject.guardfile_include?('test').should be_true
|
||||
end
|
||||
|
||||
it "detects a guard wrapped in parentheses" do
|
||||
described_class.stub(:guardfile_contents => 'guard(:test) {watch("c")}')
|
||||
subject.stub(:guardfile_contents => 'guard(:test) {watch("c")}')
|
||||
|
||||
described_class.guardfile_include?('test').should be_true
|
||||
subject.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'")
|
||||
|
||||
subject.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 })
|
||||
::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])
|
||||
subject.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 })
|
||||
::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])
|
||||
subject.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 })
|
||||
it "evaluates only the specified groups" 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])
|
||||
subject.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 })
|
||||
::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])
|
||||
subject.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 })
|
||||
it "evaluates all groups when no group option is specified" 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)
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string)
|
||||
end
|
||||
end
|
||||
|
||||
@ -279,33 +278,33 @@ describe Guard::Dsl 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 })
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :default })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => "guard 'test'")
|
||||
subject.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 })
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :default })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => 'guard "test"')
|
||||
subject.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 })
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :default })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => "guard :test")
|
||||
subject.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 })
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :default })
|
||||
|
||||
described_class.evaluate_guardfile(:guardfile_contents => "guard(:test)")
|
||||
subject.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 })
|
||||
::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'")
|
||||
subject.evaluate_guardfile(:guardfile_contents => "guard 'test', :opt_a => 1, :opt_b => 'fancy'")
|
||||
end
|
||||
end
|
||||
|
||||
@ -313,14 +312,14 @@ describe Guard::Dsl 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|
|
||||
::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 => "
|
||||
subject.evaluate_guardfile(:guardfile_contents => "
|
||||
guard :dummy do
|
||||
watch('a') { 'b' }
|
||||
watch('c')
|
||||
@ -336,14 +335,14 @@ describe Guard::Dsl do
|
||||
end
|
||||
end
|
||||
|
||||
::Guard.should_receive(:add_guard).with('dummy', anything, anything, { :group => :default }) do |name, watchers, callbacks, options|
|
||||
::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 => '
|
||||
subject.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])
|
||||
@ -362,17 +361,17 @@ private
|
||||
"
|
||||
guard :pow
|
||||
|
||||
group :w do
|
||||
guard :test
|
||||
group 'w' do
|
||||
guard 'test'
|
||||
end
|
||||
|
||||
group :x, :halt_on_fail => true do
|
||||
guard :rspec
|
||||
group :x do
|
||||
guard 'rspec'
|
||||
guard :ronn
|
||||
end
|
||||
|
||||
group :y do
|
||||
guard :less
|
||||
group 'y' do
|
||||
guard 'less'
|
||||
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 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Guard::Guard do
|
||||
|
||||
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
|
||||
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
|
||||
end
|
||||
end
|
@ -2,47 +2,48 @@ require 'spec_helper'
|
||||
require 'guard/guard'
|
||||
|
||||
describe Guard::Hook do
|
||||
subject { Guard::Hook }
|
||||
|
||||
class Guard::Dummy < Guard::Guard; end
|
||||
|
||||
let(:guard_class) { ::Guard::Dummy }
|
||||
let(:listener) { double('listener').as_null_object }
|
||||
|
||||
after { described_class.reset_callbacks! }
|
||||
after { subject.reset_callbacks! }
|
||||
|
||||
describe "--module methods--" do
|
||||
before { described_class.add_callback(listener, guard_class, :start_begin) }
|
||||
context "--module methods--" do
|
||||
before { subject.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
|
||||
subject.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
|
||||
subject.add_callback(listener, guard_class, [:event1, :event2])
|
||||
subject.has_callback?(listener, guard_class, :event1).should be_true
|
||||
subject.has_callback?(listener, guard_class, :event2).should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe ".notify" do
|
||||
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")
|
||||
subject.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)
|
||||
subject.add_callback(listener2, guard_class, :start_end)
|
||||
listener2.should_not_receive(:call).with(guard_class, :start_end)
|
||||
described_class.notify(guard_class, :start_begin)
|
||||
subject.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)
|
||||
subject.add_callback(listener, guard2_class, :start_begin)
|
||||
listener.should_not_receive(:call).with(guard2_class, :start_begin)
|
||||
described_class.notify(guard_class, :start_begin)
|
||||
subject.notify(guard_class, :start_begin)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -3,4 +3,24 @@ require 'spec_helper'
|
||||
describe Guard::Interactor do
|
||||
subject { Guard::Interactor.new }
|
||||
|
||||
describe "#initialize" do
|
||||
it "un-lock by default" do
|
||||
subject.locked.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe "#lock" do
|
||||
it "locks" do
|
||||
subject.lock
|
||||
subject.locked.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "#unlock" do
|
||||
it "unlocks" do
|
||||
subject.unlock
|
||||
subject.locked.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,291 +1,189 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Guard::Listener do
|
||||
subject { Guard::Listener }
|
||||
|
||||
describe '.select_and_init' 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
|
||||
it "uses the Darwin listener on Mac OS X" do
|
||||
RbConfig::CONFIG['target_os'] = 'darwin10.4.0'
|
||||
Guard::Darwin.stub(:usable?).and_return(true)
|
||||
Guard::Darwin.should_receive(:new)
|
||||
described_class.select_and_init
|
||||
subject.select_and_init
|
||||
end
|
||||
|
||||
it 'uses the Windows listener on Windows' do
|
||||
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
|
||||
subject.select_and_init
|
||||
end
|
||||
|
||||
it 'uses the Linux listener on Linux' do
|
||||
it "uses the Linux listener on Linux" do
|
||||
RbConfig::CONFIG['target_os'] = 'linux'
|
||||
Guard::Linux.stub(:usable?).and_return(true)
|
||||
Guard::Linux.should_receive(:new)
|
||||
described_class.select_and_init
|
||||
subject.select_and_init
|
||||
end
|
||||
|
||||
it 'forwards its arguments to the constructor' do
|
||||
described_class.stub!(:mac?).and_return(true)
|
||||
it "forwards its arguments to the constructor" do
|
||||
subject.stub!(:mac?).and_return(true)
|
||||
Guard::Darwin.stub!(:usable?).and_return(true)
|
||||
|
||||
path, opts = 'path', { :foo => 23 }
|
||||
Guard::Darwin.should_receive(:new).with(path, opts).and_return(true)
|
||||
described_class.select_and_init(path, opts)
|
||||
subject.select_and_init(path, opts)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#all_files' do
|
||||
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) }
|
||||
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
|
||||
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 )
|
||||
before :each do
|
||||
@paths = %w( /tmp/a /tmp/a/b /tmp/a.b/c.d )
|
||||
end
|
||||
|
||||
context 'when set to false' do
|
||||
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
|
||||
it "can be disabled" do
|
||||
subject.relativize_paths(@paths).should eql @paths
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update_last_event' do
|
||||
describe "#update_last_event" do
|
||||
subject { described_class.new }
|
||||
|
||||
it 'updates the last event to the current time' do
|
||||
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
|
||||
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') }
|
||||
let(:file1) { @fixture_path.join("folder1", "file1.txt") }
|
||||
let(:file2) { @fixture_path.join("folder1", "folder2", "file2.txt") }
|
||||
let(:file3) { @fixture_path.join("folder1", "deletedfile1.txt") }
|
||||
|
||||
before { listen_to subject }
|
||||
before do
|
||||
subject.update_last_event
|
||||
sleep 0.6
|
||||
end
|
||||
|
||||
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
|
||||
context "without the :all option" do
|
||||
it "finds modified files only in the directory supplied" do
|
||||
FileUtils.touch([file1, file2, file3])
|
||||
subject.modified_files([@fixture_path.join("folder1")], {}).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt"]
|
||||
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
|
||||
context "with the :all options" do
|
||||
it "finds modified files within subdirectories" do
|
||||
FileUtils.touch([file1, file2, file3])
|
||||
subject.modified_files([@fixture_path.join("folder1")], { :all => true }).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt", "spec/fixtures/folder1/folder2/file2.txt"]
|
||||
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
|
||||
context "without updating the content" do
|
||||
it "ignores the files for the second time" do
|
||||
FileUtils.touch([file1, file2, file3])
|
||||
subject.modified_files([@fixture_path.join("folder1")], {}).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt"]
|
||||
subject.update_last_event
|
||||
FileUtils.touch([file1, file2, file3])
|
||||
subject.modified_files([@fixture_path.join("folder1")], {}).should be_empty
|
||||
sleep 1
|
||||
end
|
||||
end
|
||||
|
||||
it 'should be true when set' do
|
||||
subject.instance_variable_get(:@watch_all_modifications).should eql true
|
||||
end
|
||||
context "with content that has changed" do
|
||||
after { File.open(file1, "w") { |f| f.write("") } }
|
||||
|
||||
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
|
||||
it "identifies the files for the second time" do
|
||||
FileUtils.touch([file1, file2, file3])
|
||||
subject.modified_files([@fixture_path.join("folder1")], {}).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt"]
|
||||
subject.update_last_event
|
||||
FileUtils.touch([file2, file3])
|
||||
File.open(file1, "w") { |f| f.write("changed content") }
|
||||
subject.modified_files([@fixture_path.join("folder1")], {}).should =~ ["spec/fixtures/folder1/file1.txt"]
|
||||
sleep 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'working directory' do
|
||||
context 'unspecified' do
|
||||
describe "working directory" do
|
||||
context "unspecified" do
|
||||
subject { described_class.new }
|
||||
|
||||
it 'defaults to Dir.pwd' do
|
||||
it "defaults to Dir.pwd" do
|
||||
subject.instance_variable_get(:@directory).should eql Dir.pwd
|
||||
end
|
||||
|
||||
it 'can be not changed' do
|
||||
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
|
||||
context "specified as first argument to ::new" do
|
||||
before :each do
|
||||
@wd = @fixture_path.join("folder1")
|
||||
end
|
||||
|
||||
it 'can be not changed' do
|
||||
subject { described_class.new @wd }
|
||||
it "can be inspected" do
|
||||
subject.instance_variable_get(:@directory).should eql @wd.to_s
|
||||
end
|
||||
it "can be not changed" do
|
||||
subject.should_not respond_to(:directory=)
|
||||
end
|
||||
|
||||
it 'will be used to watch' do
|
||||
subject.should_receive(:watch).with(working_directory.to_s)
|
||||
it "will be used to watch" do
|
||||
subject.should_receive(:watch).with(@wd.to_s)
|
||||
@listener = subject # indeed.
|
||||
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
|
||||
describe "#ignore_paths" do
|
||||
it "defaults to the default ignore paths" do
|
||||
subject.new.ignore_paths.should == Guard::Listener::DefaultIgnorePaths
|
||||
end
|
||||
|
||||
it 'can be added to via :ignore_paths option' do
|
||||
listener = described_class.new 'path', :ignore_paths => ['foo', 'bar']
|
||||
it "can be added to via :ignore_paths option" do
|
||||
listener = subject.new 'path', :ignore_paths => ['foo', 'bar']
|
||||
listener.ignore_paths.should include('foo', 'bar')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#exclude_ignored_paths [<dirs>]' do
|
||||
describe "#exclude_ignored_paths [<dirs>]" do
|
||||
let(:ignore_paths) { nil }
|
||||
subject { described_class.new(@fixture_path, { :ignore_paths => ignore_paths }) }
|
||||
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']
|
||||
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
|
||||
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']
|
||||
it "excludes the ignored paths" do
|
||||
subject.exclude_ignored_paths(["spec/fixtures"]).should =~ ["spec/fixtures/folder1"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -2,26 +2,26 @@ require 'spec_helper'
|
||||
require 'guard/listeners/darwin'
|
||||
|
||||
describe Guard::Darwin do
|
||||
subject { Guard::Darwin }
|
||||
|
||||
if windows?
|
||||
it "isn't usable on windows" do
|
||||
described_class.should_not be_usable
|
||||
subject.should_not be_usable
|
||||
end
|
||||
end
|
||||
|
||||
if linux?
|
||||
it "isn't usable on linux" do
|
||||
described_class.should_not be_usable
|
||||
subject.should_not be_usable
|
||||
end
|
||||
end
|
||||
|
||||
if mac? && Guard::Darwin.usable?
|
||||
it "is usable on 10.6" do
|
||||
described_class.should be_usable
|
||||
subject.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
|
||||
|
@ -3,22 +3,23 @@ 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
|
||||
subject.should_not be_usable
|
||||
end
|
||||
end
|
||||
|
||||
if windows?
|
||||
it "isn't usable on windows" do
|
||||
described_class.should_not be_usable
|
||||
subject.should_not be_usable
|
||||
end
|
||||
end
|
||||
|
||||
if linux? && Guard::Linux.usable?
|
||||
it "is usable on linux" do
|
||||
described_class.should be_usable
|
||||
subject.should be_usable
|
||||
end
|
||||
|
||||
describe "#start", :long_running => true do
|
||||
@ -71,6 +72,6 @@ describe Guard::Linux do
|
||||
stop
|
||||
File.open(file, 'w') {|f| f.write('') }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -2,8 +2,8 @@ require 'spec_helper'
|
||||
require 'guard/listeners/polling'
|
||||
|
||||
describe Guard::Polling do
|
||||
subject { Guard::Polling }
|
||||
|
||||
it_should_behave_like "a listener that reacts to #on_change"
|
||||
it_should_behave_like "a listener scoped to a specific directory"
|
||||
|
||||
end
|
||||
|
@ -2,26 +2,27 @@ require 'spec_helper'
|
||||
require 'guard/listeners/windows'
|
||||
|
||||
describe Guard::Windows do
|
||||
subject { Guard::Windows }
|
||||
|
||||
if linux?
|
||||
it "isn't usable on linux" do
|
||||
described_class.should_not be_usable
|
||||
subject.should_not be_usable
|
||||
end
|
||||
end
|
||||
|
||||
if mac?
|
||||
it "isn't usable on Mac" do
|
||||
described_class.should_not be_usable
|
||||
subject.should_not be_usable
|
||||
end
|
||||
end
|
||||
|
||||
if windows?
|
||||
it "is usable on Windows 2000 and later" do
|
||||
described_class.should be_usable
|
||||
subject.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
|
||||
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,11 +1,12 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Guard::Notifier do
|
||||
subject { Guard::Notifier }
|
||||
|
||||
describe ".turn_off" do
|
||||
before do
|
||||
ENV["GUARD_NOTIFY"] = 'true'
|
||||
described_class.turn_off
|
||||
subject.turn_off
|
||||
end
|
||||
|
||||
it "disables the notifications" do
|
||||
@ -21,27 +22,16 @@ describe Guard::Notifier do
|
||||
|
||||
context "with the GrowlNotify library available" do
|
||||
before do
|
||||
class ::GrowlNotify
|
||||
class GrowlNotFound < Exception; end
|
||||
module ::GrowlNotify
|
||||
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
|
||||
subject.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
|
||||
subject.turn_on
|
||||
subject.should be_enabled
|
||||
end
|
||||
|
||||
after do
|
||||
@ -49,46 +39,21 @@ describe Guard::Notifier do
|
||||
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
|
||||
subject.should_receive(:require).with('growl_notify').and_raise LoadError
|
||||
subject.should_receive(:require).with('growl').and_return true
|
||||
subject.turn_on
|
||||
subject.should be_enabled
|
||||
end
|
||||
end
|
||||
|
||||
context "without a Growl library available" do
|
||||
context "without the 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
|
||||
subject.should_receive(:require).with('growl_notify').and_raise LoadError
|
||||
subject.should_receive(:require).with('growl').and_raise LoadError
|
||||
subject.turn_on
|
||||
subject.should_not be_enabled
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -100,17 +65,17 @@ describe Guard::Notifier do
|
||||
|
||||
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
|
||||
subject.should_receive(:require).with('libnotify').and_return true
|
||||
subject.turn_on
|
||||
subject.should be_enabled
|
||||
end
|
||||
end
|
||||
|
||||
context "without the Libnotify library available" do
|
||||
it "disables the notifications" do
|
||||
described_class.should_receive(:require).with('libnotify').and_raise LoadError
|
||||
described_class.turn_on
|
||||
described_class.should_not be_enabled
|
||||
subject.should_receive(:require).with('libnotify').and_raise LoadError
|
||||
subject.turn_on
|
||||
subject.should_not be_enabled
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -122,36 +87,35 @@ describe Guard::Notifier do
|
||||
|
||||
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
|
||||
subject.should_receive(:require).with('rb-notifu').and_return true
|
||||
subject.turn_on
|
||||
subject.should be_enabled
|
||||
end
|
||||
end
|
||||
|
||||
context "without the rb-notify library available" do
|
||||
it "disables the notifications" do
|
||||
described_class.should_receive(:require).with('rb-notifu').and_raise LoadError
|
||||
described_class.turn_on
|
||||
described_class.should_not be_enabled
|
||||
subject.should_receive(:require).with('rb-notifu').and_raise LoadError
|
||||
subject.turn_on
|
||||
subject.should_not be_enabled
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".notify" do
|
||||
before { described_class.stub(:enabled?).and_return(true) }
|
||||
before { subject.stub(:enabled?).and_return(true) }
|
||||
|
||||
context "on Mac OS" do
|
||||
before do
|
||||
RbConfig::CONFIG.stub(:[]).and_return 'darwin'
|
||||
described_class.stub(:require_growl)
|
||||
RbConfig::CONFIG.should_receive(:[]).with('target_os').and_return 'darwin'
|
||||
subject.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
|
||||
@ -164,14 +128,13 @@ describe Guard::Notifier do
|
||||
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
||||
:name => "Guard"
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
subject.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "don't passes the notification to Growl if library is not available" do
|
||||
Growl.should_not_receive(:notify)
|
||||
described_class.growl_library = nil
|
||||
described_class.should_receive(:enabled?).and_return(false)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
subject.should_receive(:enabled?).and_return(true, false)
|
||||
subject.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "allows additional notification options" do
|
||||
@ -181,7 +144,7 @@ describe Guard::Notifier do
|
||||
:name => "Guard",
|
||||
:priority => 1
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :priority => 1
|
||||
subject.notify 'great', :title => 'Guard', :priority => 1
|
||||
end
|
||||
|
||||
it "allows to overwrite a default notification option" do
|
||||
@ -190,7 +153,7 @@ describe Guard::Notifier do
|
||||
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
||||
:name => "Guard-Cucumber"
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :name => "Guard-Cucumber"
|
||||
subject.notify 'great', :title => 'Guard', :name => "Guard-Cucumber"
|
||||
end
|
||||
end
|
||||
|
||||
@ -198,7 +161,6 @@ describe Guard::Notifier do
|
||||
before do
|
||||
Object.send(:remove_const, :GrowlNotify) if defined?(GrowlNotify)
|
||||
GrowlNotify = Object.new
|
||||
described_class.growl_library = :growl_notify
|
||||
end
|
||||
|
||||
after do
|
||||
@ -212,14 +174,13 @@ describe Guard::Notifier do
|
||||
:application_name => "Guard",
|
||||
:description => 'great'
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
subject.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'
|
||||
subject.should_receive(:enabled?).and_return(true, false)
|
||||
subject.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "allows additional notification options" do
|
||||
@ -230,7 +191,7 @@ describe Guard::Notifier do
|
||||
:description => 'great',
|
||||
:priority => 1
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :priority => 1
|
||||
subject.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
|
||||
@ -240,80 +201,15 @@ describe Guard::Notifier do
|
||||
: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
|
||||
subject.notify 'great', :title => 'Guard', :name => "Guard-Cucumber"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
context "on Linux" do
|
||||
before do
|
||||
RbConfig::CONFIG.stub(:[]).and_return 'linux'
|
||||
described_class.stub(:require_libnotify)
|
||||
RbConfig::CONFIG.should_receive(:[]).with('target_os').and_return 'linux'
|
||||
subject.stub(:require_libnotify)
|
||||
Object.send(:remove_const, :Libnotify) if defined?(Libnotify)
|
||||
Libnotify = Object.new
|
||||
end
|
||||
@ -329,13 +225,13 @@ describe Guard::Notifier do
|
||||
:icon_path => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
||||
:transient => true
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
subject.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'
|
||||
subject.should_receive(:enabled?).and_return(true, false)
|
||||
subject.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "allows additional notification options" do
|
||||
@ -346,7 +242,7 @@ describe Guard::Notifier do
|
||||
:transient => true,
|
||||
:urgency => :critical
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :urgency => :critical
|
||||
subject.notify 'great', :title => 'Guard', :urgency => :critical
|
||||
end
|
||||
|
||||
it "allows to overwrite a default notification option" do
|
||||
@ -356,14 +252,14 @@ describe Guard::Notifier do
|
||||
:icon_path => '~/.guard/success.png',
|
||||
:transient => true
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :icon_path => '~/.guard/success.png'
|
||||
subject.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)
|
||||
RbConfig::CONFIG.should_receive(:[]).with('target_os').and_return 'mswin'
|
||||
subject.stub(:require_rbnotifu)
|
||||
Object.send(:remove_const, :Notifu) if defined?(Notifu)
|
||||
Notifu = Object.new
|
||||
end
|
||||
@ -379,13 +275,13 @@ describe Guard::Notifier do
|
||||
:type => :info,
|
||||
:time => 3
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard'
|
||||
subject.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'
|
||||
subject.should_receive(:enabled?).and_return(true, false)
|
||||
subject.notify 'great', :title => 'Guard'
|
||||
end
|
||||
|
||||
it "allows additional notification options" do
|
||||
@ -396,7 +292,7 @@ describe Guard::Notifier do
|
||||
:time => 3,
|
||||
:nosound => true
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :nosound => true
|
||||
subject.notify 'great', :title => 'Guard', :nosound => true
|
||||
end
|
||||
|
||||
it "allows to overwrite a default notification option" do
|
||||
@ -406,7 +302,7 @@ describe Guard::Notifier do
|
||||
:type => :info,
|
||||
:time => 10
|
||||
)
|
||||
described_class.notify 'great', :title => 'Guard', :time => 10
|
||||
subject.notify 'great', :title => 'Guard', :time => 10
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -424,5 +320,4 @@ describe Guard::Notifier do
|
||||
it { should_not be_enabled }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -5,19 +5,19 @@ describe Guard::Watcher do
|
||||
|
||||
describe "#initialize" do
|
||||
it "requires a pattern parameter" do
|
||||
expect { described_class.new }.to raise_error(ArgumentError)
|
||||
expect { Guard::Watcher.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'
|
||||
Guard::Watcher.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/
|
||||
Guard::Watcher.new(/spec_helper\.rb/).pattern.should == /spec_helper\.rb/
|
||||
end
|
||||
end
|
||||
|
||||
@ -25,10 +25,10 @@ describe Guard::Watcher 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/
|
||||
Guard::Watcher.new('^spec_helper.rb').pattern.should == /^spec_helper.rb/
|
||||
Guard::Watcher.new('spec_helper.rb$').pattern.should == /spec_helper.rb$/
|
||||
Guard::Watcher.new('spec_helper\.rb').pattern.should == /spec_helper\.rb/
|
||||
Guard::Watcher.new('.*_spec.rb').pattern.should == /.*_spec.rb/
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -36,228 +36,144 @@ describe Guard::Watcher do
|
||||
|
||||
describe "#action" do
|
||||
it "sets the action to nothing by default" do
|
||||
described_class.new(/spec_helper\.rb/).action.should be_nil
|
||||
Guard::Watcher.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
|
||||
Guard::Watcher.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
|
||||
before(:all) { @guard = Guard::Guard.new }
|
||||
|
||||
context "with a watcher without action" do
|
||||
context "that is a regex pattern" do
|
||||
before(:all) { @guard.watchers = [described_class.new(/.*_spec\.rb/)] }
|
||||
before(:all) { @guard.watchers = [Guard::Watcher.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']
|
||||
Guard::Watcher.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')] }
|
||||
before(:all) { @guard.watchers = [Guard::Watcher.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']
|
||||
Guard::Watcher.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
|
||||
before(:all) do
|
||||
@guard.watchers = [
|
||||
Guard::Watcher.new('spec_helper.rb', lambda { 'spec' }),
|
||||
Guard::Watcher.new('addition.rb', lambda { 1 + 1 }),
|
||||
Guard::Watcher.new('hash.rb', lambda { Hash[:foo, 'bar'] }),
|
||||
Guard::Watcher.new('array.rb', lambda { ['foo', 'bar'] }),
|
||||
Guard::Watcher.new('blank.rb', lambda { '' }),
|
||||
Guard::Watcher.new(/^uptime\.rb/, lambda { `uptime > /dev/null` })
|
||||
]
|
||||
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
|
||||
Guard::Watcher.match_files(@guard, ['spec_helper.rb']).should == ['spec']
|
||||
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
|
||||
Guard::Watcher.match_files(@guard, ['hash.rb']).should == ['foo', 'bar']
|
||||
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
|
||||
Guard::Watcher.match_files(@guard, ['spec_helper.rb', 'array.rb']).should == ['spec', '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 nothing if the action returns something other than a string or an array of strings" do
|
||||
Guard::Watcher.match_files(@guard, ['addition.rb']).should == []
|
||||
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" do
|
||||
Guard::Watcher.match_files(@guard, ['blank.rb']).should == []
|
||||
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
|
||||
it "returns nothing if the action returns nothing" do
|
||||
Guard::Watcher.match_files(@guard, ['uptime.rb']).should == []
|
||||
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
|
||||
before(:all) do
|
||||
@guard.watchers = [
|
||||
Guard::Watcher.new(%r{lib/(.*)\.rb}, lambda { |m| "spec/#{m[1]}_spec.rb" }),
|
||||
Guard::Watcher.new(/addition(.*)\.rb/, lambda { |m| 1 + 1 }),
|
||||
Guard::Watcher.new('hash.rb', lambda { Hash[:foo, 'bar'] }),
|
||||
Guard::Watcher.new(/array(.*)\.rb/, lambda { |m| ['foo', 'bar'] }),
|
||||
Guard::Watcher.new(/blank(.*)\.rb/, lambda { |m| '' }),
|
||||
Guard::Watcher.new(/uptime(.*)\.rb/, lambda { |m| `uptime > /dev/null` })
|
||||
]
|
||||
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
|
||||
Guard::Watcher.match_files(@guard, ['lib/my_wonderful_lib.rb']).should == ['spec/my_wonderful_lib_spec.rb']
|
||||
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 multiple files specified within the action" do
|
||||
Guard::Watcher.match_files(@guard, ['hash.rb']).should == ['foo', 'bar']
|
||||
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
|
||||
Guard::Watcher.match_files(@guard, ['lib/my_wonderful_lib.rb', 'array.rb']).should == ['spec/my_wonderful_lib_spec.rb', 'foo', 'bar']
|
||||
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 nothing if the action returns something other than a string or an array of strings" do
|
||||
Guard::Watcher.match_files(@guard, ['addition.rb']).should == []
|
||||
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" do
|
||||
Guard::Watcher.match_files(@guard, ['blank.rb']).should == []
|
||||
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
|
||||
it "returns nothing if the action returns nothing" do
|
||||
Guard::Watcher.match_files(@guard, ['uptime.rb']).should == []
|
||||
end
|
||||
end
|
||||
|
||||
context "with an exception that is raised" do
|
||||
before(:all) { @guard.watchers = [described_class.new('evil.rb', lambda { raise "EVIL" })] }
|
||||
before(:all) { @guard.watchers = [Guard::Watcher.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
|
||||
it "displays the error and backtrace" do
|
||||
Guard::UI.should_receive(:error) { |msg|
|
||||
msg.should include("Problem with watch action!")
|
||||
msg.should include("EVIL")
|
||||
}
|
||||
|
||||
described_class.match_files(@guard, ['evil.rb'])
|
||||
end
|
||||
end
|
||||
end
|
||||
Guard::Watcher.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')])
|
||||
@guard1 = Guard::Guard.new([Guard::Watcher.new(/.*_spec\.rb/)])
|
||||
@guard2 = Guard::Guard.new([Guard::Watcher.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 }
|
||||
specify { Guard::Watcher.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 }
|
||||
specify { Guard::Watcher.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') }
|
||||
subject { Guard::Watcher.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 }
|
||||
@ -269,7 +185,7 @@ describe Guard::Watcher do
|
||||
end
|
||||
|
||||
context "that is a string representing a regexp (deprecated)" do
|
||||
subject { described_class.new('^guard_rocks_spec\.rb$') }
|
||||
subject { Guard::Watcher.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 }
|
||||
@ -282,7 +198,7 @@ describe Guard::Watcher do
|
||||
end
|
||||
|
||||
context "that is a regexp pattern" do
|
||||
subject { described_class.new(/.*_spec\.rb/) }
|
||||
subject { Guard::Watcher.new(/.*_spec\.rb/) }
|
||||
|
||||
context "with a watcher that matches a file" do
|
||||
specify { subject.match_file?('guard_rocks_spec.rb').should be_true }
|
||||
@ -298,11 +214,11 @@ describe Guard::Watcher 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 }
|
||||
specify { Guard::Watcher.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 }
|
||||
specify { Guard::Watcher.match_guardfile?(['guard_rocks.rb', 'guard_rocks_spec.rb']).should be_false }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -3,45 +3,6 @@ require 'guard/guard'
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
describe ".setup" do
|
||||
subject { ::Guard.setup }
|
||||
|
||||
@ -54,8 +15,7 @@ describe Guard do
|
||||
end
|
||||
|
||||
it "initializes @groups" do
|
||||
described_class.groups[0].name.should eql :default
|
||||
described_class.groups[0].options.should == {}
|
||||
Guard.groups.should eql [:default]
|
||||
end
|
||||
|
||||
it "initializes the options" do
|
||||
@ -93,129 +53,6 @@ describe Guard 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
|
||||
|
||||
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
|
||||
@ -292,28 +129,22 @@ describe Guard do
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe ".add_group" do
|
||||
subject { ::Guard.setup }
|
||||
before(:each) do
|
||||
Guard.setup
|
||||
end
|
||||
|
||||
it "accepts group name as string" do
|
||||
subject.add_group('backend')
|
||||
Guard.add_group('backend')
|
||||
|
||||
subject.groups[0].name.should eql :default
|
||||
subject.groups[1].name.should eql :backend
|
||||
Guard.groups.should eql [:default, :backend]
|
||||
end
|
||||
|
||||
it "accepts group name as symbol" do
|
||||
subject.add_group(:backend)
|
||||
Guard.add_group(:backend)
|
||||
|
||||
subject.groups[0].name.should eql :default
|
||||
subject.groups[1].name.should eql :backend
|
||||
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 }
|
||||
Guard.groups.should eql [:default, :backend]
|
||||
end
|
||||
end
|
||||
|
||||
@ -392,102 +223,12 @@ describe Guard do
|
||||
end
|
||||
end
|
||||
|
||||
describe ".run_guard_task" do
|
||||
describe ".supervised_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
|
||||
before(:each) 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
|
||||
@ -497,22 +238,22 @@ describe Guard do
|
||||
end
|
||||
|
||||
it "doesn't fire the Guard" do
|
||||
lambda { subject.run_supervised_task(@g, :regular_without_arg) }.should_not change(subject.guards, :size)
|
||||
lambda { subject.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
|
||||
::Guard.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")
|
||||
::Guard.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")
|
||||
::Guard.supervised_task(@g, :regular_without_arg, "given_path")
|
||||
end
|
||||
end
|
||||
|
||||
@ -522,91 +263,37 @@ describe Guard do
|
||||
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)
|
||||
lambda { subject.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"
|
||||
::Guard.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)
|
||||
::Guard.supervised_task(@g, :failing)
|
||||
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" } }
|
||||
before(:each) { @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)
|
||||
lambda { subject.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 = ::Guard.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 }
|
||||
|
||||
|
@ -12,7 +12,6 @@ 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
|
||||
|
@ -1,225 +1,139 @@
|
||||
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)
|
||||
sleep(@rest_delay || 1)
|
||||
@listener.update_last_event
|
||||
Thread.new { @listener.start }
|
||||
sleep(sleep_time)
|
||||
sleep(@rest_delay || 1)
|
||||
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 = []
|
||||
noise = %r|\.sw.$| # don't fail specs due to editor swap files, etc.
|
||||
|
||||
@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 stop
|
||||
sleep(@rest_delay || 1)
|
||||
@listener.stop
|
||||
sleep(@rest_delay || 1)
|
||||
end
|
||||
|
||||
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)
|
||||
shared_examples_for 'a listener that reacts to #on_change' do |rest_delay|
|
||||
before(:each) do
|
||||
@rest_delay = rest_delay if rest_delay.is_a?(Integer) || rest_delay.is_a?(Float) # jruby workaround
|
||||
@listener = described_class.new
|
||||
record_results
|
||||
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
|
||||
it "catches a new file" do
|
||||
file = @fixture_path.join("newfile.rb")
|
||||
if File.exists?(file)
|
||||
begin
|
||||
File.delete file
|
||||
rescue
|
||||
end
|
||||
|
||||
results.should =~ []
|
||||
end
|
||||
File.exists?(file).should be_false
|
||||
start
|
||||
FileUtils.touch file
|
||||
stop
|
||||
begin
|
||||
File.delete file
|
||||
rescue
|
||||
end
|
||||
results.should =~ ['spec/fixtures/newfile.rb']
|
||||
end
|
||||
|
||||
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
|
||||
it "catches a single file update" do
|
||||
file = @fixture_path.join("folder1/file1.txt")
|
||||
File.exists?(file).should be_true
|
||||
start
|
||||
File.open(file, 'w') { |f| f.write('') }
|
||||
stop
|
||||
results.should =~ ['spec/fixtures/folder1/file1.txt']
|
||||
end
|
||||
|
||||
it "not catches a single file chmod update" do
|
||||
file = @fixture_path.join("folder1/file1.txt")
|
||||
File.exists?(file).should be_true
|
||||
start
|
||||
File.chmod(0777, file)
|
||||
stop
|
||||
results.should =~ []
|
||||
end
|
||||
|
||||
it "catches a dotfile update" do
|
||||
file = @fixture_path.join(".dotfile")
|
||||
File.exists?(file).should be_true
|
||||
start
|
||||
File.open(file, 'w') { |f| f.write('') }
|
||||
stop
|
||||
results.should =~ ['spec/fixtures/.dotfile']
|
||||
end
|
||||
|
||||
it "catches multiple file updates" do
|
||||
file1 = @fixture_path.join("folder1/file1.txt")
|
||||
file2 = @fixture_path.join("folder1/folder2/file2.txt")
|
||||
File.exists?(file1).should be_true
|
||||
File.exists?(file2).should be_true
|
||||
start
|
||||
File.open(file1, 'w') { |f| f.write('') }
|
||||
File.open(file2, 'w') { |f| f.write('') }
|
||||
stop
|
||||
results.should =~ ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
||||
end
|
||||
|
||||
it "not catches a deleted file" do
|
||||
file = @fixture_path.join("folder1/file1.txt")
|
||||
File.exists?(file).should be_true
|
||||
start
|
||||
File.delete file
|
||||
stop
|
||||
FileUtils.touch file
|
||||
results.should =~ []
|
||||
end
|
||||
|
||||
it "not catches a moved file" do
|
||||
file1 = @fixture_path.join("folder1/file1.txt")
|
||||
file2 = @fixture_path.join("folder1/movedfile1.txt")
|
||||
File.exists?(file1).should be_true
|
||||
File.exists?(file2).should be_false
|
||||
start
|
||||
FileUtils.mv file1, file2
|
||||
stop
|
||||
FileUtils.mv file2, file1
|
||||
results.should =~ []
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
shared_examples_for "a listener scoped to a specific directory" do
|
||||
shared_examples_for "a listener scoped to a specific directory" do |rest_delay|
|
||||
before :each do
|
||||
@rest_delay = rest_delay if rest_delay.is_a?(Integer) || rest_delay.is_a?(Float) # jruby workaround
|
||||
@wd = @fixture_path.join("folder1")
|
||||
@listener = described_class.new @wd
|
||||
end
|
||||
|
||||
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
|
||||
it "should base paths within this directory" do
|
||||
record_results
|
||||
new_file = @wd.join("folder2/newfile.rb")
|
||||
modified = @wd.join("file1.txt")
|
||||
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']
|
||||
start
|
||||
FileUtils.touch new_file
|
||||
File.open(modified, 'w') { |f| f.write('') }
|
||||
stop
|
||||
File.delete new_file
|
||||
results.should =~ ["folder2/newfile.rb", 'file1.txt']
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user