Compare commits
3 Commits
master
...
guard_opti
Author | SHA1 | Date | |
---|---|---|---|
|
56d4914ce1 | ||
|
c52139b396 | ||
|
973c6ea1b4 |
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,13 +1,6 @@
|
|||||||
pkg/*
|
pkg/*
|
||||||
doc/*
|
|
||||||
*.gem
|
*.gem
|
||||||
*.rbc
|
|
||||||
.*.swp
|
|
||||||
*.bak
|
|
||||||
.bundle
|
.bundle
|
||||||
.yardoc
|
|
||||||
.rbx
|
|
||||||
.rvmrc
|
|
||||||
Gemfile.lock
|
Gemfile.lock
|
||||||
|
|
||||||
## MAC OS
|
## MAC OS
|
||||||
@ -16,4 +9,4 @@ Gemfile.lock
|
|||||||
.com.apple.timemachine.supported
|
.com.apple.timemachine.supported
|
||||||
.fseventsd
|
.fseventsd
|
||||||
Desktop DB
|
Desktop DB
|
||||||
Desktop DF
|
Desktop DF
|
19
.travis.yml
19
.travis.yml
@ -1,19 +0,0 @@
|
|||||||
rvm:
|
|
||||||
- 1.8.7
|
|
||||||
- 1.9.2
|
|
||||||
- ree
|
|
||||||
- jruby
|
|
||||||
- rbx
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- guard_dependencies
|
|
||||||
env:
|
|
||||||
- GUARD_SLEEP=1
|
|
||||||
notifications:
|
|
||||||
recipients:
|
|
||||||
- thibaud@thibaud.me
|
|
||||||
- rymai@rymai.me
|
|
||||||
- michi@netzpiraten.ch
|
|
||||||
- yann.lugrin@sans-savoir.net
|
|
||||||
irc: "irc.freenode.org#guard"
|
|
11
.yardopts
11
.yardopts
@ -1,11 +0,0 @@
|
|||||||
--title 'Guard Documentation'
|
|
||||||
--readme README.md
|
|
||||||
--markup markdown
|
|
||||||
--markup-provider kramdown
|
|
||||||
--private
|
|
||||||
--protected
|
|
||||||
--output-dir ./doc
|
|
||||||
lib/**/*.rb
|
|
||||||
-
|
|
||||||
CHANGELOG.md
|
|
||||||
LICENSE
|
|
347
CHANGELOG.md
347
CHANGELOG.md
@ -1,347 +0,0 @@
|
|||||||
## Master
|
|
||||||
|
|
||||||
### Improvements
|
|
||||||
|
|
||||||
- Add cli option (-i / --no-interactions) to turn off Guard terminal interactions. ([@thibaudgg][])
|
|
||||||
- Add support for Growl Notification Transport Protocol. ([@netzpirat][])
|
|
||||||
- [#157](https://github.com/guard/guard/pull/157): Allow any return from the Guard watchers. ([@earlonrails][])
|
|
||||||
- [#156](https://github.com/guard/guard/pull/156): Log error and diagnostic messages to STDERR. ([@sunaku][])
|
|
||||||
- [#152](https://github.com/guard/guard/pull/152): Growl Notify API update for a graceful fail. ([@scottdavis][])
|
|
||||||
|
|
||||||
### Bug fix
|
|
||||||
|
|
||||||
- [#149](https://github.com/guard/guard/issues/160): Avoid `Guard is not missing constant ...` exceptions. (reported by [@earlonrails][], fixed by [@netzpirat][])
|
|
||||||
|
|
||||||
## 0.8.4 - October 3, 2011
|
|
||||||
|
|
||||||
### Bug fix
|
|
||||||
|
|
||||||
- [#149](https://github.com/guard/guard/issues/149) & [#150](https://github.com/guard/guard/pull/150): Fix issue where interator thread was continuing to capture input from stdin while a guard is being executed. (reported by [@hardipe][], fixed by [@f1sherman][])
|
|
||||||
|
|
||||||
## 0.8.3 - October 1, 2011
|
|
||||||
|
|
||||||
### Bug fix
|
|
||||||
|
|
||||||
- [#145](https://github.com/guard/guard/pull/145): Fix over-utilization of CPU in Interactor. ([@johnbintz][])
|
|
||||||
|
|
||||||
### Improvements
|
|
||||||
|
|
||||||
- [#146](https://github.com/guard/guard/pull/146): Use a mutex instead of a lock for more efficient/simple locking. ([@f1sherman][])
|
|
||||||
- Make Guard implementation of `:task_has_failed` simple. ([@netzpirat][])
|
|
||||||
|
|
||||||
## 0.8.2 - September 30, 2011
|
|
||||||
|
|
||||||
### Bug fix
|
|
||||||
|
|
||||||
- Fixed guard stop to prevent run_guard_task(:stop) to be skipped [guard-spork issue #28](https://github.com/guard/guard-spork/issues/28). ([@thibaudgg][])
|
|
||||||
|
|
||||||
### Improvement
|
|
||||||
|
|
||||||
- Update docs regarding :task_has_failed. ([@netzpirat][])
|
|
||||||
|
|
||||||
## 0.8.1 - September 29, 2011
|
|
||||||
|
|
||||||
### Bug fix
|
|
||||||
|
|
||||||
- [#144](https://github.com/guard/guard/pull/144): Fix `guard init`. (reported by [@fabioyamate][], fixed by [@rymai][])
|
|
||||||
|
|
||||||
## 0.8.0 - September 28, 2011
|
|
||||||
|
|
||||||
### Bug fixes
|
|
||||||
|
|
||||||
- [#137](https://github.com/guard/guard/pull/137): Fix interacting with tools like ruby-debug. ([@hron][] & [@netzpirat][])
|
|
||||||
- [#138](https://github.com/guard/guard/pull/138): Fixed comments in example scaffold to reference interactions. ([@rmm5t][] & [@netzpirat][])
|
|
||||||
|
|
||||||
### New feature
|
|
||||||
|
|
||||||
- [#136](https://github.com/guard/guard/pull/136): New CLI `:watch_all_modifications`/`-A` option to watch for deleted and moved files too. ([@limeyd][] & [@netzpirat][])
|
|
||||||
- [#97](https://github.com/guard/guard/issues/97): Guard dependencies. Task execution can now be halted if a Guard throws `:task_has_failed` and `Guard::Dsl#group` options include `:halt_on_fail => true`. ([@rymai][])
|
|
||||||
- [#121](https://github.com/guard/guard/issues/121): `Guard.guards` and `Guard.groups` are now smart accessors. Filters can be passed to find a specific Guard/group or several Guards/groups that match (see YARDoc). ([@rymai][] & [@ches][])
|
|
||||||
- New `Guard::Group` class to store groups defined in Guardfile (with `Guard::Dsl#group`). ([@rymai][])
|
|
||||||
|
|
||||||
### Improvements
|
|
||||||
|
|
||||||
- Specs refactoring. ([@netzpirat][])
|
|
||||||
- Full YARD documentation. ([@netzpirat][] & a little of [@rymai][])
|
|
||||||
|
|
||||||
## 0.7.0 - September 14, 2011
|
|
||||||
|
|
||||||
## 0.7.0.rc1 - September 5, 2011
|
|
||||||
|
|
||||||
### Major Changes
|
|
||||||
|
|
||||||
- Posix Signals handlers (`Ctrl-C`, `Ctrl-\` and `Ctrl-Z`) are no more supported and replaced by `$stdin.gets`. Please refer to the "Interactions" section in the README for more information. ([@thibaudgg][])
|
|
||||||
- JRuby & Rubinius support (beta). ([@thibaudgg][] & [@netzpirat][])
|
|
||||||
|
|
||||||
### New features
|
|
||||||
|
|
||||||
- [#42](https://github.com/guard/guard/pull/42): New DSL method: `callback` allows you to execute arbitrary code before or after any of the `start`, `stop`, `reload`, `run_all` and `run_on_change` guards' method. New [Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for documenting it. ([@monocle][] & [@rymai][])
|
|
||||||
- Ability to 'pause' files modification listening. Please refer to the "Interactions" section in the README for more information. ([@thibaudgg][])
|
|
||||||
|
|
||||||
### Improvement
|
|
||||||
|
|
||||||
- Remove the need to scan the whole directory after guard's `run_on_change` method. ([@thibaudgg][])
|
|
||||||
|
|
||||||
## 0.6.3 - September 1, 2011
|
|
||||||
|
|
||||||
### New features
|
|
||||||
|
|
||||||
- [#130](https://github.com/guard/guard/pull/130): Adds `ignore_paths` method to DSL. ([@ianwhite][])
|
|
||||||
- [#128](https://github.com/guard/guard/pull/128): Users can add additional settings to `~/.guard.rb` that augment the existing Guardfile. ([@tpope][])
|
|
||||||
|
|
||||||
## 0.6.2 - August 17, 2011
|
|
||||||
|
|
||||||
### Bug fixes
|
|
||||||
|
|
||||||
- Re-add the possibility to use the `growl` gem since the `growl_notify` gem this is currently known to not work in conjunction with Spork. ([@netzpirat][])
|
|
||||||
- Ensure that scoped groups and group name are symbolized before checking for inclusion. ([@rymai][])
|
|
||||||
|
|
||||||
### New features
|
|
||||||
|
|
||||||
- Groups are now stored in a `@groups` variable (will be used for future features). ([@rymai][])
|
|
||||||
- Guards will now receive their group in the options hash at initialization (will be used for future features). ([@rymai][])
|
|
||||||
|
|
||||||
### Improvement
|
|
||||||
|
|
||||||
- Explain the growl/growl_notify differences in the README. ([@netzpirat][])
|
|
||||||
|
|
||||||
## 0.6.1 - August 15, 2011
|
|
||||||
|
|
||||||
### Bug fixes
|
|
||||||
|
|
||||||
- [#120](https://github.com/guard/guard/pull/120): remove `guardfile_contents` when re-evaluating so that the Guardfile gets reloaded correctly. ([@mordaroso][])
|
|
||||||
- [#119](https://github.com/guard/guard/pull/119): `Dsl.evaluate_guardfile` uses all groups if none specified. ([@ches][])
|
|
||||||
|
|
||||||
## 0.6.0 - August 13, 2011
|
|
||||||
|
|
||||||
### Bug fixes
|
|
||||||
|
|
||||||
- Pull request [#107](https://github.com/guard/guard/pull/107): Small spelling fix. ([@dnagir][])
|
|
||||||
- `Dir.glob` now ignores files that don't need to be watched. ([@rymai][])
|
|
||||||
|
|
||||||
### New feature
|
|
||||||
|
|
||||||
- Pull request [#112](https://github.com/guard/guard/pull/112): Add `list` command to CLI. ([@docwhat][])
|
|
||||||
|
|
||||||
### Improvements
|
|
||||||
|
|
||||||
- [#99](https://github.com/guard/guard/pull/99): [OS X] Switch from growl gem to growl_notify gem. ([@johnbintz][])
|
|
||||||
- [#115](https://github.com/guard/guard/pull/115): [Linux] Add `:transient => true` to default libnotify options. ([@zonque][])
|
|
||||||
- [#95](https://github.com/guard/guard/pull/95): Output system commands and options to be executed when in debug mode. ([@uk-ar][] and [@netzpirat][])
|
|
||||||
- `Guard::Dsl.revaluate_guardfile` has been renamed to `Guard::Dsl.reevaluate_guardfile`. ([@rymai][])
|
|
||||||
- New CLI options: ([@nestegg][])
|
|
||||||
- `watchdir`/`-w` to specify the directory in which Guard should watch for changes,
|
|
||||||
- `guardfile`/`-G` to specify an alternate location for the Guardfile to use.
|
|
||||||
- [#90](https://github.com/guard/guard/pull/90): Refactoring of color handling in the `Guard::UI`. ([@stereobooster][])
|
|
||||||
|
|
||||||
## 0.5.1 - July 2, 2011
|
|
||||||
|
|
||||||
### Bug fix
|
|
||||||
|
|
||||||
- Fixed `guard show` command. ([@bronson][] & [@thibaudgg][])
|
|
||||||
|
|
||||||
## 0.5.0 - July 2, 2011
|
|
||||||
|
|
||||||
### New features
|
|
||||||
|
|
||||||
- Guard::Ego is now part of Guard, so Guardfile is automagically re-evaluated when modified. ([@thibaudgg][])
|
|
||||||
- [#91](https://github.com/guard/guard/pull/91): Show Guards in Guardfile with the `guard -T`. ([@johnbintz][])
|
|
||||||
|
|
||||||
### Improvements
|
|
||||||
|
|
||||||
- [#98](https://github.com/guard/guard/issues/98): Multiple calls per watch event on linux with rb-inotify. ([@jeffutter][] & [@netzpirat][])
|
|
||||||
- [#94](https://github.com/guard/guard/pull/94): Show backtrace in terminal when a problem with a watch action occurs. ([@capotej][])
|
|
||||||
- [#88](https://github.com/guard/guard/pull/88): Write exception trace in the terminal when a supervised task fail. ([@mcmire][])
|
|
||||||
- Color in red the "ERROR:" flag when using `UI.error`. ([@rymai][])
|
|
||||||
- [#79](https://github.com/guard/guard/issues/79) and Pull request [#82](https://github.com/guard/guard/pull/82): Improve INotify support on Linux. ([@Gazer][] & [@yannlugrin][])
|
|
||||||
- [#12](https://github.com/guard/guard/issues/12) and Pull request [#86](https://github.com/guard/guard/pull/86): Eventually exits with SystemStackError. ([@stereobooster][])
|
|
||||||
- [#84](https://github.com/guard/guard/pull/84): Use RbConfig instead of obsolete and deprecated Config. ([@etehtsea][])
|
|
||||||
- [#80](https://github.com/guard/guard/pull/80): Watching dotfile (hidden files under unix). (reported by [@chrisberkhout][], fixed by [@yannlugrin][])
|
|
||||||
- Clear the terminal on start when the `:clear` option is given. ([@rymai][])
|
|
||||||
- Rename home directory Guardfile to `.Guardfile`. ([@tpope][])
|
|
||||||
|
|
||||||
## 0.4.2 - June 7, 2011
|
|
||||||
|
|
||||||
### Bug fixes
|
|
||||||
|
|
||||||
- Fixed Guard::Version in ruby 1.8.7 ([@thibaudgg][])
|
|
||||||
- Fix ([@mislav][]) link in CHANGELOG (Note: this is a recursive CHANGELOG item). ([@fnichol][])
|
|
||||||
|
|
||||||
## 0.4.1 - June 7, 2011
|
|
||||||
|
|
||||||
### Improvements
|
|
||||||
|
|
||||||
- [#77](https://github.com/guard/guard/pull/77): Refactor `get_guard_class` to first try the constant and fallback to require + various tweaks. ([@mislav][])
|
|
||||||
- Notifier improvement, don't use system notification library if could not be required. ([@yannlugrin][])
|
|
||||||
|
|
||||||
## 0.4.0 - June 5, 2011
|
|
||||||
|
|
||||||
### Bug fix
|
|
||||||
|
|
||||||
- In Ruby < 1.9, `Symbol#downcase` doesn't exist! ([@rymai][])
|
|
||||||
|
|
||||||
### New features
|
|
||||||
|
|
||||||
- [#73](https://github.com/guard/guard/pull/73): Allow DSL's `group` method to accept a Symbol as group name. ([@johnbintz][])
|
|
||||||
- [#51](https://github.com/guard/guard/pull/51): Allow options (like `:priority`) to be passed through to the Notifier. ([@indirect][] & [@netzpirat][])
|
|
||||||
|
|
||||||
### Improvement
|
|
||||||
|
|
||||||
- [#74](https://github.com/guard/guard/pull/74): Added link definitions to make the CHANGELOG more DRY! That's for sure now, we have the cleanest CHANGELOG ever! (even the link definitions are sorted alphabetically!) ([@pcreux][])
|
|
||||||
|
|
||||||
## 0.4.0.rc - May 28, 2011
|
|
||||||
|
|
||||||
### Bug fixes
|
|
||||||
|
|
||||||
- [#69](https://github.com/guard/guard/pull/69): Fixed typo in README: `Ctr-/` => `Ctr-\`. ([@tinogomes][])
|
|
||||||
- [#66](https://github.com/guard/guard/pull/66): Support for dashes in guard names. ([@johnbintz][])
|
|
||||||
- Require `guard/ui` because `Guard::Notifier` can be required without full Guard. ([@yannlugrin][])
|
|
||||||
- Handled quick file (<1s) modification. Avoid to catch modified files without content modification (sha1 checksum). ([@thibaudgg][] & [@netzpirat][])
|
|
||||||
- Fixed `Guard::Notifier` (when growl/libnotify not present). ([@thibaudgg][])
|
|
||||||
- Fixed Rubygems deprecation messages. ([@thibaudgg][])
|
|
||||||
|
|
||||||
### New features
|
|
||||||
|
|
||||||
- [#67](https://github.com/guard/guard/pull/67): Allow Guardfile in `$HOME` folder. ([@hashrocketeer][])
|
|
||||||
- [#64](https://github.com/guard/guard/pull/64): Windows notifications support. ([@stereobooster][])
|
|
||||||
- [#63](https://github.com/guard/guard/pull/63): Refactor listeners to work as a library. ([@niklas][])
|
|
||||||
- Use `ENV["GUARD_NOTIFY"]` to disable notifications. ([@thibaudgg][])
|
|
||||||
- Cleaning up all specs. ([@netzpirat][])
|
|
||||||
- [#60](https://github.com/guard/guard/pull/60): Added Windows support. ([@stereobooster][])
|
|
||||||
- [#58](https://github.com/guard/guard/pull/58): Extract code from signal handlers into methods. ([@nicksieger][])
|
|
||||||
- [#55](https://github.com/guard/guard/pull/55): It is now possible to pass `:guardfile` (a Guardfile path) or `:guardfile_contents` (the content of a Guardfile) to `Guard::Dsl.evaluate_guardfile`. Hence this allows the use of `Guard::Dsl.evaluate_guardfile` in a programmatic manner. ([@anithri][], improved by [@rymai][])
|
|
||||||
|
|
||||||
## 0.3.4 - April 24, 2011
|
|
||||||
|
|
||||||
### Bug fix
|
|
||||||
|
|
||||||
- [#41](https://github.com/guard/guard/issues/41): Removed useless Bundler requirement. ([@thibaudgg][])
|
|
||||||
|
|
||||||
### New features
|
|
||||||
|
|
||||||
- Changed CHANGELOG from RDOC to Markdown and cleaned it! Let's celebrate! ([@rymai][])
|
|
||||||
- Changed README from RDOC to Markdown! Let's celebrate! ([@thibaudgg][])
|
|
||||||
- [#48](https://github.com/guard/guard/issues/48): Adding support for inline Guard classes rather than requiring a gem. ([@jrsacks][])
|
|
||||||
|
|
||||||
## 0.3.3 - April 18, 2011
|
|
||||||
|
|
||||||
### Bug fix
|
|
||||||
|
|
||||||
- Fixed `new_modified_files` rerun conditions on `Guard.run_on_change_for_all_guards`. ([@thibaudgg][])
|
|
||||||
|
|
||||||
## 0.3.2 - April 17, 2011
|
|
||||||
|
|
||||||
### Bug fixe
|
|
||||||
|
|
||||||
- [#43](https://github.com/guard/guard/pull/43): Fixed `guard init` command. ([@brainopia][])
|
|
||||||
|
|
||||||
## 0.3.1 - April 14, 2011
|
|
||||||
|
|
||||||
### Bug fixes
|
|
||||||
|
|
||||||
- Return unique filenames from Linux listener. (Marian Schubert)
|
|
||||||
- `Guard.get_guard_class` return wrong class when loaded nested class. ([@koshigoe][])
|
|
||||||
- [#35](https://github.com/guard/guard/issues/35): Fixed open-gem/gem_open dependency problem by using `gem which` to locate guards gem path. (reported by [@thierryhenrio][], fixed by [@thibaudgg][])
|
|
||||||
- [#38](https://github.com/guard/guard/issues/38) & Pull request [#39](https://github.com/guard/guard/issues/39): Fixed an invalid ANSI escape code in `Guard::UI.reset_line`. ([@gix][])
|
|
||||||
|
|
||||||
### New feature
|
|
||||||
|
|
||||||
- [#28](https://github.com/guard/guard/issues/28): New `-n` command line option to disable notifications (Growl / Libnotify). ([@thibaudgg][])
|
|
||||||
|
|
||||||
## 0.3.0 - January 19, 2011
|
|
||||||
|
|
||||||
### Bug fix
|
|
||||||
|
|
||||||
- Avoid launching `run_on_change` guards method when no files matched. `--clear` guard argument is now usable. ([@thibaudgg][])
|
|
||||||
|
|
||||||
### New features
|
|
||||||
|
|
||||||
- The whole directory is now watched during `run_on_change` to detect new files modifications. ([@thibaudgg][])
|
|
||||||
- [#26](https://github.com/guard/guard/pull/26): New DSL method: `group` allows you to group several guards. New CLI option: `--group group_name` to specify certain groups of guards to start. ([@netzpirat][])
|
|
||||||
- `watch` patterns are now more strict: strings are matched with `String#==`, `Regexp` are matched with `Regexp#match`. ([@rymai][])
|
|
||||||
- A deprecation warning is displayed if your `Guardfile` contains `String` that look like `Regexp` (bad!). ([@rymai][])
|
|
||||||
- It's now possible to return an `Enumerable` in the `watch` optional blocks in the `Guardfile`. ([@rymai][])
|
|
||||||
|
|
||||||
### New specs
|
|
||||||
|
|
||||||
- `Guard::Watcher`. ([@rymai][])
|
|
||||||
- [#13](https://github.com/guard/guard/pull/13): `Guard::Dsl`. ([@oliamb][])
|
|
||||||
|
|
||||||
## 0.2.2 - October 25, 2010
|
|
||||||
|
|
||||||
### Bug fix
|
|
||||||
|
|
||||||
- [#5](https://github.com/guard/guard/issues/5): avoid creating new copy of `fsevent_watch` every time a file is changed. (reported by [@stouset][], fixed by [@thibaudgg][])
|
|
||||||
|
|
||||||
## 0.2.1 - October 24, 2010
|
|
||||||
|
|
||||||
### Bug fixes
|
|
||||||
|
|
||||||
- [#7](https://github.com/guard/guard/pull/7): Fixes for Linux support. ([@yannlugrin][])
|
|
||||||
- [#6](https://github.com/guard/guard/pull/6): Locate guard now chomp newline in result path. ([@yannlugrin][])
|
|
||||||
|
|
||||||
## 0.2.0 - October 21, 2010
|
|
||||||
|
|
||||||
### Bug fixes
|
|
||||||
|
|
||||||
- [#3](https://github.com/guard/guard/issues/3): `guard init <guard-name>` no more need `Gemfile` but `open_gem` is required now. (reported by [@wereHamster][], fixed by [@thibaudgg][])
|
|
||||||
- [#2](https://github.com/guard/guard/issues/2): 1.8.6 compatibility. (reported by [@veged][], fixed by [@thibaudgg][])
|
|
||||||
- Removes Growl & Libnotify dependencies. ([@thibaudgg][])
|
|
||||||
|
|
||||||
## 0.2.0.beta.1 - October 17, 2010
|
|
||||||
|
|
||||||
### New features
|
|
||||||
|
|
||||||
- Improved listeners support (`rb-fsevent` & `rb-inotify`). ([@thibaudgg][])
|
|
||||||
- Added polling listening fallback. ([@thibaudgg][])
|
|
||||||
|
|
||||||
[@anithri]: https://github.com/anithri
|
|
||||||
[@brainopia]: https://github.com/brainopia
|
|
||||||
[@bronson]: https://github.com/bronson
|
|
||||||
[@capotej]: https://github.com/capotej
|
|
||||||
[@ches]: https://github.com/ches
|
|
||||||
[@chrisberkhout]: https://github.com/chrisberkhout
|
|
||||||
[@dnagir]: https://github.com/dnagir
|
|
||||||
[@docwhat]: https://github.com/docwhat
|
|
||||||
[@earlonrails]: https://github.com/earlonrails
|
|
||||||
[@etehtsea]: https://github.com/etehtsea
|
|
||||||
[@f1sherman]: https://github.com/f1sherman
|
|
||||||
[@fabioyamate]: https://github.com/fabioyamate
|
|
||||||
[@fnichol]: https://github.com/fnichol
|
|
||||||
[@Gazer]: https://github.com/Gazer
|
|
||||||
[@gix]: https://github.com/gix
|
|
||||||
[@hron]: https://github.com/hron
|
|
||||||
[@hardipe]: https://github.com/hardipe
|
|
||||||
[@hashrocketeer]: https://github.com/hashrocketeer
|
|
||||||
[@ianwhite]: https://github.com/ianwhite
|
|
||||||
[@indirect]: https://github.com/indirect
|
|
||||||
[@jeffutter]: https://github.com/jeffutter
|
|
||||||
[@johnbintz]: https://github.com/johnbintz
|
|
||||||
[@jrsacks]: https://github.com/jrsacks
|
|
||||||
[@koshigoe]: https://github.com/koshigoe
|
|
||||||
[@limeyd]: https://github.com/limeyd
|
|
||||||
[@mcmire]: https://github.com/mcmire
|
|
||||||
[@mislav]: https://github.com/mislav
|
|
||||||
[@monocle]: https://github.com/monocle
|
|
||||||
[@mordaroso]: https://github.com/mordaroso
|
|
||||||
[@nestegg]: https://github.com/nestegg
|
|
||||||
[@netzpirat]: https://github.com/netzpirat
|
|
||||||
[@nicksieger]: https://github.com/nicksieger
|
|
||||||
[@niklas]: https://github.com/niklas
|
|
||||||
[@oliamb]: https://github.com/oliamb
|
|
||||||
[@pcreux]: https://github.com/pcreux
|
|
||||||
[@rmm5t]: https://github.com/rmm5t
|
|
||||||
[@rymai]: https://github.com/rymai
|
|
||||||
[@scottdavis]: https://github.com/scottdavis
|
|
||||||
[@stereobooster]: https://github.com/stereobooster
|
|
||||||
[@stouset]: https://github.com/stouset
|
|
||||||
[@sunaku]: https://github.com/sunaku
|
|
||||||
[@thibaudgg]: https://github.com/thibaudgg
|
|
||||||
[@thierryhenrio]: https://github.com/thierryhenrio
|
|
||||||
[@tinogomes]: https://github.com/tinogomes
|
|
||||||
[@tpope]: https://github.com/tpope
|
|
||||||
[@uk-ar]: https://github.com/uk-ar
|
|
||||||
[@veged]: https://github.com/veged
|
|
||||||
[@wereHamster]: https://github.com/wereHamster
|
|
||||||
[@yannlugrin]: https://github.com/yannlugrin
|
|
||||||
[@zonque]: https://github.com/zonque
|
|
26
CHANGELOG.rdoc
Normal file
26
CHANGELOG.rdoc
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
== 0.2.2 (Oct 25, 2010)
|
||||||
|
|
||||||
|
Bugs fixes:
|
||||||
|
|
||||||
|
- Avoid creating new copy of fsevent_watch every time a file is changed. (issue #5)
|
||||||
|
|
||||||
|
== 0.2.1 (Oct 24, 2010)
|
||||||
|
|
||||||
|
Bugs fixes:
|
||||||
|
|
||||||
|
- Fixes for Linux support
|
||||||
|
|
||||||
|
== 0.2.0 (Oct 21, 2010)
|
||||||
|
|
||||||
|
Bugs fixes:
|
||||||
|
|
||||||
|
- Fixes for 1.8.6 compatibility (issue #2)
|
||||||
|
- guard init <guard-name> no more need Gemfile presence but open_gem is required now (issue #3)
|
||||||
|
- Removes growl & libnotify dependencies
|
||||||
|
|
||||||
|
== 0.2.0.beta.1 (Oct 17, 2010)
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
- Improved listeners support (rb-fsevent & rb-inotify)
|
||||||
|
- Added polling listening fallback
|
25
Gemfile
25
Gemfile
@ -1,23 +1,14 @@
|
|||||||
source :rubygems
|
source "http://rubygems.org"
|
||||||
|
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
gem 'rake'
|
|
||||||
|
|
||||||
group :guard do
|
|
||||||
gem 'guard-ronn'
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'rbconfig'
|
require 'rbconfig'
|
||||||
|
|
||||||
if RbConfig::CONFIG['target_os'] =~ /darwin/i
|
if Config::CONFIG['target_os'] =~ /darwin/i
|
||||||
gem 'rb-fsevent', '>= 0.4.0', :require => false
|
gem 'rb-fsevent', '>= 0.3.5'
|
||||||
gem 'growl', '~> 1.0.3', :require => false
|
gem 'growl', '~> 1.0.3'
|
||||||
elsif RbConfig::CONFIG['target_os'] =~ /linux/i
|
end
|
||||||
gem 'rb-inotify', '>= 0.8.5', :require => false
|
if Config::CONFIG['target_os'] =~ /linux/i
|
||||||
gem 'libnotify', '~> 0.1.3', :require => false
|
gem 'rb-inotify', '>= 0.5.1'
|
||||||
elsif RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
gem 'libnotify', '~> 0.1.3'
|
||||||
gem 'win32console', :require => false
|
|
||||||
gem 'rb-fchange', '>= 0.0.2', :require => false
|
|
||||||
gem 'rb-notifu', '>= 0.0.4', :require => false
|
|
||||||
end
|
end
|
||||||
|
33
Guardfile
33
Guardfile
@ -1,28 +1,5 @@
|
|||||||
group :specs do
|
guard 'rspec', :version => 2 do
|
||||||
guard :rspec, :all_on_start => false, :all_after_pass => false, :cli => '--fail-fast --format doc' do
|
watch('^spec/(.*)_spec.rb')
|
||||||
watch(%r{^spec/.+_spec\.rb$})
|
watch('^lib/(.*).rb') { |m| "spec/#{m[1]}_spec.rb" }
|
||||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
watch('^spec/spec_helper.rb') { "spec" }
|
||||||
watch('spec/support/listener_helper.rb') { Dir.glob("spec/guard/listeners/*") }
|
end
|
||||||
watch('spec/spec_helper.rb') { "spec" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
group :docs do
|
|
||||||
guard :ronn do
|
|
||||||
watch(%r{^man/.+\.ronn?$})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# require 'guard/guard'
|
|
||||||
#
|
|
||||||
# module ::Guard
|
|
||||||
# class Breaking < ::Guard::Guard
|
|
||||||
# def run_all
|
|
||||||
# raise "Fool !"
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# group "exceptional" do
|
|
||||||
# guard :breaking
|
|
||||||
# end
|
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2011 Thibaud Guillaume-Gentil
|
Copyright (c) 2010 Thibaud Guillaume-Gentil
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
459
README.md
459
README.md
@ -1,459 +0,0 @@
|
|||||||
Guard [![Build Status](https://secure.travis-ci.org/guard/guard.png)](http://travis-ci.org/guard/guard)
|
|
||||||
=====
|
|
||||||
|
|
||||||
Guard is a command line tool that easily handle events on files modifications.
|
|
||||||
|
|
||||||
If you have any questions please join us on our [Google group](http://groups.google.com/group/guard-dev) or on `#guard` (irc.freenode.net).
|
|
||||||
|
|
||||||
Features
|
|
||||||
--------
|
|
||||||
|
|
||||||
* [FSEvent](http://en.wikipedia.org/wiki/FSEvents) support on Mac OS X 10.5+ (without RubyCocoa!, [rb-fsevent gem, >= 0.3.5](https://rubygems.org/gems/rb-fsevent) required).
|
|
||||||
* [Inotify](http://en.wikipedia.org/wiki/Inotify) support on Linux ([rb-inotify gem, >= 0.5.1](https://rubygems.org/gems/rb-inotify) required).
|
|
||||||
* [Directory Change Notification](http://msdn.microsoft.com/en-us/library/aa365261\(VS.85\).aspx) support on Windows ([rb-fchange, >= 0.0.2](https://rubygems.org/gems/rb-fchange) required).
|
|
||||||
* Polling on the other operating systems (help us to support more OS).
|
|
||||||
* Automatic & Super fast (when polling is not used) files modifications detection (even new files are detected).
|
|
||||||
* Visual notifications on Mac OSX ([Growl](http://growl.info)), Linux ([Libnotify](http://developer.gnome.org/libnotify)) and Windows ([Notifu](http://www.paralint.com/projects/notifu)).
|
|
||||||
* Tested against Ruby 1.8.7, 1.9.2, REE and the latest versions of JRuby & Rubinius.
|
|
||||||
|
|
||||||
Screencast
|
|
||||||
----------
|
|
||||||
|
|
||||||
Ryan Bates made a Railscast on Guard, you can view it here: [http://railscasts.com/episodes/264-guard](http://railscasts.com/episodes/264-guard)
|
|
||||||
|
|
||||||
Install
|
|
||||||
-------
|
|
||||||
|
|
||||||
|
|
||||||
Install the gem:
|
|
||||||
|
|
||||||
$ gem install guard
|
|
||||||
|
|
||||||
Or add it to your Gemfile (inside the `development` group):
|
|
||||||
|
|
||||||
gem 'guard'
|
|
||||||
|
|
||||||
and install it via Bundler:
|
|
||||||
|
|
||||||
$ bundle install
|
|
||||||
|
|
||||||
Generate an empty Guardfile with:
|
|
||||||
|
|
||||||
$ guard init
|
|
||||||
|
|
||||||
You may optionally place a .Guardfile in your home directory to use it across multiple projects.
|
|
||||||
Also note that if a `.guard.rb` is found in your home directory, it will be appended to the Guardfile.
|
|
||||||
|
|
||||||
Add the guards you need to your Guardfile (see the existing guards below).
|
|
||||||
|
|
||||||
Now, be sure to read the particular instructions for your operating system: [Mac OS X](#mac) | [Linux](#linux) | [Windows](#win)
|
|
||||||
|
|
||||||
<a name="mac" />
|
|
||||||
|
|
||||||
### On Mac OS X
|
|
||||||
|
|
||||||
Install the rb-fsevent gem for [FSEvent](http://en.wikipedia.org/wiki/FSEvents) support:
|
|
||||||
|
|
||||||
$ gem install rb-fsevent
|
|
||||||
|
|
||||||
You have three possibilities for getting Growl support:
|
|
||||||
|
|
||||||
Use the [growl_notify gem](https://rubygems.org/gems/growl_notify):
|
|
||||||
|
|
||||||
$ gem install growl_notify
|
|
||||||
|
|
||||||
The `growl_notify` gem is compatible with Growl >= 1.3 and uses AppleScript to send Growl notifications.
|
|
||||||
The gem needs a native C extension to make use of AppleScript and does not run on JRuby and MacRuby.
|
|
||||||
|
|
||||||
Use the [ruby_gntp gem](https://github.com/snaka/ruby_gntp):
|
|
||||||
|
|
||||||
$ gem install ruby_gntp
|
|
||||||
|
|
||||||
The `ruby_gntp` gem is compatible with Growl >= 1.3 and uses the Growl Notification Transport Protocol to send Growl
|
|
||||||
notifications. Guard supports multiple notification channels for customizing each notification type, but it's limited
|
|
||||||
to the local host currently.
|
|
||||||
|
|
||||||
Use the [growl gem](https://rubygems.org/gems/growl):
|
|
||||||
|
|
||||||
$ gem install growl
|
|
||||||
|
|
||||||
The `growl` gem is compatible with all versions of Growl and uses a command line tool [growlnotify](http://growl.info/extras.php#growlnotify)
|
|
||||||
that must be separately downloaded and installed. You can also install it with HomeBrew:
|
|
||||||
|
|
||||||
$ brew install growlnotify
|
|
||||||
|
|
||||||
Finally you have to add your Growl library of choice to your Gemfile:
|
|
||||||
|
|
||||||
gem 'rb-fsevent'
|
|
||||||
gem 'growl_notify' # or gem 'ruby_gntp' or gem 'growl'
|
|
||||||
|
|
||||||
Have a look at the [Guard Wiki](https://github.com/guard/guard/wiki/Which-Growl-library-should-I-use) for more information.
|
|
||||||
|
|
||||||
<a name="linux" />
|
|
||||||
|
|
||||||
### On Linux
|
|
||||||
|
|
||||||
Install the [rb-inotify gem](https://rubygems.org/gems/rb-inotify) for [inotify](http://en.wikipedia.org/wiki/Inotify) support:
|
|
||||||
|
|
||||||
$ gem install rb-inotify
|
|
||||||
|
|
||||||
Install the [libnotify gem](https://rubygems.org/gems/libnotify) if you want visual notification support:
|
|
||||||
|
|
||||||
$ gem install libnotify
|
|
||||||
|
|
||||||
And add them to your Gemfile:
|
|
||||||
|
|
||||||
gem 'rb-inotify'
|
|
||||||
gem 'libnotify'
|
|
||||||
|
|
||||||
<a name="win" />
|
|
||||||
|
|
||||||
### On Windows
|
|
||||||
|
|
||||||
Install the [rb-fchange gem](https://rubygems.org/gems/rb-fchange) for [Directory Change Notification](http://msdn.microsoft.com/en-us/library/aa365261\(VS.85\).aspx) support:
|
|
||||||
|
|
||||||
$ gem install rb-fchange
|
|
||||||
|
|
||||||
Install the [win32console gem](https://rubygems.org/gems/win32console) if you want colors in your terminal:
|
|
||||||
|
|
||||||
$ gem install win32console
|
|
||||||
|
|
||||||
Install the [rb-notifu gem](https://rubygems.org/gems/rb-notifu) if you want visual notification support:
|
|
||||||
|
|
||||||
$ gem install rb-notifu
|
|
||||||
|
|
||||||
And add them to your Gemfile:
|
|
||||||
|
|
||||||
gem 'rb-fchange'
|
|
||||||
gem 'rb-notifu'
|
|
||||||
gem 'win32console'
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
Just launch Guard inside your Ruby / Rails project with:
|
|
||||||
|
|
||||||
$ guard [start]
|
|
||||||
|
|
||||||
or if you use Bundler, to run the Guard executable specific to your bundle:
|
|
||||||
|
|
||||||
$ bundle exec guard [start]
|
|
||||||
|
|
||||||
Guard will look for a Guardfile in your current directory. If it does not find one, it will look in your `$HOME` directory for a .Guardfile.
|
|
||||||
|
|
||||||
Command line options
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
### `-c`/`--clear` option
|
|
||||||
|
|
||||||
Shell can be cleared after each change:
|
|
||||||
|
|
||||||
$ guard --clear
|
|
||||||
$ guard -c # shortcut
|
|
||||||
|
|
||||||
### `-n`/`--notify` option
|
|
||||||
|
|
||||||
Notifications (growl/libnotify) can be disabled:
|
|
||||||
|
|
||||||
$ guard --notify false
|
|
||||||
$ guard -n f # shortcut
|
|
||||||
|
|
||||||
Notifications can also be disabled globally by setting a `GUARD_NOTIFY` environment variable to `false`
|
|
||||||
|
|
||||||
### `-g`/`--group` option
|
|
||||||
|
|
||||||
Only certain guards groups can be run (see the Guardfile DSL below for creating groups):
|
|
||||||
|
|
||||||
$ guard --group group_name another_group_name
|
|
||||||
$ guard -g group_name another_group_name # shortcut
|
|
||||||
|
|
||||||
### `-d`/`--debug` option
|
|
||||||
|
|
||||||
Guard can be run in debug mode:
|
|
||||||
|
|
||||||
$ guard --debug
|
|
||||||
$ guard -d # shortcut
|
|
||||||
|
|
||||||
### `-w`/`--watchdir` option
|
|
||||||
|
|
||||||
Guard can watch in any directory (instead of the current directory):
|
|
||||||
|
|
||||||
$ guard --watchdir ~/your/fancy/project
|
|
||||||
$ guard -w ~/your/fancy/project # shortcut
|
|
||||||
|
|
||||||
### `-G`/`--guardfile` option
|
|
||||||
|
|
||||||
Guard can use a Guardfile not located in the current directory:
|
|
||||||
|
|
||||||
$ guard --guardfile ~/.your_global_guardfile
|
|
||||||
$ guard -G ~/.your_global_guardfile # shortcut
|
|
||||||
|
|
||||||
### `-A`/`--watch-all-modifications` option
|
|
||||||
|
|
||||||
Guard can optionally watch all file modifications like moves or deletions with:
|
|
||||||
|
|
||||||
$ guard start -A
|
|
||||||
$ guard start --watch-all-modifications
|
|
||||||
|
|
||||||
### `-i`/`--no-interactions` option
|
|
||||||
|
|
||||||
Turn off completely any Guard terminal [interactions](#interactions) with:
|
|
||||||
|
|
||||||
$ guard start -A
|
|
||||||
$ guard start --watch-all-modifications
|
|
||||||
|
|
||||||
An exhaustive list of options is available with:
|
|
||||||
|
|
||||||
$ guard help [TASK]
|
|
||||||
|
|
||||||
<a name="interactions" />
|
|
||||||
|
|
||||||
Interactions
|
|
||||||
------------
|
|
||||||
|
|
||||||
**From version >= 0.7.0 Posix Signal handlers are no more used to interact with Guard. If you're using a version < 0.7, please refer to the [README in the v0.6 branch](https://github.com/guard/guard/blob/v0.6/README.md).**
|
|
||||||
|
|
||||||
When Guard do nothing you can interact with by entering a command + hitting enter:
|
|
||||||
|
|
||||||
* `stop|quit|exit|s|q|e + enter` - Calls each guard's `#stop` method, in the same order they are declared in the Guardfile, and then quits Guard itself.
|
|
||||||
* `reload|r|z + enter` - Calls each guard's `#reload` method, in the same order they are declared in the Guardfile.
|
|
||||||
* `pause|p + enter` - Toggle files modification listening. Useful when switching git branches.
|
|
||||||
* `just enter (no commands)` - Calls each guard's `#run_all` method, in the same order they are declared in the Guardfile.
|
|
||||||
|
|
||||||
Available Guards
|
|
||||||
----------------
|
|
||||||
|
|
||||||
A list of the available guards is present [in the wiki](https://github.com/guard/guard/wiki/List-of-available-Guards).
|
|
||||||
|
|
||||||
### Add a guard to your Guardfile
|
|
||||||
|
|
||||||
Add it to your Gemfile (inside the `development` group):
|
|
||||||
|
|
||||||
gem '<guard-name>'
|
|
||||||
|
|
||||||
You can list all guards installed on your system with:
|
|
||||||
|
|
||||||
$ guard list
|
|
||||||
|
|
||||||
Insert default guard's definition to your Guardfile by running this command:
|
|
||||||
|
|
||||||
$ guard init <guard-name>
|
|
||||||
|
|
||||||
You are good to go, or you can modify your guards' definition to suit your needs.
|
|
||||||
|
|
||||||
Guardfile DSL
|
|
||||||
-------------
|
|
||||||
|
|
||||||
The Guardfile DSL consists of the following methods:
|
|
||||||
|
|
||||||
* `#guard` - Allows you to add a guard with an optional hash of options.
|
|
||||||
* `#watch` - Allows you to define which files are supervised by a guard. An optional block can be added to overwrite the paths sent to the guard's `#run_on_change` method or to launch any arbitrary command.
|
|
||||||
* `#group` - Allows you to group several guards together. Groups to be run can be specified with the Guard DSL option `--group` (or `-g`). This comes in handy especially when you have a huge Guardfile and want to focus your development on a certain part. Guards that don't belong to a group are considered global and are always run.
|
|
||||||
* `#callback` - Allows you to execute arbitrary code before or after any of the `start`, `stop`, `reload`, `run_all` and `run_on_change` guards' method. You can even insert more hooks inside these methods. Please [checkout the Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for more details.
|
|
||||||
* `#ignore_paths` - Allows you to ignore top level directories altogether. This comes is handy when you have large amounts of non-source data in you project. By default .bundle, .git, log, tmp, and vendor are ignored. Currently it is only possible to ignore the immediate descendants of the watched directory.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
ignore_paths 'foo', 'bar'
|
|
||||||
|
|
||||||
group 'backend' do
|
|
||||||
guard 'bundler' do
|
|
||||||
watch('Gemfile')
|
|
||||||
end
|
|
||||||
|
|
||||||
guard 'rspec', :cli => '--color --format doc' do
|
|
||||||
# Regexp watch patterns are matched with Regexp#match
|
|
||||||
watch(%r{^spec/.+_spec\.rb$})
|
|
||||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
|
||||||
watch(%r{^spec/models/.+\.rb$}) { ["spec/models", "spec/acceptance"] }
|
|
||||||
watch(%r{^spec/.+\.rb$}) { `say hello` }
|
|
||||||
|
|
||||||
# String watch patterns are matched with simple '=='
|
|
||||||
watch('spec/spec_helper.rb') { "spec" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
group 'frontend' do
|
|
||||||
guard 'coffeescript', :output => 'public/javascripts/compiled' do
|
|
||||||
watch(%r{^app/coffeescripts/.+\.coffee$})
|
|
||||||
end
|
|
||||||
|
|
||||||
guard 'livereload' do
|
|
||||||
watch(%r{^app/.+\.(erb|haml)$})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Using a Guardfile without the `guard` binary
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
The Guardfile DSL can also be used in a programmatic fashion by calling directly `Guard::Dsl.evaluate_guardfile`.
|
|
||||||
Available options are as follow:
|
|
||||||
|
|
||||||
* `:guardfile` - The path to a valid Guardfile.
|
|
||||||
* `:guardfile_contents` - A string representing the content of a valid Guardfile
|
|
||||||
|
|
||||||
Remember, without any options given, Guard will look for a Guardfile in your current directory and if it does not find one, it will look for it in your `$HOME` directory.
|
|
||||||
|
|
||||||
For instance, you could use it as follow:
|
|
||||||
|
|
||||||
gem 'guard'
|
|
||||||
require 'guard'
|
|
||||||
|
|
||||||
Guard.setup
|
|
||||||
|
|
||||||
Guard::Dsl.evaluate_guardfile(:guardfile => '/your/custom/path/to/a/valid/Guardfile')
|
|
||||||
# or
|
|
||||||
Guard::Dsl.evaluate_guardfile(:guardfile_contents => "
|
|
||||||
guard 'rspec' do
|
|
||||||
watch(%r{^spec/.+_spec\.rb$})
|
|
||||||
end
|
|
||||||
")
|
|
||||||
|
|
||||||
### Listing defined guards/groups for the current project
|
|
||||||
|
|
||||||
You can list the defined groups and guards for the current Guardfile from the command line using `guard show` or `guard -T`:
|
|
||||||
|
|
||||||
$ guard -T
|
|
||||||
|
|
||||||
(global):
|
|
||||||
shell
|
|
||||||
Group backend:
|
|
||||||
bundler
|
|
||||||
rspec: cli => "--color --format doc"
|
|
||||||
Group frontend:
|
|
||||||
coffeescript: output => "public/javascripts/compiled"
|
|
||||||
livereload
|
|
||||||
|
|
||||||
User config file
|
|
||||||
----------------
|
|
||||||
|
|
||||||
If a `.guard.rb` is found in your home directory, it will be appended to
|
|
||||||
the Guardfile. This can be used for tasks you want guard to handle but
|
|
||||||
other users probably don't. For example, indexing your source tree with
|
|
||||||
[Ctags](http://ctags.sourceforge.net):
|
|
||||||
|
|
||||||
guard 'shell' do
|
|
||||||
watch(%r{^(?:app|lib)/.+\.rb$}) { `ctags -R` }
|
|
||||||
end
|
|
||||||
|
|
||||||
Create a new guard
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Creating a new guard is very easy, just create a new gem (`bundle gem` if you use Bundler) with this basic structure:
|
|
||||||
|
|
||||||
.travis.yml # bonus point!
|
|
||||||
CHANGELOG.md # bonus point!
|
|
||||||
Gemfile
|
|
||||||
guard-name.gemspec
|
|
||||||
Guardfile
|
|
||||||
lib/
|
|
||||||
guard/
|
|
||||||
guard-name/
|
|
||||||
templates/
|
|
||||||
Guardfile # needed for `guard init <guard-name>`
|
|
||||||
version.rb
|
|
||||||
guard-name.rb
|
|
||||||
test/ # or spec/
|
|
||||||
README.md
|
|
||||||
|
|
||||||
`Guard::GuardName` (in `lib/guard/guard-name.rb`) must inherit from
|
|
||||||
[Guard::Guard](http://rubydoc.info/github/guard/guard/master/Guard/Guard) and should overwrite at least one of
|
|
||||||
the basic `Guard::Guard` task methods.
|
|
||||||
|
|
||||||
Here is an example scaffold for `lib/guard/guard-name.rb`:
|
|
||||||
|
|
||||||
require 'guard'
|
|
||||||
require 'guard/guard'
|
|
||||||
|
|
||||||
module Guard
|
|
||||||
class GuardName < Guard
|
|
||||||
|
|
||||||
# Initialize a Guard.
|
|
||||||
# @param [Array<Guard::Watcher>] watchers the Guard file watchers
|
|
||||||
# @param [Hash] options the custom Guard options
|
|
||||||
def initialize(watchers = [], options = {})
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
# Call once when Guard starts. Please override initialize method to init stuff.
|
|
||||||
# @raise [:task_has_failed] when start has failed
|
|
||||||
def start
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when `stop|quit|exit|s|q|e + enter` is pressed (when Guard quits).
|
|
||||||
# @raise [:task_has_failed] when stop has failed
|
|
||||||
def stop
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when `reload|r|z + enter` is pressed.
|
|
||||||
# This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
|
||||||
# @raise [:task_has_failed] when reload has failed
|
|
||||||
def reload
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when just `enter` is pressed
|
|
||||||
# This method should be principally used for long action like running all specs/tests/...
|
|
||||||
# @raise [:task_has_failed] when run_all has failed
|
|
||||||
def run_all
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called on file(s) modifications that the Guard watches.
|
|
||||||
# @param [Array<String>] paths the changes files or paths
|
|
||||||
# @raise [:task_has_failed] when run_on_change has failed
|
|
||||||
def run_on_change(paths)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called on file(s) deletions that the Guard watches.
|
|
||||||
# @param [Array<String>] paths the deleted files or paths
|
|
||||||
# @raise [:task_has_failed] when run_on_change has failed
|
|
||||||
def run_on_deletion(paths)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Please take a look at the [existing guards' source code](https://github.com/guard/guard/wiki/List-of-available-Guards)
|
|
||||||
for more concrete example and inspiration.
|
|
||||||
|
|
||||||
Alternatively, a new guard can be added inline to a Guardfile with this basic structure:
|
|
||||||
|
|
||||||
require 'guard/guard'
|
|
||||||
|
|
||||||
module ::Guard
|
|
||||||
class InlineGuard < ::Guard::Guard
|
|
||||||
def run_all
|
|
||||||
end
|
|
||||||
|
|
||||||
def run_on_change(paths)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Here is a very cool example by [@avdi](https://github.com/avdi) : [http://avdi.org/devblog/2011/06/15/a-guardfile-for-redis](http://avdi.org/devblog/2011/06/15/a-guardfile-for-redis)
|
|
||||||
|
|
||||||
Development
|
|
||||||
-----------
|
|
||||||
|
|
||||||
* Documentation hosted at [RubyDoc](http://rubydoc.info/github/guard/guard/master/frames).
|
|
||||||
* Source hosted at [GitHub](https://github.com/guard/guard).
|
|
||||||
* Report issues and feature requests to [GitHub Issues](https://github.com/guard/guard/issues).
|
|
||||||
|
|
||||||
Pull requests are very welcome! Please try to follow these simple "rules", though:
|
|
||||||
|
|
||||||
- Please create a topic branch for every separate change you make;
|
|
||||||
- Make sure your patches are well tested;
|
|
||||||
- Update the README (if applicable);
|
|
||||||
- Update the CHANGELOG (maybe not for a typo but don't hesitate!);
|
|
||||||
- Please **do not change** the version number.
|
|
||||||
|
|
||||||
For questions please join us on our [Google group](http://groups.google.com/group/guard-dev) or on `#guard` (irc.freenode.net).
|
|
||||||
|
|
||||||
Author
|
|
||||||
------
|
|
||||||
|
|
||||||
[Thibaud Guillaume-Gentil](https://github.com/thibaudgg)
|
|
||||||
|
|
||||||
Contributors
|
|
||||||
------------
|
|
||||||
|
|
||||||
[https://github.com/guard/guard/contributors](https://github.com/guard/guard/contributors)
|
|
204
README.rdoc
Normal file
204
README.rdoc
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
= Guard
|
||||||
|
|
||||||
|
Guard is a command line tool to easly handle events on files modifications.
|
||||||
|
|
||||||
|
== Features
|
||||||
|
|
||||||
|
- {FSEvent}[http://en.wikipedia.org/wiki/FSEvents] support on Mac OS X 10.5+ (without RubyCocoa!, {rb-fsevent gem, >= 0.3.5}[https://rubygems.org/gems/rb-fsevent] required)
|
||||||
|
- {Inotify}[http://en.wikipedia.org/wiki/Inotify] support on Linux ({rb-inotify gem, >= 0.5.1}[https://rubygems.org/gems/rb-inotify] required)
|
||||||
|
- Polling for others (help us to support more systems)
|
||||||
|
- Super fast change detection (when polling not used)
|
||||||
|
- Automatic files modifications detection (even new files are detected)
|
||||||
|
- Growl notification ({growlnotify}[http://growl.info/documentation/growlnotify.php] & {growl gem}[https://rubygems.org/gems/growl] required)
|
||||||
|
- Libnotify notification ({libnotify gem}[https://rubygems.org/gems/libnotify] required)
|
||||||
|
- Tested on Ruby 1.8.6, 1.8.7 & 1.9.2
|
||||||
|
|
||||||
|
== Install
|
||||||
|
|
||||||
|
Install the gem:
|
||||||
|
|
||||||
|
gem install guard
|
||||||
|
|
||||||
|
Add it to your Gemfile (inside test group):
|
||||||
|
|
||||||
|
gem 'guard'
|
||||||
|
|
||||||
|
Generate an empty Guardfile with:
|
||||||
|
|
||||||
|
guard init
|
||||||
|
|
||||||
|
Add guard(s) you need (see available guards below)
|
||||||
|
|
||||||
|
=== On Mac OS X
|
||||||
|
|
||||||
|
Install rb-fsevent for {FSEvent}[http://en.wikipedia.org/wiki/FSEvents] support
|
||||||
|
|
||||||
|
gem install rb-fsevent
|
||||||
|
|
||||||
|
Install growl for Growl notification support
|
||||||
|
|
||||||
|
gem install growl
|
||||||
|
|
||||||
|
And add it to you Gemfile
|
||||||
|
|
||||||
|
gem 'growl'
|
||||||
|
|
||||||
|
=== On Linux
|
||||||
|
|
||||||
|
Install rb-inotify for {inotify}[http://en.wikipedia.org/wiki/Inotify] support
|
||||||
|
|
||||||
|
gem install rb-inotify
|
||||||
|
|
||||||
|
Install libnotify for libonity notification support
|
||||||
|
|
||||||
|
gem install libnotify
|
||||||
|
|
||||||
|
And add it to you Gemfile
|
||||||
|
|
||||||
|
gem 'libnotify'
|
||||||
|
|
||||||
|
== Usage
|
||||||
|
|
||||||
|
Just launch Guard inside your ruby/rails project with:
|
||||||
|
|
||||||
|
guard
|
||||||
|
|
||||||
|
Shell can be cleared after each change with:
|
||||||
|
|
||||||
|
guard -c
|
||||||
|
|
||||||
|
Options list is available with:
|
||||||
|
|
||||||
|
guard help [TASK]
|
||||||
|
|
||||||
|
Signal handlers are used to interact with Guard:
|
||||||
|
|
||||||
|
- Ctrl-C - Quit Guard (call stop guard(s) method before)
|
||||||
|
- Ctrl-\ - Call run_all guard(s) method
|
||||||
|
- Ctrl-Z - Call reload guard(s) method
|
||||||
|
|
||||||
|
== Available Guards
|
||||||
|
|
||||||
|
- {guard-bundler}[http://github.com/guard/guard-bundler] by {Yann Lugrin}[http://github.com/yannlugrin]
|
||||||
|
- {guard-coffeescript}[http://github.com/guard/guard-coffeescript] by {Michael Kessler}[http://github.com/netzpirat]
|
||||||
|
- {guard-compass}[http://github.com/guard/guard-compass] by {Olivier Amblet}[http://github.com/oliamb]
|
||||||
|
- {guard-cucumber}[http://github.com/guard/guard-cucumber] by {Michael Kessler}[http://github.com/netzpirat]
|
||||||
|
- {guard-livereload}[http://github.com/guard/guard-livereload] by {Thibaud Guillaume-Gentil}[http://github.com/thibaudgg]
|
||||||
|
- {guard-minitest}[http://github.com/guard/guard-minitest] by {Yann Lugrin}[http://github.com/yannlugrin]
|
||||||
|
- {guard-nanoc}[http://github.com/guard/guard-nanoc] by {Yann Lugrin}[http://github.com/yannlugrin]
|
||||||
|
- {guard-passenger}[http://github.com/guard/guard-passenger] by {Fabio Kuhn}[http://github.com/mordaroso]
|
||||||
|
- {guard-rspec}[http://github.com/guard/guard-rspec] by {Thibaud Guillaume-Gentil}[http://github.com/thibaudgg]
|
||||||
|
- {guard-sass}[http://github.com/guard/guard-sass] by {Joshua Hawxwell}[http://github.com/hawx]
|
||||||
|
- {guard-shell}[http://github.com/guard/guard-shell] by {Joshua Hawxwell}[http://github.com/hawx]
|
||||||
|
- {guard-test}[http://github.com/guard/guard-test] by {Rémy Coutable}[http://github.com/rymai]
|
||||||
|
|
||||||
|
guard ideas:
|
||||||
|
|
||||||
|
- guard-spork
|
||||||
|
- others ideas?
|
||||||
|
|
||||||
|
=== Add a guard to your Guardfile
|
||||||
|
|
||||||
|
Add it to your Gemfile (inside test group):
|
||||||
|
|
||||||
|
gem '<guard-name>'
|
||||||
|
|
||||||
|
Add guard definition to your Guardfile by running this command:
|
||||||
|
|
||||||
|
guard init <guard-name>
|
||||||
|
|
||||||
|
You are good to go!
|
||||||
|
|
||||||
|
== Create a guard
|
||||||
|
|
||||||
|
Create a new guard is very easy, just create a new gem with this basic structure:
|
||||||
|
|
||||||
|
lib/
|
||||||
|
guard/
|
||||||
|
guard-name/
|
||||||
|
templates/
|
||||||
|
Guardfile (needed for guard init <guard-name>)
|
||||||
|
guard-name.rb
|
||||||
|
|
||||||
|
lib/guard/guard-name.rb inherit from guard/guard and should overwrite at least one of the five guard methods. Example:
|
||||||
|
|
||||||
|
require 'guard'
|
||||||
|
require 'guard/guard'
|
||||||
|
|
||||||
|
module Guard
|
||||||
|
class GuardName < Guard
|
||||||
|
|
||||||
|
def initialize(watchers = [], options = {})
|
||||||
|
super
|
||||||
|
# init stuff here, thx!
|
||||||
|
end
|
||||||
|
|
||||||
|
# ================
|
||||||
|
# = Guard method =
|
||||||
|
# ================
|
||||||
|
|
||||||
|
# If one of those methods raise an exception, the Guard instance
|
||||||
|
# will be removed from the active guard.
|
||||||
|
|
||||||
|
# Call once when guard starts
|
||||||
|
# Please override initialize method to init stuff
|
||||||
|
def start
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Call with Ctrl-C signal (when Guard quit)
|
||||||
|
def stop
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Call with Ctrl-Z signal
|
||||||
|
# This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
||||||
|
def reload
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Call with Ctrl-/ signal
|
||||||
|
# This method should be principally used for long action like running all specs/tests/...
|
||||||
|
def run_all
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Call on file(s) modifications
|
||||||
|
def run_on_change(paths)
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Looks at available guards code for more concrete example.
|
||||||
|
|
||||||
|
== Guardfile DSL
|
||||||
|
|
||||||
|
Guardfile DSL consists of just two simple methods: guard & watch. Example:
|
||||||
|
|
||||||
|
guard 'rspec', :version => 2 do
|
||||||
|
watch('^spec/(.*)_spec.rb')
|
||||||
|
watch('^lib/(.*)\.rb') { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||||
|
watch('^spec/spec_helper.rb') { "spec" }
|
||||||
|
watch('^spec/spec_helper.rb') { `say hello` }
|
||||||
|
end
|
||||||
|
|
||||||
|
- "guard" method allow to add a guard with an optional options hash
|
||||||
|
- "watch" method allow to define which files are supervised per this guard. A optional block can be added to overwrite path sending to run_on_change guard method or launch simple command.
|
||||||
|
|
||||||
|
== TODO
|
||||||
|
|
||||||
|
- Add more specs! Shame on me :)
|
||||||
|
|
||||||
|
== Development
|
||||||
|
|
||||||
|
- Source hosted at {GitHub}[http://github.com/guard/guard]
|
||||||
|
- Report issues/Questions/Feature requests on {GitHub Issues}[http://github.com/guard/guard/issues]
|
||||||
|
|
||||||
|
Pull requests are very welcome! Make sure your patches are well tested. Please create a topic branch for every separate change
|
||||||
|
you make.
|
||||||
|
|
||||||
|
== Authors
|
||||||
|
|
||||||
|
{Thibaud Guillaume-Gentil}[http://github.com/thibaudgg]
|
49
Rakefile
49
Rakefile
@ -5,44 +5,17 @@ require 'rspec/core/rake_task'
|
|||||||
RSpec::Core::RakeTask.new(:spec)
|
RSpec::Core::RakeTask.new(:spec)
|
||||||
task :default => :spec
|
task :default => :spec
|
||||||
|
|
||||||
require 'rbconfig'
|
|
||||||
namespace(:spec) do
|
namespace(:spec) do
|
||||||
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/i
|
desc "Run all specs on multiple ruby versions (requires rvm)"
|
||||||
desc "Run all specs on multiple ruby versions (requires pik)"
|
task(:portability) do
|
||||||
task(:portability) do
|
%w[1.8.6 1.8.7 1.9.2].each do |version|
|
||||||
%w[187 192 161].each do |version|
|
system <<-BASH
|
||||||
system "cmd /c echo -----------#{version}------------ & " +
|
bash -c 'source ~/.rvm/scripts/rvm;
|
||||||
"pik use #{version} & " +
|
rvm #{version};
|
||||||
"bundle install & " +
|
echo "--------- version #{version} ----------\n";
|
||||||
"bundle exec rspec spec"
|
bundle install;
|
||||||
end
|
rake spec'
|
||||||
end
|
BASH
|
||||||
else
|
|
||||||
desc "Run all specs on multiple ruby versions (requires rvm)"
|
|
||||||
task(:portability) do
|
|
||||||
travis_config_file = File.expand_path("../.travis.yml", __FILE__)
|
|
||||||
begin
|
|
||||||
travis_options ||= YAML::load_file(travis_config_file)
|
|
||||||
rescue => ex
|
|
||||||
puts "Travis config file '#{travis_config_file}' could not be found: #{ex.message}"
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
travis_options['rvm'].each do |version|
|
|
||||||
system <<-BASH
|
|
||||||
bash -c 'source ~/.rvm/scripts/rvm;
|
|
||||||
rvm #{version};
|
|
||||||
ruby_version_string_size=`ruby -v | wc -m`
|
|
||||||
echo;
|
|
||||||
for ((c=1; c<$ruby_version_string_size; c++)); do echo -n "="; done
|
|
||||||
echo;
|
|
||||||
echo "`ruby -v`";
|
|
||||||
for ((c=1; c<$ruby_version_string_size; c++)); do echo -n "="; done
|
|
||||||
echo;
|
|
||||||
RBXOPT="-Xrbc.db" bundle install;
|
|
||||||
RBXOPT="-Xrbc.db" bundle exec rspec spec -f doc 2>&1;'
|
|
||||||
BASH
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -3,4 +3,4 @@
|
|||||||
require 'guard'
|
require 'guard'
|
||||||
require 'guard/cli'
|
require 'guard/cli'
|
||||||
|
|
||||||
Guard::CLI.start
|
Guard::CLI.start
|
@ -1,5 +1,6 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
Kernel.load File.expand_path('../lib/guard/version.rb', __FILE__)
|
$:.push File.expand_path('../lib', __FILE__)
|
||||||
|
require 'guard/version'
|
||||||
|
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = 'guard'
|
s.name = 'guard'
|
||||||
@ -7,22 +8,21 @@ Gem::Specification.new do |s|
|
|||||||
s.platform = Gem::Platform::RUBY
|
s.platform = Gem::Platform::RUBY
|
||||||
s.authors = ['Thibaud Guillaume-Gentil']
|
s.authors = ['Thibaud Guillaume-Gentil']
|
||||||
s.email = ['thibaud@thibaud.me']
|
s.email = ['thibaud@thibaud.me']
|
||||||
s.homepage = 'https://github.com/guard/guard'
|
s.homepage = 'http://rubygems.org/gems/guard'
|
||||||
s.summary = 'Guard keeps an eye on your file modifications'
|
s.summary = 'Guard keep an eye on your files modifications.'
|
||||||
s.description = 'Guard is a command line tool to easily handle events on file system modifications.'
|
s.description = 'Guard is a command line tool to easily handle events on files modifications.'
|
||||||
|
|
||||||
s.required_rubygems_version = '>= 1.3.6'
|
s.required_rubygems_version = '>= 1.3.6'
|
||||||
s.rubyforge_project = 'guard'
|
s.rubyforge_project = 'guard'
|
||||||
|
|
||||||
s.add_dependency 'thor', '~> 0.14.6'
|
s.add_development_dependency 'bundler', '~> 1.0.3'
|
||||||
|
s.add_development_dependency 'rspec', '~> 2.0.1'
|
||||||
s.add_development_dependency 'bundler'
|
s.add_development_dependency 'guard-rspec', '~> 0.1.4'
|
||||||
s.add_development_dependency 'rspec', '~> 2.6.0'
|
|
||||||
s.add_development_dependency 'guard-rspec', '~> 0.3.1'
|
s.add_dependency 'thor', '~> 0.14.3'
|
||||||
s.add_development_dependency 'yard', '~> 0.7.2'
|
s.add_dependency 'open_gem', '~> 1.4.2'
|
||||||
s.add_development_dependency 'kramdown', '~> 0.13.3'
|
|
||||||
|
s.files = Dir.glob('{bin,images,lib}/**/*') + %w[LICENSE README.rdoc]
|
||||||
s.files = Dir.glob('{bin,images,lib}/**/*') + %w[CHANGELOG.md LICENSE man/guard.1 man/guard.1.html README.md]
|
|
||||||
s.executable = 'guard'
|
s.executable = 'guard'
|
||||||
s.require_path = 'lib'
|
s.require_path = 'lib'
|
||||||
end
|
end
|
501
lib/guard.rb
501
lib/guard.rb
@ -1,436 +1,101 @@
|
|||||||
require 'thread'
|
require 'bundler'
|
||||||
|
|
||||||
# Guard is the main module for all Guard related modules and classes.
|
|
||||||
# Also other Guard implementation should use this namespace.
|
|
||||||
#
|
|
||||||
module Guard
|
module Guard
|
||||||
|
|
||||||
autoload :UI, 'guard/ui'
|
autoload :UI, 'guard/ui'
|
||||||
autoload :Dsl, 'guard/dsl'
|
autoload :Dsl, 'guard/dsl'
|
||||||
autoload :DslDescriber, 'guard/dsl_describer'
|
autoload :Interactor, 'guard/interactor'
|
||||||
autoload :Group, 'guard/group'
|
autoload :Listener, 'guard/listener'
|
||||||
autoload :Interactor, 'guard/interactor'
|
autoload :Watcher, 'guard/watcher'
|
||||||
autoload :Listener, 'guard/listener'
|
autoload :Notifier, 'guard/notifier'
|
||||||
autoload :Watcher, 'guard/watcher'
|
|
||||||
autoload :Notifier, 'guard/notifier'
|
|
||||||
autoload :Hook, 'guard/hook'
|
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
attr_accessor :options, :interactor, :listener, :lock
|
attr_accessor :options, :guards, :listener
|
||||||
|
|
||||||
# Creates the initial Guardfile template or add a Guard implementation
|
# initialize this singleton
|
||||||
# Guardfile template to an existing Guardfile.
|
def init(options = {})
|
||||||
#
|
@options = options
|
||||||
# @see Guard::Guard.init
|
@listener = Listener.init
|
||||||
#
|
@guards = []
|
||||||
# @param [String] guard_name the name of the Guard to initialize
|
return self
|
||||||
#
|
|
||||||
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
|
end
|
||||||
|
|
||||||
# Initialize the Guard singleton.
|
|
||||||
#
|
|
||||||
# @option options [Boolean] clear if auto clear the UI should be done
|
|
||||||
# @option options [Boolean] notify if system notifications should be shown
|
|
||||||
# @option options [Boolean] debug if debug output should be shown
|
|
||||||
# @option options [Array<String>] group the list of groups to start
|
|
||||||
# @option options [String] watchdir the director to watch
|
|
||||||
# @option options [String] guardfile the path to the Guardfile
|
|
||||||
# @option options [Boolean] watch_all_modifications watches all file modifications if true
|
|
||||||
#
|
|
||||||
def setup(options = {})
|
|
||||||
@lock = Mutex.new
|
|
||||||
|
|
||||||
@options = options
|
|
||||||
@guards = []
|
|
||||||
@groups = [Group.new(:default)]
|
|
||||||
@interactor = Interactor.new unless @options[:no_interactions]
|
|
||||||
@listener = Listener.select_and_init(@options[:watchdir] ? File.expand_path(@options[:watchdir]) : Dir.pwd, options)
|
|
||||||
|
|
||||||
@options[:notify] && ENV['GUARD_NOTIFY'] != 'false' ? Notifier.turn_on : Notifier.turn_off
|
|
||||||
|
|
||||||
UI.clear if @options[:clear]
|
|
||||||
|
|
||||||
debug_command_execution if @options[:debug]
|
|
||||||
|
|
||||||
self
|
|
||||||
end
|
|
||||||
|
|
||||||
# Smart accessor for retrieving a specific guard or several guards at once.
|
|
||||||
#
|
|
||||||
# @param [String, Symbol] filter return the guard with the given name, or nil if not found
|
|
||||||
# @param [Regexp] filter returns all guards matching the Regexp, or [] if no guard found
|
|
||||||
# @param [Hash] filter returns all guards matching the given Hash.
|
|
||||||
# Example: `{ :name => 'rspec', :group => 'backend' }`, or [] if no guard found
|
|
||||||
# @param [NilClass] filter returns all guards
|
|
||||||
#
|
|
||||||
# @see Guard.groups
|
|
||||||
#
|
|
||||||
def guards(filter = nil)
|
|
||||||
case filter
|
|
||||||
when String, Symbol
|
|
||||||
@guards.find { |guard| guard.class.to_s.downcase.sub('guard::', '') == filter.to_s.downcase.gsub('-', '') }
|
|
||||||
when Regexp
|
|
||||||
@guards.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') =~ filter }
|
|
||||||
when Hash
|
|
||||||
filter.inject(@guards) do |matches, (k, v)|
|
|
||||||
if k.to_sym == :name
|
|
||||||
matches.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') == v.to_s.downcase.gsub('-', '') }
|
|
||||||
else
|
|
||||||
matches.find_all { |guard| guard.send(k).to_sym == v.to_sym }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
@guards
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Smart accessor for retrieving a specific group or several groups at once.
|
|
||||||
#
|
|
||||||
# @param [NilClass] filter returns all groups
|
|
||||||
# @param [String, Symbol] filter return the group with the given name, or nil if not found
|
|
||||||
# @param [Regexp] filter returns all groups matching the Regexp, or [] if no group found
|
|
||||||
#
|
|
||||||
# @see Guard.guards
|
|
||||||
#
|
|
||||||
def groups(filter = nil)
|
|
||||||
case filter
|
|
||||||
when String, Symbol
|
|
||||||
@groups.find { |group| group.name == filter.to_sym }
|
|
||||||
when Regexp
|
|
||||||
@groups.find_all { |group| group.name.to_s =~ filter }
|
|
||||||
else
|
|
||||||
@groups
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Start Guard by evaluate the `Guardfile`, initialize the declared Guards
|
|
||||||
# and start the available file change listener.
|
|
||||||
#
|
|
||||||
# @option options [Boolean] clear if auto clear the UI should be done
|
|
||||||
# @option options [Boolean] notify if system notifications should be shown
|
|
||||||
# @option options [Boolean] debug if debug output should be shown
|
|
||||||
# @option options [Array<String>] group the list of groups to start
|
|
||||||
# @option options [String] watchdir the director to watch
|
|
||||||
# @option options [String] guardfile the path to the Guardfile
|
|
||||||
#
|
|
||||||
def start(options = {})
|
def start(options = {})
|
||||||
setup(options)
|
init options
|
||||||
|
|
||||||
Dsl.evaluate_guardfile(options)
|
Dsl.evaluate_guardfile
|
||||||
|
if guards.empty?
|
||||||
listener.on_change do |files|
|
UI.error "No guards found in Guardfile, please add at least one."
|
||||||
Dsl.reevaluate_guardfile if Watcher.match_guardfile?(files)
|
|
||||||
listener.changed_files += files if Watcher.match_files?(guards, files)
|
|
||||||
end
|
|
||||||
|
|
||||||
UI.info "Guard is now watching at '#{ listener.directory }'"
|
|
||||||
|
|
||||||
run_guard_task(:start)
|
|
||||||
|
|
||||||
interactor.start if interactor
|
|
||||||
listener.start
|
|
||||||
end
|
|
||||||
|
|
||||||
# Stop Guard listening to file changes
|
|
||||||
#
|
|
||||||
def stop
|
|
||||||
UI.info 'Bye bye...', :reset => true
|
|
||||||
|
|
||||||
run_guard_task(:stop)
|
|
||||||
|
|
||||||
listener.stop
|
|
||||||
abort
|
|
||||||
end
|
|
||||||
|
|
||||||
# Reload all Guards currently enabled.
|
|
||||||
#
|
|
||||||
def reload
|
|
||||||
run do
|
|
||||||
run_guard_task(:reload)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Trigger `run_all` on all Guards currently enabled.
|
|
||||||
#
|
|
||||||
def run_all
|
|
||||||
run do
|
|
||||||
run_guard_task(:run_all)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Pause Guard listening to file changes.
|
|
||||||
#
|
|
||||||
def pause
|
|
||||||
if listener.paused?
|
|
||||||
UI.info 'Un-paused files modification listening', :reset => true
|
|
||||||
listener.clear_changed_files
|
|
||||||
listener.run
|
|
||||||
else
|
else
|
||||||
UI.info 'Paused files modification listening', :reset => true
|
Interactor.init_signal_traps
|
||||||
listener.pause
|
|
||||||
end
|
listener.on_change do |files|
|
||||||
end
|
run do
|
||||||
|
guards.each do |guard|
|
||||||
# Trigger `run_on_change` on all Guards currently enabled.
|
paths = Watcher.match_files(guard, files)
|
||||||
#
|
supervised_task(guard, :run_on_change, paths) unless paths.empty?
|
||||||
def run_on_change(paths)
|
|
||||||
run do
|
|
||||||
run_guard_task(:run_on_change, paths)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Run a block where the listener and the interactor is
|
|
||||||
# blocked.
|
|
||||||
#
|
|
||||||
# @yield the block to run
|
|
||||||
#
|
|
||||||
def run
|
|
||||||
UI.clear if options[:clear]
|
|
||||||
|
|
||||||
lock.synchronize do
|
|
||||||
begin
|
|
||||||
interactor.stop_if_not_current if interactor
|
|
||||||
yield
|
|
||||||
rescue Interrupt
|
|
||||||
end
|
|
||||||
|
|
||||||
interactor.start if interactor
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Loop through all groups and run the given task for each Guard.
|
|
||||||
#
|
|
||||||
# Stop the task run for the all Guards within a group if one Guard
|
|
||||||
# throws `:task_has_failed`.
|
|
||||||
#
|
|
||||||
# @param [Symbol] task the task to run
|
|
||||||
# @param [Array<String>] files the list of files to pass to the task
|
|
||||||
#
|
|
||||||
def run_guard_task(task, files = nil)
|
|
||||||
groups.each do |group|
|
|
||||||
catch :task_has_failed do
|
|
||||||
guards(:group => group.name).each do |guard|
|
|
||||||
if task == :run_on_change
|
|
||||||
run_on_change_task(files, guard, task)
|
|
||||||
else
|
|
||||||
run_supervised_task(guard, task)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
UI.info "Guard is now watching at '#{Dir.pwd}'"
|
||||||
|
guards.each do |guard|
|
||||||
# Run the `:run_on_change` task. When the option `:watch_all_modifications` is set,
|
if supervised_task(guard, :start)
|
||||||
# the task is split to run changed paths on {Guard::Guard#run_on_change}, whereas
|
%w[reload run_all].each do |m|
|
||||||
# deleted paths run on {Guard::Guard#run_on_deletion}.
|
guard.send(m) if guard.respond_to?(:"#{m}_at_start?") && guard.send(:"#{m}_at_start?")
|
||||||
#
|
end
|
||||||
# @param [Array<String>] files the list of files to pass to the task
|
end
|
||||||
# @param [Guard::Guard] guard the guard to run
|
|
||||||
# @param [Symbol] task the task to run
|
|
||||||
# @raise [:task_has_failed] when task has failed
|
|
||||||
#
|
|
||||||
def run_on_change_task(files, guard, task)
|
|
||||||
paths = Watcher.match_files(guard, files)
|
|
||||||
changes = changed_paths(paths)
|
|
||||||
deletions = deleted_paths(paths)
|
|
||||||
|
|
||||||
unless changes.empty?
|
|
||||||
UI.debug "#{ guard.class.name }##{ task } with #{ changes.inspect }"
|
|
||||||
run_supervised_task(guard, task, changes)
|
|
||||||
end
|
|
||||||
|
|
||||||
unless deletions.empty?
|
|
||||||
UI.debug "#{ guard.class.name }#run_on_deletion with #{ deletions.inspect }"
|
|
||||||
run_supervised_task(guard, :run_on_deletion, deletions)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Detects the paths that have changed.
|
|
||||||
#
|
|
||||||
# Deleted paths are prefixed by an exclamation point.
|
|
||||||
# @see Guard::Listener#modified_files
|
|
||||||
#
|
|
||||||
# @param [Array<String>] paths the watched paths
|
|
||||||
# @return [Array<String>] the changed paths
|
|
||||||
#
|
|
||||||
def changed_paths(paths)
|
|
||||||
paths.select { |f| !f.start_with?('!') }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Detects the paths that have been deleted.
|
|
||||||
#
|
|
||||||
# Deleted paths are prefixed by an exclamation point.
|
|
||||||
# @see Guard::Listener#modified_files
|
|
||||||
#
|
|
||||||
# @param [Array<String>] paths the watched paths
|
|
||||||
# @return [Array<String>] the deleted paths
|
|
||||||
#
|
|
||||||
def deleted_paths(paths)
|
|
||||||
paths.select { |f| f.start_with?('!') }.map { |f| f.slice(1..-1) }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Run a Guard task, but remove the Guard when his work leads to a system failure.
|
|
||||||
#
|
|
||||||
# When the Group has `:halt_on_fail` disabled, we've to catch `:task_has_failed`
|
|
||||||
# here in order to avoid an uncaught throw error.
|
|
||||||
#
|
|
||||||
# @param [Guard::Guard] guard the Guard to execute
|
|
||||||
# @param [Symbol] task the task to run
|
|
||||||
# @param [Array] args the arguments for the task
|
|
||||||
# @raise [:task_has_failed] when task has failed
|
|
||||||
#
|
|
||||||
def run_supervised_task(guard, task, *args)
|
|
||||||
catch guard_symbol(guard) do
|
|
||||||
guard.hook("#{ task }_begin", *args)
|
|
||||||
result = guard.send(task, *args)
|
|
||||||
guard.hook("#{ task }_end", result)
|
|
||||||
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
rescue Exception => ex
|
|
||||||
UI.error("#{ guard.class.name } failed to achieve its <#{ task.to_s }>, exception was:" +
|
|
||||||
"\n#{ ex.class }: #{ ex.message }\n#{ ex.backtrace.join("\n") }")
|
|
||||||
|
|
||||||
guards.delete guard
|
|
||||||
UI.info("\n#{ guard.class.name } has just been fired")
|
|
||||||
|
|
||||||
ex
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the symbol we have to catch when running a supervised task.
|
|
||||||
# If we are within a Guard group that has the `:halt_on_fail`
|
|
||||||
# option set, we do NOT catch it here, it will be catched at the
|
|
||||||
# group level.
|
|
||||||
#
|
|
||||||
# @see .run_guard_task
|
|
||||||
#
|
|
||||||
# @param [Guard::Guard] guard the Guard to execute
|
|
||||||
# @return [Symbol] the symbol to catch
|
|
||||||
#
|
|
||||||
def guard_symbol(guard)
|
|
||||||
if guard.group.class == Symbol
|
|
||||||
group = groups(guard.group)
|
|
||||||
group.options[:halt_on_fail] ? :no_catch : :task_has_failed
|
|
||||||
else
|
|
||||||
:task_has_failed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add a Guard to use.
|
|
||||||
#
|
|
||||||
# @param [String] name the Guard name
|
|
||||||
# @param [Array<Watcher>] watchers the list of declared watchers
|
|
||||||
# @param [Array<Hash>] callbacks the list of callbacks
|
|
||||||
# @param [Hash] options the Guard options (see the given Guard documentation)
|
|
||||||
#
|
|
||||||
def add_guard(name, watchers = [], callbacks = [], options = {})
|
|
||||||
if name.to_sym == :ego
|
|
||||||
UI.deprecation('Guard::Ego is now part of Guard. You can remove it from your Guardfile.')
|
|
||||||
else
|
|
||||||
guard_class = get_guard_class(name)
|
|
||||||
callbacks.each { |callback| Hook.add_callback(callback[:listener], guard_class, callback[:events]) }
|
|
||||||
@guards << guard_class.new(watchers, options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add a Guard group.
|
|
||||||
#
|
|
||||||
# @param [String] name the group name
|
|
||||||
# @option options [Boolean] halt_on_fail if a task execution
|
|
||||||
# should be halted for all Guards in this group if one Guard throws `:task_has_failed`
|
|
||||||
# @return [Guard::Group] the group added (or retrieved from the `@groups` variable if already present)
|
|
||||||
#
|
|
||||||
def add_group(name, options = {})
|
|
||||||
group = groups(name)
|
|
||||||
if group.nil?
|
|
||||||
group = Group.new(name, options)
|
|
||||||
@groups << group
|
|
||||||
end
|
|
||||||
group
|
|
||||||
end
|
|
||||||
|
|
||||||
# Tries to load the Guard main class.
|
|
||||||
#
|
|
||||||
# @param [String] name the name of the Guard
|
|
||||||
# @return [Class, nil] the loaded class
|
|
||||||
#
|
|
||||||
def get_guard_class(name)
|
|
||||||
name = name.to_s
|
|
||||||
try_require = false
|
|
||||||
const_name = name.downcase.gsub('-', '')
|
|
||||||
begin
|
|
||||||
require "guard/#{ name.downcase }" if try_require
|
|
||||||
self.const_get(self.constants.find { |c| c.to_s.downcase == const_name })
|
|
||||||
rescue TypeError
|
|
||||||
unless try_require
|
|
||||||
try_require = true
|
|
||||||
retry
|
|
||||||
else
|
|
||||||
UI.error "Could not find class Guard::#{ const_name.capitalize }"
|
|
||||||
end
|
end
|
||||||
rescue LoadError => loadError
|
listener.start
|
||||||
UI.error "Could not load 'guard/#{ name.downcase }' or find class Guard::#{ const_name.capitalize }"
|
|
||||||
UI.error loadError.to_s
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Locate a path to a Guard gem.
|
def add_guard(name, watchers = [], options = {})
|
||||||
#
|
guard_class = get_guard_class(name)
|
||||||
# @param [String] name the name of the Guard without the prefix `guard-`
|
@guards << guard_class.new(watchers, options)
|
||||||
# @return [String] the full path to the Guard gem
|
end
|
||||||
#
|
|
||||||
|
def get_guard_class(name)
|
||||||
|
require "guard/#{name.downcase}"
|
||||||
|
klasses = []
|
||||||
|
ObjectSpace.each_object(Class) do |klass|
|
||||||
|
klasses << klass if klass.to_s.downcase.match "^guard::#{name.downcase}"
|
||||||
|
end
|
||||||
|
klasses.first
|
||||||
|
rescue LoadError
|
||||||
|
UI.error "Could not find gem 'guard-#{name}' in the current Gemfile."
|
||||||
|
end
|
||||||
|
|
||||||
|
# Let a guard execute his task but
|
||||||
|
# fire it if his work lead to system failure
|
||||||
|
def supervised_task(guard, task_to_supervise, *args)
|
||||||
|
if !guard.respond_to(:"#{task_to_supervise}?") || guard.send(:"#{task_to_supervise}?")
|
||||||
|
guard.send(task_to_supervise, *args)
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
rescue Exception
|
||||||
|
UI.error("#{guard.class.name} guard failed to achieve its <#{task_to_supervise.to_s}> command: #{$!}")
|
||||||
|
::Guard.guards.delete guard
|
||||||
|
UI.info("Guard #{guard.class.name} has just been fired")
|
||||||
|
return $!
|
||||||
|
end
|
||||||
|
|
||||||
def locate_guard(name)
|
def locate_guard(name)
|
||||||
if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
|
`gem open guard-#{name} --latest --command echo`.chomp
|
||||||
Gem::Specification.find_by_name("guard-#{ name }").full_gem_path
|
|
||||||
else
|
|
||||||
Gem.source_index.find_name("guard-#{ name }").last.full_gem_path
|
|
||||||
end
|
|
||||||
rescue
|
rescue
|
||||||
UI.error "Could not find 'guard-#{ name }' gem path."
|
UI.error "Could not find 'guard-#{name}' gem path."
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a list of guard Gem names installed locally.
|
def run
|
||||||
#
|
listener.stop
|
||||||
# @return [Array<String>] a list of guard gem names
|
UI.clear if options[:clear]
|
||||||
#
|
begin
|
||||||
def guard_gem_names
|
yield
|
||||||
if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
|
rescue Interrupt
|
||||||
Gem::Specification.find_all.select { |x| x.name =~ /^guard-/ }
|
|
||||||
else
|
|
||||||
Gem.source_index.find_name(/^guard-/)
|
|
||||||
end.map { |x| x.name.sub /^guard-/, '' }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds a command logger in debug mode. This wraps common command
|
|
||||||
# execution functions and logs the executed command before execution.
|
|
||||||
#
|
|
||||||
def debug_command_execution
|
|
||||||
Kernel.send(:alias_method, :original_system, :system)
|
|
||||||
Kernel.send(:define_method, :system) do |command, *args|
|
|
||||||
::Guard::UI.debug "Command execution: #{ command } #{ args.join(' ') }"
|
|
||||||
original_system command, *args
|
|
||||||
end
|
|
||||||
|
|
||||||
Kernel.send(:alias_method, :original_backtick, :'`')
|
|
||||||
Kernel.send(:define_method, :'`') do |command|
|
|
||||||
::Guard::UI.debug "Command execution: #{ command }"
|
|
||||||
original_backtick command
|
|
||||||
end
|
end
|
||||||
|
listener.start
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
129
lib/guard/cli.rb
129
lib/guard/cli.rb
@ -2,118 +2,37 @@ require 'thor'
|
|||||||
require 'guard/version'
|
require 'guard/version'
|
||||||
|
|
||||||
module Guard
|
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
|
class CLI < Thor
|
||||||
|
|
||||||
default_task :start
|
default_task :start
|
||||||
|
|
||||||
desc 'start', 'Starts Guard'
|
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 :clear,
|
method_option :debug, :type => :boolean, :default => false, :aliases => '-d', :banner => "Print debug messages"
|
||||||
: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
|
|
||||||
#
|
|
||||||
def start
|
def start
|
||||||
::Guard.start(options)
|
::Guard.start(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'list', 'Lists guards that can be used with init'
|
desc "version", "Prints the guard's version information"
|
||||||
|
|
||||||
# List the Guards that are available for use in your system and marks
|
|
||||||
# those that are currently used in your `Guardfile`.
|
|
||||||
#
|
|
||||||
# @see Guard::DslDescriber.list
|
|
||||||
#
|
|
||||||
def list
|
|
||||||
::Guard::DslDescriber.list(options)
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'version', 'Show the Guard version'
|
|
||||||
map %w(-v --version) => :version
|
|
||||||
|
|
||||||
# Shows the current version of Guard.
|
|
||||||
#
|
|
||||||
# @see Guard::VERSION
|
|
||||||
#
|
|
||||||
def version
|
def version
|
||||||
::Guard::UI.info "Guard version #{ Guard::VERSION }"
|
::Guard::UI.info "Guard version #{Guard::VERSION}"
|
||||||
end
|
end
|
||||||
|
map %w(-v --version) => :version
|
||||||
desc 'init [GUARD]', 'Generates a Guardfile at the current working directory, or insert the given GUARD to an existing Guardfile'
|
|
||||||
|
desc "init [GUARD]", "Generates a Guardfile into the current working directory, or add it given guard"
|
||||||
# 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
|
|
||||||
#
|
|
||||||
def init(guard_name = nil)
|
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
|
end
|
||||||
|
|
||||||
desc 'show', 'Show all defined Guards and their options'
|
|
||||||
map %w(-T) => :show
|
|
||||||
|
|
||||||
# Shows all Guards and their options that are defined in
|
|
||||||
# the `Guardfile`.
|
|
||||||
#
|
|
||||||
# @see Guard::DslDescriber.show
|
|
||||||
#
|
|
||||||
def show
|
|
||||||
::Guard::DslDescriber.show(options)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
381
lib/guard/dsl.rb
381
lib/guard/dsl.rb
@ -1,370 +1,35 @@
|
|||||||
module Guard
|
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 Dsl
|
||||||
class << self
|
|
||||||
|
def self.evaluate_guardfile
|
||||||
@@options = nil
|
guardfile = "#{Dir.pwd}/Guardfile"
|
||||||
|
if File.exists? guardfile
|
||||||
# Evaluate the DSL methods in the `Guardfile`.
|
begin
|
||||||
#
|
dsl = new
|
||||||
# @option options [Array<Symbol,String>] groups the groups to evaluate
|
dsl.instance_eval(File.read(guardfile.to_s), guardfile.to_s, 1)
|
||||||
# @option options [String] guardfile the path to a valid Guardfile
|
rescue
|
||||||
# @option options [String] guardfile_contents a string representing the content of a valid Guardfile
|
UI.error "Invalid Guardfile, original error is:\n#{$!}"
|
||||||
# @raise [ArgumentError] when options are not a Hash
|
|
||||||
#
|
|
||||||
def evaluate_guardfile(options = {})
|
|
||||||
raise ArgumentError.new('No option hash passed to evaluate_guardfile!') unless options.is_a?(Hash)
|
|
||||||
|
|
||||||
@@options = options.dup
|
|
||||||
|
|
||||||
fetch_guardfile_contents
|
|
||||||
instance_eval_guardfile(guardfile_contents_with_user_config)
|
|
||||||
|
|
||||||
UI.error 'No guards found in Guardfile, please add at least one.' if !::Guard.guards.nil? && ::Guard.guards.empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Re-evaluate the `Guardfile` to update the current Guard configuration.
|
|
||||||
#
|
|
||||||
def reevaluate_guardfile
|
|
||||||
::Guard.guards.clear
|
|
||||||
::Guard.groups.clear
|
|
||||||
@@options.delete(:guardfile_contents)
|
|
||||||
Dsl.evaluate_guardfile(@@options)
|
|
||||||
msg = 'Guardfile has been re-evaluated.'
|
|
||||||
UI.info(msg)
|
|
||||||
Notifier.notify(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Evaluate the content of the `Guardfile`.
|
|
||||||
#
|
|
||||||
# @param [String] contents the content to evaluate.
|
|
||||||
#
|
|
||||||
def instance_eval_guardfile(contents)
|
|
||||||
new.instance_eval(contents, @@options[:guardfile_path], 1)
|
|
||||||
rescue
|
|
||||||
UI.error "Invalid Guardfile, original error is:\n#{ $! }"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test if the current `Guardfile` contains a specific Guard.
|
|
||||||
#
|
|
||||||
# @param [String] guard_name the name of the Guard
|
|
||||||
# @return [Boolean] whether the Guard has been declared
|
|
||||||
#
|
|
||||||
def guardfile_include?(guard_name)
|
|
||||||
guardfile_contents.match(/^guard\s*\(?\s*['":]#{ guard_name }['"]?/)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Read the current `Guardfile` content.
|
|
||||||
#
|
|
||||||
# @param [String] the path to the Guardfile
|
|
||||||
#
|
|
||||||
def read_guardfile(guardfile_path)
|
|
||||||
@@options[:guardfile_path] = guardfile_path
|
|
||||||
@@options[:guardfile_contents] = File.read(guardfile_path)
|
|
||||||
rescue
|
|
||||||
UI.error("Error reading file #{ guardfile_path }")
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the content to evaluate and stores it into
|
|
||||||
# the options as `:guardfile_contents`.
|
|
||||||
#
|
|
||||||
def fetch_guardfile_contents
|
|
||||||
if @@options[:guardfile_contents]
|
|
||||||
UI.info 'Using inline Guardfile.'
|
|
||||||
@@options[:guardfile_path] = 'Inline Guardfile'
|
|
||||||
|
|
||||||
elsif @@options[:guardfile]
|
|
||||||
if File.exist?(@@options[:guardfile])
|
|
||||||
read_guardfile(@@options[:guardfile])
|
|
||||||
UI.info "Using Guardfile at #{ @@options[:guardfile] }."
|
|
||||||
else
|
|
||||||
UI.error "No Guardfile exists at #{ @@options[:guardfile] }."
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
if File.exist?(guardfile_default_path)
|
|
||||||
read_guardfile(guardfile_default_path)
|
|
||||||
else
|
|
||||||
UI.error 'No Guardfile found, please create one with `guard init`.'
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
unless guardfile_contents_usable?
|
|
||||||
UI.error "The command file(#{ @@options[:guardfile] }) seems to be empty."
|
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
end
|
else
|
||||||
|
UI.error "No Guardfile in current folder, please create one."
|
||||||
# Get the content of the `Guardfile`.
|
exit 1
|
||||||
#
|
|
||||||
# @return [String] the Guardfile content
|
|
||||||
#
|
|
||||||
def guardfile_contents
|
|
||||||
@@options ? @@options[:guardfile_contents] : ''
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the content of the `Guardfile` and the global
|
|
||||||
# user configuration file.
|
|
||||||
#
|
|
||||||
# @see #user_config_path
|
|
||||||
#
|
|
||||||
# @return [String] the Guardfile content
|
|
||||||
#
|
|
||||||
def guardfile_contents_with_user_config
|
|
||||||
config = File.read(user_config_path) if File.exist?(user_config_path)
|
|
||||||
[guardfile_contents, config].join("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the file path to the project `Guardfile`.
|
|
||||||
#
|
|
||||||
# @return [String] the path to the Guardfile
|
|
||||||
#
|
|
||||||
def guardfile_path
|
|
||||||
@@options ? @@options[:guardfile_path] : ''
|
|
||||||
end
|
|
||||||
|
|
||||||
# Tests if the current `Guardfile` content is usable.
|
|
||||||
#
|
|
||||||
# @return [Boolean] if the Guardfile is usable
|
|
||||||
#
|
|
||||||
def guardfile_contents_usable?
|
|
||||||
guardfile_contents && guardfile_contents.size >= 'guard :a'.size # Smallest Guard definition
|
|
||||||
end
|
|
||||||
|
|
||||||
# Gets the default path of the `Guardfile`. This returns the `Guardfile`
|
|
||||||
# from the current directory when existing, or the global `.Guardfile`
|
|
||||||
# at the home directory.
|
|
||||||
#
|
|
||||||
# @return [String] the path to the Guardfile
|
|
||||||
#
|
|
||||||
def guardfile_default_path
|
|
||||||
File.exist?(local_guardfile_path) ? local_guardfile_path : home_guardfile_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# The path to the `Guardfile` that is located at
|
|
||||||
# the directory, where Guard has been started from.
|
|
||||||
#
|
|
||||||
# @param [String] the path to the local Guardfile
|
|
||||||
#
|
|
||||||
def local_guardfile_path
|
|
||||||
File.join(Dir.pwd, 'Guardfile')
|
|
||||||
end
|
|
||||||
|
|
||||||
# The path to the `.Guardfile` that is located at
|
|
||||||
# the users home directory.
|
|
||||||
#
|
|
||||||
# @param [String] the path to ~/.Guardfile
|
|
||||||
#
|
|
||||||
def home_guardfile_path
|
|
||||||
File.expand_path(File.join('~', '.Guardfile'))
|
|
||||||
end
|
|
||||||
|
|
||||||
# The path to the user configuration `.guard.rb`
|
|
||||||
# that is located at the users home directory.
|
|
||||||
#
|
|
||||||
# @param [String] the path to ~/.guard.rb
|
|
||||||
#
|
|
||||||
def user_config_path
|
|
||||||
File.expand_path(File.join('~', '.guard.rb'))
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# Declares a group of guards to be run with `guard start --group group_name`.
|
|
||||||
#
|
|
||||||
# @example Declare two groups of Guards
|
|
||||||
#
|
|
||||||
# group 'backend' do
|
|
||||||
# guard 'spork'
|
|
||||||
# guard 'rspec'
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# group 'frontend' do
|
|
||||||
# guard 'passenger'
|
|
||||||
# guard 'livereload'
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# @param [Symbol, String] name the group's name called from the CLI
|
|
||||||
# @param [Hash] options the options accepted by the group
|
|
||||||
# @yield a block where you can declare several guards
|
|
||||||
#
|
|
||||||
# @see Guard.add_group
|
|
||||||
# @see Dsl#guard
|
|
||||||
# @see Guard::DslDescriber
|
|
||||||
#
|
|
||||||
def group(name, options = {})
|
|
||||||
@groups = @@options[:group] || []
|
|
||||||
name = name.to_sym
|
|
||||||
|
|
||||||
if block_given? && (@groups.empty? || @groups.map(&:to_sym).include?(name))
|
|
||||||
::Guard.add_group(name.to_s.downcase, options)
|
|
||||||
@current_group = name
|
|
||||||
|
|
||||||
yield if block_given?
|
|
||||||
|
|
||||||
@current_group = nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Declare a guard to be used when running `guard start`.
|
def self.guardfile_included?(guard_name)
|
||||||
#
|
File.read('Guardfile').include?("guard '#{guard_name}'")
|
||||||
# The name parameter is usually the name of the gem without
|
|
||||||
# the 'guard-' prefix.
|
|
||||||
#
|
|
||||||
# The available options are different for each Guard implementation.
|
|
||||||
#
|
|
||||||
# @example Declare a Guard
|
|
||||||
#
|
|
||||||
# guard 'rspec' do
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# @param [String] name the Guard name
|
|
||||||
# @param [Hash] options the options accepted by the Guard
|
|
||||||
# @yield a block where you can declare several watch patterns and actions
|
|
||||||
#
|
|
||||||
# @see Guard.add_guard
|
|
||||||
# @see Dsl#group
|
|
||||||
# @see Dsl#watch
|
|
||||||
# @see Guard::DslDescriber
|
|
||||||
#
|
|
||||||
def guard(name, options = {})
|
|
||||||
@watchers = []
|
|
||||||
@callbacks = []
|
|
||||||
|
|
||||||
yield if block_given?
|
|
||||||
|
|
||||||
options.update(:group => (@current_group || :default))
|
|
||||||
::Guard.add_guard(name.to_s.downcase, @watchers, @callbacks, options)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Define a pattern to be watched in order to run actions on file modification.
|
def guard(name, options = {}, &definition)
|
||||||
#
|
@watchers = []
|
||||||
# @example Declare watchers for a Guard
|
definition.call if definition
|
||||||
#
|
::Guard.add_guard(name, @watchers, options)
|
||||||
# guard 'rspec' do
|
end
|
||||||
# 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)
|
def watch(pattern, &action)
|
||||||
@watchers << ::Guard::Watcher.new(pattern, action)
|
@watchers << ::Guard::Watcher.new(pattern, action)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Define a callback to execute arbitrary code before or after any of
|
|
||||||
# the `start`, `stop`, `reload`, `run_all` and `run_on_change` guards' method.
|
|
||||||
#
|
|
||||||
# @param [Array] args the callback arguments
|
|
||||||
# @yield a block with listeners
|
|
||||||
#
|
|
||||||
# @see Guard::Hook
|
|
||||||
#
|
|
||||||
def callback(*args, &listener)
|
|
||||||
listener, events = args.size > 1 ? args : [listener, args[0]]
|
|
||||||
@callbacks << { :events => events, :listener => listener }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Ignore certain paths globally.
|
|
||||||
#
|
|
||||||
# @example Ignore some paths
|
|
||||||
# ignore_paths ".git", ".svn"
|
|
||||||
#
|
|
||||||
# @param [Array] paths the list of paths to ignore
|
|
||||||
#
|
|
||||||
# @see Guard::Listener
|
|
||||||
#
|
|
||||||
def ignore_paths(*paths)
|
|
||||||
UI.info "Ignoring paths: #{ paths.join(', ') }"
|
|
||||||
::Guard.listener.ignore_paths.push(*paths)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,150 +0,0 @@
|
|||||||
require 'guard/dsl'
|
|
||||||
|
|
||||||
module Guard
|
|
||||||
|
|
||||||
autoload :UI, 'guard/ui'
|
|
||||||
|
|
||||||
# The DslDescriber overrides methods to create an internal structure
|
|
||||||
# of the Guardfile that is used in some inspection utility methods
|
|
||||||
# like the CLI commands `show` and `list`.
|
|
||||||
#
|
|
||||||
# @see Guard::Dsl
|
|
||||||
# @see Guard::CLI
|
|
||||||
#
|
|
||||||
class DslDescriber < Dsl
|
|
||||||
|
|
||||||
class << self
|
|
||||||
|
|
||||||
# Evaluate the DSL methods in the `Guardfile`.
|
|
||||||
#
|
|
||||||
# @option options [Array<Symbol,String>] groups the groups to evaluate
|
|
||||||
# @option options [String] guardfile the path to a valid Guardfile
|
|
||||||
# @option options [String] guardfile_contents a string representing the content of a valid Guardfile
|
|
||||||
# @raise [ArgumentError] when options are not a Hash
|
|
||||||
#
|
|
||||||
def evaluate_guardfile(options = {})
|
|
||||||
@@guardfile_structure = [{ :guards => [] }]
|
|
||||||
super options
|
|
||||||
end
|
|
||||||
|
|
||||||
# List the Guards that are available for use in your system and marks
|
|
||||||
# those that are currently used in your `Guardfile`.
|
|
||||||
#
|
|
||||||
# @example Guard list output
|
|
||||||
#
|
|
||||||
# Available guards:
|
|
||||||
# bundler *
|
|
||||||
# livereload
|
|
||||||
# ronn
|
|
||||||
# rspec *
|
|
||||||
# spork
|
|
||||||
#
|
|
||||||
# See also https://github.com/guard/guard/wiki/List-of-available-Guards
|
|
||||||
# * denotes ones already in your Guardfile
|
|
||||||
#
|
|
||||||
# @param [Hash] options the Guard options
|
|
||||||
#
|
|
||||||
def list(options)
|
|
||||||
evaluate_guardfile(options)
|
|
||||||
|
|
||||||
installed = guardfile_structure.inject([]) do |installed, group|
|
|
||||||
group[:guards].each { |guard| installed << guard[:name] } if group[:guards]
|
|
||||||
installed
|
|
||||||
end
|
|
||||||
|
|
||||||
UI.info 'Available guards:'
|
|
||||||
|
|
||||||
::Guard.guard_gem_names.sort.uniq.each do |name|
|
|
||||||
UI.info " #{ name }#{ installed.include?(name) ? '*' : '' }"
|
|
||||||
end
|
|
||||||
|
|
||||||
UI.info ''
|
|
||||||
UI.info 'See also https://github.com/guard/guard/wiki/List-of-available-Guards'
|
|
||||||
UI.info '* denotes ones already in your Guardfile'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Shows all Guards and their options that are defined in
|
|
||||||
# the `Guardfile`.
|
|
||||||
#
|
|
||||||
# @example guard show output
|
|
||||||
#
|
|
||||||
# (global):
|
|
||||||
# bundler
|
|
||||||
# coffeescript: input => "app/assets/javascripts", noop => true
|
|
||||||
# jasmine
|
|
||||||
# rspec: cli => "--fail-fast --format Fuubar
|
|
||||||
#
|
|
||||||
# @param [Hash] options the Guard options
|
|
||||||
#
|
|
||||||
def show(options)
|
|
||||||
evaluate_guardfile(options)
|
|
||||||
|
|
||||||
guardfile_structure.each do |group|
|
|
||||||
unless group[:guards].empty?
|
|
||||||
if group[:group]
|
|
||||||
UI.info "Group #{ group[:group] }:"
|
|
||||||
else
|
|
||||||
UI.info '(global):'
|
|
||||||
end
|
|
||||||
|
|
||||||
group[:guards].each do |guard|
|
|
||||||
line = " #{ guard[:name] }"
|
|
||||||
|
|
||||||
unless guard[:options].empty?
|
|
||||||
line += ": #{ guard[:options].sort.collect { |k, v| "#{ k } => #{ v.inspect }" }.join(', ') }"
|
|
||||||
end
|
|
||||||
|
|
||||||
UI.info line
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
UI.info ''
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Get the Guardfile structure.
|
|
||||||
#
|
|
||||||
# @return [Array<Hash>] the structure
|
|
||||||
#
|
|
||||||
def guardfile_structure
|
|
||||||
@@guardfile_structure
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Declares a group of guards.
|
|
||||||
#
|
|
||||||
# @param [String] name the group's name called from the CLI
|
|
||||||
# @yield a block where you can declare several guards
|
|
||||||
#
|
|
||||||
# @see Guard::Dsl#group
|
|
||||||
#
|
|
||||||
def group(name)
|
|
||||||
@@guardfile_structure << { :group => name.to_sym, :guards => [] }
|
|
||||||
@group = true
|
|
||||||
|
|
||||||
yield if block_given?
|
|
||||||
|
|
||||||
@group = false
|
|
||||||
end
|
|
||||||
|
|
||||||
# Declares a Guard.
|
|
||||||
#
|
|
||||||
# @param [String] name the Guard name
|
|
||||||
# @param [Hash] options the options accepted by the Guard
|
|
||||||
# @yield a block where you can declare several watch patterns and actions
|
|
||||||
#
|
|
||||||
# @see Guard::Dsl#guard
|
|
||||||
#
|
|
||||||
def guard(name, options = { })
|
|
||||||
node = (@group ? @@guardfile_structure.last : @@guardfile_structure.first)
|
|
||||||
|
|
||||||
node[:guards] << { :name => name, :options => options }
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,37 +0,0 @@
|
|||||||
module Guard
|
|
||||||
|
|
||||||
# A group of Guards. There are two reasons why you want to group your guards:
|
|
||||||
#
|
|
||||||
# - You can start only certain Groups from the command line by passing the `--group` option.
|
|
||||||
# - Abort task execution chain on failure within a group.
|
|
||||||
#
|
|
||||||
# @example Group that aborts on failure
|
|
||||||
#
|
|
||||||
# group :frontend, :halt_on_fail => true do
|
|
||||||
# guard 'coffeescript', :input => 'spec/coffeescripts', :output => 'spec/javascripts'
|
|
||||||
# guard 'jasmine-headless-webkit' do
|
|
||||||
# watch(%r{^spec/javascripts/(.*)\..*}) { |m| newest_js_file("spec/javascripts/#{m[1]}_spec") }
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# @see Guard::CLI
|
|
||||||
#
|
|
||||||
class Group
|
|
||||||
|
|
||||||
attr_accessor :name, :options
|
|
||||||
|
|
||||||
# Initialize a Group.
|
|
||||||
#
|
|
||||||
# @param [String] name the name of the group
|
|
||||||
# @param [Hash] options the group options
|
|
||||||
# @option options [Boolean] halt_on_fail if a task execution
|
|
||||||
# should be halted for all Guards in this group if one Guard throws `:task_has_failed`
|
|
||||||
#
|
|
||||||
def initialize(name, options = {})
|
|
||||||
@name = name.to_sym
|
|
||||||
@options = options
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,129 +1,80 @@
|
|||||||
module Guard
|
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
|
class Guard
|
||||||
include Hook
|
attr_accessor :watchers, :options
|
||||||
|
|
||||||
attr_accessor :watchers, :options, :group
|
DEFAULT_OPTIONS = {
|
||||||
|
:reload => {:at_start => false, :disable => false},
|
||||||
# Initialize a Guard.
|
:run_all => {:at_start => false, :disable => false}
|
||||||
#
|
}.freeze
|
||||||
# @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 = {})
|
def initialize(watchers = [], options = {})
|
||||||
@group = options[:group] ? options.delete(:group).to_sym : :default
|
@watchers, @options = watchers, DEFAULT_OPTIONS.merge(options)
|
||||||
@watchers, @options = watchers, options
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Initialize the Guard. This will copy the Guardfile template inside the Guard gem.
|
# Guardfile template needed inside 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
|
|
||||||
#
|
|
||||||
def self.init(name)
|
def self.init(name)
|
||||||
if ::Guard::Dsl.guardfile_include?(name)
|
if ::Guard::Dsl.guardfile_included?(name)
|
||||||
::Guard::UI.info "Guardfile already includes #{ name } guard"
|
::Guard::UI.info "Guardfile already include #{name} guard"
|
||||||
else
|
else
|
||||||
content = File.read('Guardfile')
|
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|
|
File.open('Guardfile', 'wb') do |f|
|
||||||
f.puts(content)
|
f.puts content
|
||||||
f.puts("")
|
f.puts ""
|
||||||
f.puts(guard)
|
f.puts guard
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
# Call once when Guard starts. Please override initialize method to init stuff.
|
# ================
|
||||||
#
|
# = Guard method =
|
||||||
# @raise [:task_has_failed] when start has failed
|
# ================
|
||||||
# @return [Object] the task result
|
|
||||||
#
|
# Call once when guard starts
|
||||||
|
# Please override initialize method to init stuff
|
||||||
def start
|
def start
|
||||||
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Called when `stop|quit|exit|s|q|e + enter` is pressed (when Guard quits).
|
# Call once when guard quit
|
||||||
#
|
|
||||||
# @raise [:task_has_failed] when stop has failed
|
|
||||||
# @return [Object] the task result
|
|
||||||
#
|
|
||||||
def stop
|
def stop
|
||||||
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Called when `reload|r|z + enter` is pressed.
|
# Should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
||||||
# 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
|
|
||||||
#
|
|
||||||
def reload
|
def reload
|
||||||
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Called when just `enter` is pressed
|
# disable reload method if return true. When is overwriten, must return false if `super' return false
|
||||||
# This method should be principally used for long action like running all specs/tests/...
|
def reload?
|
||||||
#
|
options[:reload] == true || (options[:reload].is_a?(Hash) && !options[:reload][:disable])
|
||||||
# @raise [:task_has_failed] when run_all has failed
|
end
|
||||||
# @return [Object] the task result
|
|
||||||
#
|
# enable call of reload method after start method if return true. When is overwriten, must return false if `super' return false
|
||||||
|
def reload_at_start?
|
||||||
|
options[:reload].is_a?(Hash) && !!options[:reload][:at_start]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Should be principally used for long action like running all specs/tests/...
|
||||||
def run_all
|
def run_all
|
||||||
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Called on file(s) modifications that the Guard watches.
|
# disable run_all method if return true. When is overwriten, must return false if `super' return false
|
||||||
#
|
def run_all?
|
||||||
# @param [Array<String>] paths the changes files or paths
|
options[:run_all] == true || (options[:run_all].is_a?(Hash) && !options[:run_all][:disable])
|
||||||
# @raise [:task_has_failed] when run_on_change has failed
|
end
|
||||||
# @return [Object] the task result
|
|
||||||
#
|
# enable call of run_all method after start method if return true. When is overwriten, must return false if `super' return false
|
||||||
|
def run_all_at_start?
|
||||||
|
options[:run_all].is_a?(Hash) && !!options[:run_all][:at_start]
|
||||||
|
end
|
||||||
|
|
||||||
def run_on_change(paths)
|
def run_on_change(paths)
|
||||||
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Called on file(s) deletions that the Guard watches.
|
|
||||||
#
|
|
||||||
# @param [Array<String>] paths the deleted files or paths
|
|
||||||
# @raise [:task_has_failed] when run_on_change has failed
|
|
||||||
# @return [Object] the task result
|
|
||||||
#
|
|
||||||
def run_on_deletion(paths)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
|
@ -1,118 +0,0 @@
|
|||||||
module Guard
|
|
||||||
|
|
||||||
# Guard has a hook mechanism that allows you to insert callbacks for individual Guards.
|
|
||||||
# By default, each of the Guard instance methods has a "_begin" and an "_end" hook.
|
|
||||||
# For example, the Guard::Guard#start method has a :start_begin hook that is runs immediately
|
|
||||||
# before Guard::Guard#start, and a :start_end hook that runs immediately after Guard::Guard#start.
|
|
||||||
#
|
|
||||||
# Read more about [hooks and callbacks on the wiki](https://github.com/guard/guard/wiki/Hooks-and-callbacks).
|
|
||||||
#
|
|
||||||
module Hook
|
|
||||||
|
|
||||||
# The Hook module gets included.
|
|
||||||
#
|
|
||||||
# @param [Class] base the class that includes the module
|
|
||||||
#
|
|
||||||
def self.included(base)
|
|
||||||
base.send :include, InstanceMethods
|
|
||||||
end
|
|
||||||
|
|
||||||
# Instance methods that gets included in the base class.
|
|
||||||
#
|
|
||||||
module InstanceMethods
|
|
||||||
|
|
||||||
# When event is a Symbol, {#hook} will generate a hook name
|
|
||||||
# by concatenating the method name from where {#hook} is called
|
|
||||||
# with the given Symbol.
|
|
||||||
#
|
|
||||||
# @example Add a hook with a Symbol
|
|
||||||
#
|
|
||||||
# def run_all
|
|
||||||
# hook :foo
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# Here, when {Guard::Guard#run_all} is called, {#hook} will notify callbacks
|
|
||||||
# registered for the "run_all_foo" event.
|
|
||||||
#
|
|
||||||
# When event is a String, {#hook} will directly turn the String
|
|
||||||
# into a Symbol.
|
|
||||||
#
|
|
||||||
# @example Add a hook with a String
|
|
||||||
#
|
|
||||||
# def run_all
|
|
||||||
# hook "foo_bar"
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# When {Guard::Guard#run_all} is called, {#hook} will notify callbacks
|
|
||||||
# registered for the "foo_bar" event.
|
|
||||||
#
|
|
||||||
# @param [Symbol, String] event the name of the Guard event
|
|
||||||
# @param [Array] args the parameters are passed as is to the callbacks registered for the given event.
|
|
||||||
#
|
|
||||||
def hook(event, *args)
|
|
||||||
hook_name = if event.is_a? Symbol
|
|
||||||
calling_method = caller[0][/`([^']*)'/, 1]
|
|
||||||
"#{ calling_method }_#{ event }"
|
|
||||||
else
|
|
||||||
event
|
|
||||||
end.to_sym
|
|
||||||
|
|
||||||
UI.debug "Hook :#{ hook_name } executed for #{ self.class }"
|
|
||||||
|
|
||||||
Hook.notify(self.class, hook_name, *args)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class << self
|
|
||||||
|
|
||||||
# Get all callbacks.
|
|
||||||
#
|
|
||||||
def callbacks
|
|
||||||
@callbacks ||= Hash.new { |hash, key| hash[key] = [] }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add a callback.
|
|
||||||
#
|
|
||||||
# @param [Block] listener the listener to notify
|
|
||||||
# @param [Guard::Guard] guard_class the Guard class to add the callback
|
|
||||||
# @param [Array<Symbol>] events the events to register
|
|
||||||
#
|
|
||||||
def add_callback(listener, guard_class, events)
|
|
||||||
_events = events.is_a?(Array) ? events : [events]
|
|
||||||
_events.each do |event|
|
|
||||||
callbacks[[guard_class, event]] << listener
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Checks if a callback has been registered.
|
|
||||||
#
|
|
||||||
# @param [Block] listener the listener to notify
|
|
||||||
# @param [Guard::Guard] guard_class the Guard class to add the callback
|
|
||||||
# @param [Symbol] event the event to look for
|
|
||||||
#
|
|
||||||
def has_callback?(listener, guard_class, event)
|
|
||||||
callbacks[[guard_class, event]].include?(listener)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Notify a callback.
|
|
||||||
#
|
|
||||||
# @param [Guard::Guard] guard_class the Guard class to add the callback
|
|
||||||
# @param [Symbol] event the event to trigger
|
|
||||||
# @param [Array] args the arguments for the listener
|
|
||||||
#
|
|
||||||
def notify(guard_class, event, *args)
|
|
||||||
callbacks[[guard_class, event]].each do |listener|
|
|
||||||
listener.call(guard_class, event, *args)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Reset all callbacks.
|
|
||||||
#
|
|
||||||
def reset_callbacks!
|
|
||||||
@callbacks = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,44 +1,29 @@
|
|||||||
module Guard
|
module Guard
|
||||||
|
module Interactor
|
||||||
# The interactor reads user input and triggers
|
|
||||||
# specific action upon them unless its locked.
|
def self.init_signal_traps
|
||||||
#
|
# Run all (Ctrl-\)
|
||||||
# Currently the following actions are implemented:
|
Signal.trap('QUIT') do
|
||||||
#
|
::Guard.run do
|
||||||
# - stop, quit, exit, s, q, e => Exit Guard
|
::Guard.guards.each { |g| ::Guard.supervised_task g, :run_all }
|
||||||
# - reload, r, z => Reload Guard
|
end
|
||||||
# - pause, p => Pause Guard
|
end
|
||||||
# - Everything else => Run all
|
|
||||||
#
|
# Stop (Ctrl-C)
|
||||||
class Interactor
|
Signal.trap('INT') do
|
||||||
# Start the interactor in its own thread.
|
::Guard.listener.stop
|
||||||
#
|
::Guard.guards.each { |g| ::Guard.supervised_task g, :stop }
|
||||||
def start
|
UI.info "Bye bye...", :reset => true
|
||||||
return if ENV["GUARD_ENV"] == 'test'
|
abort("\n")
|
||||||
|
end
|
||||||
if !@thread || @thread.stop?
|
|
||||||
@thread = Thread.new do
|
# Reload (Ctrl-Z)
|
||||||
while entry = $stdin.gets.chomp
|
Signal.trap('TSTP') do
|
||||||
case entry
|
::Guard.run do
|
||||||
when 'stop', 'quit', 'exit', 's', 'q', 'e'
|
::Guard.guards.each { |g| ::Guard.supervised_task g, :reload }
|
||||||
::Guard.stop
|
|
||||||
when 'reload', 'r', 'z'
|
|
||||||
::Guard::Dsl.reevaluate_guardfile
|
|
||||||
::Guard.reload
|
|
||||||
when 'pause', 'p'
|
|
||||||
::Guard.pause
|
|
||||||
else
|
|
||||||
::Guard.run_all
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def stop_if_not_current
|
|
||||||
unless Thread.current == @thread
|
|
||||||
@thread.kill
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -1,350 +1,58 @@
|
|||||||
require 'rbconfig'
|
require 'rbconfig'
|
||||||
require 'digest/sha1'
|
|
||||||
|
|
||||||
module Guard
|
module Guard
|
||||||
|
|
||||||
autoload :Darwin, 'guard/listeners/darwin'
|
autoload :Darwin, 'guard/listeners/darwin'
|
||||||
autoload :Linux, 'guard/listeners/linux'
|
autoload :Linux, 'guard/listeners/linux'
|
||||||
autoload :Windows, 'guard/listeners/windows'
|
|
||||||
autoload :Polling, 'guard/listeners/polling'
|
autoload :Polling, 'guard/listeners/polling'
|
||||||
|
|
||||||
# The Listener is the base class for all listener
|
|
||||||
# implementations.
|
|
||||||
#
|
|
||||||
# @abstract
|
|
||||||
#
|
|
||||||
class Listener
|
class Listener
|
||||||
|
attr_reader :last_event
|
||||||
# Default paths that gets ignored by the listener
|
|
||||||
DEFAULT_IGNORE_PATHS = %w[. .. .bundle .git log tmp vendor]
|
def self.init
|
||||||
|
|
||||||
attr_accessor :changed_files
|
|
||||||
attr_reader :directory, :ignore_paths
|
|
||||||
|
|
||||||
def paused?
|
|
||||||
@paused
|
|
||||||
end
|
|
||||||
|
|
||||||
# Select the appropriate listener implementation for the
|
|
||||||
# current OS and initializes it.
|
|
||||||
#
|
|
||||||
# @param [Array] args the arguments for the listener
|
|
||||||
# @return [Guard::Listener] the chosen listener
|
|
||||||
#
|
|
||||||
def self.select_and_init(*args)
|
|
||||||
if mac? && Darwin.usable?
|
if mac? && Darwin.usable?
|
||||||
Darwin.new(*args)
|
Darwin.new
|
||||||
elsif linux? && Linux.usable?
|
elsif linux? && Linux.usable?
|
||||||
Linux.new(*args)
|
Linux.new
|
||||||
elsif windows? && Windows.usable?
|
|
||||||
Windows.new(*args)
|
|
||||||
else
|
else
|
||||||
UI.info 'Using polling (Please help us to support your system better than that).'
|
UI.info "Using polling (Please help us to support your system better than that.)"
|
||||||
Polling.new(*args)
|
Polling.new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Initialize the listener.
|
def initialize
|
||||||
#
|
|
||||||
# @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)
|
|
||||||
|
|
||||||
update_last_event
|
update_last_event
|
||||||
start_reactor
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Start the listener thread.
|
private
|
||||||
#
|
|
||||||
def start_reactor
|
|
||||||
return if ENV["GUARD_ENV"] == 'test'
|
|
||||||
|
|
||||||
Thread.new do
|
|
||||||
loop do
|
|
||||||
if @changed_files != [] && !@paused
|
|
||||||
changed_files = @changed_files.dup
|
|
||||||
clear_changed_files
|
|
||||||
::Guard.run_on_change(changed_files)
|
|
||||||
else
|
|
||||||
sleep 0.1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Start watching the root directory.
|
|
||||||
#
|
|
||||||
def start
|
|
||||||
watch(@directory)
|
|
||||||
timestamp_files
|
|
||||||
end
|
|
||||||
|
|
||||||
# Stop listening for events.
|
|
||||||
#
|
|
||||||
def stop
|
|
||||||
end
|
|
||||||
|
|
||||||
# Pause the listener to ignore change events.
|
|
||||||
#
|
|
||||||
def pause
|
|
||||||
@paused = true
|
|
||||||
end
|
|
||||||
|
|
||||||
# Unpause the listener to listen again to change events.
|
|
||||||
#
|
|
||||||
def run
|
|
||||||
@paused = false
|
|
||||||
end
|
|
||||||
|
|
||||||
# Clear the list of changed files.
|
|
||||||
#
|
|
||||||
def clear_changed_files
|
|
||||||
@changed_files.clear
|
|
||||||
end
|
|
||||||
|
|
||||||
# Store a listener callback.
|
|
||||||
#
|
|
||||||
# @param [Block] callback the callback to store
|
|
||||||
#
|
|
||||||
def on_change(&callback)
|
|
||||||
@callback = callback
|
|
||||||
end
|
|
||||||
|
|
||||||
# Updates the timestamp of the last event.
|
|
||||||
#
|
|
||||||
def update_last_event
|
|
||||||
@last_event = Time.now
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the modified files.
|
|
||||||
#
|
|
||||||
# If the `:watch_all_modifications` option is true, then moved and
|
|
||||||
# deleted files are also reported, but prefixed by an exclamation point.
|
|
||||||
#
|
|
||||||
# @example Deleted or moved file
|
|
||||||
# !/home/user/dir/file.rb
|
|
||||||
#
|
|
||||||
# @param [Array<String>] dirs the watched directories
|
|
||||||
# @param [Hash] options the listener options
|
|
||||||
# @option options [Symbol] all whether to files in sub directories
|
|
||||||
# @return [Array<String>] paths of files that have been modified
|
|
||||||
#
|
|
||||||
def modified_files(dirs, options = {})
|
def modified_files(dirs, options = {})
|
||||||
last_event = @last_event
|
files = potentially_modified_files(dirs, options).select { |path| File.file?(path) && recent_file?(path) }
|
||||||
files = []
|
files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
|
||||||
if @watch_all_modifications
|
|
||||||
deleted_files = @file_timestamp_hash.collect do |path, ts|
|
|
||||||
unless File.exists?(path)
|
|
||||||
@sha1_checksums_hash.delete(path)
|
|
||||||
@file_timestamp_hash.delete(path)
|
|
||||||
"!#{path}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
files.concat(deleted_files.compact)
|
|
||||||
end
|
|
||||||
update_last_event
|
|
||||||
files.concat(potentially_modified_files(dirs, options).select { |path| file_modified?(path, last_event) })
|
|
||||||
|
|
||||||
relativize_paths(files)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Register a directory to watch.
|
|
||||||
# Must be implemented by the subclasses.
|
|
||||||
#
|
|
||||||
# @param [String] directory the directory to watch
|
|
||||||
#
|
|
||||||
def watch(directory)
|
|
||||||
raise NotImplementedError, "do whatever you want here, given the directory as only argument"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get all files that are in the watched directory.
|
|
||||||
#
|
|
||||||
# @return [Array<String>] the list of files
|
|
||||||
#
|
|
||||||
def all_files
|
|
||||||
potentially_modified_files([@directory], :all => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Scopes all given paths to the current directory.
|
|
||||||
#
|
|
||||||
# @param [Array<String>] paths the paths to change
|
|
||||||
# @return [Array<String>] all paths now relative to the current dir
|
|
||||||
#
|
|
||||||
def relativize_paths(paths)
|
|
||||||
return paths unless relativize_paths?
|
|
||||||
paths.map do |path|
|
|
||||||
path.gsub(%r{^(!)?#{ @directory }/},'\1')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Use paths relative to the current directory.
|
|
||||||
#
|
|
||||||
# @return [Boolean] whether to use relative or absolute paths
|
|
||||||
#
|
|
||||||
def relativize_paths?
|
|
||||||
!!@relativize_paths
|
|
||||||
end
|
|
||||||
|
|
||||||
# Populate initial timestamp file hash to watch for deleted or moved files.
|
|
||||||
#
|
|
||||||
def timestamp_files
|
|
||||||
all_files.each {|path| set_file_timestamp_hash(path, file_timestamp(path)) } if @watch_all_modifications
|
|
||||||
end
|
|
||||||
|
|
||||||
# Removes the ignored paths from the directory list.
|
|
||||||
#
|
|
||||||
# @param [Array<String>] dirs the directory to listen to
|
|
||||||
# @param [Array<String>] ignore_paths the paths to ignore
|
|
||||||
# @return children of the passed dirs that are not in the ignore_paths list
|
|
||||||
#
|
|
||||||
def exclude_ignored_paths(dirs, ignore_paths = self.ignore_paths)
|
|
||||||
Dir.glob(dirs.map { |d| "#{d.sub(%r{/+$}, '')}/*" }, File::FNM_DOTMATCH).reject do |path|
|
|
||||||
ignore_paths.include?(File.basename(path))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Gets a list of files that are in the modified directories.
|
|
||||||
#
|
|
||||||
# @param [Array<String>] dirs the list of directories
|
|
||||||
# @param [Hash] options the find file option
|
|
||||||
# @option options [Symbol] all whether to files in sub directories
|
|
||||||
#
|
|
||||||
def potentially_modified_files(dirs, options = {})
|
def potentially_modified_files(dirs, options = {})
|
||||||
paths = exclude_ignored_paths(dirs)
|
match = options[:all] ? "**/*" : "*"
|
||||||
|
Dir.glob(dirs.map { |dir| "#{dir}#{match}" })
|
||||||
if options[:all]
|
|
||||||
paths.inject([]) do |array, path|
|
|
||||||
if File.file?(path)
|
|
||||||
array << path
|
|
||||||
else
|
|
||||||
array += Dir.glob("#{ path }/**/*", File::FNM_DOTMATCH).select { |p| File.file?(p) }
|
|
||||||
end
|
|
||||||
array
|
|
||||||
end
|
|
||||||
else
|
|
||||||
paths.select { |path| File.file?(path) }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Test if the file content has changed.
|
def recent_file?(file)
|
||||||
#
|
File.mtime(file) >= last_event
|
||||||
# Depending on the filesystem, mtime/ctime is probably only precise to the second, so round
|
|
||||||
# both values down to the second for the comparison.
|
|
||||||
#
|
|
||||||
# ctime is used only on == comparison to always catches Rails 3.1 Assets pipelined on Mac OSX
|
|
||||||
#
|
|
||||||
# @param [String] path the file path
|
|
||||||
# @param [Time] last_event the time of the last event
|
|
||||||
# @return [Boolean] Whether the file content has changed or not.
|
|
||||||
#
|
|
||||||
def file_modified?(path, last_event)
|
|
||||||
ctime = File.ctime(path).to_i
|
|
||||||
mtime = File.mtime(path).to_i
|
|
||||||
if [mtime, ctime].max == last_event.to_i
|
|
||||||
file_content_modified?(path, sha1_checksum(path))
|
|
||||||
elsif mtime > last_event.to_i
|
|
||||||
set_sha1_checksums_hash(path, sha1_checksum(path))
|
|
||||||
true
|
|
||||||
elsif @watch_all_modifications
|
|
||||||
ts = file_timestamp(path)
|
|
||||||
if ts != @file_timestamp_hash[path]
|
|
||||||
set_file_timestamp_hash(path, ts)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
rescue
|
rescue
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Tests if the file content has been modified by
|
def update_last_event
|
||||||
# comparing the SHA1 checksum.
|
@last_event = Time.now
|
||||||
#
|
|
||||||
# @param [String] path the file path
|
|
||||||
# @param [String] sha1_checksum the checksum of the file
|
|
||||||
#
|
|
||||||
def file_content_modified?(path, sha1_checksum)
|
|
||||||
if @sha1_checksums_hash[path] != sha1_checksum
|
|
||||||
set_sha1_checksums_hash(path, sha1_checksum)
|
|
||||||
true
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
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?
|
def self.mac?
|
||||||
RbConfig::CONFIG['target_os'] =~ /darwin/i
|
Config::CONFIG['target_os'] =~ /darwin/i
|
||||||
end
|
end
|
||||||
|
|
||||||
# Test if the OS is Linux.
|
|
||||||
#
|
|
||||||
# @return [Boolean] Whether the OS is Linux
|
|
||||||
#
|
|
||||||
def self.linux?
|
def self.linux?
|
||||||
RbConfig::CONFIG['target_os'] =~ /linux/i
|
Config::CONFIG['target_os'] =~ /linux/i
|
||||||
end
|
end
|
||||||
|
|
||||||
# Test if the OS is Windows.
|
|
||||||
#
|
|
||||||
# @return [Boolean] Whether the OS is Windows
|
|
||||||
#
|
|
||||||
def self.windows?
|
|
||||||
RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -1,66 +1,40 @@
|
|||||||
module Guard
|
module Guard
|
||||||
|
|
||||||
# Listener implementation for Mac OS X `FSEvents`.
|
|
||||||
#
|
|
||||||
class Darwin < Listener
|
class Darwin < Listener
|
||||||
|
attr_reader :fsevent
|
||||||
# Initialize the Listener.
|
|
||||||
#
|
def initialize
|
||||||
def initialize(*)
|
|
||||||
super
|
super
|
||||||
@fsevent = FSEvent.new
|
@fsevent = FSEvent.new
|
||||||
end
|
end
|
||||||
|
|
||||||
# Start the listener.
|
def on_change(&callback)
|
||||||
#
|
@fsevent.watch Dir.pwd do |modified_dirs|
|
||||||
|
files = modified_files(modified_dirs)
|
||||||
|
update_last_event
|
||||||
|
callback.call(files)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def start
|
def start
|
||||||
super
|
@fsevent.run
|
||||||
worker.run
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Stop the listener.
|
|
||||||
#
|
|
||||||
def stop
|
def stop
|
||||||
super
|
@fsevent.stop
|
||||||
worker.stop
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check if the listener is usable on the current OS.
|
|
||||||
#
|
|
||||||
# @return [Boolean] whether usable or not
|
|
||||||
#
|
|
||||||
def self.usable?
|
def self.usable?
|
||||||
require 'rb-fsevent'
|
require 'rb-fsevent'
|
||||||
if !defined?(FSEvent::VERSION) || (defined?(Gem::Version) &&
|
if !defined?(FSEvent::VERSION) || Gem::Version.new(FSEvent::VERSION) < Gem::Version.new('0.3.5')
|
||||||
Gem::Version.new(FSEvent::VERSION) < Gem::Version.new('0.4.0'))
|
UI.info "Please update rb-fsevent (>= 0.3.5)"
|
||||||
UI.info 'Please update rb-fsevent (>= 0.4.0)'
|
|
||||||
false
|
false
|
||||||
else
|
else
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
rescue LoadError
|
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
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Get the listener worker.
|
|
||||||
#
|
|
||||||
def worker
|
|
||||||
@fsevent
|
|
||||||
end
|
|
||||||
|
|
||||||
# Watch the given directory for file changes.
|
|
||||||
#
|
|
||||||
# @param [String] directory the directory to watch
|
|
||||||
#
|
|
||||||
def watch(directory)
|
|
||||||
worker.watch(directory) do |modified_dirs|
|
|
||||||
files = modified_files(modified_dirs)
|
|
||||||
@callback.call(files) unless files.empty?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -1,65 +1,28 @@
|
|||||||
module Guard
|
module Guard
|
||||||
|
|
||||||
# Listener implementation for Linux `inotify`.
|
|
||||||
#
|
|
||||||
class Linux < Listener
|
class Linux < Listener
|
||||||
|
attr_reader :inotify, :files, :latency, :callback
|
||||||
|
|
||||||
# Initialize the Listener.
|
def initialize
|
||||||
#
|
|
||||||
def initialize(*)
|
|
||||||
super
|
super
|
||||||
|
|
||||||
@inotify = INotify::Notifier.new
|
@inotify = INotify::Notifier.new
|
||||||
@files = []
|
@files = []
|
||||||
@latency = 0.5
|
@latency = 0.5
|
||||||
end
|
end
|
||||||
|
|
||||||
# Start the listener.
|
|
||||||
#
|
|
||||||
def start
|
def start
|
||||||
@stop = false
|
@stop = false
|
||||||
super
|
|
||||||
watch_change unless watch_change?
|
watch_change unless watch_change?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Stop the listener.
|
|
||||||
#
|
|
||||||
def stop
|
def stop
|
||||||
super
|
|
||||||
@stop = true
|
@stop = true
|
||||||
|
sleep latency
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check if the listener is usable on the current OS.
|
def on_change(&callback)
|
||||||
#
|
@callback = callback
|
||||||
# @return [Boolean] whether usable or not
|
inotify.watch(Dir.pwd, :recursive, :modify, :create, :delete, :move) do |event|
|
||||||
#
|
|
||||||
def self.usable?
|
|
||||||
require 'rb-inotify'
|
|
||||||
if !defined?(INotify::VERSION) || (defined?(Gem::Version) &&
|
|
||||||
Gem::Version.new(INotify::VERSION.join('.')) < Gem::Version.new('0.8.5'))
|
|
||||||
UI.info 'Please update rb-inotify (>= 0.8.5)'
|
|
||||||
false
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
rescue LoadError
|
|
||||||
UI.info 'Please install rb-inotify gem for Linux inotify support'
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Get the listener worker.
|
|
||||||
#
|
|
||||||
def worker
|
|
||||||
@inotify
|
|
||||||
end
|
|
||||||
|
|
||||||
# Watch the given directory for file changes.
|
|
||||||
#
|
|
||||||
# @param [String] directory the directory to watch
|
|
||||||
#
|
|
||||||
def watch(directory)
|
|
||||||
worker.watch(directory, :recursive, :attrib, :create, :move_self, :close_write) do |event|
|
|
||||||
unless event.name == "" # Event on root directory
|
unless event.name == "" # Event on root directory
|
||||||
@files << event.absolute_name
|
@files << event.absolute_name
|
||||||
end
|
end
|
||||||
@ -67,31 +30,44 @@ module Guard
|
|||||||
rescue Interrupt
|
rescue Interrupt
|
||||||
end
|
end
|
||||||
|
|
||||||
# Test if inotify is watching for changes.
|
def self.usable?
|
||||||
#
|
require 'rb-inotify'
|
||||||
# @return [Boolean] whether inotify is active or not
|
if !defined?(INotify::VERSION) || Gem::Version.new(INotify::VERSION.join('.')) < Gem::Version.new('0.5.1')
|
||||||
#
|
UI.info "Please update rb-inotify (>= 0.5.1)"
|
||||||
|
false
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
rescue LoadError
|
||||||
|
UI.info "Please install rb-inotify gem for Linux inotify support"
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
def watch_change?
|
def watch_change?
|
||||||
!!@watch_change
|
!!@watch_change
|
||||||
end
|
end
|
||||||
|
|
||||||
# Watch for file system changes.
|
private
|
||||||
#
|
|
||||||
def watch_change
|
def watch_change
|
||||||
@watch_change = true
|
@watch_change = true
|
||||||
until @stop
|
while !@stop
|
||||||
if RbConfig::CONFIG['build'] =~ /java/ || IO.select([worker.to_io], [], [], @latency)
|
if Config::CONFIG['build'] =~ /java/ || IO.select([inotify.to_io], [], [], latency)
|
||||||
break if @stop
|
break if @stop
|
||||||
|
|
||||||
sleep(@latency)
|
sleep latency
|
||||||
worker.process
|
inotify.process
|
||||||
|
update_last_event
|
||||||
|
|
||||||
files = modified_files(@files.shift(@files.size).map { |f| File.dirname(f) }.uniq)
|
unless files.empty?
|
||||||
@callback.call(files) unless files.empty?
|
files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
|
||||||
|
callback.call(files)
|
||||||
|
files.clear
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@watch_change = false
|
@watch_change = false
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -1,55 +1,37 @@
|
|||||||
module Guard
|
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
|
class Polling < Listener
|
||||||
|
attr_reader :callback, :latency
|
||||||
# Initialize the Listener.
|
|
||||||
#
|
def initialize
|
||||||
def initialize(*)
|
|
||||||
super
|
super
|
||||||
@latency = 1.5
|
@latency = 1.5
|
||||||
end
|
end
|
||||||
|
|
||||||
# Start the listener.
|
def on_change(&callback)
|
||||||
#
|
@callback = callback
|
||||||
|
end
|
||||||
|
|
||||||
def start
|
def start
|
||||||
@stop = false
|
@stop = false
|
||||||
super
|
|
||||||
watch_change
|
watch_change
|
||||||
end
|
end
|
||||||
|
|
||||||
# Stop the listener.
|
|
||||||
#
|
|
||||||
def stop
|
def stop
|
||||||
super
|
|
||||||
@stop = true
|
@stop = true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Watch the given directory for file changes.
|
private
|
||||||
#
|
|
||||||
# @param [String] directory the directory to watch
|
|
||||||
#
|
|
||||||
def watch(directory)
|
|
||||||
@existing = all_files
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Watch for file system changes.
|
|
||||||
#
|
|
||||||
def watch_change
|
def watch_change
|
||||||
until @stop
|
while !@stop
|
||||||
start = Time.now.to_f
|
start = Time.now.to_f
|
||||||
files = modified_files([@directory], :all => true)
|
files = modified_files([Dir.pwd + '/'], :all => true)
|
||||||
@callback.call(files) unless files.empty?
|
update_last_event
|
||||||
nap_time = @latency - (Time.now.to_f - start)
|
callback.call(files) unless files.empty?
|
||||||
|
nap_time = latency - (Time.now.to_f - start)
|
||||||
sleep(nap_time) if nap_time > 0
|
sleep(nap_time) if nap_time > 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -1,61 +0,0 @@
|
|||||||
module Guard
|
|
||||||
|
|
||||||
# Listener implementation for Windows `fchange`.
|
|
||||||
#
|
|
||||||
class Windows < Listener
|
|
||||||
|
|
||||||
# Initialize the Listener.
|
|
||||||
#
|
|
||||||
def initialize(*)
|
|
||||||
super
|
|
||||||
@fchange = FChange::Notifier.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Start the listener.
|
|
||||||
#
|
|
||||||
def start
|
|
||||||
super
|
|
||||||
worker.run
|
|
||||||
end
|
|
||||||
|
|
||||||
# Stop the listener.
|
|
||||||
#
|
|
||||||
def stop
|
|
||||||
super
|
|
||||||
worker.stop
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if the listener is usable on the current OS.
|
|
||||||
#
|
|
||||||
# @return [Boolean] whether usable or not
|
|
||||||
#
|
|
||||||
def self.usable?
|
|
||||||
require 'rb-fchange'
|
|
||||||
true
|
|
||||||
rescue LoadError
|
|
||||||
UI.info 'Please install rb-fchange gem for Windows file events support'
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Watch the given directory for file changes.
|
|
||||||
#
|
|
||||||
# @param [String] directory the directory to watch
|
|
||||||
#
|
|
||||||
def watch(directory)
|
|
||||||
worker.watch(directory, :all_events, :recursive) do |event|
|
|
||||||
paths = [File.expand_path(event.watcher.path)]
|
|
||||||
files = modified_files(paths, :all => true)
|
|
||||||
@callback.call(files) unless files.empty?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the listener worker.
|
|
||||||
#
|
|
||||||
def worker
|
|
||||||
@fchange
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,290 +1,62 @@
|
|||||||
require 'rbconfig'
|
require 'rbconfig'
|
||||||
require 'pathname'
|
require 'pathname'
|
||||||
require 'guard/ui'
|
|
||||||
|
|
||||||
module Guard
|
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
|
module Notifier
|
||||||
|
|
||||||
# Application name as shown in the specific notification settings
|
def self.notify(message, options = {})
|
||||||
APPLICATION_NAME = "Guard"
|
unless ENV["GUARD_ENV"] == "test"
|
||||||
|
image = options[:image] || :success
|
||||||
class << self
|
title = options[:title] || "Guard"
|
||||||
|
case Config::CONFIG['target_os']
|
||||||
attr_accessor :growl_library, :gntp
|
when /darwin/i
|
||||||
|
if growl_installed?
|
||||||
# Turn notifications off.
|
Growl.notify message, :title => title, :icon => image_path(image), :name => "Guard"
|
||||||
#
|
end
|
||||||
def turn_off
|
when /linux/i
|
||||||
ENV["GUARD_NOTIFY"] = 'false'
|
if libnotify_installed?
|
||||||
end
|
Libnotify.show :body => message, :summary => title, :icon_path => image_path(image)
|
||||||
|
|
||||||
# Turn notifications on. This tries to load the platform
|
|
||||||
# specific notification library.
|
|
||||||
#
|
|
||||||
# @return [Boolean] whether the notification could be enabled.
|
|
||||||
#
|
|
||||||
def turn_on
|
|
||||||
ENV["GUARD_NOTIFY"] = 'true'
|
|
||||||
case RbConfig::CONFIG['target_os']
|
|
||||||
when /darwin/i
|
|
||||||
require_growl
|
|
||||||
when /linux/i
|
|
||||||
require_libnotify
|
|
||||||
when /mswin|mingw/i
|
|
||||||
require_rbnotifu
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Show a message with the system notification.
|
|
||||||
#
|
|
||||||
# @see .image_path
|
|
||||||
#
|
|
||||||
# @param [String] the message to show
|
|
||||||
# @option options [Symbol, String] image the image symbol or path to an image
|
|
||||||
# @option options [String] title the notification title
|
|
||||||
#
|
|
||||||
def notify(message, options = { })
|
|
||||||
if enabled?
|
|
||||||
image = options.delete(:image) || :success
|
|
||||||
title = options.delete(:title) || "Guard"
|
|
||||||
|
|
||||||
case RbConfig::CONFIG['target_os']
|
|
||||||
when /darwin/i
|
|
||||||
notify_mac(title, message, image, options)
|
|
||||||
when /linux/i
|
|
||||||
notify_linux(title, message, image, options)
|
|
||||||
when /mswin|mingw/i
|
|
||||||
notify_windows(title, message, image, options)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Test if the notifications are enabled and available.
|
|
||||||
#
|
|
||||||
# @return [Boolean] whether the notifications are available
|
|
||||||
#
|
|
||||||
def enabled?
|
|
||||||
ENV["GUARD_NOTIFY"] == 'true'
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Send a message to Growl either with the `growl` gem or the `growl_notify` gem.
|
|
||||||
#
|
|
||||||
# @param [String] title the notification title
|
|
||||||
# @param [String] message the message to show
|
|
||||||
# @param [Symbol, String] the image to user
|
|
||||||
# @param [Hash] options the growl options
|
|
||||||
#
|
|
||||||
def notify_mac(title, message, image, options = { })
|
|
||||||
require_growl # need for guard-rspec formatter that is called out of guard scope
|
|
||||||
|
|
||||||
notification = { :title => title, :icon => image_path(image) }.merge(options)
|
|
||||||
|
|
||||||
case self.growl_library
|
|
||||||
when :growl_notify
|
|
||||||
notification.delete(:name)
|
|
||||||
|
|
||||||
GrowlNotify.send_notification({
|
|
||||||
:description => message,
|
|
||||||
:application_name => APPLICATION_NAME
|
|
||||||
}.merge(notification))
|
|
||||||
|
|
||||||
when :ruby_gntp
|
|
||||||
icon = "file://#{ notification.delete(:icon) }"
|
|
||||||
|
|
||||||
self.gntp.notify({
|
|
||||||
:name => [:pending, :success, :failed].include?(image) ? image.to_s : 'notify',
|
|
||||||
:text => message,
|
|
||||||
:icon => icon
|
|
||||||
}.merge(notification))
|
|
||||||
|
|
||||||
when :growl
|
|
||||||
Growl.notify(message, {
|
|
||||||
:name => APPLICATION_NAME
|
|
||||||
}.merge(notification))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Send a message to libnotify.
|
|
||||||
#
|
|
||||||
# @param [String] title the notification title
|
|
||||||
# @param [String] message the message to show
|
|
||||||
# @param [Symbol, String] the image to user
|
|
||||||
# @param [Hash] options the libnotify options
|
|
||||||
#
|
|
||||||
def notify_linux(title, message, image, options = { })
|
|
||||||
require_libnotify # need for guard-rspec formatter that is called out of guard scope
|
|
||||||
|
|
||||||
notification = { :body => message, :summary => title, :icon_path => image_path(image), :transient => true }
|
|
||||||
Libnotify.show notification.merge(options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Send a message to notifu.
|
|
||||||
#
|
|
||||||
# @param [String] title the notification title
|
|
||||||
# @param [String] message the message to show
|
|
||||||
# @param [Symbol, String] the image to user
|
|
||||||
# @param [Hash] options the notifu options
|
|
||||||
#
|
|
||||||
def notify_windows(title, message, image, options = { })
|
|
||||||
require_rbnotifu # need for guard-rspec formatter that is called out of guard scope
|
|
||||||
|
|
||||||
notification = { :message => message, :title => title, :type => image_level(image), :time => 3 }
|
|
||||||
Notifu.show notification.merge(options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the image path for an image symbol.
|
|
||||||
#
|
|
||||||
# Known symbols are:
|
|
||||||
#
|
|
||||||
# - failed
|
|
||||||
# - pending
|
|
||||||
# - success
|
|
||||||
#
|
|
||||||
# @param [Symbol] image the image name
|
|
||||||
# @return [String] the image path
|
|
||||||
#
|
|
||||||
def image_path(image)
|
|
||||||
images_path = Pathname.new(File.dirname(__FILE__)).join('../../images')
|
|
||||||
case image
|
|
||||||
when :failed
|
|
||||||
images_path.join("failed.png").to_s
|
|
||||||
when :pending
|
|
||||||
images_path.join("pending.png").to_s
|
|
||||||
when :success
|
|
||||||
images_path.join("success.png").to_s
|
|
||||||
else
|
|
||||||
# path given
|
|
||||||
image
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# The notification level type for the given image.
|
|
||||||
#
|
|
||||||
# @param [Symbol] image the image
|
|
||||||
# @return [Symbol] the level
|
|
||||||
#
|
|
||||||
def image_level(image)
|
|
||||||
case image
|
|
||||||
when :failed
|
|
||||||
:error
|
|
||||||
when :pending
|
|
||||||
:warn
|
|
||||||
when :success
|
|
||||||
:info
|
|
||||||
else
|
|
||||||
:info
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Try to safely load growl and turns notifications off on load failure.
|
|
||||||
# The Guard notifier knows three different library to handle sending
|
|
||||||
# Growl messages and tries to loading them in the given order:
|
|
||||||
#
|
|
||||||
# - [Growl Notify](https://github.com/scottdavis/growl_notify)
|
|
||||||
# - [Ruby GNTP](https://github.com/snaka/ruby_gntp)
|
|
||||||
# - [Growl](https://github.com/visionmedia/growl)
|
|
||||||
#
|
|
||||||
# On successful loading of any of the libraries, the active library name is
|
|
||||||
# accessible through `.growl_library`.
|
|
||||||
#
|
|
||||||
def require_growl
|
|
||||||
self.growl_library = try_growl_notify || try_ruby_gntp || try_growl
|
|
||||||
|
|
||||||
unless self.growl_library
|
|
||||||
turn_off
|
|
||||||
UI.info "Please install growl_notify or growl gem for Mac OS X notification support and add it to your Gemfile"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Try to load the `growl_notify` gem.
|
|
||||||
#
|
|
||||||
# @return [Symbol, nil] A symbol with the name of the loaded library
|
|
||||||
#
|
|
||||||
def try_growl_notify
|
|
||||||
require 'growl_notify'
|
|
||||||
|
|
||||||
begin
|
|
||||||
if GrowlNotify.application_name != APPLICATION_NAME
|
|
||||||
GrowlNotify.config do |c|
|
|
||||||
c.notifications = c.default_notifications = [APPLICATION_NAME]
|
|
||||||
c.application_name = c.notifications.first
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
rescue ::GrowlNotify::GrowlNotFound
|
|
||||||
turn_off
|
|
||||||
UI.info "Please install Growl from http://growl.info"
|
|
||||||
end
|
|
||||||
|
|
||||||
:growl_notify
|
|
||||||
|
|
||||||
rescue LoadError
|
|
||||||
end
|
|
||||||
|
|
||||||
# Try to load the `ruby_gntp` gem and register the available
|
|
||||||
# notification channels.
|
|
||||||
#
|
|
||||||
# @return [Symbol, nil] A symbol with the name of the loaded library
|
|
||||||
#
|
|
||||||
def try_ruby_gntp
|
|
||||||
require 'ruby_gntp'
|
|
||||||
|
|
||||||
self.gntp = GNTP.new(APPLICATION_NAME)
|
|
||||||
self.gntp.register(:notifications => [
|
|
||||||
{ :name => 'notify', :enabled => true },
|
|
||||||
{ :name => 'failed', :enabled => true },
|
|
||||||
{ :name => 'pending', :enabled => true },
|
|
||||||
{ :name => 'success', :enabled => true }
|
|
||||||
])
|
|
||||||
|
|
||||||
:ruby_gntp
|
|
||||||
|
|
||||||
rescue LoadError
|
|
||||||
end
|
|
||||||
|
|
||||||
# Try to load the `growl_notify` gem.
|
|
||||||
#
|
|
||||||
# @return [Symbol, nil] A symbol with the name of the loaded library
|
|
||||||
#
|
|
||||||
def try_growl
|
|
||||||
require 'growl'
|
|
||||||
|
|
||||||
:growl
|
|
||||||
|
|
||||||
rescue LoadError
|
|
||||||
end
|
|
||||||
|
|
||||||
# Try to safely load libnotify and turns notifications
|
|
||||||
# off on load failure.
|
|
||||||
#
|
|
||||||
def require_libnotify
|
|
||||||
require 'libnotify'
|
|
||||||
|
|
||||||
rescue LoadError
|
|
||||||
turn_off
|
|
||||||
UI.info "Please install libnotify gem for Linux notification support and add it to your Gemfile"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Try to safely load rb-notifu and turns notifications
|
|
||||||
# off on load failure.
|
|
||||||
#
|
|
||||||
def require_rbnotifu
|
|
||||||
require 'rb-notifu'
|
|
||||||
|
|
||||||
rescue LoadError
|
|
||||||
turn_off
|
|
||||||
UI.info "Please install rb-notifu gem for Windows notification support and add it to your Gemfile"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def self.image_path(image)
|
||||||
|
images_path = Pathname.new(File.dirname(__FILE__)).join('../../images')
|
||||||
|
case image
|
||||||
|
when :failed
|
||||||
|
images_path.join("failed.png").to_s
|
||||||
|
when :pending
|
||||||
|
images_path.join("pending.png").to_s
|
||||||
|
when :success
|
||||||
|
images_path.join("success.png").to_s
|
||||||
|
else
|
||||||
|
# path given
|
||||||
|
image
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.growl_installed?
|
||||||
|
@installed ||= begin
|
||||||
|
require 'growl'
|
||||||
|
true
|
||||||
|
rescue LoadError
|
||||||
|
UI.info "Please install growl gem for Mac OS X notification support and add it to your Gemfile"
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.libnotify_installed?
|
||||||
|
@installed ||= begin
|
||||||
|
require 'libnotify'
|
||||||
|
true
|
||||||
|
rescue LoadError
|
||||||
|
UI.info "Please install libnotify gem for Linux notification support and add it to your Gemfile"
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -1,2 +1,2 @@
|
|||||||
# A sample Guardfile
|
# A sample Guardfile
|
||||||
# More info at https://github.com/guard/guard#readme
|
# More info at http://github.com/guard/guard#readme
|
||||||
|
199
lib/guard/ui.rb
199
lib/guard/ui.rb
@ -1,193 +1,42 @@
|
|||||||
module Guard
|
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
|
module UI
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
color_enabled = nil
|
def info(message, options = {})
|
||||||
|
unless ENV["GUARD_ENV"] == "test"
|
||||||
# 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'
|
|
||||||
reset_line if options[:reset]
|
reset_line if options[:reset]
|
||||||
STDERR.puts color(message) if message != ''
|
puts reset_color(message) if message != ''
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Show a red error message that is prefixed with ERROR.
|
def error(message)
|
||||||
#
|
puts "ERROR: #{message}"
|
||||||
# @param [String] message the message to show
|
end
|
||||||
# @option options [Boolean] reset whether to clean the output before
|
|
||||||
#
|
def debug(message)
|
||||||
def error(message, options = { })
|
unless ENV["GUARD_ENV"] == "test"
|
||||||
unless ENV['GUARD_ENV'] == 'test'
|
puts "DEBUG: #{message}" if ::Guard.options && ::Guard.options[:debug]
|
||||||
reset_line if options[:reset]
|
|
||||||
STDERR.puts color('ERROR: ', :red) + message
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Show a red deprecation message that is prefixed with DEPRECATION.
|
|
||||||
#
|
|
||||||
# @param [String] message the message to show
|
|
||||||
# @option options [Boolean] reset whether to clean the output before
|
|
||||||
#
|
|
||||||
def deprecation(message, options = { })
|
|
||||||
unless ENV['GUARD_ENV'] == 'test'
|
|
||||||
reset_line if options[:reset]
|
|
||||||
STDERR.puts color('DEPRECATION: ', :red) + message
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Show a debug message that is prefixed with DEBUG and a timestamp.
|
|
||||||
#
|
|
||||||
# @param [String] message the message to show
|
|
||||||
# @option options [Boolean] reset whether to clean the output before
|
|
||||||
#
|
|
||||||
def debug(message, options = { })
|
|
||||||
unless ENV['GUARD_ENV'] == 'test'
|
|
||||||
reset_line if options[:reset]
|
|
||||||
STDERR.puts color("DEBUG (#{Time.now.strftime('%T')}): ", :yellow) + message if ::Guard.options && ::Guard.options[:debug]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Reset a line.
|
|
||||||
#
|
|
||||||
def reset_line
|
def reset_line
|
||||||
STDERR.print(color_enabled? ? "\r\e[0m" : "\r\n")
|
print "\r\e "
|
||||||
end
|
end
|
||||||
|
|
||||||
# Clear the output.
|
|
||||||
#
|
|
||||||
def clear
|
def clear
|
||||||
system('clear;')
|
system("clear;")
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Reset a color sequence.
|
|
||||||
#
|
|
||||||
# @deprecated
|
|
||||||
# @param [String] text the text
|
|
||||||
#
|
|
||||||
def reset_color(text)
|
def reset_color(text)
|
||||||
deprecation('UI.reset_color(text) is deprecated, please use color(text, ' ') instead.')
|
color(text, "\e[0m")
|
||||||
color(text, '')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks if color output can be enabled.
|
def color(text, color_code)
|
||||||
#
|
"#{color_code}#{text}\e[0m"
|
||||||
# @return [Boolean] whether color is enabled or not
|
|
||||||
#
|
|
||||||
def color_enabled?
|
|
||||||
if @color_enabled.nil?
|
|
||||||
if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
|
||||||
if ENV['ANSICON']
|
|
||||||
@color_enabled = true
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
|
||||||
require 'Win32/Console/ANSI'
|
|
||||||
@color_enabled = true
|
|
||||||
rescue LoadError
|
|
||||||
@color_enabled = false
|
|
||||||
info "You must 'gem install win32console' to use color on Windows"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
@color_enabled = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@color_enabled
|
|
||||||
end
|
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
|
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
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
module Guard
|
module Guard
|
||||||
unless defined? Guard::VERSION
|
VERSION = "0.2.2"
|
||||||
# The current gem version of Guard
|
end
|
||||||
VERSION = '0.8.4'
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,114 +1,34 @@
|
|||||||
module Guard
|
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
|
class Watcher
|
||||||
|
|
||||||
attr_accessor :pattern, :action
|
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)
|
def initialize(pattern, action = nil)
|
||||||
@pattern, @action = pattern, action
|
@pattern, @action = pattern, action
|
||||||
@@warning_printed ||= false
|
|
||||||
|
|
||||||
# deprecation warning
|
|
||||||
if @pattern.is_a?(String) && @pattern =~ /(^(\^))|(>?(\\\.)|(\.\*))|(\(.*\))|(\[.*\])|(\$$)/
|
|
||||||
unless @@warning_printed
|
|
||||||
UI.info "*"*20 + "\nDEPRECATION WARNING!\n" + "*"*20
|
|
||||||
UI.info <<-MSG
|
|
||||||
You have a string in your Guardfile watch patterns that seem to represent a Regexp.
|
|
||||||
Guard matches String with == and Regexp with Regexp#match.
|
|
||||||
You should either use plain String (without Regexp special characters) or real Regexp.
|
|
||||||
MSG
|
|
||||||
@@warning_printed = true
|
|
||||||
end
|
|
||||||
|
|
||||||
UI.info "\"#{@pattern}\" has been converted to #{ Regexp.new(@pattern).inspect }\n"
|
|
||||||
@pattern = Regexp.new(@pattern)
|
|
||||||
end
|
|
||||||
end
|
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)
|
def self.match_files(guard, files)
|
||||||
guard.watchers.inject([]) do |paths, watcher|
|
guard.watchers.inject([]) do |paths, watcher|
|
||||||
files.each do |file|
|
files.each do |file|
|
||||||
if matches = watcher.match_file?(file)
|
if matches = file.match(watcher.pattern)
|
||||||
if watcher.action
|
if watcher.action
|
||||||
result = watcher.call_action(matches)
|
begin
|
||||||
if guard.options[:any_return]
|
if watcher.action.arity == 1
|
||||||
paths << result
|
result = watcher.action.call(matches)
|
||||||
elsif result.respond_to?(:empty?) && !result.empty?
|
else
|
||||||
paths << Array(result)
|
result = watcher.action.call
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
UI.info "Problem with watch action"
|
||||||
end
|
end
|
||||||
|
paths << result if result.is_a?(String) && result != ''
|
||||||
else
|
else
|
||||||
paths << matches[0]
|
paths << matches[0]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
paths
|
||||||
guard.options[:any_return] ? paths : paths.flatten.map { |p| p.to_s }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Test if a file would be matched by any of the Guards watchers.
|
|
||||||
#
|
|
||||||
# @param [Array<Guard::Guard>] guards the guards to use the watchers from
|
|
||||||
# @param [Array<String>] files the files to test
|
|
||||||
# @return [Boolean] Whether a file matches
|
|
||||||
#
|
|
||||||
def self.match_files?(guards, files)
|
|
||||||
guards.any? do |guard|
|
|
||||||
guard.watchers.any? do |watcher|
|
|
||||||
files.any? { |file| watcher.match_file?(file) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test the watchers pattern against a file.
|
|
||||||
#
|
|
||||||
# @param [String] file the file to test
|
|
||||||
# @return [Boolean] whether the given file is matched
|
|
||||||
#
|
|
||||||
def match_file?(file)
|
|
||||||
if @pattern.is_a?(Regexp)
|
|
||||||
file.match(@pattern)
|
|
||||||
else
|
|
||||||
file == @pattern ? [file] : nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test if any of the files is the Guardfile.
|
|
||||||
#
|
|
||||||
# @param [Array<String>] the files to test
|
|
||||||
# @return [Boolean] whether one of these files is the Guardfile
|
|
||||||
#
|
|
||||||
def self.match_guardfile?(files)
|
|
||||||
files.any? { |file| "#{ Dir.pwd }/#{ file }" == Dsl.guardfile_path }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Executes a watcher action.
|
|
||||||
#
|
|
||||||
# @param [String, MatchData] the matched path or the match from the Regex
|
|
||||||
# @return [String] the final paths
|
|
||||||
#
|
|
||||||
def call_action(matches)
|
|
||||||
begin
|
|
||||||
@action.arity > 0 ? @action.call(matches) : @action.call
|
|
||||||
rescue Exception => e
|
|
||||||
UI.error "Problem with watch action!\n#{ e.message }\n\n#{ e.backtrace.join("\n") }"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
93
man/guard.1
93
man/guard.1
@ -1,93 +0,0 @@
|
|||||||
.\" generated with Ronn/v0.7.3
|
|
||||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
|
||||||
.
|
|
||||||
.TH "GUARD" "1" "September 2011" "" ""
|
|
||||||
.
|
|
||||||
.SH "NAME"
|
|
||||||
\fBguard\fR \- Guard keeps an eye on your file modifications\.
|
|
||||||
.
|
|
||||||
.SH "DESCRIPTION"
|
|
||||||
Guard is a command line tool that easily handle events on files modifications\.
|
|
||||||
.
|
|
||||||
.SH "SYNOPSIS"
|
|
||||||
\fBguard <COMMAND> <OPTIONS>\fR
|
|
||||||
.
|
|
||||||
.SH "COMMANDS"
|
|
||||||
.
|
|
||||||
.SS "start"
|
|
||||||
Starts Guard\. This is the default command if none is provided\.
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
The following options are available:
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
\fB\-c\fR, \fB\-\-clear\fR Clears the Shell after each change\.
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
\fB\-n\fR, \fB\-\-notify\fR \fIFLAG\fR Disable notifications (Growl or Libnotify depending on your system)\. Notifications can be disabled globally by setting a GUARD_NOTIFY environment variable to false\. FLAG can be \fBtrue\fR/\fBfalse\fR or \fBt\fR/\fBf\fR\.
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
\fB\-d\fR, \fB\-\-debug\fR Runs Guard in debug mode\.
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
\fB\-g\fR, \fB\-\-group\fR \fIGROUP1\fR \fIGROUP2\fR\.\.\. Runs only the groups specified by GROUP1, GROUP2 etc\. Groups name should be separated by spaces\. Guards that don\'t belong to a group are considered global and are always run\.
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
\fB\-w\fR, \fB\-\-watchdir\fR \fIPATH\fR
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
Tells Guard to watch PATH instead of \fB\./\fR\.
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
\fB\-G\fR, \fB\-\-guardfile\fR \fIFILE\fR Tells Guard to use FILE as its Guardfile instead of \fB\./Guardfile\fR or \fB~/\.Guardfile\fR\.
|
|
||||||
.
|
|
||||||
.SS "init [GUARD]"
|
|
||||||
If no Guardfile is present in the current directory, creates an empty Guardfile\.
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
If \fIGUARD\fR is present, add its default Guardfile configuration to the current Guardfile\. Note that \fIGUARD\fR is the guard\'s name without the \fBguard\-\fR prefix\. For instance to initialize guard\-rspec, run \fBguard init rspec\fR\.
|
|
||||||
.
|
|
||||||
.SS "list"
|
|
||||||
Lists guards that can be used with the \fBinit\fR command\.
|
|
||||||
.
|
|
||||||
.SS "\-T, show"
|
|
||||||
List defined groups and guards for the current Guardfile\.
|
|
||||||
.
|
|
||||||
.SS "\-h, help [COMMAND]"
|
|
||||||
List all of Guard\'s available commands\.
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
If \fICOMMAND\fR is given, displays a specific help for \fITASK\fR\.
|
|
||||||
.
|
|
||||||
.SH "EXAMPLES"
|
|
||||||
Initialize Guard and a specific guard at the same time:
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
\fB[bundle exec] guard init [rspec]\fR
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
Run Guard:
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
\fB[bundle exec] guard [start] \-\-watchdir ~/dev \-\-guardfile ~/env/Guardfile \-\-clear \-\-group backend frontend \-\-notify false \-\-debug\fR
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
or in a more concise way:
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
\fB[bundle exec] guard [start] \-w ~/dev \-G ~/env/Guardfile \-c \-g backend frontend \-n f \-d\fR
|
|
||||||
.
|
|
||||||
.SH "AUTHORS / CONTRIBUTORS"
|
|
||||||
Thibaud Guillaume\-Gentil is the main author\.
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
A list of contributors based on all commits can be found here: https://github\.com/guard/guard/contributors
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
For an exhaustive list of all the contributors, please see the CHANGELOG: https://github\.com/guard/guard/blob/master/CHANGELOG\.md
|
|
||||||
.
|
|
||||||
.P
|
|
||||||
This manual has been written by Remy Coutable\.
|
|
||||||
.
|
|
||||||
.SH "WWW"
|
|
||||||
https://github\.com/guard/guard
|
|
176
man/guard.1.html
176
man/guard.1.html
@ -1,176 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv='content-type' value='text/html;charset=utf8'>
|
|
||||||
<meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
|
|
||||||
<title>guard(1) - Guard keeps an eye on your file modifications.</title>
|
|
||||||
<style type='text/css' media='all'>
|
|
||||||
/* style: man */
|
|
||||||
body#manpage {margin:0}
|
|
||||||
.mp {max-width:100ex;padding:0 9ex 1ex 4ex}
|
|
||||||
.mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
|
|
||||||
.mp h2 {margin:10px 0 0 0}
|
|
||||||
.mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
|
|
||||||
.mp h3 {margin:0 0 0 4ex}
|
|
||||||
.mp dt {margin:0;clear:left}
|
|
||||||
.mp dt.flush {float:left;width:8ex}
|
|
||||||
.mp dd {margin:0 0 0 9ex}
|
|
||||||
.mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
|
|
||||||
.mp pre {margin-bottom:20px}
|
|
||||||
.mp pre+h2,.mp pre+h3 {margin-top:22px}
|
|
||||||
.mp h2+pre,.mp h3+pre {margin-top:5px}
|
|
||||||
.mp img {display:block;margin:auto}
|
|
||||||
.mp h1.man-title {display:none}
|
|
||||||
.mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
|
|
||||||
.mp h2 {font-size:16px;line-height:1.25}
|
|
||||||
.mp h1 {font-size:20px;line-height:2}
|
|
||||||
.mp {text-align:justify;background:#fff}
|
|
||||||
.mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
|
|
||||||
.mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
|
|
||||||
.mp u {text-decoration:underline}
|
|
||||||
.mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
|
|
||||||
.mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
|
|
||||||
.mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
|
|
||||||
.mp b.man-ref {font-weight:normal;color:#434241}
|
|
||||||
.mp pre {padding:0 4ex}
|
|
||||||
.mp pre code {font-weight:normal;color:#434241}
|
|
||||||
.mp h2+pre,h3+pre {padding-left:0}
|
|
||||||
ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
|
|
||||||
ol.man-decor {width:100%}
|
|
||||||
ol.man-decor li.tl {text-align:left}
|
|
||||||
ol.man-decor li.tc {text-align:center;letter-spacing:4px}
|
|
||||||
ol.man-decor li.tr {text-align:right;float:right}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<!--
|
|
||||||
The following styles are deprecated and will be removed at some point:
|
|
||||||
div#man, div#man ol.man, div#man ol.head, div#man ol.man.
|
|
||||||
|
|
||||||
The .man-page, .man-decor, .man-head, .man-foot, .man-title, and
|
|
||||||
.man-navigation should be used instead.
|
|
||||||
-->
|
|
||||||
<body id='manpage'>
|
|
||||||
<div class='mp' id='man'>
|
|
||||||
|
|
||||||
<div class='man-navigation' style='display:none'>
|
|
||||||
<a href="#NAME">NAME</a>
|
|
||||||
<a href="#DESCRIPTION">DESCRIPTION</a>
|
|
||||||
<a href="#SYNOPSIS">SYNOPSIS</a>
|
|
||||||
<a href="#COMMANDS">COMMANDS</a>
|
|
||||||
<a href="#EXAMPLES">EXAMPLES</a>
|
|
||||||
<a href="#AUTHORS-CONTRIBUTORS">AUTHORS / CONTRIBUTORS</a>
|
|
||||||
<a href="#WWW">WWW</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ol class='man-decor man-head man head'>
|
|
||||||
<li class='tl'>guard(1)</li>
|
|
||||||
<li class='tc'></li>
|
|
||||||
<li class='tr'>guard(1)</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h2 id="NAME">NAME</h2>
|
|
||||||
<p class="man-name">
|
|
||||||
<code>guard</code> - <span class="man-whatis">Guard keeps an eye on your file modifications.</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2 id="DESCRIPTION">DESCRIPTION</h2>
|
|
||||||
|
|
||||||
<p>Guard is a command line tool that easily handle events on files modifications.</p>
|
|
||||||
|
|
||||||
<h2 id="SYNOPSIS">SYNOPSIS</h2>
|
|
||||||
|
|
||||||
<p><code>guard <COMMAND> <OPTIONS></code></p>
|
|
||||||
|
|
||||||
<h2 id="COMMANDS">COMMANDS</h2>
|
|
||||||
|
|
||||||
<h3 id="start">start</h3>
|
|
||||||
|
|
||||||
<p>Starts Guard. This is the default command if none is provided.</p>
|
|
||||||
|
|
||||||
<p>The following options are available:</p>
|
|
||||||
|
|
||||||
<p><code>-c</code>, <code>--clear</code>
|
|
||||||
Clears the Shell after each change.</p>
|
|
||||||
|
|
||||||
<p><code>-n</code>, <code>--notify</code> <var>FLAG</var>
|
|
||||||
Disable notifications (Growl or Libnotify depending on your system).
|
|
||||||
Notifications can be disabled globally by setting a GUARD_NOTIFY environment variable to false.
|
|
||||||
FLAG can be <code>true</code>/<code>false</code> or <code>t</code>/<code>f</code>.</p>
|
|
||||||
|
|
||||||
<p><code>-d</code>, <code>--debug</code>
|
|
||||||
Runs Guard in debug mode.</p>
|
|
||||||
|
|
||||||
<p><code>-g</code>, <code>--group</code> <var>GROUP1</var> <var>GROUP2</var>...
|
|
||||||
Runs only the groups specified by GROUP1, GROUP2 etc.
|
|
||||||
Groups name should be separated by spaces.
|
|
||||||
Guards that don't belong to a group are considered global and are always run.</p>
|
|
||||||
|
|
||||||
<p><code>-w</code>, <code>--watchdir</code> <var>PATH</var></p>
|
|
||||||
|
|
||||||
<p>Tells Guard to watch PATH instead of <code>./</code>.</p>
|
|
||||||
|
|
||||||
<p><code>-G</code>, <code>--guardfile</code> <var>FILE</var>
|
|
||||||
Tells Guard to use FILE as its Guardfile instead of <code>./Guardfile</code> or <code>~/.Guardfile</code>.</p>
|
|
||||||
|
|
||||||
<h3 id="init-GUARD-">init [GUARD]</h3>
|
|
||||||
|
|
||||||
<p>If no Guardfile is present in the current directory, creates an empty Guardfile.</p>
|
|
||||||
|
|
||||||
<p>If <var>GUARD</var> is present, add its default Guardfile configuration to the current Guardfile.
|
|
||||||
Note that <var>GUARD</var> is the guard's name without the <code>guard-</code> prefix.
|
|
||||||
For instance to initialize guard-rspec, run <code>guard init rspec</code>.</p>
|
|
||||||
|
|
||||||
<h3 id="list">list</h3>
|
|
||||||
|
|
||||||
<p>Lists guards that can be used with the <code>init</code> command.</p>
|
|
||||||
|
|
||||||
<h3 id="-T-show">-T, show</h3>
|
|
||||||
|
|
||||||
<p>List defined groups and guards for the current Guardfile.</p>
|
|
||||||
|
|
||||||
<h3 id="-h-help-COMMAND-">-h, help [COMMAND]</h3>
|
|
||||||
|
|
||||||
<p>List all of Guard's available commands.</p>
|
|
||||||
|
|
||||||
<p>If <var>COMMAND</var> is given, displays a specific help for <var>TASK</var>.</p>
|
|
||||||
|
|
||||||
<h2 id="EXAMPLES">EXAMPLES</h2>
|
|
||||||
|
|
||||||
<p>Initialize Guard and a specific guard at the same time:</p>
|
|
||||||
|
|
||||||
<p><code>[bundle exec] guard init [rspec]</code></p>
|
|
||||||
|
|
||||||
<p>Run Guard:</p>
|
|
||||||
|
|
||||||
<p><code>[bundle exec] guard [start] --watchdir ~/dev --guardfile ~/env/Guardfile --clear --group backend frontend --notify false --debug</code></p>
|
|
||||||
|
|
||||||
<p>or in a more concise way:</p>
|
|
||||||
|
|
||||||
<p><code>[bundle exec] guard [start] -w ~/dev -G ~/env/Guardfile -c -g backend frontend -n f -d</code></p>
|
|
||||||
|
|
||||||
<h2 id="AUTHORS-CONTRIBUTORS">AUTHORS / CONTRIBUTORS</h2>
|
|
||||||
|
|
||||||
<p>Thibaud Guillaume-Gentil is the main author.</p>
|
|
||||||
|
|
||||||
<p>A list of contributors based on all commits can be found here:
|
|
||||||
https://github.com/guard/guard/contributors</p>
|
|
||||||
|
|
||||||
<p>For an exhaustive list of all the contributors, please see the CHANGELOG:
|
|
||||||
https://github.com/guard/guard/blob/master/CHANGELOG.md</p>
|
|
||||||
|
|
||||||
<p>This manual has been written by Remy Coutable.</p>
|
|
||||||
|
|
||||||
<h2 id="WWW">WWW</h2>
|
|
||||||
|
|
||||||
<p>https://github.com/guard/guard</p>
|
|
||||||
|
|
||||||
|
|
||||||
<ol class='man-decor man-foot man foot'>
|
|
||||||
<li class='tl'></li>
|
|
||||||
<li class='tc'>September 2011</li>
|
|
||||||
<li class='tr'>guard(1)</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,93 +0,0 @@
|
|||||||
guard(1) -- Guard keeps an eye on your file modifications.
|
|
||||||
========================================================
|
|
||||||
|
|
||||||
## DESCRIPTION
|
|
||||||
|
|
||||||
Guard is a command line tool that easily handle events on files modifications.
|
|
||||||
|
|
||||||
## SYNOPSIS
|
|
||||||
|
|
||||||
`guard <COMMAND> <OPTIONS>`
|
|
||||||
|
|
||||||
## COMMANDS
|
|
||||||
|
|
||||||
### start
|
|
||||||
|
|
||||||
Starts Guard. This is the default command if none is provided.
|
|
||||||
|
|
||||||
The following options are available:
|
|
||||||
|
|
||||||
`-c`, `--clear`
|
|
||||||
Clears the Shell after each change.
|
|
||||||
|
|
||||||
`-n`, `--notify` <FLAG>
|
|
||||||
Disable notifications (Growl or Libnotify depending on your system).
|
|
||||||
Notifications can be disabled globally by setting a GUARD_NOTIFY environment variable to false.
|
|
||||||
FLAG can be `true`/`false` or `t`/`f`.
|
|
||||||
|
|
||||||
`-d`, `--debug`
|
|
||||||
Runs Guard in debug mode.
|
|
||||||
|
|
||||||
`-g`, `--group` <GROUP1> <GROUP2>...
|
|
||||||
Runs only the groups specified by GROUP1, GROUP2 etc.
|
|
||||||
Groups name should be separated by spaces.
|
|
||||||
Guards that don't belong to a group are considered global and are always run.
|
|
||||||
|
|
||||||
`-w`, `--watchdir` <PATH>
|
|
||||||
|
|
||||||
Tells Guard to watch PATH instead of `./`.
|
|
||||||
|
|
||||||
`-G`, `--guardfile` <FILE>
|
|
||||||
Tells Guard to use FILE as its Guardfile instead of `./Guardfile` or `~/.Guardfile`.
|
|
||||||
|
|
||||||
### init [GUARD]
|
|
||||||
|
|
||||||
If no Guardfile is present in the current directory, creates an empty Guardfile.
|
|
||||||
|
|
||||||
If <GUARD> is present, add its default Guardfile configuration to the current Guardfile.
|
|
||||||
Note that <GUARD> is the guard's name without the `guard-` prefix.
|
|
||||||
For instance to initialize guard-rspec, run `guard init rspec`.
|
|
||||||
|
|
||||||
### list
|
|
||||||
|
|
||||||
Lists guards that can be used with the `init` command.
|
|
||||||
|
|
||||||
### -T, show
|
|
||||||
|
|
||||||
List defined groups and guards for the current Guardfile.
|
|
||||||
|
|
||||||
### -h, help [COMMAND]
|
|
||||||
|
|
||||||
List all of Guard's available commands.
|
|
||||||
|
|
||||||
If <COMMAND> is given, displays a specific help for <TASK>.
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
Initialize Guard and a specific guard at the same time:
|
|
||||||
|
|
||||||
`[bundle exec] guard init [rspec]`
|
|
||||||
|
|
||||||
Run Guard:
|
|
||||||
|
|
||||||
`[bundle exec] guard [start] --watchdir ~/dev --guardfile ~/env/Guardfile --clear --group backend frontend --notify false --debug`
|
|
||||||
|
|
||||||
or in a more concise way:
|
|
||||||
|
|
||||||
`[bundle exec] guard [start] -w ~/dev -G ~/env/Guardfile -c -g backend frontend -n f -d`
|
|
||||||
|
|
||||||
## AUTHORS / CONTRIBUTORS
|
|
||||||
|
|
||||||
Thibaud Guillaume-Gentil is the main author.
|
|
||||||
|
|
||||||
A list of contributors based on all commits can be found here:
|
|
||||||
https://github.com/guard/guard/contributors
|
|
||||||
|
|
||||||
For an exhaustive list of all the contributors, please see the CHANGELOG:
|
|
||||||
https://github.com/guard/guard/blob/master/CHANGELOG.md
|
|
||||||
|
|
||||||
This manual has been written by Remy Coutable.
|
|
||||||
|
|
||||||
## WWW
|
|
||||||
|
|
||||||
https://github.com/guard/guard
|
|
0
spec/fixtures/.dotfile
vendored
0
spec/fixtures/.dotfile
vendored
1
spec/fixtures/Guardfile
vendored
1
spec/fixtures/Guardfile
vendored
@ -1 +0,0 @@
|
|||||||
# nothing here, it's just for feeding the specs! :)
|
|
0
spec/fixtures/folder1/file1.txt
vendored
Executable file → Normal file
0
spec/fixtures/folder1/file1.txt
vendored
Executable file → Normal file
@ -1,70 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe Guard::DslDescriber do
|
|
||||||
|
|
||||||
let(:describer) { ::Guard::DslDescriber }
|
|
||||||
|
|
||||||
let(:guardfile) do
|
|
||||||
<<-GUARD
|
|
||||||
guard 'test', :a => :b do
|
|
||||||
watch('c')
|
|
||||||
end
|
|
||||||
|
|
||||||
group :a do
|
|
||||||
guard 'test', :x => 1 do
|
|
||||||
watch('c')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
group "b" do
|
|
||||||
guard 'another' do
|
|
||||||
watch('c')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
GUARD
|
|
||||||
end
|
|
||||||
|
|
||||||
before do
|
|
||||||
@output = ''
|
|
||||||
Guard::UI.stub(:info) { |msg| @output << msg + "\n" }
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
Guard::UI.unstub(:info)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.list' do
|
|
||||||
it 'lists the available Guards' do
|
|
||||||
Guard.stub(:guard_gem_names).and_return ['test', 'another', 'even', 'more']
|
|
||||||
describer.list(:guardfile_contents => guardfile)
|
|
||||||
@output.should eql <<OUTPUT
|
|
||||||
Using inline Guardfile.
|
|
||||||
Available guards:
|
|
||||||
another*
|
|
||||||
even
|
|
||||||
more
|
|
||||||
test*
|
|
||||||
|
|
||||||
See also https://github.com/guard/guard/wiki/List-of-available-Guards
|
|
||||||
* denotes ones already in your Guardfile
|
|
||||||
OUTPUT
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.show' do
|
|
||||||
it 'shows the Guards and their options' do
|
|
||||||
describer.show(:guardfile_contents => guardfile)
|
|
||||||
@output.should eql <<OUTPUT
|
|
||||||
Using inline Guardfile.
|
|
||||||
(global):
|
|
||||||
test: a => :b
|
|
||||||
Group a:
|
|
||||||
test: x => 1
|
|
||||||
Group b:
|
|
||||||
another
|
|
||||||
|
|
||||||
OUTPUT
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,383 +1,62 @@
|
|||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'guard/guard'
|
require 'guard/dsl'
|
||||||
|
|
||||||
describe Guard::Dsl do
|
describe Guard::Dsl do
|
||||||
|
subject { Guard::Dsl }
|
||||||
class Guard::Dummy < Guard::Guard; end
|
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@local_guardfile_path = File.join(Dir.pwd, 'Guardfile')
|
::Guard.stub!(:add_guard)
|
||||||
@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
|
end
|
||||||
|
|
||||||
def self.disable_user_config
|
it "write an error message when no Guardfile is found" do
|
||||||
before(:each) { File.stub(:exist?).with(@user_config_path) { false } }
|
Dir.stub!(:pwd).and_return("no_guardfile_here")
|
||||||
|
|
||||||
|
Guard::UI.should_receive(:error).with("No Guardfile in current folder, please create one.")
|
||||||
|
lambda { subject.evaluate_guardfile }.should raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "it should select the correct data source for Guardfile" do
|
it "write an error message when Guardfile is not valid" do
|
||||||
before(:each) { ::Guard::Dsl.stub!(:instance_eval_guardfile) }
|
mock_guardfile_content("This Guardfile is invalid!")
|
||||||
disable_user_config
|
|
||||||
|
Guard::UI.should_receive(:error).with(/Invalid Guardfile, original error is:\n/)
|
||||||
it "should use a string for initializing" do
|
lambda { subject.evaluate_guardfile }.should raise_error
|
||||||
Guard::UI.should_not_receive(:error)
|
|
||||||
lambda { described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string) }.should_not raise_error
|
|
||||||
described_class.guardfile_contents.should == valid_guardfile_string
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should use a given file over the default loc" do
|
|
||||||
fake_guardfile('/abc/Guardfile', "guard :foo")
|
|
||||||
|
|
||||||
Guard::UI.should_not_receive(:error)
|
|
||||||
lambda { described_class.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
|
|
||||||
described_class.guardfile_contents.should == "guard :foo"
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should use a default file if no other options are given" do
|
|
||||||
fake_guardfile(@local_guardfile_path, "guard :bar")
|
|
||||||
|
|
||||||
Guard::UI.should_not_receive(:error)
|
|
||||||
lambda { described_class.evaluate_guardfile }.should_not raise_error
|
|
||||||
described_class.guardfile_contents.should == "guard :bar"
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should use a string over any other method" do
|
|
||||||
fake_guardfile('/abc/Guardfile', "guard :foo")
|
|
||||||
fake_guardfile(@local_guardfile_path, "guard :bar")
|
|
||||||
|
|
||||||
Guard::UI.should_not_receive(:error)
|
|
||||||
lambda { described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string) }.should_not raise_error
|
|
||||||
described_class.guardfile_contents.should == valid_guardfile_string
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should use the given Guardfile over default Guardfile" do
|
|
||||||
fake_guardfile('/abc/Guardfile', "guard :foo")
|
|
||||||
fake_guardfile(@local_guardfile_path, "guard :bar")
|
|
||||||
|
|
||||||
Guard::UI.should_not_receive(:error)
|
|
||||||
lambda { described_class.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
|
|
||||||
described_class.guardfile_contents.should == "guard :foo"
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should append the user config file if present' do
|
|
||||||
fake_guardfile('/abc/Guardfile', "guard :foo")
|
|
||||||
fake_guardfile(@user_config_path, "guard :bar")
|
|
||||||
Guard::UI.should_not_receive(:error)
|
|
||||||
lambda { described_class.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
|
|
||||||
described_class.guardfile_contents_with_user_config.should == "guard :foo\nguard :bar"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "displays an error message when no Guardfile is found" do
|
it "load a guard from the DSL" do
|
||||||
described_class.stub(:guardfile_default_path).and_return("no_guardfile_here")
|
mock_guardfile_content("guard 'test'")
|
||||||
Guard::UI.should_receive(:error).with("No Guardfile found, please create one with `guard init`.")
|
|
||||||
lambda { described_class.evaluate_guardfile }.should raise_error
|
::Guard.should_receive(:add_guard).with('test', [], {})
|
||||||
|
subject.evaluate_guardfile
|
||||||
end
|
end
|
||||||
|
|
||||||
it "displays an error message when no guard are defined in Guardfile" do
|
it "receive watchers when specified" do
|
||||||
::Guard::Dsl.stub!(:instance_eval_guardfile)
|
mock_guardfile_content("
|
||||||
::Guard.stub!(:guards).and_return([])
|
guard 'test' do
|
||||||
Guard::UI.should_receive(:error)
|
watch('a') { 'b' }
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string)
|
watch('c')
|
||||||
end
|
|
||||||
|
|
||||||
describe "correctly reads data from its valid data source" do
|
|
||||||
before(:each) { ::Guard::Dsl.stub!(:instance_eval_guardfile) }
|
|
||||||
disable_user_config
|
|
||||||
|
|
||||||
it "reads correctly from a string" do
|
|
||||||
lambda { described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string) }.should_not raise_error
|
|
||||||
described_class.guardfile_contents.should == valid_guardfile_string
|
|
||||||
end
|
|
||||||
|
|
||||||
it "reads correctly from a Guardfile" do
|
|
||||||
fake_guardfile('/abc/Guardfile', "guard :foo" )
|
|
||||||
|
|
||||||
lambda { described_class.evaluate_guardfile(:guardfile => '/abc/Guardfile') }.should_not raise_error
|
|
||||||
described_class.guardfile_contents.should == "guard :foo"
|
|
||||||
end
|
|
||||||
|
|
||||||
it "reads correctly from a Guardfile" do
|
|
||||||
fake_guardfile(File.join(Dir.pwd, 'Guardfile'), valid_guardfile_string)
|
|
||||||
|
|
||||||
lambda { described_class.evaluate_guardfile }.should_not raise_error
|
|
||||||
described_class.guardfile_contents.should == valid_guardfile_string
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "correctly throws errors when initializing with invalid data" do
|
|
||||||
before(:each) { ::Guard::Dsl.stub!(:instance_eval_guardfile) }
|
|
||||||
|
|
||||||
it "raises error when there's a problem reading a file" do
|
|
||||||
File.stub!(:exist?).with('/def/Guardfile') { true }
|
|
||||||
File.stub!(:read).with('/def/Guardfile') { raise Errno::EACCES.new("permission error") }
|
|
||||||
|
|
||||||
Guard::UI.should_receive(:error).with(/^Error reading file/)
|
|
||||||
lambda { described_class.evaluate_guardfile(:guardfile => '/def/Guardfile') }.should raise_error
|
|
||||||
end
|
|
||||||
|
|
||||||
it "raises error when given Guardfile doesn't exist" do
|
|
||||||
File.stub!(:exist?).with('/def/Guardfile') { false }
|
|
||||||
|
|
||||||
Guard::UI.should_receive(:error).with(/No Guardfile exists at/)
|
|
||||||
lambda { described_class.evaluate_guardfile(:guardfile => '/def/Guardfile') }.should raise_error
|
|
||||||
end
|
|
||||||
|
|
||||||
it "raises error when resorting to use default, finds no default" do
|
|
||||||
File.stub!(:exist?).with(@local_guardfile_path) { false }
|
|
||||||
File.stub!(:exist?).with(@home_guardfile_path) { false }
|
|
||||||
|
|
||||||
Guard::UI.should_receive(:error).with("No Guardfile found, please create one with `guard init`.")
|
|
||||||
lambda { described_class.evaluate_guardfile }.should raise_error
|
|
||||||
end
|
|
||||||
|
|
||||||
it "raises error when guardfile_content ends up empty or nil" do
|
|
||||||
Guard::UI.should_receive(:error).with(/The command file/)
|
|
||||||
lambda { described_class.evaluate_guardfile(:guardfile_contents => "") }.should raise_error
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't raise error when guardfile_content is nil (skipped)" do
|
|
||||||
Guard::UI.should_not_receive(:error)
|
|
||||||
lambda { described_class.evaluate_guardfile(:guardfile_contents => nil) }.should_not raise_error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "displays an error message when Guardfile is not valid" do
|
|
||||||
Guard::UI.should_receive(:error).with(/Invalid Guardfile, original error is:/)
|
|
||||||
|
|
||||||
lambda { described_class.evaluate_guardfile(:guardfile_contents => invalid_guardfile_string ) }.should raise_error
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".reevaluate_guardfile" do
|
|
||||||
before(:each) { ::Guard::Dsl.stub!(:instance_eval_guardfile) }
|
|
||||||
|
|
||||||
it "resets already definded guards before calling evaluate_guardfile" do
|
|
||||||
Guard::Notifier.turn_off
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => invalid_guardfile_string)
|
|
||||||
::Guard.guards.should_not be_empty
|
|
||||||
::Guard::Dsl.should_receive(:evaluate_guardfile)
|
|
||||||
described_class.reevaluate_guardfile
|
|
||||||
::Guard.guards.should be_empty
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".guardfile_default_path" do
|
|
||||||
let(:local_path) { File.join(Dir.pwd, 'Guardfile') }
|
|
||||||
let(:user_path) { File.expand_path(File.join("~", '.Guardfile')) }
|
|
||||||
before(:each) { File.stub(:exist? => false) }
|
|
||||||
|
|
||||||
context "when there is a local Guardfile" do
|
|
||||||
it "returns the path to the local Guardfile" do
|
|
||||||
File.stub(:exist?).with(local_path).and_return(true)
|
|
||||||
described_class.guardfile_default_path.should == local_path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when there is a Guardfile in the user's home directory" do
|
|
||||||
it "returns the path to the user Guardfile" do
|
|
||||||
File.stub(:exist?).with(user_path).and_return(true)
|
|
||||||
described_class.guardfile_default_path.should == user_path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when there's both a local and user Guardfile" do
|
|
||||||
it "returns the path to the local Guardfile" do
|
|
||||||
File.stub(:exist?).with(local_path).and_return(true)
|
|
||||||
File.stub(:exist?).with(user_path).and_return(true)
|
|
||||||
described_class.guardfile_default_path.should == local_path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".guardfile_include?" do
|
|
||||||
it "detects a guard specified by a string with double quotes" do
|
|
||||||
described_class.stub(:guardfile_contents => 'guard "test" {watch("c")}')
|
|
||||||
|
|
||||||
described_class.guardfile_include?('test').should be_true
|
|
||||||
end
|
|
||||||
|
|
||||||
it "detects a guard specified by a string with single quote" do
|
|
||||||
described_class.stub(:guardfile_contents => 'guard \'test\' {watch("c")}')
|
|
||||||
|
|
||||||
described_class.guardfile_include?('test').should be_true
|
|
||||||
end
|
|
||||||
|
|
||||||
it "detects a guard specified by a symbol" do
|
|
||||||
described_class.stub(:guardfile_contents => 'guard :test {watch("c")}')
|
|
||||||
|
|
||||||
described_class.guardfile_include?('test').should be_true
|
|
||||||
end
|
|
||||||
|
|
||||||
it "detects a guard wrapped in parentheses" do
|
|
||||||
described_class.stub(:guardfile_contents => 'guard(:test) {watch("c")}')
|
|
||||||
|
|
||||||
described_class.guardfile_include?('test').should be_true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#ignore_paths" do
|
|
||||||
disable_user_config
|
|
||||||
|
|
||||||
it "adds the paths to the listener's ignore_paths" do
|
|
||||||
::Guard.stub!(:listener).and_return(mock('Listener'))
|
|
||||||
::Guard.listener.should_receive(:ignore_paths).and_return(ignore_paths = ['faz'])
|
|
||||||
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => "ignore_paths 'foo', 'bar'")
|
|
||||||
ignore_paths.should == ['faz', 'foo', 'bar']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#group" do
|
|
||||||
disable_user_config
|
|
||||||
|
|
||||||
it "evaluates only the specified string group" do
|
|
||||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
|
||||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :w })
|
|
||||||
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:w])
|
|
||||||
end
|
|
||||||
|
|
||||||
it "evaluates only the specified symbol group" do
|
|
||||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
|
||||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :w })
|
|
||||||
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:w])
|
|
||||||
end
|
|
||||||
|
|
||||||
it "evaluates only the specified groups (with their options)" do
|
|
||||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
|
||||||
::Guard.should_receive(:add_guard).with('rspec', [], [], { :group => :x })
|
|
||||||
::Guard.should_receive(:add_guard).with('ronn', [], [], { :group => :x })
|
|
||||||
::Guard.should_receive(:add_guard).with('less', [], [], { :group => :y })
|
|
||||||
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:x, :y])
|
|
||||||
end
|
|
||||||
|
|
||||||
it "evaluates always guard outside any group (even when a group is given)" do
|
|
||||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
|
||||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :w })
|
|
||||||
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:w])
|
|
||||||
end
|
|
||||||
|
|
||||||
it "evaluates all groups when no group option is specified (with their options)" do
|
|
||||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
|
||||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :w })
|
|
||||||
::Guard.should_receive(:add_guard).with('rspec', [], [], { :group => :x })
|
|
||||||
::Guard.should_receive(:add_guard).with('ronn', [], [], { :group => :x })
|
|
||||||
::Guard.should_receive(:add_guard).with('less', [], [], { :group => :y })
|
|
||||||
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => valid_guardfile_string)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#guard" do
|
|
||||||
disable_user_config
|
|
||||||
|
|
||||||
it "loads a guard specified as a quoted string from the DSL" do
|
|
||||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :default })
|
|
||||||
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => "guard 'test'")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "loads a guard specified as a double quoted string from the DSL" do
|
|
||||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :default })
|
|
||||||
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => 'guard "test"')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "loads a guard specified as a symbol from the DSL" do
|
|
||||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :default })
|
|
||||||
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => "guard :test")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "loads a guard specified as a symbol and called with parens from the DSL" do
|
|
||||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :default })
|
|
||||||
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => "guard(:test)")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "receives options when specified, from normal arg" do
|
|
||||||
::Guard.should_receive(:add_guard).with('test', [], [], { :opt_a => 1, :opt_b => 'fancy', :group => :default })
|
|
||||||
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => "guard 'test', :opt_a => 1, :opt_b => 'fancy'")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#watch" do
|
|
||||||
disable_user_config
|
|
||||||
|
|
||||||
it "should receive watchers when specified" do
|
|
||||||
::Guard.should_receive(:add_guard).with('dummy', anything, anything, { :group => :default }) do |name, watchers, callbacks, options|
|
|
||||||
watchers.size.should == 2
|
|
||||||
watchers[0].pattern.should == 'a'
|
|
||||||
watchers[0].action.call.should == proc { 'b' }.call
|
|
||||||
watchers[1].pattern.should == 'c'
|
|
||||||
watchers[1].action.should == nil
|
|
||||||
end
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => "
|
|
||||||
guard :dummy do
|
|
||||||
watch('a') { 'b' }
|
|
||||||
watch('c')
|
|
||||||
end")
|
end")
|
||||||
|
|
||||||
|
::Guard.should_receive(:add_guard).with('test', anything, {}) do |name, watchers, options|
|
||||||
|
watchers.size.should == 2
|
||||||
|
watchers[0].pattern.should == 'a'
|
||||||
|
watchers[0].action.call.should == proc { 'b' }.call
|
||||||
|
watchers[1].pattern.should == 'c'
|
||||||
|
watchers[1].action.should be_nil
|
||||||
end
|
end
|
||||||
|
subject.evaluate_guardfile
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#callback" do
|
it "receive options when specified" do
|
||||||
it "creates callbacks for the guard" do
|
mock_guardfile_content("guard 'test', :opt_a => 1, :opt_b => 'fancy'")
|
||||||
class MyCustomCallback
|
|
||||||
def self.call(guard_class, event, args)
|
::Guard.should_receive(:add_guard).with('test', anything, { :opt_a => 1, :opt_b => 'fancy' })
|
||||||
# do nothing
|
subject.evaluate_guardfile
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
::Guard.should_receive(:add_guard).with('dummy', anything, anything, { :group => :default }) do |name, watchers, callbacks, options|
|
|
||||||
callbacks.should have(2).items
|
|
||||||
callbacks[0][:events].should == :start_end
|
|
||||||
callbacks[0][:listener].call(Guard::Dummy, :start_end, 'foo').should == "Guard::Dummy executed 'start_end' hook with foo!"
|
|
||||||
callbacks[1][:events].should == [:start_begin, :run_all_begin]
|
|
||||||
callbacks[1][:listener].should == MyCustomCallback
|
|
||||||
end
|
|
||||||
described_class.evaluate_guardfile(:guardfile_contents => '
|
|
||||||
guard :dummy do
|
|
||||||
callback(:start_end) { |guard_class, event, args| "#{guard_class} executed \'#{event}\' hook with #{args}!" }
|
|
||||||
callback(MyCustomCallback, [:start_begin, :run_all_begin])
|
|
||||||
end')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def fake_guardfile(name, contents)
|
def mock_guardfile_content(content)
|
||||||
File.stub!(:exist?).with(name) { true }
|
File.stub!(:read).with(File.expand_path('../../../Guardfile', __FILE__)) { content }
|
||||||
File.stub!(:read).with(name) { contents }
|
|
||||||
end
|
|
||||||
|
|
||||||
def valid_guardfile_string
|
|
||||||
"
|
|
||||||
guard :pow
|
|
||||||
|
|
||||||
group :w do
|
|
||||||
guard :test
|
|
||||||
end
|
|
||||||
|
|
||||||
group :x, :halt_on_fail => true do
|
|
||||||
guard :rspec
|
|
||||||
guard :ronn
|
|
||||||
end
|
|
||||||
|
|
||||||
group :y do
|
|
||||||
guard :less
|
|
||||||
end
|
|
||||||
"
|
|
||||||
end
|
|
||||||
|
|
||||||
def invalid_guardfile_string
|
|
||||||
"Bad Guardfile"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe Guard::Group do
|
|
||||||
|
|
||||||
describe ".initialize" do
|
|
||||||
it "accepts a name as a string and provides an accessor for it (returning a symbol)" do
|
|
||||||
described_class.new('foo').name.should eql :foo
|
|
||||||
end
|
|
||||||
|
|
||||||
it "accepts a name as a symbol and provides an accessor for it (returning a symbol)" do
|
|
||||||
described_class.new(:foo).name.should eql :foo
|
|
||||||
end
|
|
||||||
|
|
||||||
it "accepts options and provides an accessor for it" do
|
|
||||||
described_class.new('foo', :halt_on_fail => true).options.should == { :halt_on_fail => true }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,60 +1,152 @@
|
|||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
require 'guard/guard'
|
||||||
|
|
||||||
describe Guard::Guard do
|
describe Guard::Guard do
|
||||||
|
subject { Guard::Guard }
|
||||||
|
|
||||||
describe '#initialize' do
|
it "should be initialized with watchers" do
|
||||||
|
watcher = mock(Guard::Watcher)
|
||||||
it 'assigns the defined watchers' do
|
guard = subject.new([watcher])
|
||||||
watchers = [ Guard::Watcher.new('*') ]
|
guard.watchers.should == [watcher]
|
||||||
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
|
end
|
||||||
|
|
||||||
describe '#init' do
|
it "should be initialized with options" do
|
||||||
context 'when the Guard is already in the Guardfile' do
|
watcher = mock(Guard::Watcher)
|
||||||
before { ::Guard::Dsl.stub(:guardfile_include?).and_return true }
|
guard = subject.new([], {:test => true})
|
||||||
|
guard.options[:test].should be_true
|
||||||
it 'shows an info message' do
|
guard.options[:fake].should be_nil
|
||||||
::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
|
||||||
|
|
||||||
|
context "#reload?" do
|
||||||
|
|
||||||
|
it "should return true by default" do
|
||||||
|
subject.new.reload?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return true with option `:reload => {:disable => false}'" do
|
||||||
|
guard = subject.new([], :reload => {:disable => false})
|
||||||
|
guard.reload?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return true with option `:reload => {}'" do
|
||||||
|
guard = subject.new([], :reload => {})
|
||||||
|
guard.reload?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return true with option `:reload => true'" do
|
||||||
|
guard = subject.new([], :reload => true)
|
||||||
|
guard.reload?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false with option `:reload => {:disable => true}'" do
|
||||||
|
guard = subject.new([], :reload => {:disable => true})
|
||||||
|
guard.reload?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false with option `:reload => false'" do
|
||||||
|
guard = subject.new([], :reload => false)
|
||||||
|
guard.reload?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#reload_at_start?" do
|
||||||
|
|
||||||
|
it "should return false by default" do
|
||||||
|
subject.new.reload_at_start?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false with option `:reload => {:at_start => false}'" do
|
||||||
|
guard = subject.new([], :reload => {:at_start => false})
|
||||||
|
guard.reload_at_start?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false with option `:reload => {}'" do
|
||||||
|
guard = subject.new([], :reload => {})
|
||||||
|
guard.reload_at_start?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false with option `:reload => true'" do
|
||||||
|
guard = subject.new([], :reload => true)
|
||||||
|
guard.reload_at_start?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false with option `:reload => false'" do
|
||||||
|
guard = subject.new([], :reload => false)
|
||||||
|
guard.reload_at_start?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return true with option `:reload => {:at_start => true}'" do
|
||||||
|
guard = subject.new([], :reload => {:at_start => true})
|
||||||
|
guard.reload_at_start?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#run_all?" do
|
||||||
|
|
||||||
|
it "should return true by default" do
|
||||||
|
subject.new.run_all?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return true with option `:reload => {:disable => false}'" do
|
||||||
|
guard = subject.new([], :run_all => {:disable => false})
|
||||||
|
guard.run_all?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return true with option `:reload => {}'" do
|
||||||
|
guard = subject.new([], :run_all => {})
|
||||||
|
guard.run_all?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return true with option `:reload => true'" do
|
||||||
|
guard = subject.new([], :run_all => true)
|
||||||
|
guard.run_all?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false with option `:reload => {:disable => true}'" do
|
||||||
|
guard = subject.new([], :run_all => {:disable => true})
|
||||||
|
guard.run_all?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false with option `:reload => false'" do
|
||||||
|
guard = subject.new([], :run_all => false)
|
||||||
|
guard.run_all?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#run_all_at_start?" do
|
||||||
|
|
||||||
|
it "should return false by default" do
|
||||||
|
subject.new.run_all_at_start?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false with option `:reload => {:at_start => false}'" do
|
||||||
|
guard = subject.new([], :run_all => {:at_start => false})
|
||||||
|
guard.run_all_at_start?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false with option `:reload => {}'" do
|
||||||
|
guard = subject.new([], :run_all => {})
|
||||||
|
guard.run_all_at_start?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false with option `:reload => true'" do
|
||||||
|
guard = subject.new([], :run_all => true)
|
||||||
|
guard.run_all_at_start?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false with option `:reload => false'" do
|
||||||
|
guard = subject.new([], :run_all => false)
|
||||||
|
guard.run_all_at_start?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return true with option `:reload => {:at_start => true}'" do
|
||||||
|
guard = subject.new([], :run_all => {:at_start => true})
|
||||||
|
guard.run_all_at_start?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
require 'guard/guard'
|
|
||||||
|
|
||||||
describe Guard::Hook do
|
|
||||||
|
|
||||||
class Guard::Dummy < Guard::Guard; end
|
|
||||||
|
|
||||||
let(:guard_class) { ::Guard::Dummy }
|
|
||||||
let(:listener) { double('listener').as_null_object }
|
|
||||||
|
|
||||||
after { described_class.reset_callbacks! }
|
|
||||||
|
|
||||||
describe "--module methods--" do
|
|
||||||
before { described_class.add_callback(listener, guard_class, :start_begin) }
|
|
||||||
|
|
||||||
describe ".add_callback" do
|
|
||||||
it "can add a single callback" do
|
|
||||||
described_class.has_callback?(listener, guard_class, :start_begin).should be_true
|
|
||||||
end
|
|
||||||
|
|
||||||
it "can add multiple callbacks" do
|
|
||||||
described_class.add_callback(listener, guard_class, [:event1, :event2])
|
|
||||||
described_class.has_callback?(listener, guard_class, :event1).should be_true
|
|
||||||
described_class.has_callback?(listener, guard_class, :event2).should be_true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".notify" do
|
|
||||||
it "sends :call to the given Guard class's callbacks" do
|
|
||||||
listener.should_receive(:call).with(guard_class, :start_begin, "args")
|
|
||||||
described_class.notify(guard_class, :start_begin, "args")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "runs only the given callbacks" do
|
|
||||||
listener2 = double('listener2')
|
|
||||||
described_class.add_callback(listener2, guard_class, :start_end)
|
|
||||||
listener2.should_not_receive(:call).with(guard_class, :start_end)
|
|
||||||
described_class.notify(guard_class, :start_begin)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "runs callbacks only for the guard given" do
|
|
||||||
guard2_class = double('Guard::Dummy2').class
|
|
||||||
described_class.add_callback(listener, guard2_class, :start_begin)
|
|
||||||
listener.should_not_receive(:call).with(guard2_class, :start_begin)
|
|
||||||
described_class.notify(guard_class, :start_begin)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#hook" do
|
|
||||||
before(:all) do
|
|
||||||
guard_class.class_eval do
|
|
||||||
def start
|
|
||||||
hook "my_hook"
|
|
||||||
end
|
|
||||||
|
|
||||||
def run_all
|
|
||||||
hook :begin
|
|
||||||
hook :end
|
|
||||||
end
|
|
||||||
|
|
||||||
def stop
|
|
||||||
hook :begin, 'args'
|
|
||||||
hook 'special_sauce', 'first_arg', 'second_arg'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@guard = guard_class.new
|
|
||||||
end
|
|
||||||
|
|
||||||
it "calls Guard::Hook.notify" do
|
|
||||||
Guard::Hook.should_receive(:notify).with(guard_class, :run_all_begin)
|
|
||||||
Guard::Hook.should_receive(:notify).with(guard_class, :run_all_end)
|
|
||||||
@guard.run_all
|
|
||||||
end
|
|
||||||
|
|
||||||
it "if passed a string parameter, will use that for the hook name" do
|
|
||||||
Guard::Hook.should_receive(:notify).with(guard_class, :my_hook)
|
|
||||||
@guard.start
|
|
||||||
end
|
|
||||||
|
|
||||||
it "accepts extra args" do
|
|
||||||
Guard::Hook.should_receive(:notify).with(guard_class, :stop_begin, 'args')
|
|
||||||
Guard::Hook.should_receive(:notify).with(guard_class, :special_sauce, 'first_arg', 'second_arg')
|
|
||||||
@guard.stop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,6 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe Guard::Interactor do
|
|
||||||
subject { Guard::Interactor.new }
|
|
||||||
|
|
||||||
end
|
|
@ -1,291 +1,34 @@
|
|||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Guard::Listener do
|
describe Guard::Listener do
|
||||||
|
subject { described_class }
|
||||||
describe '.select_and_init' do
|
|
||||||
before(:each) { @target_os = RbConfig::CONFIG['target_os'] }
|
describe "init" do
|
||||||
after(:each) { RbConfig::CONFIG['target_os'] = @target_os }
|
|
||||||
|
before(:each) { @target_os = Config::CONFIG['target_os'] }
|
||||||
it 'uses the Darwin listener on Mac OS X' do
|
after(:each) { Config::CONFIG['target_os'] = @target_os }
|
||||||
RbConfig::CONFIG['target_os'] = 'darwin10.4.0'
|
|
||||||
|
it "should use darwin listener on Mac OS X" do
|
||||||
|
Config::CONFIG['target_os'] = 'darwin10.4.0'
|
||||||
Guard::Darwin.stub(:usable?).and_return(true)
|
Guard::Darwin.stub(:usable?).and_return(true)
|
||||||
Guard::Darwin.should_receive(:new)
|
Guard::Darwin.should_receive(:new)
|
||||||
described_class.select_and_init
|
subject.init
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'uses the Windows listener on Windows' do
|
it "should use polling listener on Windows" do
|
||||||
RbConfig::CONFIG['target_os'] = 'mingw'
|
Config::CONFIG['target_os'] = 'win32'
|
||||||
Guard::Windows.stub(:usable?).and_return(true)
|
Guard::Polling.stub(:usable?).and_return(true)
|
||||||
Guard::Windows.should_receive(:new)
|
Guard::Polling.should_receive(:new)
|
||||||
described_class.select_and_init
|
subject.init
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'uses the Linux listener on Linux' do
|
it "should use linux listener on Linux" do
|
||||||
RbConfig::CONFIG['target_os'] = 'linux'
|
Config::CONFIG['target_os'] = 'linux'
|
||||||
Guard::Linux.stub(:usable?).and_return(true)
|
Guard::Linux.stub(:usable?).and_return(true)
|
||||||
Guard::Linux.should_receive(:new)
|
Guard::Linux.should_receive(:new)
|
||||||
described_class.select_and_init
|
subject.init
|
||||||
end
|
|
||||||
|
|
||||||
it 'forwards its arguments to the constructor' do
|
|
||||||
described_class.stub!(:mac?).and_return(true)
|
|
||||||
Guard::Darwin.stub!(:usable?).and_return(true)
|
|
||||||
|
|
||||||
path, opts = 'path', { :foo => 23 }
|
|
||||||
Guard::Darwin.should_receive(:new).with(path, opts).and_return(true)
|
|
||||||
described_class.select_and_init(path, opts)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#all_files' do
|
end
|
||||||
subject { described_class.new(@fixture_path) }
|
|
||||||
|
|
||||||
it 'should return all files' do
|
|
||||||
subject.all_files.should =~
|
|
||||||
Dir.glob("#{ @fixture_path }/**/*", File::FNM_DOTMATCH).select { |file| File.file?(file) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#relativize_paths' do
|
|
||||||
subject { described_class.new('/tmp') }
|
|
||||||
|
|
||||||
let(:paths) { %w( /tmp/a /tmp/a/b /tmp/a.b/c.d ) }
|
|
||||||
|
|
||||||
it 'should relativize paths to the configured directory' do
|
|
||||||
subject.relativize_paths(paths).should =~ %w( a a/b a.b/c.d )
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when set to false' do
|
|
||||||
subject { described_class.new('/tmp', :relativize_paths => false) }
|
|
||||||
|
|
||||||
it 'can be disabled' do
|
|
||||||
subject.relativize_paths(paths).should eql paths
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#update_last_event' do
|
|
||||||
subject { described_class.new }
|
|
||||||
|
|
||||||
it 'updates the last event to the current time' do
|
|
||||||
time = Time.now
|
|
||||||
subject.update_last_event
|
|
||||||
subject.instance_variable_get(:@last_event).to_i.should >= time.to_i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#modified_files' do
|
|
||||||
subject { described_class.new }
|
|
||||||
|
|
||||||
let(:file1) { fixture('folder1', 'file1.txt') }
|
|
||||||
let(:file2) { fixture('folder1', 'folder2', 'file2.txt') }
|
|
||||||
let(:file3) { fixture('folder1', 'deletedfile1.txt') }
|
|
||||||
let(:file4) { fixture('folder1', 'movedfile1.txt') }
|
|
||||||
let(:file5) { fixture('folder1', 'folder2', 'movedfile1.txt') }
|
|
||||||
|
|
||||||
before { listen_to subject }
|
|
||||||
|
|
||||||
context 'without the :all option' do
|
|
||||||
it 'finds modified files only in the directory supplied' do
|
|
||||||
watch do
|
|
||||||
FileUtils.touch([file1, file2, file3])
|
|
||||||
subject.modified_files([fixture('folder1')], {}).should =~
|
|
||||||
['spec/fixtures/folder1/deletedfile1.txt', 'spec/fixtures/folder1/file1.txt']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with the :all options' do
|
|
||||||
it 'finds modified files within subdirectories' do
|
|
||||||
watch do
|
|
||||||
FileUtils.touch([file1, file2, file3])
|
|
||||||
subject.modified_files([fixture('folder1')], { :all => true }).should =~
|
|
||||||
['spec/fixtures/folder1/deletedfile1.txt',
|
|
||||||
'spec/fixtures/folder1/file1.txt',
|
|
||||||
'spec/fixtures/folder1/folder2/file2.txt']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'without updating the content' do
|
|
||||||
it 'ignores the files for the second time' do
|
|
||||||
watch do
|
|
||||||
FileUtils.touch([file1, file2, file3])
|
|
||||||
subject.modified_files([fixture('folder1')], {}).should =~
|
|
||||||
['spec/fixtures/folder1/deletedfile1.txt', 'spec/fixtures/folder1/file1.txt']
|
|
||||||
|
|
||||||
subject.update_last_event
|
|
||||||
|
|
||||||
FileUtils.touch([file1, file2, file3])
|
|
||||||
subject.modified_files([fixture('folder1')], {}).should be_empty
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with content that has changed' do
|
|
||||||
after { File.open(file1, 'w') { |f| f.write('') } }
|
|
||||||
|
|
||||||
it 'identifies the files for the second time' do
|
|
||||||
watch do
|
|
||||||
FileUtils.touch([file1, file2, file3])
|
|
||||||
subject.modified_files([fixture('folder1')], {}).should =~
|
|
||||||
['spec/fixtures/folder1/deletedfile1.txt', 'spec/fixtures/folder1/file1.txt']
|
|
||||||
|
|
||||||
subject.update_last_event
|
|
||||||
|
|
||||||
FileUtils.touch([file2, file3])
|
|
||||||
File.open(file1, 'w') { |f| f.write('changed content') }
|
|
||||||
subject.modified_files([fixture('folder1')], {}).should =~
|
|
||||||
['spec/fixtures/folder1/file1.txt']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'without the :watch_all_modifications option' do
|
|
||||||
after { FileUtils.touch(file3) }
|
|
||||||
|
|
||||||
it 'defaults to false' do
|
|
||||||
subject.instance_variable_get(:@watch_all_modifications).should eql false
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a deleted file' do
|
|
||||||
after { FileUtils.touch(file3) }
|
|
||||||
|
|
||||||
it 'does not catch the deletion' do
|
|
||||||
File.exists?(file3).should be_true
|
|
||||||
|
|
||||||
watch do
|
|
||||||
FileUtils.remove_file(file3)
|
|
||||||
end
|
|
||||||
|
|
||||||
subject.modified_files([fixture('folder1')], {}).should =~ []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a moved file' do
|
|
||||||
after { FileUtils.move(file4, file1) }
|
|
||||||
|
|
||||||
it 'does not catch the move' do
|
|
||||||
File.exists?(file1).should be_true
|
|
||||||
File.exists?(file4).should be_false
|
|
||||||
|
|
||||||
watch do
|
|
||||||
FileUtils.move(file1, file4)
|
|
||||||
end
|
|
||||||
|
|
||||||
subject.modified_files([@fixture_path.join('folder1')], {}).should =~ []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with the :watch_all_modifications option' do
|
|
||||||
subject { described_class.new(Dir.pwd, :watch_all_modifications => true) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
subject.timestamp_files
|
|
||||||
subject.update_last_event
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should be true when set' do
|
|
||||||
subject.instance_variable_get(:@watch_all_modifications).should eql true
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a deleted file' do
|
|
||||||
after { FileUtils.touch(file3) }
|
|
||||||
|
|
||||||
it 'catches the deletion' do
|
|
||||||
File.exists?(file3).should be_true
|
|
||||||
|
|
||||||
watch do
|
|
||||||
FileUtils.remove_file(file3)
|
|
||||||
end
|
|
||||||
|
|
||||||
subject.modified_files([fixture('folder1')], {}).should =~
|
|
||||||
['!spec/fixtures/folder1/deletedfile1.txt']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a moved file' do
|
|
||||||
after { FileUtils.move(file4, file1) }
|
|
||||||
|
|
||||||
it 'catches the move' do
|
|
||||||
File.exists?(file1).should be_true
|
|
||||||
File.exists?(file4).should be_false
|
|
||||||
|
|
||||||
watch do
|
|
||||||
FileUtils.move(file1, file4)
|
|
||||||
end
|
|
||||||
|
|
||||||
subject.modified_files([@fixture_path.join('folder1')], {}).should =~
|
|
||||||
['!spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/movedfile1.txt']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'working directory' do
|
|
||||||
context 'unspecified' do
|
|
||||||
subject { described_class.new }
|
|
||||||
|
|
||||||
it 'defaults to Dir.pwd' do
|
|
||||||
subject.instance_variable_get(:@directory).should eql Dir.pwd
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'can be not changed' do
|
|
||||||
subject.should_not respond_to(:directory=)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'specified as first argument to ::new' do
|
|
||||||
let(:working_directory) { fixture('folder1') }
|
|
||||||
|
|
||||||
subject { described_class.new working_directory }
|
|
||||||
|
|
||||||
before { listen_to subject }
|
|
||||||
|
|
||||||
it 'can be inspected' do
|
|
||||||
subject.instance_variable_get(:@directory).should eql working_directory.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'can be not changed' do
|
|
||||||
subject.should_not respond_to(:directory=)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'will be used to watch' do
|
|
||||||
subject.should_receive(:watch).with(working_directory.to_s)
|
|
||||||
start
|
|
||||||
stop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#ignore_paths' do
|
|
||||||
it 'defaults to the default ignore paths' do
|
|
||||||
described_class.new.ignore_paths.should == Guard::Listener::DEFAULT_IGNORE_PATHS
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'can be added to via :ignore_paths option' do
|
|
||||||
listener = described_class.new 'path', :ignore_paths => ['foo', 'bar']
|
|
||||||
listener.ignore_paths.should include('foo', 'bar')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#exclude_ignored_paths [<dirs>]' do
|
|
||||||
let(:ignore_paths) { nil }
|
|
||||||
subject { described_class.new(@fixture_path, { :ignore_paths => ignore_paths }) }
|
|
||||||
|
|
||||||
it 'returns children of <dirs>' do
|
|
||||||
subject.exclude_ignored_paths(['spec/fixtures']).should =~
|
|
||||||
['spec/fixtures/.dotfile', 'spec/fixtures/folder1', 'spec/fixtures/Guardfile']
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'when ignore_paths set to some of <dirs> children' do
|
|
||||||
let(:ignore_paths) { ['Guardfile', '.dotfile'] }
|
|
||||||
|
|
||||||
it 'excludes the ignored paths' do
|
|
||||||
subject.exclude_ignored_paths(['spec/fixtures']).should =~ ['spec/fixtures/folder1']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -2,26 +2,72 @@ require 'spec_helper'
|
|||||||
require 'guard/listeners/darwin'
|
require 'guard/listeners/darwin'
|
||||||
|
|
||||||
describe Guard::Darwin do
|
describe Guard::Darwin do
|
||||||
|
subject { Guard::Darwin }
|
||||||
if windows?
|
|
||||||
it "isn't usable on windows" do
|
|
||||||
described_class.should_not be_usable
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if linux?
|
if linux?
|
||||||
it "isn't usable on linux" do
|
it "should not be usable on linux" do
|
||||||
described_class.should_not be_usable
|
subject.should_not be_usable
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if mac? && Guard::Darwin.usable?
|
if mac?
|
||||||
it "is usable on 10.6" do
|
it "should be usable on 10.6" do
|
||||||
described_class.should be_usable
|
subject.should be_usable
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "watch" do
|
||||||
|
before(:each) do
|
||||||
|
@results = []
|
||||||
|
@listener = Guard::Darwin.new
|
||||||
|
@listener.on_change do |files|
|
||||||
|
@results += files
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should catch new file" do
|
||||||
|
file = @fixture_path.join("newfile.rb")
|
||||||
|
File.exists?(file).should be_false
|
||||||
|
start
|
||||||
|
FileUtils.touch file
|
||||||
|
stop
|
||||||
|
File.delete file
|
||||||
|
@results.should == ['spec/fixtures/newfile.rb']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should catch file update" do
|
||||||
|
file = @fixture_path.join("folder1/file1.txt")
|
||||||
|
File.exists?(file).should be_true
|
||||||
|
start
|
||||||
|
FileUtils.touch file
|
||||||
|
stop
|
||||||
|
@results.should == ['spec/fixtures/folder1/file1.txt']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should catch files update" do
|
||||||
|
file1 = @fixture_path.join("folder1/file1.txt")
|
||||||
|
file2 = @fixture_path.join("folder1/folder2/file2.txt")
|
||||||
|
File.exists?(file1).should be_true
|
||||||
|
File.exists?(file2).should be_true
|
||||||
|
start
|
||||||
|
FileUtils.touch file1
|
||||||
|
FileUtils.touch file2
|
||||||
|
stop
|
||||||
|
@results.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
||||||
|
end
|
||||||
end
|
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
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def start
|
||||||
|
sleep 1
|
||||||
|
Thread.new { @listener.start }
|
||||||
|
sleep 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def stop
|
||||||
|
sleep 1
|
||||||
|
@listener.stop
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -3,35 +3,30 @@ require 'fileutils'
|
|||||||
require 'guard/listeners/linux'
|
require 'guard/listeners/linux'
|
||||||
|
|
||||||
describe Guard::Linux do
|
describe Guard::Linux do
|
||||||
|
subject { Guard::Linux }
|
||||||
|
|
||||||
if mac?
|
if mac?
|
||||||
it "isn't usable on 10.6" do
|
it "should not be usable on 10.6" do
|
||||||
described_class.should_not be_usable
|
subject.should_not be_usable
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if windows?
|
if linux?
|
||||||
it "isn't usable on windows" do
|
it "should be usable on linux" do
|
||||||
described_class.should_not be_usable
|
subject.should be_usable
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if linux? && Guard::Linux.usable?
|
|
||||||
it "is usable on linux" do
|
|
||||||
described_class.should be_usable
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#start", :long_running => true do
|
describe "start" do
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@listener = Guard::Linux.new
|
@listener = Guard::Linux.new
|
||||||
end
|
end
|
||||||
|
|
||||||
it "calls watch_change on the first start" do
|
it "should call watch_change if first start" do
|
||||||
@listener.should_receive(:watch_change)
|
@listener.should_receive(:watch_change)
|
||||||
start
|
start
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't call watch_change on subsequent starts after a stop" do
|
it "should not call watch_change if start after stop" do
|
||||||
@listener.stub!(:stop)
|
@listener.stub!(:stop)
|
||||||
start
|
start
|
||||||
stop
|
stop
|
||||||
@ -42,35 +37,94 @@ describe Guard::Linux do
|
|||||||
stop
|
stop
|
||||||
@listener.should_not be_watch_change
|
@listener.should_not be_watch_change
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "watch" do
|
||||||
|
before(:each) do
|
||||||
|
@results = []
|
||||||
|
@listener = Guard::Linux.new
|
||||||
|
@listener.on_change do |files|
|
||||||
|
@results += files
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should catch new file" do
|
||||||
|
file = @fixture_path.join("newfile.rb")
|
||||||
|
File.exists?(file).should be_false
|
||||||
|
start
|
||||||
|
FileUtils.touch file
|
||||||
|
stop
|
||||||
|
File.delete file
|
||||||
|
@results.should == ['spec/fixtures/newfile.rb']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should catch file update" do
|
||||||
|
file = @fixture_path.join("folder1/file1.txt")
|
||||||
|
File.exists?(file).should be_true
|
||||||
|
start
|
||||||
|
File.open(file, 'w') {|f| f.write('') }
|
||||||
|
stop
|
||||||
|
@results.should == ['spec/fixtures/folder1/file1.txt']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should catch files update" do
|
||||||
|
file1 = @fixture_path.join("folder1/file1.txt")
|
||||||
|
file2 = @fixture_path.join("folder1/folder2/file2.txt")
|
||||||
|
File.exists?(file1).should be_true
|
||||||
|
File.exists?(file2).should be_true
|
||||||
|
start
|
||||||
|
File.open(file1, 'w') {|f| f.write('') }
|
||||||
|
File.open(file2, 'w') {|f| f.write('') }
|
||||||
|
stop
|
||||||
|
@results.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should catch deleted file" do
|
||||||
|
file = @fixture_path.join("folder1/file1.txt")
|
||||||
|
File.exists?(file).should be_true
|
||||||
|
start
|
||||||
|
File.delete file
|
||||||
|
stop
|
||||||
|
FileUtils.touch file
|
||||||
|
@results.should == ['spec/fixtures/folder1/file1.txt']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should catch moved file" do
|
||||||
|
file1 = @fixture_path.join("folder1/file1.txt")
|
||||||
|
file2 = @fixture_path.join("folder1/movedfile1.txt")
|
||||||
|
File.exists?(file1).should be_true
|
||||||
|
File.exists?(file2).should be_false
|
||||||
|
start
|
||||||
|
FileUtils.mv file1, file2
|
||||||
|
stop
|
||||||
|
FileUtils.mv file2, file1
|
||||||
|
@results.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/movedfile1.txt']
|
||||||
|
end
|
||||||
|
|
||||||
it_should_behave_like "a listener that reacts to #on_change"
|
it "should not process change if stopped" do
|
||||||
it_should_behave_like "a listener scoped to a specific directory"
|
file = @fixture_path.join("folder1/file1.txt")
|
||||||
|
File.exists?(file).should be_true
|
||||||
# Fun fact: FileUtils.touch seems not to be enough on Linux to trigger a modify event
|
start
|
||||||
|
@listener.inotify.should_not_receive(:process)
|
||||||
it "catches modified files with glib saving routine (like Vim, Emacs or Gedit)" do
|
stop
|
||||||
@listener = described_class.new
|
File.open(file, 'w') {|f| f.write('') }
|
||||||
record_results
|
end
|
||||||
file = @fixture_path.join("folder1/file1.txt")
|
|
||||||
File.exists?(file).should be_true
|
|
||||||
start
|
|
||||||
File.open(file, 'r+').close
|
|
||||||
FileUtils.touch(file)
|
|
||||||
stop
|
|
||||||
results.should =~ ['spec/fixtures/folder1/file1.txt']
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't process a change when it is stopped" do
|
|
||||||
@listener = described_class.new
|
|
||||||
record_results
|
|
||||||
file = @fixture_path.join("folder1/file1.txt")
|
|
||||||
File.exists?(file).should be_true
|
|
||||||
start
|
|
||||||
@listener.instance_variable_get(:@inotify).should_not_receive(:process)
|
|
||||||
stop
|
|
||||||
File.open(file, 'w') {|f| f.write('') }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def start
|
||||||
|
sleep 1
|
||||||
|
Thread.new { @listener.start }
|
||||||
|
sleep 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def stop
|
||||||
|
sleep 1
|
||||||
|
@listener.stop
|
||||||
|
sleep 1
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -2,8 +2,56 @@ require 'spec_helper'
|
|||||||
require 'guard/listeners/polling'
|
require 'guard/listeners/polling'
|
||||||
|
|
||||||
describe Guard::Polling do
|
describe Guard::Polling do
|
||||||
|
|
||||||
it_should_behave_like "a listener that reacts to #on_change"
|
before(:each) do
|
||||||
it_should_behave_like "a listener scoped to a specific directory"
|
@results = []
|
||||||
|
@listener = Guard::Polling.new
|
||||||
|
@listener.on_change do |files|
|
||||||
|
@results += files
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should catch new file" do
|
||||||
|
file = @fixture_path.join("newfile.rb")
|
||||||
|
File.exists?(file).should be_false
|
||||||
|
start
|
||||||
|
FileUtils.touch file
|
||||||
|
stop
|
||||||
|
File.delete file
|
||||||
|
@results.should == ['spec/fixtures/newfile.rb']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should catch file update" do
|
||||||
|
file = @fixture_path.join("folder1/file1.txt")
|
||||||
|
File.exists?(file).should be_true
|
||||||
|
start
|
||||||
|
FileUtils.touch file
|
||||||
|
stop
|
||||||
|
@results.should == ['spec/fixtures/folder1/file1.txt']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should catch files update" do
|
||||||
|
file1 = @fixture_path.join("folder1/file1.txt")
|
||||||
|
file2 = @fixture_path.join("folder1/folder2/file2.txt")
|
||||||
|
File.exists?(file1).should be_true
|
||||||
|
File.exists?(file2).should be_true
|
||||||
|
start
|
||||||
|
FileUtils.touch file1
|
||||||
|
FileUtils.touch file2
|
||||||
|
stop
|
||||||
|
@results.sort.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def start
|
||||||
|
Thread.new { @listener.start }
|
||||||
|
sleep 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def stop
|
||||||
|
sleep 1
|
||||||
|
@listener.stop
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
require 'guard/listeners/windows'
|
|
||||||
|
|
||||||
describe Guard::Windows do
|
|
||||||
|
|
||||||
if linux?
|
|
||||||
it "isn't usable on linux" do
|
|
||||||
described_class.should_not be_usable
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if mac?
|
|
||||||
it "isn't usable on Mac" do
|
|
||||||
described_class.should_not be_usable
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if windows?
|
|
||||||
it "is usable on Windows 2000 and later" do
|
|
||||||
described_class.should be_usable
|
|
||||||
end
|
|
||||||
|
|
||||||
it_should_behave_like "a listener that reacts to #on_change"
|
|
||||||
it_should_behave_like "a listener scoped to a specific directory"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,428 +1,36 @@
|
|||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Guard::Notifier do
|
describe Guard::Notifier do
|
||||||
|
subject { Guard::Notifier }
|
||||||
describe ".turn_off" do
|
|
||||||
before do
|
describe "notify" do
|
||||||
ENV["GUARD_NOTIFY"] = 'true'
|
before(:each) { ENV["GUARD_ENV"] = 'special_test' }
|
||||||
described_class.turn_off
|
|
||||||
end
|
if mac?
|
||||||
|
require 'growl'
|
||||||
it "disables the notifications" do
|
it "should use Growl on Mac OS X" do
|
||||||
ENV["GUARD_NOTIFY"].should eql 'false'
|
Growl.should_receive(:notify).with("great",
|
||||||
end
|
:title => "Guard",
|
||||||
end
|
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
||||||
|
:name => "Guard"
|
||||||
describe ".turn_on" do
|
)
|
||||||
context "on Mac OS" do
|
subject.notify 'great', :title => 'Guard'
|
||||||
before do
|
|
||||||
RbConfig::CONFIG.should_receive(:[]).with('target_os').and_return 'darwin'
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with the GrowlNotify library available" do
|
|
||||||
before do
|
|
||||||
class ::GrowlNotify
|
|
||||||
class GrowlNotFound < Exception; end
|
|
||||||
def self.config ; end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "loads the library and enables the notifications" do
|
|
||||||
described_class.should_receive(:require).with('growl_notify').and_return true
|
|
||||||
GrowlNotify.should_receive(:application_name).and_return ''
|
|
||||||
described_class.turn_on
|
|
||||||
described_class.should be_enabled
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should respond properly to a GrowlNotify exception" do
|
|
||||||
::GrowlNotify.should_receive(:config).and_raise ::GrowlNotify::GrowlNotFound
|
|
||||||
::GrowlNotify.should_receive(:application_name).and_return ''
|
|
||||||
::Guard::UI.should_receive(:info)
|
|
||||||
described_class.should_receive(:require).with('growl_notify').and_return true
|
|
||||||
described_class.turn_on
|
|
||||||
described_class.should_not be_enabled
|
|
||||||
described_class.growl_library.should eql :growl_notify
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
Object.send(:remove_const, :GrowlNotify)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with the GNTP library available" do
|
|
||||||
before do
|
|
||||||
class ::GNTP
|
|
||||||
def initialize(app); end
|
|
||||||
def register(config) ; end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "loads the library and enables the notifications" do
|
|
||||||
described_class.should_receive(:require).with('growl_notify').and_raise LoadError
|
|
||||||
described_class.should_receive(:require).with('ruby_gntp').and_return true
|
|
||||||
described_class.turn_on
|
|
||||||
described_class.should be_enabled
|
|
||||||
described_class.growl_library.should eql :ruby_gntp
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
Object.send(:remove_const, :GNTP)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with the Growl library available" do
|
|
||||||
it "loads the library and enables the notifications" do
|
|
||||||
described_class.should_receive(:require).with('growl_notify').and_raise LoadError
|
|
||||||
described_class.should_receive(:require).with('ruby_gntp').and_raise LoadError
|
|
||||||
described_class.should_receive(:require).with('growl').and_return true
|
|
||||||
described_class.turn_on
|
|
||||||
described_class.should be_enabled
|
|
||||||
described_class.growl_library.should eql :growl
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "without a Growl library available" do
|
|
||||||
it "disables the notifications" do
|
|
||||||
described_class.should_receive(:require).with('growl_notify').and_raise LoadError
|
|
||||||
described_class.should_receive(:require).with('ruby_gntp').and_raise LoadError
|
|
||||||
described_class.should_receive(:require).with('growl').and_raise LoadError
|
|
||||||
described_class.turn_on
|
|
||||||
described_class.should_not be_enabled
|
|
||||||
described_class.growl_library.should be nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "on Linux" do
|
if linux?
|
||||||
before do
|
require 'libnotify'
|
||||||
RbConfig::CONFIG.should_receive(:[]).with('target_os').and_return 'linux'
|
it "should use Libnotify on Linux" do
|
||||||
end
|
|
||||||
|
|
||||||
context "with the Libnotify library available" do
|
|
||||||
it "loads the library and enables the notifications" do
|
|
||||||
described_class.should_receive(:require).with('libnotify').and_return true
|
|
||||||
described_class.turn_on
|
|
||||||
described_class.should be_enabled
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "without the Libnotify library available" do
|
|
||||||
it "disables the notifications" do
|
|
||||||
described_class.should_receive(:require).with('libnotify').and_raise LoadError
|
|
||||||
described_class.turn_on
|
|
||||||
described_class.should_not be_enabled
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "on Windows" do
|
|
||||||
before do
|
|
||||||
RbConfig::CONFIG.should_receive(:[]).with('target_os').and_return 'mswin'
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with the rb-notifu library available" do
|
|
||||||
it "loads the library and enables the notifications" do
|
|
||||||
described_class.should_receive(:require).with('rb-notifu').and_return true
|
|
||||||
described_class.turn_on
|
|
||||||
described_class.should be_enabled
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "without the rb-notify library available" do
|
|
||||||
it "disables the notifications" do
|
|
||||||
described_class.should_receive(:require).with('rb-notifu').and_raise LoadError
|
|
||||||
described_class.turn_on
|
|
||||||
described_class.should_not be_enabled
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".notify" do
|
|
||||||
before { described_class.stub(:enabled?).and_return(true) }
|
|
||||||
|
|
||||||
context "on Mac OS" do
|
|
||||||
before do
|
|
||||||
RbConfig::CONFIG.stub(:[]).and_return 'darwin'
|
|
||||||
described_class.stub(:require_growl)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with growl gem' do
|
|
||||||
before do
|
|
||||||
Object.send(:remove_const, :Growl) if defined?(Growl)
|
|
||||||
Growl = Object.new
|
|
||||||
described_class.growl_library = :growl
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
Object.send(:remove_const, :Growl)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "passes the notification to Growl" do
|
|
||||||
Growl.should_receive(:notify).with("great",
|
|
||||||
:title => "Guard",
|
|
||||||
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
|
||||||
:name => "Guard"
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "don't passes the notification to Growl if library is not available" do
|
|
||||||
Growl.should_not_receive(:notify)
|
|
||||||
described_class.growl_library = nil
|
|
||||||
described_class.should_receive(:enabled?).and_return(false)
|
|
||||||
described_class.notify 'great', :title => 'Guard'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "allows additional notification options" do
|
|
||||||
Growl.should_receive(:notify).with("great",
|
|
||||||
:title => "Guard",
|
|
||||||
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
|
||||||
:name => "Guard",
|
|
||||||
:priority => 1
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard', :priority => 1
|
|
||||||
end
|
|
||||||
|
|
||||||
it "allows to overwrite a default notification option" do
|
|
||||||
Growl.should_receive(:notify).with("great",
|
|
||||||
:title => "Guard",
|
|
||||||
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
|
||||||
:name => "Guard-Cucumber"
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard', :name => "Guard-Cucumber"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with growl_notify gem' do
|
|
||||||
before do
|
|
||||||
Object.send(:remove_const, :GrowlNotify) if defined?(GrowlNotify)
|
|
||||||
GrowlNotify = Object.new
|
|
||||||
described_class.growl_library = :growl_notify
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
Object.send(:remove_const, :GrowlNotify)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "passes the notification to Growl" do
|
|
||||||
GrowlNotify.should_receive(:send_notification).with(
|
|
||||||
:title => "Guard",
|
|
||||||
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
|
||||||
:application_name => "Guard",
|
|
||||||
:description => 'great'
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "don't passes the notification to Growl if library is not available" do
|
|
||||||
GrowlNotify.should_not_receive(:send_notification)
|
|
||||||
described_class.growl_library = nil
|
|
||||||
described_class.should_receive(:enabled?).and_return(false)
|
|
||||||
described_class.notify 'great', :title => 'Guard'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "allows additional notification options" do
|
|
||||||
GrowlNotify.should_receive(:send_notification).with(
|
|
||||||
:title => "Guard",
|
|
||||||
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
|
||||||
:application_name => "Guard",
|
|
||||||
:description => 'great',
|
|
||||||
:priority => 1
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard', :priority => 1
|
|
||||||
end
|
|
||||||
|
|
||||||
it "throws out the application name since Guard should only use one Growl App Name while running" do
|
|
||||||
GrowlNotify.should_receive(:send_notification).with(
|
|
||||||
:title => "Guard",
|
|
||||||
:icon => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
|
||||||
:application_name => "Guard",
|
|
||||||
:description => 'great'
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard', :name => "Guard-Cucumber"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with ruby_gntp gem' do
|
|
||||||
before do
|
|
||||||
described_class.growl_library = :ruby_gntp
|
|
||||||
described_class.gntp = Object.new
|
|
||||||
end
|
|
||||||
|
|
||||||
it "passes a success notification to Ruby GNTP" do
|
|
||||||
described_class.gntp.should_receive(:notify).with(
|
|
||||||
:name => "success",
|
|
||||||
:text => 'great',
|
|
||||||
:title => "Guard",
|
|
||||||
:icon => 'file://' + Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "passes a pending notification to Ruby GNTP" do
|
|
||||||
described_class.gntp.should_receive(:notify).with(
|
|
||||||
:name => "pending",
|
|
||||||
:text => 'great',
|
|
||||||
:title => "Guard",
|
|
||||||
:icon => 'file://' + Pathname.new(File.dirname(__FILE__)).join('../../images/pending.png').to_s
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard', :image => :pending
|
|
||||||
end
|
|
||||||
|
|
||||||
it "passes a failure notification to Ruby GNTP" do
|
|
||||||
described_class.gntp.should_receive(:notify).with(
|
|
||||||
:name => "failed",
|
|
||||||
:text => 'great',
|
|
||||||
:title => "Guard",
|
|
||||||
:icon => 'file://' + Pathname.new(File.dirname(__FILE__)).join('../../images/failed.png').to_s
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard', :image => :failed
|
|
||||||
end
|
|
||||||
|
|
||||||
it "passes a general notification to Ruby GNTP" do
|
|
||||||
described_class.gntp.should_receive(:notify).with(
|
|
||||||
:name => "notify",
|
|
||||||
:text => 'great',
|
|
||||||
:title => "Guard",
|
|
||||||
:icon => 'file:///path/to/custom.png'
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard', :image => '/path/to/custom.png'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "don't passes the notification to Ruby GNTP if library is not available" do
|
|
||||||
described_class.gntp.should_not_receive(:notify)
|
|
||||||
described_class.growl_library = nil
|
|
||||||
described_class.should_receive(:enabled?).and_return(false)
|
|
||||||
described_class.notify 'great', :title => 'Guard'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "allows additional notification options" do
|
|
||||||
described_class.gntp.should_receive(:notify).with(
|
|
||||||
:name => "success",
|
|
||||||
:text => 'great',
|
|
||||||
:title => "Guard",
|
|
||||||
:icon => 'file://' + Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
|
||||||
:sticky => true
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard', :sticky => true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "on Linux" do
|
|
||||||
before do
|
|
||||||
RbConfig::CONFIG.stub(:[]).and_return 'linux'
|
|
||||||
described_class.stub(:require_libnotify)
|
|
||||||
Object.send(:remove_const, :Libnotify) if defined?(Libnotify)
|
|
||||||
Libnotify = Object.new
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
Object.send(:remove_const, :Libnotify)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "passes the notification to Libnotify" do
|
|
||||||
Libnotify.should_receive(:show).with(
|
Libnotify.should_receive(:show).with(
|
||||||
:body => "great",
|
:body => "great",
|
||||||
:summary => 'Guard',
|
:summary => 'Guard',
|
||||||
:icon_path => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
: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'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "allows additional notification options" do
|
|
||||||
Libnotify.should_receive(:show).with(
|
|
||||||
:body => "great",
|
|
||||||
:summary => 'Guard',
|
|
||||||
:icon_path => Pathname.new(File.dirname(__FILE__)).join('../../images/success.png').to_s,
|
|
||||||
:transient => true,
|
|
||||||
:urgency => :critical
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard', :urgency => :critical
|
|
||||||
end
|
|
||||||
|
|
||||||
it "allows to overwrite a default notification option" do
|
|
||||||
Libnotify.should_receive(:show).with(
|
|
||||||
:body => "great",
|
|
||||||
:summary => 'Guard',
|
|
||||||
:icon_path => '~/.guard/success.png',
|
|
||||||
:transient => true
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard', :icon_path => '~/.guard/success.png'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "on Windows" do
|
|
||||||
before do
|
|
||||||
RbConfig::CONFIG.stub(:[]).and_return 'mswin'
|
|
||||||
described_class.stub(:require_rbnotifu)
|
|
||||||
Object.send(:remove_const, :Notifu) if defined?(Notifu)
|
|
||||||
Notifu = Object.new
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
Object.send(:remove_const, :Notifu)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "passes the notification to rb-notifu" do
|
|
||||||
Notifu.should_receive(:show).with(
|
|
||||||
:message => "great",
|
|
||||||
:title => 'Guard',
|
|
||||||
:type => :info,
|
|
||||||
:time => 3
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "don't passes the notification to rb-notifu if library is not available" do
|
|
||||||
Notifu.should_not_receive(:show)
|
|
||||||
described_class.should_receive(:enabled?).and_return(false)
|
|
||||||
described_class.notify 'great', :title => 'Guard'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "allows additional notification options" do
|
|
||||||
Notifu.should_receive(:show).with(
|
|
||||||
:message => "great",
|
|
||||||
:title => 'Guard',
|
|
||||||
:type => :info,
|
|
||||||
:time => 3,
|
|
||||||
:nosound => true
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard', :nosound => true
|
|
||||||
end
|
|
||||||
|
|
||||||
it "allows to overwrite a default notification option" do
|
|
||||||
Notifu.should_receive(:show).with(
|
|
||||||
:message => "great",
|
|
||||||
:title => 'Guard',
|
|
||||||
:type => :info,
|
|
||||||
:time => 10
|
|
||||||
)
|
|
||||||
described_class.notify 'great', :title => 'Guard', :time => 10
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
after(:each) { ENV["GUARD_ENV"] = 'test' }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".enabled?" do
|
end
|
||||||
context "when enabled" do
|
|
||||||
before { ENV["GUARD_NOTIFY"] = 'true' }
|
|
||||||
|
|
||||||
it { should be_enabled }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when disabled" do
|
|
||||||
before { ENV["GUARD_NOTIFY"] = 'false' }
|
|
||||||
|
|
||||||
it { should_not be_enabled }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,309 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
require 'guard/guard'
|
|
||||||
|
|
||||||
describe Guard::Watcher do
|
|
||||||
|
|
||||||
describe "#initialize" do
|
|
||||||
it "requires a pattern parameter" do
|
|
||||||
expect { described_class.new }.to raise_error(ArgumentError)
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with a pattern parameter" do
|
|
||||||
context "that is a string" do
|
|
||||||
it "keeps the string pattern unmodified" do
|
|
||||||
described_class.new('spec_helper.rb').pattern.should == 'spec_helper.rb'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "that is a regexp" do
|
|
||||||
it "keeps the regex pattern unmodified" do
|
|
||||||
described_class.new(/spec_helper\.rb/).pattern.should == /spec_helper\.rb/
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "that is a string looking like a regex (deprecated)" do
|
|
||||||
before(:each) { Guard::UI.should_receive(:info).any_number_of_times }
|
|
||||||
|
|
||||||
it "converts the string automatically to a regex" do
|
|
||||||
described_class.new('^spec_helper.rb').pattern.should == /^spec_helper.rb/
|
|
||||||
described_class.new('spec_helper.rb$').pattern.should == /spec_helper.rb$/
|
|
||||||
described_class.new('spec_helper\.rb').pattern.should == /spec_helper\.rb/
|
|
||||||
described_class.new('.*_spec.rb').pattern.should == /.*_spec.rb/
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#action" do
|
|
||||||
it "sets the action to nothing by default" do
|
|
||||||
described_class.new(/spec_helper\.rb/).action.should be_nil
|
|
||||||
end
|
|
||||||
|
|
||||||
it "sets the action to the supplied block" do
|
|
||||||
action = lambda { |m| "spec/#{m[1]}_spec.rb" }
|
|
||||||
described_class.new(%r{^lib/(.*).rb}, action).action.should == action
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".match_files" do
|
|
||||||
before(:all) do
|
|
||||||
@guard = Guard::Guard.new
|
|
||||||
@guard_any_return = Guard::Guard.new
|
|
||||||
@guard_any_return.options[:any_return] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with a watcher without action" do
|
|
||||||
context "that is a regex pattern" do
|
|
||||||
before(:all) { @guard.watchers = [described_class.new(/.*_spec\.rb/)] }
|
|
||||||
|
|
||||||
it "returns the paths that matches the regex" do
|
|
||||||
described_class.match_files(@guard, ['guard_rocks_spec.rb', 'guard_rocks.rb']).should == ['guard_rocks_spec.rb']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "that is a string pattern" do
|
|
||||||
before(:all) { @guard.watchers = [described_class.new('guard_rocks_spec.rb')] }
|
|
||||||
|
|
||||||
it "returns the path that matches the string" do
|
|
||||||
described_class.match_files(@guard, ['guard_rocks_spec.rb', 'guard_rocks.rb']).should == ['guard_rocks_spec.rb']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with a watcher action without parameter" do
|
|
||||||
context "for a watcher that matches file strings" do
|
|
||||||
before(:all) do
|
|
||||||
@guard.watchers = [
|
|
||||||
described_class.new('spec_helper.rb', lambda { 'spec' }),
|
|
||||||
described_class.new('addition.rb', lambda { 1 + 1 }),
|
|
||||||
described_class.new('hash.rb', lambda { Hash[:foo, 'bar'] }),
|
|
||||||
described_class.new('array.rb', lambda { ['foo', 'bar'] }),
|
|
||||||
described_class.new('blank.rb', lambda { '' }),
|
|
||||||
described_class.new(/^uptime\.rb/, lambda { `uptime > /dev/null` })
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns a single file specified within the action" do
|
|
||||||
described_class.match_files(@guard, ['spec_helper.rb']).should == ['spec']
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns multiple files specified within the action" do
|
|
||||||
described_class.match_files(@guard, ['hash.rb']).should == ['foo', 'bar']
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns multiple files by combining the results of different actions" do
|
|
||||||
described_class.match_files(@guard, ['spec_helper.rb', 'array.rb']).should == ['spec', 'foo', 'bar']
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nothing if the action returns something other than a string or an array of strings" do
|
|
||||||
described_class.match_files(@guard, ['addition.rb']).should == []
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nothing if the action response is empty" do
|
|
||||||
described_class.match_files(@guard, ['blank.rb']).should == []
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nothing if the action returns nothing" do
|
|
||||||
described_class.match_files(@guard, ['uptime.rb']).should == []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a watcher that matches information objects' do
|
|
||||||
before(:all) do
|
|
||||||
@guard_any_return.watchers = [
|
|
||||||
described_class.new('spec_helper.rb', lambda { 'spec' }),
|
|
||||||
described_class.new('addition.rb', lambda { 1 + 1 }),
|
|
||||||
described_class.new('hash.rb', lambda { Hash[:foo, 'bar'] }),
|
|
||||||
described_class.new('array.rb', lambda { ['foo', 'bar'] }),
|
|
||||||
described_class.new('blank.rb', lambda { '' }),
|
|
||||||
described_class.new(/^uptime\.rb/, lambda { `uptime > /dev/null` })
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns a single file specified within the action" do
|
|
||||||
described_class.match_files(@guard_any_return, ['spec_helper.rb']).class.should == Array
|
|
||||||
described_class.match_files(@guard_any_return, ['spec_helper.rb']).empty?.should == false
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns multiple files specified within the action" do
|
|
||||||
described_class.match_files(@guard_any_return, ['hash.rb']).should == [{:foo => 'bar'}]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns multiple files by combining the results of different actions" do
|
|
||||||
described_class.match_files(@guard_any_return, ['spec_helper.rb', 'array.rb']).should == ['spec', ['foo', 'bar']]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the evaluated addition argument in an array" do
|
|
||||||
described_class.match_files(@guard_any_return, ['addition.rb']).class.should == Array
|
|
||||||
described_class.match_files(@guard_any_return, ['addition.rb'])[0].should == 2
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nothing if the action response is empty string" do
|
|
||||||
described_class.match_files(@guard_any_return, ['blank.rb']).should == ['']
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nothing if the action returns empty string" do
|
|
||||||
described_class.match_files(@guard_any_return, ['uptime.rb']).should == ['']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with a watcher action that takes a parameter" do
|
|
||||||
context "for a watcher that matches file strings" do
|
|
||||||
before(:all) do
|
|
||||||
@guard.watchers = [
|
|
||||||
described_class.new(%r{lib/(.*)\.rb}, lambda { |m| "spec/#{m[1]}_spec.rb" }),
|
|
||||||
described_class.new(/addition(.*)\.rb/, lambda { |m| 1 + 1 }),
|
|
||||||
described_class.new('hash.rb', lambda { |m| Hash[:foo, 'bar'] }),
|
|
||||||
described_class.new(/array(.*)\.rb/, lambda { |m| ['foo', 'bar'] }),
|
|
||||||
described_class.new(/blank(.*)\.rb/, lambda { |m| '' }),
|
|
||||||
described_class.new(/uptime(.*)\.rb/, lambda { |m| `uptime > /dev/null` })
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns a substituted single file specified within the action" do
|
|
||||||
described_class.match_files(@guard, ['lib/my_wonderful_lib.rb']).should == ['spec/my_wonderful_lib_spec.rb']
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns multiple files specified within the action" do
|
|
||||||
described_class.match_files(@guard, ['hash.rb']).should == ['foo', 'bar']
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns multiple files by combining the results of different actions" do
|
|
||||||
described_class.match_files(@guard, ['lib/my_wonderful_lib.rb', 'array.rb']).should == ['spec/my_wonderful_lib_spec.rb', 'foo', 'bar']
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nothing if the action returns something other than a string or an array of strings" do
|
|
||||||
described_class.match_files(@guard, ['addition.rb']).should == []
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nothing if the action response is empty" do
|
|
||||||
described_class.match_files(@guard, ['blank.rb']).should == []
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nothing if the action returns nothing" do
|
|
||||||
described_class.match_files(@guard, ['uptime.rb']).should == []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "for a watcher that matches information objects" do
|
|
||||||
before(:all) do
|
|
||||||
@guard_any_return.watchers = [
|
|
||||||
described_class.new(%r{lib/(.*)\.rb}, lambda { |m| "spec/#{m[1]}_spec.rb" }),
|
|
||||||
described_class.new(/addition(.*)\.rb/, lambda { |m| (1 + 1).to_s + m[0] }),
|
|
||||||
described_class.new('hash.rb', lambda { |m| Hash[:foo, 'bar', :file_name, m[0]] }),
|
|
||||||
described_class.new(/array(.*)\.rb/, lambda { |m| ['foo', 'bar', m[0]] }),
|
|
||||||
described_class.new(/blank(.*)\.rb/, lambda { |m| '' }),
|
|
||||||
described_class.new(/uptime(.*)\.rb/, lambda { |m| `uptime > /dev/null` })
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns a substituted single file specified within the action" do
|
|
||||||
described_class.match_files(@guard_any_return, ['lib/my_wonderful_lib.rb']).should == ['spec/my_wonderful_lib_spec.rb']
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns a hash specified within the action" do
|
|
||||||
described_class.match_files(@guard_any_return, ['hash.rb']).should == [{:foo => 'bar', :file_name => 'hash.rb'}]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns multiple files by combining the results of different actions" do
|
|
||||||
described_class.match_files(@guard_any_return, ['lib/my_wonderful_lib.rb', 'array.rb']).should == ['spec/my_wonderful_lib_spec.rb', ['foo', 'bar', "array.rb"]]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the evaluated addition argument + the path" do
|
|
||||||
described_class.match_files(@guard_any_return, ['addition.rb']).should == ["2addition.rb"]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nothing if the action response is empty string" do
|
|
||||||
described_class.match_files(@guard_any_return, ['blank.rb']).should == ['']
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nothing if the action returns empty string" do
|
|
||||||
described_class.match_files(@guard_any_return, ['uptime.rb']).should == ['']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with an exception that is raised" do
|
|
||||||
before(:all) { @guard.watchers = [described_class.new('evil.rb', lambda { raise "EVIL" })] }
|
|
||||||
|
|
||||||
it "displays the error and backtrace" do
|
|
||||||
Guard::UI.should_receive(:error) do |msg|
|
|
||||||
msg.should include("Problem with watch action!")
|
|
||||||
msg.should include("EVIL")
|
|
||||||
end
|
|
||||||
|
|
||||||
described_class.match_files(@guard, ['evil.rb'])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".match_files?" do
|
|
||||||
before(:all) do
|
|
||||||
@guard1 = Guard::Guard.new([described_class.new(/.*_spec\.rb/)])
|
|
||||||
@guard2 = Guard::Guard.new([described_class.new('spec_helper.rb', 'spec')])
|
|
||||||
@guards = [@guard1, @guard2]
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with a watcher that matches a file" do
|
|
||||||
specify { described_class.match_files?(@guards, ['lib/my_wonderful_lib.rb', 'guard_rocks_spec.rb']).should be_true }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with no watcher that matches a file" do
|
|
||||||
specify { described_class.match_files?(@guards, ['lib/my_wonderful_lib.rb']).should be_false }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#match_file?" do
|
|
||||||
context "with a string pattern" do
|
|
||||||
context "that is a normal string" do
|
|
||||||
subject { described_class.new('guard_rocks_spec.rb') }
|
|
||||||
|
|
||||||
context "with a watcher that matches a file" do
|
|
||||||
specify { subject.match_file?('guard_rocks_spec.rb').should be_true }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with no watcher that matches a file" do
|
|
||||||
specify { subject.match_file?('lib/my_wonderful_lib.rb').should be_false }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "that is a string representing a regexp (deprecated)" do
|
|
||||||
subject { described_class.new('^guard_rocks_spec\.rb$') }
|
|
||||||
|
|
||||||
context "with a watcher that matches a file" do
|
|
||||||
specify { subject.match_file?('guard_rocks_spec.rb').should be_true }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with no watcher that matches a file" do
|
|
||||||
specify { subject.match_file?('lib/my_wonderful_lib.rb').should be_false }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "that is a regexp pattern" do
|
|
||||||
subject { described_class.new(/.*_spec\.rb/) }
|
|
||||||
|
|
||||||
context "with a watcher that matches a file" do
|
|
||||||
specify { subject.match_file?('guard_rocks_spec.rb').should be_true }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with no watcher that matches a file" do
|
|
||||||
specify { subject.match_file?('lib/my_wonderful_lib.rb').should be_false }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".match_guardfile?" do
|
|
||||||
before(:all) { Guard::Dsl.stub(:guardfile_path) { Dir.pwd + '/Guardfile' } }
|
|
||||||
|
|
||||||
context "with files that match the Guardfile" do
|
|
||||||
specify { described_class.match_guardfile?(['Guardfile', 'guard_rocks_spec.rb']).should be_true }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with no files that match the Guardfile" do
|
|
||||||
specify { described_class.match_guardfile?(['guard_rocks.rb', 'guard_rocks_spec.rb']).should be_false }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,638 +1,191 @@
|
|||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'guard/guard'
|
|
||||||
|
# mute UI
|
||||||
|
module Guard::UI
|
||||||
|
class << self
|
||||||
|
def info(message, options = {})
|
||||||
|
end
|
||||||
|
|
||||||
|
def error(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def debug(message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe Guard do
|
describe Guard do
|
||||||
|
|
||||||
describe ".initialize_template" do
|
describe "get_guard_class" do
|
||||||
context "with a Guard name" do
|
|
||||||
it "initializes a the Guard" do
|
it "should return Guard::RSpec" do
|
||||||
class Guard::TestGuard < Guard::Guard
|
Guard.get_guard_class('rspec').should == Guard::RSpec
|
||||||
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".setup" do
|
describe "locate_guard" do
|
||||||
subject { ::Guard.setup }
|
|
||||||
|
it "should return guard-rspec gem path" do
|
||||||
it "returns itself for chaining" do
|
guard_path = Guard.locate_guard('rspec')
|
||||||
subject.should be ::Guard
|
guard_path.should match(/^.*\/guard-rspec-.*$/)
|
||||||
|
guard_path.should == guard_path.chomp
|
||||||
end
|
end
|
||||||
|
|
||||||
it "initializes @guards" do
|
end
|
||||||
subject.guards.should eql []
|
|
||||||
|
describe "init" do
|
||||||
|
subject { ::Guard.init }
|
||||||
|
|
||||||
|
it "Should retrieve itself for chaining" do
|
||||||
|
subject.should be_kind_of(Module)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "initializes @groups" do
|
it "Should init guards array" do
|
||||||
described_class.groups[0].name.should eql :default
|
::Guard.guards.should be_kind_of(Array)
|
||||||
described_class.groups[0].options.should == {}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "initializes the options" do
|
it "Should init options" do
|
||||||
opts = { :my_opts => true }
|
opts = {:my_opts => true}
|
||||||
Guard.setup(opts).options.should include(:my_opts)
|
::Guard.init(opts).options.should be_include(:my_opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "initializes the listener" do
|
it "Should init listeners" do
|
||||||
::Guard.listener.should be_kind_of(Guard::Listener)
|
::Guard.listener.should be_kind_of(Guard::Listener)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "respect the watchdir option" do
|
|
||||||
::Guard.setup(:watchdir => "/foo/bar")
|
|
||||||
::Guard.listener.directory.should eql "/foo/bar"
|
|
||||||
end
|
|
||||||
|
|
||||||
it "turns on the notifier by default" do
|
|
||||||
ENV["GUARD_NOTIFY"] = nil
|
|
||||||
::Guard::Notifier.should_receive(:turn_on)
|
|
||||||
::Guard.setup(:notify => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "turns off the notifier if the notify option is false" do
|
|
||||||
::Guard::Notifier.should_receive(:turn_off)
|
|
||||||
::Guard.setup(:notify => false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "turns off the notifier if environment variable GUARD_NOTIFY is false" do
|
|
||||||
ENV["GUARD_NOTIFY"] = 'false'
|
|
||||||
::Guard::Notifier.should_receive(:turn_off)
|
|
||||||
::Guard.setup(:notify => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "logs command execution if the debug option is true" do
|
|
||||||
::Guard.should_receive(:debug_command_execution)
|
|
||||||
::Guard.setup(:debug => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "initializes the interactor" do
|
|
||||||
::Guard.setup
|
|
||||||
::Guard.interactor.should be_kind_of(Guard::Interactor)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "skips the interactor initalization if no-interactions is true" do
|
|
||||||
::Guard.interactor = nil
|
|
||||||
::Guard.setup(:no_interactions => true)
|
|
||||||
::Guard.interactor.should be_nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".guards" do
|
describe "start" do
|
||||||
|
|
||||||
class Guard::FooBar < Guard::Guard; end
|
|
||||||
class Guard::FooBaz < Guard::Guard; end
|
|
||||||
|
|
||||||
subject do
|
|
||||||
guard = ::Guard.setup
|
|
||||||
@guard_foo_bar_backend = Guard::FooBar.new([], { :group => 'backend' })
|
|
||||||
@guard_foo_bar_frontend = Guard::FooBar.new([], { :group => 'frontend' })
|
|
||||||
@guard_foo_baz_backend = Guard::FooBaz.new([], { :group => 'backend' })
|
|
||||||
@guard_foo_baz_frontend = Guard::FooBaz.new([], { :group => 'frontend' })
|
|
||||||
guard.instance_variable_get("@guards").push(@guard_foo_bar_backend)
|
|
||||||
guard.instance_variable_get("@guards").push(@guard_foo_bar_frontend)
|
|
||||||
guard.instance_variable_get("@guards").push(@guard_foo_baz_backend)
|
|
||||||
guard.instance_variable_get("@guards").push(@guard_foo_baz_frontend)
|
|
||||||
guard
|
|
||||||
end
|
|
||||||
|
|
||||||
it "return @guards without any argument" do
|
|
||||||
subject.guards.should eql subject.instance_variable_get("@guards")
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "find a guard by as string/symbol" do
|
|
||||||
it "find a guard by a string" do
|
|
||||||
subject.guards('foo-bar').should eql @guard_foo_bar_backend
|
|
||||||
end
|
|
||||||
|
|
||||||
it "find a guard by a symbol" do
|
|
||||||
subject.guards(:'foo-bar').should eql @guard_foo_bar_backend
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nil if guard is not found" do
|
|
||||||
subject.guards('foo-foo').should be_nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "find guards matching a regexp" do
|
|
||||||
it "with matches" do
|
|
||||||
subject.guards(/^foobar/).should eql [@guard_foo_bar_backend, @guard_foo_bar_frontend]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "without matches" do
|
|
||||||
subject.guards(/foo$/).should eql []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "find guards by their group" do
|
|
||||||
it "group name is a string" do
|
|
||||||
subject.guards(:group => 'backend').should eql [@guard_foo_bar_backend, @guard_foo_baz_backend]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "group name is a symbol" do
|
|
||||||
subject.guards(:group => :frontend).should eql [@guard_foo_bar_frontend, @guard_foo_baz_frontend]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns [] if guard is not found" do
|
|
||||||
subject.guards(:group => :unknown).should eql []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "find guards by their group & name" do
|
|
||||||
it "group name is a string" do
|
|
||||||
subject.guards(:group => 'backend', :name => 'foo-bar').should eql [@guard_foo_bar_backend]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "group name is a symbol" do
|
|
||||||
subject.guards(:group => :frontend, :name => :'foo-baz').should eql [@guard_foo_baz_frontend]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns [] if guard is not found" do
|
|
||||||
subject.guards(:group => :unknown, :name => :'foo-baz').should eql []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".groups" do
|
|
||||||
subject do
|
|
||||||
guard = ::Guard.setup
|
|
||||||
@group_backend = guard.add_group(:backend)
|
|
||||||
@group_backflip = guard.add_group(:backflip)
|
|
||||||
guard
|
|
||||||
end
|
|
||||||
|
|
||||||
it "return @groups without any argument" do
|
|
||||||
subject.groups.should eql subject.instance_variable_get("@groups")
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "find a group by as string/symbol" do
|
|
||||||
it "find a group by a string" do
|
|
||||||
subject.groups('backend').should eql @group_backend
|
|
||||||
end
|
|
||||||
|
|
||||||
it "find a group by a symbol" do
|
|
||||||
subject.groups(:backend).should eql @group_backend
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nil if group is not found" do
|
|
||||||
subject.groups(:foo).should be_nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "find groups matching a regexp" do
|
|
||||||
it "with matches" do
|
|
||||||
subject.groups(/^back/).should eql [@group_backend, @group_backflip]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "without matches" do
|
|
||||||
subject.groups(/back$/).should eql []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".start" do
|
|
||||||
it "basic check that core methods are called" do
|
|
||||||
opts = { :my_opts => true, :guardfile => File.join(@fixture_path, "Guardfile") }
|
|
||||||
::Guard.should_receive(:setup).with(opts)
|
|
||||||
::Guard::Dsl.should_receive(:evaluate_guardfile).with(opts)
|
|
||||||
::Guard.listener.should_receive(:start)
|
|
||||||
|
|
||||||
::Guard.start(opts)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".add_guard" do
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@guard_rspec_class = double('Guard::RSpec')
|
@guard = mock(::Guard::Guard)
|
||||||
@guard_rspec = double('Guard::RSpec')
|
@guard.stub!(:reload_at_start?).and_return(false)
|
||||||
|
@guard.stub!(:run_all_at_start?).and_return(false)
|
||||||
|
@guard.stub!(:respond_to?)
|
||||||
|
|
||||||
Guard.stub!(:get_guard_class) { @guard_rspec_class }
|
@listener = mock(::Guard::Polling)
|
||||||
|
@listener.stub!(:on_change)
|
||||||
|
@listener.stub!(:start).and_return(true)
|
||||||
|
|
||||||
Guard.setup
|
::Guard::Listener.stub!(:init).and_return(@listener)
|
||||||
|
::Guard::Dsl.stub!(:evaluate_guardfile)
|
||||||
|
::Guard::Interactor.stub!(:init_signal_traps)
|
||||||
|
subject.stub!(:guards).and_return([@guard])
|
||||||
|
subject.stub!(:supervised_task).with(anything(), :start).and_return(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "accepts guard name as string" do
|
it 'should evaluate Guardfile' do
|
||||||
@guard_rspec_class.should_receive(:new).and_return(@guard_rspec)
|
::Guard::Dsl.should_receive(:evaluate_guardfile)
|
||||||
|
subject.start
|
||||||
Guard.add_guard('rspec')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "accepts guard name as symbol" do
|
it 'should init signal traps' do
|
||||||
@guard_rspec_class.should_receive(:new).and_return(@guard_rspec)
|
::Guard::Interactor.should_receive(:init_signal_traps)
|
||||||
|
subject.start
|
||||||
Guard.add_guard(:rspec)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "adds guard to the @guards array" do
|
it 'should define listener on_change' do
|
||||||
@guard_rspec_class.should_receive(:new).and_return(@guard_rspec)
|
@listener.should_receive(:on_change)
|
||||||
|
subject.start
|
||||||
Guard.add_guard(:rspec)
|
|
||||||
|
|
||||||
Guard.guards.should eql [@guard_rspec]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with no watchers given" do
|
it 'should start guards' do
|
||||||
it "gives an empty array of watchers" do
|
subject.should_receive(:supervised_task).with(@guard, :start).and_return(true)
|
||||||
@guard_rspec_class.should_receive(:new).with([], {}).and_return(@guard_rspec)
|
subject.start
|
||||||
|
|
||||||
Guard.add_guard(:rspec, [])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with watchers given" do
|
it 'should call reload at start if needed' do
|
||||||
it "give the watchers array" do
|
@guard.should_receive(:respond_to?).with(:reload_at_start?).and_return(true)
|
||||||
@guard_rspec_class.should_receive(:new).with([:foo], {}).and_return(@guard_rspec)
|
@guard.should_receive(:reload_at_start?).and_return(true)
|
||||||
|
@guard.should_receive(:reload).and_return(true)
|
||||||
|
|
||||||
Guard.add_guard(:rspec, [:foo])
|
subject.start
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with no options given" do
|
it 'should call run_all at start if needed' do
|
||||||
it "gives an empty hash of options" do
|
@guard.should_receive(:respond_to?).with(:run_all_at_start?).and_return(true)
|
||||||
@guard_rspec_class.should_receive(:new).with([], {}).and_return(@guard_rspec)
|
@guard.should_receive(:run_all_at_start?).and_return(true)
|
||||||
|
@guard.should_receive(:run_all).and_return(true)
|
||||||
|
|
||||||
Guard.add_guard(:rspec, [], [], {})
|
subject.start
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with options given" do
|
it 'should not call reload and run_all at start if not needed' do
|
||||||
it "give the options hash" do
|
@guard.should_receive(:respond_to?).with(:reload_at_start?).and_return(true)
|
||||||
@guard_rspec_class.should_receive(:new).with([], { :foo => true, :group => :backend }).and_return(@guard_rspec)
|
@guard.should_receive(:reload_at_start?).and_return(false)
|
||||||
|
@guard.should_not_receive(:reload)
|
||||||
|
|
||||||
Guard.add_guard(:rspec, [], [], { :foo => true, :group => :backend })
|
@guard.should_receive(:respond_to?).with(:run_all_at_start?).and_return(true)
|
||||||
end
|
@guard.should_receive(:run_all_at_start?).and_return(false)
|
||||||
|
@guard.should_not_receive(:run_all)
|
||||||
|
|
||||||
|
subject.start
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'should start listener' do
|
||||||
|
@listener.should_receive(:start)
|
||||||
|
subject.start
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".add_group" do
|
describe "supervised_task" do
|
||||||
subject { ::Guard.setup }
|
subject { ::Guard.init }
|
||||||
|
|
||||||
it "accepts group name as string" do
|
before :each do
|
||||||
subject.add_group('backend')
|
@g = mock(Guard::Guard)
|
||||||
|
@g.stub!(:respond_to).and_return { false }
|
||||||
subject.groups[0].name.should eql :default
|
@g.stub!(:regular).and_return { true }
|
||||||
subject.groups[1].name.should eql :backend
|
@g.stub!(:spy).and_return { raise "I break your system" }
|
||||||
|
@g.stub!(:pirate).and_raise Exception.new("I blow your system up")
|
||||||
|
@g.stub!(:regular_arg).with("given_path").and_return { "given_path" }
|
||||||
|
@g.stub!(:enable_method).and_return { true }
|
||||||
|
@g.stub!(:enable_method?).and_return { true }
|
||||||
|
@g.stub!(:respond_to).with(:enable_method?).and_return { true }
|
||||||
|
@g.stub!(:disable_method).and_return { true }
|
||||||
|
@g.stub!(:disable_method?).and_return { false }
|
||||||
|
@g.stub!(:respond_to).with(:disable_method?).and_return { true }
|
||||||
|
subject.guards.push @g
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should let it go when nothing special occurs" do
|
||||||
|
subject.guards.should be_include(@g)
|
||||||
|
subject.supervised_task(@g, :regular).should be_true
|
||||||
|
subject.guards.should be_include(@g)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should let it work with some tools" do
|
||||||
|
subject.guards.should be_include(@g)
|
||||||
|
subject.supervised_task(@g, :regular).should be_true
|
||||||
|
subject.guards.should be_include(@g)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should fire the guard on spy act discovery" do
|
||||||
|
subject.guards.should be_include(@g)
|
||||||
|
::Guard.supervised_task(@g, :spy).should be_kind_of(Exception)
|
||||||
|
subject.guards.should_not be_include(@g)
|
||||||
|
::Guard.supervised_task(@g, :spy).message.should == 'I break your system'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should fire the guard on pirate act discovery" do
|
||||||
|
subject.guards.should be_include(@g)
|
||||||
|
::Guard.supervised_task(@g, :regular_arg, "given_path").should be_kind_of(String)
|
||||||
|
subject.guards.should be_include(@g)
|
||||||
|
::Guard.supervised_task(@g, :regular_arg, "given_path").should == "given_path"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "accepts group name as symbol" do
|
it 'should let it go when method is enable by guard options' do
|
||||||
subject.add_group(:backend)
|
subject.guards.should be_include(@g)
|
||||||
|
subject.supervised_task(@g, :enable_method).should be_true
|
||||||
subject.groups[0].name.should eql :default
|
subject.guards.should be_include(@g)
|
||||||
subject.groups[1].name.should eql :backend
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "accepts options" do
|
it 'should not let it go when method is disable by guard options' do
|
||||||
subject.add_group(:backend, { :halt_on_fail => true })
|
subject.guards.should be_include(@g)
|
||||||
|
subject.supervised_task(@g, :disable_method).should be_false
|
||||||
subject.groups[0].options.should == {}
|
subject.guards.should be_include(@g)
|
||||||
subject.groups[1].options.should == { :halt_on_fail => true }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".get_guard_class" do
|
|
||||||
after do
|
|
||||||
[:Classname, :DashedClassName, :Inline].each do |const|
|
|
||||||
Guard.send(:remove_const, const) rescue nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "reports an error if the class is not found" do
|
|
||||||
::Guard::UI.should_receive(:error).twice
|
|
||||||
Guard.get_guard_class('notAGuardClass')
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with a nested Guard class' do
|
|
||||||
it "resolves the Guard class from string" do
|
|
||||||
Guard.should_receive(:require) { |classname|
|
|
||||||
classname.should == 'guard/classname'
|
|
||||||
class Guard::Classname
|
|
||||||
end
|
|
||||||
}
|
|
||||||
Guard.get_guard_class('classname').should == Guard::Classname
|
|
||||||
end
|
|
||||||
|
|
||||||
it "resolves the Guard class from symbol" do
|
|
||||||
Guard.should_receive(:require) { |classname|
|
|
||||||
classname.should == 'guard/classname'
|
|
||||||
class Guard::Classname
|
|
||||||
end
|
|
||||||
}
|
|
||||||
Guard.get_guard_class(:classname).should == Guard::Classname
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with a name with dashes' do
|
|
||||||
it "returns the Guard class" do
|
|
||||||
Guard.should_receive(:require) { |classname|
|
|
||||||
classname.should == 'guard/dashed-class-name'
|
|
||||||
class Guard::DashedClassName
|
|
||||||
end
|
|
||||||
}
|
|
||||||
Guard.get_guard_class('dashed-class-name').should == Guard::DashedClassName
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with an inline Guard class' do
|
|
||||||
it 'returns the Guard class' do
|
|
||||||
module Guard
|
|
||||||
class Inline < Guard
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Guard.should_not_receive(:require)
|
|
||||||
Guard.get_guard_class('inline').should == Guard::Inline
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".locate_guard" do
|
|
||||||
it "returns the path of a Guard gem" do
|
|
||||||
if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
|
|
||||||
gem_location = Gem::Specification.find_by_name("guard-rspec").full_gem_path
|
|
||||||
else
|
|
||||||
gem_location = Gem.source_index.find_name("guard-rspec").last.full_gem_path
|
|
||||||
end
|
|
||||||
|
|
||||||
Guard.locate_guard('rspec').should == gem_location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".guard_gem_names" do
|
|
||||||
it "returns the list of guard gems" do
|
|
||||||
gems = Guard.guard_gem_names
|
|
||||||
gems.should include("rspec")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".run_guard_task" do
|
|
||||||
subject { ::Guard.setup }
|
|
||||||
|
|
||||||
before do
|
|
||||||
class Guard::Dummy < Guard::Guard; end
|
|
||||||
|
|
||||||
subject.add_group(:foo, { :halt_on_fail => true })
|
|
||||||
subject.add_group(:bar)
|
|
||||||
subject.add_guard(:dummy, [], [], { :group => :foo })
|
|
||||||
subject.add_guard(:dummy, [], [], { :group => :foo })
|
|
||||||
subject.add_guard(:dummy, [], [], { :group => :bar })
|
|
||||||
subject.add_guard(:dummy, [], [], { :group => :bar })
|
|
||||||
@sum = { :foo => 0, :bar => 0}
|
|
||||||
end
|
|
||||||
|
|
||||||
context "all tasks succeed" do
|
|
||||||
before do
|
|
||||||
subject.guards.each { |guard| guard.stub!(:task) { @sum[guard.group] += 1; true } }
|
|
||||||
end
|
|
||||||
|
|
||||||
it "executes the task for each guard in each group" do
|
|
||||||
subject.run_guard_task(:task)
|
|
||||||
|
|
||||||
@sum.all? { |k, v| v == 2 }.should be_true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "one guard fails" do
|
|
||||||
before do
|
|
||||||
subject.guards.each_with_index do |g, i|
|
|
||||||
g.stub!(:task) do
|
|
||||||
@sum[g.group] += i+1
|
|
||||||
if i % 2 == 0
|
|
||||||
throw :task_has_failed
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "executes the task only for guards that didn't fail for group with :halt_on_fail == true" do
|
|
||||||
subject.run_guard_task(:task)
|
|
||||||
|
|
||||||
@sum[:foo].should eql 1
|
|
||||||
@sum[:bar].should eql 7
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".run_on_change_task" do
|
|
||||||
let(:guard) do
|
|
||||||
class Guard::Dummy < Guard::Guard
|
|
||||||
def watchers
|
|
||||||
[Guard::Watcher.new(/.+\.rb/)]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Guard::Dummy.new
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'runs the :run_on_change task with the watched file changes' do
|
|
||||||
Guard.should_receive(:run_supervised_task).with(guard, :run_on_change, ['a.rb', 'b.rb'])
|
|
||||||
Guard.run_on_change_task(['a.rb', 'b.rb', 'templates/d.haml'], guard, :run_on_change)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'runs the :run_on_deletion task with the watched file deletions' do
|
|
||||||
Guard.should_receive(:run_supervised_task).with(guard, :run_on_deletion, ['c.rb'])
|
|
||||||
Guard.run_on_change_task(['!c.rb', '!templates/e.haml'], guard, :run_on_change)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".changed_paths" do
|
|
||||||
let(:paths) { ['a.rb', 'b.rb', '!c.rb', 'templates/d.haml', '!templates/e.haml'] }
|
|
||||||
|
|
||||||
it 'returns the changed paths' do
|
|
||||||
Guard.changed_paths(paths).should =~ ['a.rb', 'b.rb', 'templates/d.haml']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".deleted_paths" do
|
|
||||||
let(:paths) { ['a.rb', 'b.rb', '!c.rb', 'templates/d.haml', '!templates/e.haml'] }
|
|
||||||
|
|
||||||
it 'returns the deleted paths' do
|
|
||||||
Guard.deleted_paths(paths).should =~ ['c.rb', 'templates/e.haml']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".run_supervised_task" do
|
|
||||||
subject { ::Guard.setup }
|
|
||||||
|
|
||||||
before do
|
|
||||||
@g = mock(Guard::Guard).as_null_object
|
|
||||||
subject.guards.push(@g)
|
|
||||||
subject.add_group(:foo, { :halt_on_fail => true })
|
|
||||||
subject.add_group(:bar, { :halt_on_fail => false })
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with a task that succeed" do
|
|
||||||
context 'without any arguments' do
|
|
||||||
before(:each) do
|
|
||||||
@g.stub!(:regular_without_arg) { true }
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't fire the Guard" do
|
|
||||||
lambda { subject.run_supervised_task(@g, :regular_without_arg) }.should_not change(subject.guards, :size)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the result of the task" do
|
|
||||||
::Guard.run_supervised_task(@g, :regular_without_arg).should be_true
|
|
||||||
end
|
|
||||||
|
|
||||||
it "passes the args to the :begin hook" do
|
|
||||||
@g.should_receive(:hook).with("regular_without_arg_begin", "given_path")
|
|
||||||
::Guard.run_supervised_task(@g, :regular_without_arg, "given_path")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "passes the result of the supervised method to the :end hook" do
|
|
||||||
@g.should_receive(:hook).with("regular_without_arg_begin", "given_path")
|
|
||||||
@g.should_receive(:hook).with("regular_without_arg_end", true)
|
|
||||||
::Guard.run_supervised_task(@g, :regular_without_arg, "given_path")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with arguments' do
|
|
||||||
before(:each) do
|
|
||||||
@g.stub!(:regular_with_arg).with("given_path") { "I'm a success" }
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't fire the Guard" do
|
|
||||||
lambda { subject.run_supervised_task(@g, :regular_with_arg, "given_path") }.should_not change(subject.guards, :size)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the result of the task" do
|
|
||||||
::Guard.run_supervised_task(@g, :regular_with_arg, "given_path").should eql "I'm a success"
|
|
||||||
end
|
|
||||||
|
|
||||||
it "calls the default begin hook but not the default end hook" do
|
|
||||||
@g.should_receive(:hook).with("failing_begin")
|
|
||||||
@g.should_not_receive(:hook).with("failing_end")
|
|
||||||
::Guard.run_supervised_task(@g, :failing)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with a task that throw :task_has_failed" do
|
|
||||||
context "for a guard's group has the :halt_on_fail option == true" do
|
|
||||||
before(:each) { @g.stub!(:group) { :foo }; @g.stub!(:failing) { throw :task_has_failed } }
|
|
||||||
|
|
||||||
it "throws :task_has_failed" do
|
|
||||||
expect { subject.run_supervised_task(@g, :failing) }.to throw_symbol(:task_has_failed)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "for a guard's group has the :halt_on_fail option == false" do
|
|
||||||
before(:each) { @g.stub!(:group) { :bar }; @g.stub!(:failing) { throw :task_has_failed } }
|
|
||||||
|
|
||||||
it "catches :task_has_failed" do
|
|
||||||
expect { subject.run_supervised_task(@g, :failing) }.to_not throw_symbol(:task_has_failed)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with a task that raises an exception" do
|
|
||||||
before(:each) { @g.stub!(:group) { :foo }; @g.stub!(:failing) { raise "I break your system" } }
|
|
||||||
|
|
||||||
it "fires the Guard" do
|
|
||||||
lambda { subject.run_supervised_task(@g, :failing) }.should change(subject.guards, :size).by(-1)
|
|
||||||
subject.guards.should_not include(@g)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the exception" do
|
|
||||||
failing_result = ::Guard.run_supervised_task(@g, :failing)
|
|
||||||
failing_result.should be_kind_of(Exception)
|
|
||||||
failing_result.message.should == 'I break your system'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.guard_symbol' do
|
|
||||||
let(:guard) { mock(Guard::Guard).as_null_object }
|
|
||||||
|
|
||||||
it 'returns :task_has_failed when the group is missing' do
|
|
||||||
subject.guard_symbol(guard).should eql :task_has_failed
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a group with :halt_on_fail' do
|
|
||||||
let(:group) { mock(Guard::Group) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
guard.stub(:group).and_return :foo
|
|
||||||
group.stub(:options).and_return({ :halt_on_fail => true })
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns :no_catch' do
|
|
||||||
subject.should_receive(:groups).with(:foo).and_return group
|
|
||||||
subject.guard_symbol(guard).should eql :no_catch
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a group without :halt_on_fail' do
|
|
||||||
let(:group) { mock(Guard::Group) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
guard.stub(:group).and_return :foo
|
|
||||||
group.stub(:options).and_return({ :halt_on_fail => false })
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns :task_has_failed' do
|
|
||||||
subject.should_receive(:groups).with(:foo).and_return group
|
|
||||||
subject.guard_symbol(guard).should eql :task_has_failed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".debug_command_execution" do
|
|
||||||
subject { ::Guard.setup }
|
|
||||||
|
|
||||||
before do
|
|
||||||
@original_system = Kernel.method(:system)
|
|
||||||
@original_command = Kernel.method(:"`")
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
Kernel.send(:define_method, :system, @original_system.to_proc )
|
|
||||||
Kernel.send(:define_method, :"`", @original_command.to_proc )
|
|
||||||
end
|
|
||||||
|
|
||||||
it "outputs Kernel.#system method parameters" do
|
|
||||||
::Guard.setup(:debug => true)
|
|
||||||
::Guard::UI.should_receive(:debug).with("Command execution: echo test")
|
|
||||||
system("echo", "test").should be_true
|
|
||||||
end
|
|
||||||
|
|
||||||
it "outputs Kernel.#` method parameters" do
|
|
||||||
::Guard.setup(:debug => true)
|
|
||||||
::Guard::UI.should_receive(:debug).twice.with("Command execution: echo test")
|
|
||||||
`echo test`.should eql "test\n"
|
|
||||||
%x{echo test}.should eql "test\n"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -2,21 +2,20 @@ require 'rubygems'
|
|||||||
require 'guard'
|
require 'guard'
|
||||||
require 'rspec'
|
require 'rspec'
|
||||||
|
|
||||||
ENV["GUARD_ENV"] = 'test'
|
|
||||||
|
|
||||||
Dir["#{File.expand_path('..', __FILE__)}/support/**/*.rb"].each { |f| require f }
|
Dir["#{File.expand_path('..', __FILE__)}/support/**/*.rb"].each { |f| require f }
|
||||||
|
|
||||||
puts "Please do not update/create files while tests are running."
|
puts "Please do not update/create files while tests are running."
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
config.color_enabled = true
|
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
|
config.before(:each) do
|
||||||
|
ENV["GUARD_ENV"] = 'test'
|
||||||
@fixture_path = Pathname.new(File.expand_path('../fixtures/', __FILE__))
|
@fixture_path = Pathname.new(File.expand_path('../fixtures/', __FILE__))
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
config.after(:each) do
|
||||||
|
ENV["GUARD_ENV"] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@ -1,20 +0,0 @@
|
|||||||
def growl_installed?
|
|
||||||
require 'growl'
|
|
||||||
true
|
|
||||||
rescue LoadError
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def libnotify_installed?
|
|
||||||
require 'libnotify'
|
|
||||||
true
|
|
||||||
rescue LoadError
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def rbnotifu_installed?
|
|
||||||
require 'rb-notifu'
|
|
||||||
true
|
|
||||||
rescue LoadError
|
|
||||||
false
|
|
||||||
end
|
|
@ -1,225 +0,0 @@
|
|||||||
private
|
|
||||||
|
|
||||||
# Set the sleep time around start/stop the listener. This defaults
|
|
||||||
# to one second but can be overridden by setting the environment
|
|
||||||
# variable `GUARD_SLEEP`.
|
|
||||||
#
|
|
||||||
def sleep_time
|
|
||||||
@sleep_time ||= ENV['GUARD_SLEEP'] ? ENV['GUARD_SLEEP'].to_f : 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# Make the spec listen to a specific listener.
|
|
||||||
# This automatically starts to record results for the supplied listener.
|
|
||||||
#
|
|
||||||
# @param [Guard::Listener] listener the Guard listener
|
|
||||||
#
|
|
||||||
def listen_to(listener)
|
|
||||||
@listener = listener
|
|
||||||
record_results
|
|
||||||
end
|
|
||||||
|
|
||||||
# Start the listener. Normally you use {#watch} to wrap
|
|
||||||
# the code block that should be listen to instead of starting
|
|
||||||
# it manually.
|
|
||||||
#
|
|
||||||
def start
|
|
||||||
sleep(sleep_time)
|
|
||||||
@listener.update_last_event
|
|
||||||
Thread.new { @listener.start }
|
|
||||||
sleep(sleep_time)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Stop the listener. Normally you use {#watch} to wrap
|
|
||||||
# the code block that should be listen to instead of stopping
|
|
||||||
# it manually.
|
|
||||||
#
|
|
||||||
def stop
|
|
||||||
sleep(sleep_time)
|
|
||||||
@listener.stop
|
|
||||||
sleep(sleep_time)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Watch file changes in a code block.
|
|
||||||
#
|
|
||||||
# @example Watch file changes
|
|
||||||
# watch do
|
|
||||||
# File.mv file1, file2
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# @yield The block to listen for file changes
|
|
||||||
#
|
|
||||||
def watch
|
|
||||||
start
|
|
||||||
yield if block_given?
|
|
||||||
stop
|
|
||||||
end
|
|
||||||
|
|
||||||
# Start recording results from the current listener.
|
|
||||||
# You may want to use {#listen_to} to set a listener
|
|
||||||
# instead of set it up manually.
|
|
||||||
#
|
|
||||||
def record_results
|
|
||||||
# Don't fail specs due to editor swap files, etc.
|
|
||||||
noise = %r|\.sw.$|
|
|
||||||
@results = []
|
|
||||||
|
|
||||||
@listener.on_change do |files|
|
|
||||||
@results += files.reject { |f| f =~ noise }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the recorded result from the listener.
|
|
||||||
#
|
|
||||||
# @return [Array<String>] the result files
|
|
||||||
#
|
|
||||||
def results
|
|
||||||
@results.flatten
|
|
||||||
end
|
|
||||||
|
|
||||||
# Define a file absolute to the fixture path.
|
|
||||||
#
|
|
||||||
# @param [String, Array<String>] file the relative file name, separated by segment
|
|
||||||
# @return [String] the absolute file
|
|
||||||
#
|
|
||||||
def fixture(*file)
|
|
||||||
@fixture_path.join(*file)
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples_for 'a listener that reacts to #on_change' do
|
|
||||||
before do
|
|
||||||
listen_to described_class.new
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a new file' do
|
|
||||||
let(:file) { fixture('newfile.rb') }
|
|
||||||
|
|
||||||
before { File.delete(file) if File.exists?(file) }
|
|
||||||
after { File.delete file }
|
|
||||||
|
|
||||||
it 'catches the new file' do
|
|
||||||
File.exists?(file).should be_false
|
|
||||||
|
|
||||||
watch do
|
|
||||||
FileUtils.touch file
|
|
||||||
end
|
|
||||||
|
|
||||||
results.should =~ ['spec/fixtures/newfile.rb']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a single file update' do
|
|
||||||
let(:file) { fixture('folder1', 'file1.txt') }
|
|
||||||
|
|
||||||
it 'catches the update' do
|
|
||||||
File.exists?(file).should be_true
|
|
||||||
|
|
||||||
watch do
|
|
||||||
File.open(file, 'w') { |f| f.write('') }
|
|
||||||
end
|
|
||||||
|
|
||||||
results.should =~ ['spec/fixtures/folder1/file1.txt']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a single file chmod update' do
|
|
||||||
let(:file) { fixture('folder1/file1.txt') }
|
|
||||||
|
|
||||||
it 'does not catch the update' do
|
|
||||||
File.exists?(file).should be_true
|
|
||||||
|
|
||||||
watch do
|
|
||||||
File.chmod(0777, file)
|
|
||||||
end
|
|
||||||
|
|
||||||
results.should =~ []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a dotfile update' do
|
|
||||||
let(:file) { fixture('.dotfile') }
|
|
||||||
|
|
||||||
it "catches the update" do
|
|
||||||
File.exists?(file).should be_true
|
|
||||||
|
|
||||||
watch do
|
|
||||||
File.open(file, 'w') { |f| f.write('') }
|
|
||||||
end
|
|
||||||
|
|
||||||
results.should =~ ['spec/fixtures/.dotfile']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for multiple file updates' do
|
|
||||||
let(:file1) { fixture('folder1', 'file1.txt') }
|
|
||||||
let(:file2) { fixture('folder1', 'folder2', 'file2.txt') }
|
|
||||||
|
|
||||||
it 'catches the updates' do
|
|
||||||
File.exists?(file1).should be_true
|
|
||||||
File.exists?(file2).should be_true
|
|
||||||
|
|
||||||
watch do
|
|
||||||
File.open(file1, 'w') { |f| f.write('') }
|
|
||||||
File.open(file2, 'w') { |f| f.write('') }
|
|
||||||
end
|
|
||||||
|
|
||||||
results.should =~ ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a deleted file' do
|
|
||||||
let(:file) { fixture('folder1', 'file1.txt') }
|
|
||||||
|
|
||||||
after { FileUtils.touch file }
|
|
||||||
|
|
||||||
it 'does not catch the deletion' do
|
|
||||||
File.exists?(file).should be_true
|
|
||||||
|
|
||||||
watch do
|
|
||||||
File.delete file
|
|
||||||
end
|
|
||||||
|
|
||||||
results.should =~ []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a moved file' do
|
|
||||||
let(:file1) { fixture('folder1', 'file1.txt') }
|
|
||||||
let(:file2) { fixture('folder1', 'movedfile1.txt') }
|
|
||||||
|
|
||||||
after { FileUtils.mv file2, file1 }
|
|
||||||
|
|
||||||
it 'does not catch the move' do
|
|
||||||
File.exists?(file1).should be_true
|
|
||||||
File.exists?(file2).should be_false
|
|
||||||
|
|
||||||
watch do
|
|
||||||
FileUtils.mv file1, file2
|
|
||||||
end
|
|
||||||
|
|
||||||
results.should =~ []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples_for "a listener scoped to a specific directory" do
|
|
||||||
|
|
||||||
let(:work_directory) { fixture('folder1') }
|
|
||||||
|
|
||||||
let(:new_file) { work_directory.join('folder2', 'newfile.rb') }
|
|
||||||
let(:modified) { work_directory.join('file1.txt') }
|
|
||||||
|
|
||||||
before { listen_to described_class.new(work_directory) }
|
|
||||||
after { File.delete new_file }
|
|
||||||
|
|
||||||
it 'should base paths within this directory' do
|
|
||||||
File.exists?(modified).should be_true
|
|
||||||
File.exists?(new_file).should be_false
|
|
||||||
|
|
||||||
watch do
|
|
||||||
FileUtils.touch new_file
|
|
||||||
File.open(modified, 'w') { |f| f.write('') }
|
|
||||||
end
|
|
||||||
|
|
||||||
results.should =~ ['folder2/newfile.rb', 'file1.txt']
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,11 +1,7 @@
|
|||||||
def mac?
|
def mac?
|
||||||
RbConfig::CONFIG['target_os'] =~ /darwin/i
|
Config::CONFIG['target_os'] =~ /darwin/i
|
||||||
end
|
end
|
||||||
|
|
||||||
def linux?
|
def linux?
|
||||||
RbConfig::CONFIG['target_os'] =~ /linux/i
|
Config::CONFIG['target_os'] =~ /linux/i
|
||||||
end
|
end
|
||||||
|
|
||||||
def windows?
|
|
||||||
RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
|
||||||
end
|
|
Loading…
Reference in New Issue
Block a user