Merge branch 'master' into guard_dependencies
Conflicts: lib/guard.rb lib/guard/dsl.rb man/guard man/guard.html
This commit is contained in:
commit
4c1cf825e5
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,9 +1,11 @@
|
||||
pkg/*
|
||||
doc/*
|
||||
*.gem
|
||||
*.rbc
|
||||
.*.swp
|
||||
*.bak
|
||||
.bundle
|
||||
.yardoc
|
||||
Gemfile.lock
|
||||
|
||||
## MAC OS
|
||||
@ -12,4 +14,4 @@ Gemfile.lock
|
||||
.com.apple.timemachine.supported
|
||||
.fseventsd
|
||||
Desktop DB
|
||||
Desktop DF
|
||||
Desktop DF
|
||||
|
11
.yardopts
Normal file
11
.yardopts
Normal file
@ -0,0 +1,11 @@
|
||||
--title 'Guard Documentation'
|
||||
--readme README.md
|
||||
--markup markdown
|
||||
--markup-provider kramdown
|
||||
--private
|
||||
--protected
|
||||
--output-dir ./doc
|
||||
lib/**/*.rb
|
||||
-
|
||||
CHANGELOG.md
|
||||
LICENSE
|
@ -1,3 +1,7 @@
|
||||
## Master
|
||||
|
||||
- Pull request [#137](https://github.com/guard/guard/pull/137): Fix interacting with tools like ruby-debug. ([@hron][] & [@netzpirat][])
|
||||
|
||||
## 0.7.0 - September 14, 2011
|
||||
|
||||
## 0.7.0.rc1 - September 5, 2011
|
||||
@ -250,6 +254,7 @@
|
||||
[@fnichol]: https://github.com/fnichol
|
||||
[@Gazer]: https://github.com/Gazer
|
||||
[@gix]: https://github.com/gix
|
||||
[@hron]: https://github.com/hron
|
||||
[@hashrocketeer]: https://github.com/hashrocketeer
|
||||
[@ianwhite]: https://github.com/ianwhite
|
||||
[@indirect]: https://github.com/indirect
|
||||
|
373
README.md
373
README.md
@ -26,27 +26,19 @@ Install
|
||||
|
||||
Install the gem:
|
||||
|
||||
``` bash
|
||||
$ gem install guard
|
||||
```
|
||||
$ gem install guard
|
||||
|
||||
Or add it to your Gemfile (inside the `development` group):
|
||||
|
||||
``` ruby
|
||||
gem 'guard'
|
||||
```
|
||||
gem 'guard'
|
||||
|
||||
and install it via Bundler:
|
||||
|
||||
``` bash
|
||||
$ bundle install
|
||||
```
|
||||
$ bundle install
|
||||
|
||||
Generate an empty Guardfile with:
|
||||
|
||||
``` bash
|
||||
$ guard init
|
||||
```
|
||||
$ 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.
|
||||
@ -57,31 +49,23 @@ Add the guards you need to your Guardfile (see the existing guards below).
|
||||
|
||||
Install the rb-fsevent gem for [FSEvent](http://en.wikipedia.org/wiki/FSEvents) support:
|
||||
|
||||
``` bash
|
||||
$ gem install rb-fsevent
|
||||
```
|
||||
$ gem install rb-fsevent
|
||||
|
||||
You have two possibilities:
|
||||
|
||||
Use the [growl_notify gem](https://rubygems.org/gems/growl_notify) (recommended):
|
||||
|
||||
``` bash
|
||||
$ gem install growl_notify
|
||||
```
|
||||
$ gem install growl_notify
|
||||
|
||||
Use the [growlnotify](http://growl.info/extras.php#growlnotify) (cli tool for growl) + the [growl gem](https://rubygems.org/gems/growl).
|
||||
|
||||
``` bash
|
||||
$ brew install growlnotify
|
||||
$ gem install growl
|
||||
```
|
||||
$ brew install growlnotify
|
||||
$ gem install growl
|
||||
|
||||
And add them to your Gemfile:
|
||||
|
||||
``` ruby
|
||||
gem 'rb-fsevent'
|
||||
gem 'growl_notify' # or gem 'growl'
|
||||
```
|
||||
gem 'rb-fsevent'
|
||||
gem 'growl_notify' # or gem 'growl'
|
||||
|
||||
The difference between growl and growl_notify is that growl_notify uses AppleScript to
|
||||
display a message, whereas growl uses the `growlnotify` command. In general the AppleScript
|
||||
@ -92,65 +76,47 @@ approach is preferred, but you may also use the older growl gem. Have a look at
|
||||
|
||||
Install the [rb-inotify gem](https://rubygems.org/gems/rb-inotify) for [inotify](http://en.wikipedia.org/wiki/Inotify) support:
|
||||
|
||||
``` bash
|
||||
$ gem install rb-inotify
|
||||
```
|
||||
$ gem install rb-inotify
|
||||
|
||||
Install the [libnotify gem](https://rubygems.org/gems/libnotify) if you want visual notification support:
|
||||
|
||||
``` bash
|
||||
$ gem install libnotify
|
||||
```
|
||||
$ gem install libnotify
|
||||
|
||||
And add them to your Gemfile:
|
||||
|
||||
``` ruby
|
||||
gem 'rb-inotify'
|
||||
gem 'libnotify'
|
||||
```
|
||||
gem 'rb-inotify'
|
||||
gem 'libnotify'
|
||||
|
||||
### On Windows
|
||||
|
||||
Install the [rb-fchange gem](https://rubygems.org/gems/rb-fchange) for [Directory Change Notification](http://msdn.microsoft.com/en-us/library/aa365261\(VS.85\).aspx) support:
|
||||
|
||||
``` bash
|
||||
$ gem install rb-fchange
|
||||
```
|
||||
$ gem install rb-fchange
|
||||
|
||||
Install the [win32console gem](https://rubygems.org/gems/win32console) if you want colors in your terminal:
|
||||
|
||||
``` bash
|
||||
$ gem install win32console
|
||||
```
|
||||
$ gem install win32console
|
||||
|
||||
Install the [rb-notifu gem](https://rubygems.org/gems/rb-notifu) if you want visual notification support:
|
||||
|
||||
``` bash
|
||||
$ gem install rb-notifu
|
||||
```
|
||||
$ gem install rb-notifu
|
||||
|
||||
And add them to your Gemfile:
|
||||
|
||||
``` ruby
|
||||
gem 'rb-fchange'
|
||||
gem 'rb-notifu'
|
||||
gem 'win32console'
|
||||
```
|
||||
gem 'rb-fchange'
|
||||
gem 'rb-notifu'
|
||||
gem 'win32console'
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Just launch Guard inside your Ruby / Rails project with:
|
||||
|
||||
``` bash
|
||||
$ guard [start]
|
||||
```
|
||||
$ guard [start]
|
||||
|
||||
or if you use Bundler, to run the Guard executable specific to your bundle:
|
||||
|
||||
``` bash
|
||||
$ bundle exec guard [start]
|
||||
```
|
||||
$ 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.
|
||||
|
||||
@ -161,19 +127,15 @@ Command line options
|
||||
|
||||
Shell can be cleared after each change:
|
||||
|
||||
``` bash
|
||||
$ guard --clear
|
||||
$ guard -c # shortcut
|
||||
```
|
||||
$ guard --clear
|
||||
$ guard -c # shortcut
|
||||
|
||||
### `-n`/`--notify` option
|
||||
|
||||
Notifications (growl/libnotify) can be disabled:
|
||||
|
||||
``` bash
|
||||
$ guard --notify false
|
||||
$ guard -n f # shortcut
|
||||
```
|
||||
$ guard --notify false
|
||||
$ guard -n f # shortcut
|
||||
|
||||
Notifications can also be disabled globally by setting a `GUARD_NOTIFY` environment variable to `false`
|
||||
|
||||
@ -181,43 +143,33 @@ Notifications can also be disabled globally by setting a `GUARD_NOTIFY` environm
|
||||
|
||||
Only certain guards groups can be run (see the Guardfile DSL below for creating groups):
|
||||
|
||||
``` bash
|
||||
$ guard --group group_name another_group_name
|
||||
$ guard -g group_name another_group_name # shortcut
|
||||
```
|
||||
$ 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:
|
||||
|
||||
``` bash
|
||||
$ guard --debug
|
||||
$ guard -d # shortcut
|
||||
```
|
||||
$ guard --debug
|
||||
$ guard -d # shortcut
|
||||
|
||||
### `-w`/`--watchdir` option
|
||||
|
||||
Guard can watch in any directory (instead of the current directory):
|
||||
|
||||
``` bash
|
||||
$ guard --watchdir ~/your/fancy/project
|
||||
$ guard -w ~/your/fancy/project # shortcut
|
||||
```
|
||||
$ 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:
|
||||
|
||||
``` bash
|
||||
$ guard --guardfile ~/.your_global_guardfile
|
||||
$ guard -G ~/.your_global_guardfile # shortcut
|
||||
```
|
||||
$ guard --guardfile ~/.your_global_guardfile
|
||||
$ guard -G ~/.your_global_guardfile # shortcut
|
||||
|
||||
An exhaustive list of options is available with:
|
||||
|
||||
``` bash
|
||||
$ guard help [TASK]
|
||||
```
|
||||
$ guard help [TASK]
|
||||
|
||||
Interactions
|
||||
------------
|
||||
@ -240,21 +192,15 @@ A list of the available guards is present [in the wiki](https://github.com/guard
|
||||
|
||||
Add it to your Gemfile (inside the `development` group):
|
||||
|
||||
``` ruby
|
||||
gem '<guard-name>'
|
||||
```
|
||||
gem '<guard-name>'
|
||||
|
||||
You can list all guards installed on your system with:
|
||||
|
||||
``` bash
|
||||
$ guard list
|
||||
```
|
||||
$ guard list
|
||||
|
||||
Insert default guard's definition to your Guardfile by running this command:
|
||||
|
||||
``` bash
|
||||
$ guard init <guard-name>
|
||||
```
|
||||
$ guard init <guard-name>
|
||||
|
||||
You are good to go, or you can modify your guards' definition to suit your needs.
|
||||
|
||||
@ -271,36 +217,34 @@ The Guardfile DSL consists of the following methods:
|
||||
|
||||
Example:
|
||||
|
||||
``` ruby
|
||||
ignore_paths 'foo', 'bar'
|
||||
ignore_paths 'foo', 'bar'
|
||||
|
||||
group 'backend' do
|
||||
guard 'bundler' do
|
||||
watch('Gemfile')
|
||||
end
|
||||
group 'backend' do
|
||||
guard 'bundler' do
|
||||
watch('Gemfile')
|
||||
end
|
||||
|
||||
guard 'rspec', :cli => '--color --format doc' do
|
||||
# Regexp watch patterns are matched with Regexp#match
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||
watch(%r{^spec/models/.+\.rb$}) { ["spec/models", "spec/acceptance"] }
|
||||
watch(%r{^spec/.+\.rb$}) { `say hello` }
|
||||
guard 'rspec', :cli => '--color --format doc' do
|
||||
# Regexp watch patterns are matched with Regexp#match
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||
watch(%r{^spec/models/.+\.rb$}) { ["spec/models", "spec/acceptance"] }
|
||||
watch(%r{^spec/.+\.rb$}) { `say hello` }
|
||||
|
||||
# String watch patterns are matched with simple '=='
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
end
|
||||
end
|
||||
# String watch patterns are matched with simple '=='
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
end
|
||||
end
|
||||
|
||||
group 'frontend' do
|
||||
guard 'coffeescript', :output => 'public/javascripts/compiled' do
|
||||
watch(%r{^app/coffeescripts/.+\.coffee$})
|
||||
end
|
||||
group 'frontend' do
|
||||
guard 'coffeescript', :output => 'public/javascripts/compiled' do
|
||||
watch(%r{^app/coffeescripts/.+\.coffee$})
|
||||
end
|
||||
|
||||
guard 'livereload' do
|
||||
watch(%r{^app/.+\.(erb|haml)$})
|
||||
end
|
||||
end
|
||||
```
|
||||
guard 'livereload' do
|
||||
watch(%r{^app/.+\.(erb|haml)$})
|
||||
end
|
||||
end
|
||||
|
||||
Using a Guardfile without the `guard` binary
|
||||
--------------------------------------------
|
||||
@ -315,37 +259,33 @@ Remember, without any options given, Guard will look for a Guardfile in your cur
|
||||
|
||||
For instance, you could use it as follow:
|
||||
|
||||
``` ruby
|
||||
gem 'guard'
|
||||
require 'guard'
|
||||
gem 'guard'
|
||||
require 'guard'
|
||||
|
||||
Guard.setup
|
||||
Guard.setup
|
||||
|
||||
Guard::Dsl.evaluate_guardfile(:guardfile => '/your/custom/path/to/a/valid/Guardfile')
|
||||
# or
|
||||
Guard::Dsl.evaluate_guardfile(:guardfile_contents => "
|
||||
guard 'rspec' do
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
end
|
||||
")
|
||||
```
|
||||
Guard::Dsl.evaluate_guardfile(:guardfile => '/your/custom/path/to/a/valid/Guardfile')
|
||||
# or
|
||||
Guard::Dsl.evaluate_guardfile(:guardfile_contents => "
|
||||
guard 'rspec' do
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
end
|
||||
")
|
||||
|
||||
### Listing defined guards/groups for the current project
|
||||
|
||||
You can list the defined groups and guards for the current Guardfile from the command line using `guard show` or `guard -T`:
|
||||
|
||||
``` bash
|
||||
# guard -T
|
||||
$ guard -T
|
||||
|
||||
(global):
|
||||
shell
|
||||
Group backend:
|
||||
bundler
|
||||
rspec: cli => "--color --format doc"
|
||||
Group frontend:
|
||||
coffeescript: output => "public/javascripts/compiled"
|
||||
livereload
|
||||
```
|
||||
(global):
|
||||
shell
|
||||
Group backend:
|
||||
bundler
|
||||
rspec: cli => "--color --format doc"
|
||||
Group frontend:
|
||||
coffeescript: output => "public/javascripts/compiled"
|
||||
livereload
|
||||
|
||||
User config file
|
||||
----------------
|
||||
@ -355,114 +295,107 @@ 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):
|
||||
|
||||
``` ruby
|
||||
guard 'shell' do
|
||||
watch(%r{^(?:app|lib)/.+\.rb$}) { `ctags -R` }
|
||||
end
|
||||
```
|
||||
guard 'shell' do
|
||||
watch(%r{^(?:app|lib)/.+\.rb$}) { `ctags -R` }
|
||||
end
|
||||
|
||||
Create a new guard
|
||||
------------------
|
||||
|
||||
Creating a new guard is very easy, just create a new gem (`bundle gem` if you use Bundler) with this basic structure:
|
||||
|
||||
```
|
||||
.travis.yml # bonus point!
|
||||
CHANGELOG.md # bonus point!
|
||||
Gemfile
|
||||
guard-name.gemspec
|
||||
Guardfile
|
||||
lib/
|
||||
guard/
|
||||
guard-name/
|
||||
templates/
|
||||
Guardfile # needed for `guard init <guard-name>`
|
||||
version.rb
|
||||
guard-name.rb
|
||||
test/ # or spec/
|
||||
README.md
|
||||
```
|
||||
.travis.yml # bonus point!
|
||||
CHANGELOG.md # bonus point!
|
||||
Gemfile
|
||||
guard-name.gemspec
|
||||
Guardfile
|
||||
lib/
|
||||
guard/
|
||||
guard-name/
|
||||
templates/
|
||||
Guardfile # needed for `guard init <guard-name>`
|
||||
version.rb
|
||||
guard-name.rb
|
||||
test/ # or spec/
|
||||
README.md
|
||||
|
||||
`Guard::GuardName` (in `lib/guard/guard-name.rb`) must inherit from `Guard::Guard` and should overwrite at least one of the five basic `Guard::Guard` instance methods.
|
||||
|
||||
Here is an example scaffold for `lib/guard/guard-name.rb`:
|
||||
|
||||
``` ruby
|
||||
require 'guard'
|
||||
require 'guard/guard'
|
||||
require 'guard'
|
||||
require 'guard/guard'
|
||||
|
||||
module Guard
|
||||
class GuardName < Guard
|
||||
module Guard
|
||||
class GuardName < Guard
|
||||
|
||||
def initialize(watchers=[], options={})
|
||||
super
|
||||
# init stuff here, thx!
|
||||
def initialize(watchers=[], options={})
|
||||
super
|
||||
# init stuff here, thx!
|
||||
end
|
||||
|
||||
# =================
|
||||
# = Guard methods =
|
||||
# =================
|
||||
|
||||
# If one of those methods raise an exception, the Guard::GuardName instance
|
||||
# will be removed from the active guards.
|
||||
|
||||
# Called once when Guard starts
|
||||
# Please override initialize method to init stuff
|
||||
def start
|
||||
true
|
||||
end
|
||||
|
||||
# Called when `stop|quit|exit|s|q|e + enter` is pressed (when Guard quits)
|
||||
def stop
|
||||
true
|
||||
end
|
||||
|
||||
# Called when `reload|r|z + enter` is pressed
|
||||
# This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
||||
def reload
|
||||
true
|
||||
end
|
||||
|
||||
# Called when just `enter` is pressed
|
||||
# This method should be principally used for long action like running all specs/tests/...
|
||||
def run_all
|
||||
true
|
||||
end
|
||||
|
||||
# Called on file(s) modifications
|
||||
def run_on_change(paths)
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# =================
|
||||
# = Guard methods =
|
||||
# =================
|
||||
|
||||
# If one of those methods raise an exception, the Guard::GuardName instance
|
||||
# will be removed from the active guards.
|
||||
|
||||
# Called once when Guard starts
|
||||
# Please override initialize method to init stuff
|
||||
def start
|
||||
true
|
||||
end
|
||||
|
||||
# Called on Ctrl-C signal (when Guard quits)
|
||||
def stop
|
||||
true
|
||||
end
|
||||
|
||||
# Called on Ctrl-Z signal
|
||||
# This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
||||
def reload
|
||||
true
|
||||
end
|
||||
|
||||
# Called on Ctrl-\ signal
|
||||
# This method should be principally used for long action like running all specs/tests/...
|
||||
def run_all
|
||||
true
|
||||
end
|
||||
|
||||
# Called on file(s) modifications
|
||||
def run_on_change(paths)
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Please take a look at the [existing guards' source code](https://github.com/guard/guard/wiki/List-of-available-Guards) for more concrete example and inspiration.
|
||||
|
||||
Alternatively, a new guard can be added inline to a Guardfile with this basic structure:
|
||||
|
||||
``` ruby
|
||||
require 'guard/guard'
|
||||
require 'guard/guard'
|
||||
|
||||
module ::Guard
|
||||
class InlineGuard < ::Guard::Guard
|
||||
def run_all
|
||||
true
|
||||
end
|
||||
module ::Guard
|
||||
class InlineGuard < ::Guard::Guard
|
||||
def run_all
|
||||
true
|
||||
end
|
||||
|
||||
def run_on_change(paths)
|
||||
true
|
||||
def run_on_change(paths)
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Here is a very cool example by [@avdi](https://github.com/avdi) : http://avdi.org/devblog/2011/06/15/a-guardfile-for-redis
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
* Documentation hosted at [RubyDoc](http://rubydoc.info/github/guard/guard/master/frames).
|
||||
* Source hosted at [GitHub](https://github.com/guard/guard).
|
||||
* Report issues and feature requests to [GitHub Issues](https://github.com/guard/guard/issues).
|
||||
|
||||
|
@ -14,11 +14,13 @@ Gem::Specification.new do |s|
|
||||
s.required_rubygems_version = '>= 1.3.6'
|
||||
s.rubyforge_project = 'guard'
|
||||
|
||||
s.add_dependency 'thor', '~> 0.14.6'
|
||||
|
||||
s.add_development_dependency 'bundler'
|
||||
s.add_development_dependency 'rspec', '~> 2.6.0'
|
||||
s.add_development_dependency 'guard-rspec', '~> 0.3.1'
|
||||
|
||||
s.add_dependency 'thor', '~> 0.14.6'
|
||||
s.add_development_dependency 'yard', '~> 0.7.2'
|
||||
s.add_development_dependency 'kramdown', '~> 0.13.3'
|
||||
|
||||
s.files = Dir.glob('{bin,images,lib}/**/*') + %w[CHANGELOG.md LICENSE man/guard.1 man/guard.1.html README.md]
|
||||
s.executable = 'guard'
|
||||
|
141
lib/guard.rb
141
lib/guard.rb
@ -1,3 +1,6 @@
|
||||
# Guard is the main module for all Guard related modules and classes.
|
||||
# Also other Guard implementation should use this namespace.
|
||||
#
|
||||
module Guard
|
||||
|
||||
autoload :UI, 'guard/ui'
|
||||
@ -12,7 +15,16 @@ module Guard
|
||||
class << self
|
||||
attr_accessor :options, :guards, :groups, :interactor, :listener
|
||||
|
||||
# initialize this singleton
|
||||
# Initialize the Guard singleton.
|
||||
#
|
||||
# @param [Hash] options the Guard options.
|
||||
# @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 setup(options = {})
|
||||
@options = options
|
||||
@guards = []
|
||||
@ -20,7 +32,7 @@ module Guard
|
||||
@interactor = Interactor.new
|
||||
@listener = Listener.select_and_init(@options[:watchdir] ? File.expand_path(@options[:watchdir]) : Dir.pwd)
|
||||
|
||||
@options[:notify] && ENV["GUARD_NOTIFY"] != 'false' ? Notifier.turn_on : Notifier.turn_off
|
||||
@options[:notify] && ENV['GUARD_NOTIFY'] != 'false' ? Notifier.turn_on : Notifier.turn_off
|
||||
|
||||
UI.clear if @options[:clear]
|
||||
debug_command_execution if @options[:debug]
|
||||
@ -28,6 +40,17 @@ module Guard
|
||||
self
|
||||
end
|
||||
|
||||
# Start Guard by evaluate the `Guardfile`, initialize the declared Guards
|
||||
# and start the available file change listener.
|
||||
#
|
||||
# @param [Hash] options the Guard options.
|
||||
# @option options [Boolean] clear if auto clear the UI should be done
|
||||
# @option options [Boolean] notify if system notifications should be shown
|
||||
# @option options [Boolean] debug if debug output should be shown
|
||||
# @option options [Array<String>] group the list of groups to start
|
||||
# @option options [String] watchdir the director to watch
|
||||
# @option options [String] guardfile the path to the Guardfile
|
||||
#
|
||||
def start(options = {})
|
||||
setup(options)
|
||||
|
||||
@ -38,7 +61,7 @@ module Guard
|
||||
listener.changed_files += files if Watcher.match_files?(guards, files)
|
||||
end
|
||||
|
||||
UI.info "Guard is now watching at '#{listener.directory}'"
|
||||
UI.info "Guard is now watching at '#{ listener.directory }'"
|
||||
|
||||
execute_supervised_task_for_all_guards(:start)
|
||||
|
||||
@ -46,42 +69,57 @@ module Guard
|
||||
listener.start
|
||||
end
|
||||
|
||||
# Stop Guard listening to file changes
|
||||
#
|
||||
def stop
|
||||
UI.info "Bye bye...", :reset => true
|
||||
UI.info 'Bye bye...', :reset => true
|
||||
listener.stop
|
||||
execute_supervised_task_for_all_guards(:stop)
|
||||
abort
|
||||
end
|
||||
|
||||
# Reload all Guards currently enabled.
|
||||
#
|
||||
def reload
|
||||
run do
|
||||
execute_supervised_task_for_all_guards(:reload)
|
||||
end
|
||||
end
|
||||
|
||||
# Trigger `run_all` on all Guards currently enabled.
|
||||
#
|
||||
def run_all
|
||||
run do
|
||||
execute_supervised_task_for_all_guards(:run_all)
|
||||
end
|
||||
end
|
||||
|
||||
# Pause Guard listening to file changes.
|
||||
#
|
||||
def pause
|
||||
if listener.locked
|
||||
UI.info "Un-paused files modification listening", :reset => true
|
||||
UI.info 'Un-paused files modification listening', :reset => true
|
||||
listener.clear_changed_files
|
||||
listener.unlock
|
||||
else
|
||||
UI.info "Paused files modification listening", :reset => true
|
||||
UI.info 'Paused files modification listening', :reset => true
|
||||
listener.lock
|
||||
end
|
||||
end
|
||||
|
||||
def run_on_change(files)
|
||||
# Trigger `run_on_change` on all Guards currently enabled.
|
||||
#
|
||||
def run_on_change(paths)
|
||||
run do
|
||||
execute_supervised_task_for_all_guards(:run_on_change, files)
|
||||
execute_supervised_task_for_all_guards(:run_on_change, paths)
|
||||
end
|
||||
end
|
||||
|
||||
# Run a block where the listener and the interactor is
|
||||
# blocked.
|
||||
#
|
||||
# @yield the block to run
|
||||
#
|
||||
def run
|
||||
listener.lock
|
||||
interactor.lock
|
||||
@ -94,12 +132,19 @@ module Guard
|
||||
listener.unlock
|
||||
end
|
||||
|
||||
# Loop through all groups and execute the given task for each Guard in it,
|
||||
# but halt the task execution for the all Guards within a group if one Guard
|
||||
# throws `:task_has_failed` and the group has its `:halt_on_fail` option to `true`.
|
||||
#
|
||||
# @param [Symbol] task the task to run
|
||||
# @param [Array] files the list of files to pass to the task
|
||||
#
|
||||
def execute_supervised_task_for_all_guards(task, files = nil)
|
||||
groups.each do |group_hash|
|
||||
catch group_hash[:options][:halt_on_fail] == true ? :task_has_failed : :no_catch do
|
||||
guards.find_all { |guard| guard.group == group_hash[:name] }.each do |guard|
|
||||
paths = Watcher.match_files(guard, files) if files
|
||||
if paths && !paths.empty?
|
||||
if task == :run_on_change
|
||||
paths = Watcher.match_files(guard, files)
|
||||
UI.debug "#{guard.class.name}##{task} with #{paths.inspect}"
|
||||
supervised_task(guard, task, paths)
|
||||
else
|
||||
@ -110,25 +155,40 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# Let a guard execute its task but
|
||||
# fire it if his work leads to a system failure
|
||||
# Let a Guard execute its task, but fire it
|
||||
# if his work leads to a system failure.
|
||||
#
|
||||
# @param [Guard::Guard] guard the Guard to execute
|
||||
# @param [Symbol] task_to_supervise the task to run
|
||||
# @param [Array] args the arguments for the task
|
||||
# @return [Boolean, Exception] the result of the Guard
|
||||
#
|
||||
def supervised_task(guard, task_to_supervise, *args)
|
||||
guard.hook("#{task_to_supervise}_begin", *args)
|
||||
guard.hook("#{ task_to_supervise }_begin", *args)
|
||||
result = guard.send(task_to_supervise, *args)
|
||||
guard.hook("#{task_to_supervise}_end", result)
|
||||
guard.hook("#{ task_to_supervise }_end", result)
|
||||
|
||||
result
|
||||
|
||||
rescue Exception => ex
|
||||
UI.error("#{guard.class.name} failed to achieve its <#{task_to_supervise.to_s}>, exception was:" +
|
||||
"\n#{ex.class}: #{ex.message}\n#{ex.backtrace.join("\n")}")
|
||||
UI.error("#{ guard.class.name } failed to achieve its <#{ task_to_supervise.to_s }>, exception was:" +
|
||||
"\n#{ ex.class }: #{ ex.message }\n#{ ex.backtrace.join("\n") }")
|
||||
guards.delete guard
|
||||
UI.info("\n#{guard.class.name} has just been fired")
|
||||
return ex
|
||||
UI.info("\n#{ guard.class.name } has just been fired")
|
||||
|
||||
ex
|
||||
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
|
||||
#
|
||||
def add_guard(name, watchers = [], callbacks = [], options = {})
|
||||
if name.to_sym == :ego
|
||||
UI.deprecation("Guard::Ego is now part of Guard. You can remove it from your Guardfile.")
|
||||
UI.deprecation('Guard::Ego is now part of Guard. You can remove it from your Guardfile.')
|
||||
else
|
||||
guard_class = get_guard_class(name)
|
||||
callbacks.each { |callback| Hook.add_callback(callback[:listener], guard_class, callback[:events]) }
|
||||
@ -136,42 +196,60 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# Add a Guard group.
|
||||
#
|
||||
# @param [String] name the group name
|
||||
# @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 add_group(name, options = {})
|
||||
@groups << { :name => name.to_sym, :options => options } unless name.nil? || @groups.find { |group| group[:name] == name }
|
||||
end
|
||||
|
||||
# Tries to load the Guard main class.
|
||||
#
|
||||
# @param [String] name the name of the Guard
|
||||
# @return [Class, nil] the loaded class
|
||||
#
|
||||
def get_guard_class(name)
|
||||
name = name.to_s
|
||||
try_require = false
|
||||
const_name = name.downcase.gsub('-', '')
|
||||
begin
|
||||
require "guard/#{name.downcase}" if try_require
|
||||
require "guard/#{ name.downcase }" if try_require
|
||||
self.const_get(self.constants.find { |c| c.to_s.downcase == const_name })
|
||||
rescue TypeError
|
||||
unless try_require
|
||||
try_require = true
|
||||
retry
|
||||
else
|
||||
UI.error "Could not find class Guard::#{const_name.capitalize}"
|
||||
UI.error "Could not find class Guard::#{ const_name.capitalize }"
|
||||
end
|
||||
rescue LoadError => loadError
|
||||
UI.error "Could not load 'guard/#{name.downcase}' or find class Guard::#{const_name.capitalize}"
|
||||
UI.error "Could not load 'guard/#{ name.downcase }' or find class Guard::#{ const_name.capitalize }"
|
||||
UI.error loadError.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Locate a path to a Guard gem.
|
||||
#
|
||||
# @param [String] name the name of the Guard without the prefix `guard-`
|
||||
# @return [String] the full path to the Guard gem
|
||||
#
|
||||
def locate_guard(name)
|
||||
if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
|
||||
Gem::Specification.find_by_name("guard-#{name}").full_gem_path
|
||||
Gem::Specification.find_by_name("guard-#{ name }").full_gem_path
|
||||
else
|
||||
Gem.source_index.find_name("guard-#{name}").last.full_gem_path
|
||||
Gem.source_index.find_name("guard-#{ name }").last.full_gem_path
|
||||
end
|
||||
rescue
|
||||
UI.error "Could not find 'guard-#{name}' gem path."
|
||||
UI.error "Could not find 'guard-#{ name }' gem path."
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a list of guard Gem names installed locally.
|
||||
#
|
||||
# @return [Array<String>] a list of guard gem names
|
||||
#
|
||||
def guard_gem_names
|
||||
if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
|
||||
Gem::Specification.find_all.select { |x| x.name =~ /^guard-/ }
|
||||
@ -180,16 +258,19 @@ module Guard
|
||||
end.map { |x| x.name.sub /^guard-/, '' }
|
||||
end
|
||||
|
||||
# Adds a command logger in debug mode. This wraps common command
|
||||
# execution functions and logs the executed command before execution.
|
||||
#
|
||||
def debug_command_execution
|
||||
Kernel.send(:alias_method, :original_system, :system)
|
||||
Kernel.send(:define_method, :system) do |command, *args|
|
||||
::Guard::UI.debug "Command execution: #{command} #{args.join(' ')}"
|
||||
::Guard::UI.debug "Command execution: #{ command } #{ args.join(' ') }"
|
||||
original_system command, *args
|
||||
end
|
||||
|
||||
Kernel.send(:alias_method, :original_backtick, :"`")
|
||||
Kernel.send(:define_method, :"`") do |command|
|
||||
::Guard::UI.debug "Command execution: #{command}"
|
||||
Kernel.send(:alias_method, :original_backtick, :'`')
|
||||
Kernel.send(:define_method, :'`') do |command|
|
||||
::Guard::UI.debug "Command execution: #{ command }"
|
||||
original_backtick command
|
||||
end
|
||||
end
|
||||
|
179
lib/guard/cli.rb
179
lib/guard/cli.rb
@ -2,89 +2,172 @@ require 'thor'
|
||||
require 'guard/version'
|
||||
|
||||
module Guard
|
||||
|
||||
# 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`.
|
||||
#
|
||||
class CLI < Thor
|
||||
|
||||
default_task :start
|
||||
|
||||
method_option :clear, :type => :boolean, :default => false, :aliases => '-c', :banner => "Auto clear shell before each change/run_all/reload"
|
||||
method_option :notify, :type => :boolean, :default => true, :aliases => '-n', :banner => "Notifications feature (growl/libnotify)"
|
||||
method_option :debug, :type => :boolean, :default => false, :aliases => '-d', :banner => "Print debug messages"
|
||||
method_option :group, :type => :array, :default => [], :aliases => '-g', :banner => "Run only the passed groups"
|
||||
method_option :watchdir, :type => :string, :aliases => '-w', :banner => "Specify the directory to watch"
|
||||
method_option :guardfile, :type => :string, :aliases => '-G', :banner => "Specify a Guardfile"
|
||||
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 :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'
|
||||
|
||||
# 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
|
||||
::Guard.start(options)
|
||||
end
|
||||
|
||||
desc "list", "Lists guards that can be used with init"
|
||||
desc 'list', 'Lists guards that can be used with init'
|
||||
|
||||
# List the Guards that are available for use in your system and marks
|
||||
# those that are currently used in your `Guardfile`.
|
||||
#
|
||||
# @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
|
||||
#
|
||||
# @see Guard::DslDescriber
|
||||
#
|
||||
def list
|
||||
::Guard::DslDescriber.evaluate_guardfile(options)
|
||||
installed = []
|
||||
::Guard::DslDescriber.guardfile_structure.each do |group|
|
||||
group[:guards].each {|x| installed << x[:name]} if group[:guards]
|
||||
Guard::DslDescriber.evaluate_guardfile(options)
|
||||
|
||||
installed = Guard::DslDescriber.guardfile_structure.inject([]) do |installed, group|
|
||||
group[:guards].each { |guard| installed << guard[:name] } if group[:guards]
|
||||
installed
|
||||
end
|
||||
|
||||
::Guard::UI.info "Available guards:"
|
||||
::Guard::guard_gem_names.sort.each do |name|
|
||||
if installed.include? name
|
||||
::Guard::UI.info " #{name} *"
|
||||
else
|
||||
::Guard::UI.info " #{name}"
|
||||
end
|
||||
Guard::UI.info 'Available guards:'
|
||||
|
||||
Guard::guard_gem_names.sort.uniq.each do |name|
|
||||
Guard::UI.info " #{ name } #{ installed.include?(name) ? '*' : '' }"
|
||||
end
|
||||
::Guard::UI.info ' '
|
||||
::Guard::UI.info "See also https://github.com/guard/guard/wiki/List-of-available-Guards"
|
||||
::Guard::UI.info "* denotes ones already in your Guardfile"
|
||||
|
||||
Guard::UI.info ' '
|
||||
Guard::UI.info 'See also https://github.com/guard/guard/wiki/List-of-available-Guards'
|
||||
Guard::UI.info '* denotes ones already in your Guardfile'
|
||||
end
|
||||
|
||||
desc "version", "Prints Guard's version"
|
||||
def version
|
||||
::Guard::UI.info "Guard version #{Guard::VERSION}"
|
||||
end
|
||||
desc 'version', 'Show the Guard version'
|
||||
map %w(-v --version) => :version
|
||||
|
||||
desc "init [GUARD]", "Generates a Guardfile into the current working directory, or insert the given GUARD in an existing Guardfile"
|
||||
def init(guard_name = nil)
|
||||
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
|
||||
# Shows the current version of Guard.
|
||||
#
|
||||
# @see Guard::VERSION
|
||||
#
|
||||
def version
|
||||
Guard::UI.info "Guard version #{ Guard::VERSION }"
|
||||
end
|
||||
|
||||
desc 'init [GUARD]', 'Generates a Guardfile at the current working directory, or insert the given GUARD to an existing Guardfile'
|
||||
|
||||
# Appends the Guard template to the `Guardfile`, or creates an initial
|
||||
# `Guardfile` when no Guard name is passed.
|
||||
#
|
||||
# @param [String] guard_name the name of the Guard to initialize
|
||||
#
|
||||
def init(guard_name = nil)
|
||||
if guard_name
|
||||
guard_class = ::Guard.get_guard_class(guard_name)
|
||||
guard_class.init(guard_name)
|
||||
|
||||
else
|
||||
if File.exist?('Guardfile')
|
||||
puts '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
|
||||
|
||||
desc "show", "Show all defined Guards and their options"
|
||||
def show
|
||||
::Guard::DslDescriber.evaluate_guardfile(options)
|
||||
desc 'show', 'Show all defined Guards and their options'
|
||||
map %w(-T) => :show
|
||||
|
||||
::Guard::DslDescriber.guardfile_structure.each do |group|
|
||||
if !group[:guards].empty?
|
||||
# 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
|
||||
#
|
||||
# @see Guard::DslDescriber
|
||||
#
|
||||
def show
|
||||
Guard::DslDescriber.evaluate_guardfile(options)
|
||||
|
||||
Guard::DslDescriber.guardfile_structure.each do |group|
|
||||
unless group[:guards].empty?
|
||||
if group[:group]
|
||||
::Guard::UI.info "Group #{group[:group]}:"
|
||||
Guard::UI.info "Group #{ group[:group] }:"
|
||||
else
|
||||
::Guard::UI.info "(global):"
|
||||
Guard::UI.info '(global):'
|
||||
end
|
||||
|
||||
group[:guards].each do |guard|
|
||||
line = " #{guard[:name]}"
|
||||
line = " #{ guard[:name] }"
|
||||
|
||||
if !guard[:options].empty?
|
||||
line += ": #{guard[:options].collect { |k, v| "#{k} => #{v.inspect}" }.join(", ")}"
|
||||
unless guard[:options].empty?
|
||||
line += ": #{ guard[:options].collect { |k, v| "#{ k } => #{ v.inspect }" }.join(', ') }"
|
||||
end
|
||||
::Guard::UI.info line
|
||||
|
||||
Guard::UI.info line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
::Guard::UI.info ''
|
||||
Guard::UI.info ''
|
||||
end
|
||||
map %w(-T) => :show
|
||||
|
||||
end
|
||||
end
|
||||
|
299
lib/guard/dsl.rb
299
lib/guard/dsl.rb
@ -1,63 +1,161 @@
|
||||
module Guard
|
||||
|
||||
# The DSL class provides the methods that are used in each `Guardfile` to describe
|
||||
# the behaviour of Guard.
|
||||
#
|
||||
# The main keywords of the DSL are `guard` and `watch`. These are necessary to define
|
||||
# the used Guards and the file changes they are watching.
|
||||
#
|
||||
# You can optionally group the Guards with the `group` keyword and ignore certain paths
|
||||
# with the `ignore_paths` keyword.
|
||||
#
|
||||
# A more advanced DSL use is the `callback` keyword that allows you to execute arbitrary
|
||||
# code before or after any of the `start`, `stop`, `reload`, `run_all` and `run_on_change`
|
||||
# Guards' method. You can even insert more hooks inside these methods.
|
||||
# Please [checkout the Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for more details.
|
||||
#
|
||||
# The DSL will also evaluate normal Ruby code.
|
||||
#
|
||||
# There are two possible locations for the `Guardfile`:
|
||||
# - The `Guardfile` in the current directory where Guard has been started
|
||||
# - The `.Guardfile` in your home directory.
|
||||
#
|
||||
# In addition, if a user configuration `.guard.rb` in your home directory is found, it will
|
||||
# be appended to the current project `Guardfile`.
|
||||
#
|
||||
# @example A sample of a complex Guardfile
|
||||
#
|
||||
# group 'frontend' do
|
||||
# guard 'passenger', :ping => true do
|
||||
# watch('config/application.rb')
|
||||
# watch('config/environment.rb')
|
||||
# watch(%r{^config/environments/.+\.rb})
|
||||
# watch(%r{^config/initializers/.+\.rb})
|
||||
# end
|
||||
#
|
||||
# guard 'livereload', :apply_js_live => false do
|
||||
# watch(%r{^app/.+\.(erb|haml)})
|
||||
# watch(%r{^app/helpers/.+\.rb})
|
||||
# watch(%r{^public/javascripts/.+\.js})
|
||||
# watch(%r{^public/stylesheets/.+\.css})
|
||||
# watch(%r{^public/.+\.html})
|
||||
# watch(%r{^config/locales/.+\.yml})
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# group 'backend' do
|
||||
# # Reload the bundle when the Gemfile is modified
|
||||
# guard 'bundler' do
|
||||
# watch('Gemfile')
|
||||
# end
|
||||
#
|
||||
# # for big project you can fine tune the "timeout" before Spork's launch is considered failed
|
||||
# guard 'spork', :wait => 40 do
|
||||
# watch('Gemfile')
|
||||
# watch('config/application.rb')
|
||||
# watch('config/environment.rb')
|
||||
# watch(%r{^config/environments/.+\.rb})
|
||||
# watch(%r{^config/initializers/.+\.rb})
|
||||
# watch('spec/spec_helper.rb')
|
||||
# end
|
||||
#
|
||||
# # use RSpec 2, from the system's gem and with some direct RSpec CLI options
|
||||
# guard 'rspec', :version => 2, :cli => "--color --drb -f doc", :bundler => false do
|
||||
# watch('spec/spec_helper.rb') { "spec" }
|
||||
# watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
||||
# watch('config/routes.rb') { "spec/routing" }
|
||||
# watch(%r{^spec/support/(controllers|acceptance)_helpers\.rb}) { |m| "spec/#{m[1]}" }
|
||||
# watch(%r{^spec/.+_spec\.rb})
|
||||
#
|
||||
# watch(%r{^app/controllers/(.+)_(controller)\.rb}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
||||
#
|
||||
# watch(%r{^app/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
|
||||
# watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||
# end
|
||||
# end
|
||||
#
|
||||
class Dsl
|
||||
class << self
|
||||
|
||||
@@options = nil
|
||||
|
||||
# Evaluate the DSL methods in the `Guardfile`.
|
||||
#
|
||||
# @param [Hash] options the Guard options
|
||||
# @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 = {})
|
||||
options.is_a?(Hash) or raise ArgumentError.new("evaluate_guardfile not passed a Hash!")
|
||||
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?
|
||||
UI.error 'No guards found in Guardfile, please add at least one.' if !::Guard.guards.nil? && ::Guard.guards.empty?
|
||||
end
|
||||
|
||||
# Re-evaluate the `Guardfile` to update the current Guard configuration.
|
||||
#
|
||||
def reevaluate_guardfile
|
||||
::Guard.guards.clear
|
||||
::Guard.groups.clear
|
||||
@@options.delete(:guardfile_contents)
|
||||
Dsl.evaluate_guardfile(@@options)
|
||||
msg = "Guardfile has been re-evaluated."
|
||||
msg = 'Guardfile has been re-evaluated.'
|
||||
UI.info(msg)
|
||||
Notifier.notify(msg)
|
||||
end
|
||||
|
||||
# Evaluate the content of the `Guardfile`.
|
||||
#
|
||||
# @param [String] contents the content to evaluate.
|
||||
#
|
||||
def instance_eval_guardfile(contents)
|
||||
begin
|
||||
new.instance_eval(contents, @@options[:guardfile_path], 1)
|
||||
rescue
|
||||
UI.error "Invalid Guardfile, original error is:\n#{$!}"
|
||||
exit 1
|
||||
end
|
||||
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}['"]?/)
|
||||
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)
|
||||
begin
|
||||
@@options[:guardfile_path] = guardfile_path
|
||||
@@options[:guardfile_contents] = File.read(guardfile_path)
|
||||
rescue
|
||||
UI.error("Error reading file #{guardfile_path}")
|
||||
exit 1
|
||||
end
|
||||
@@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
|
||||
# TODO: do we need .rc file interaction?
|
||||
if @@options[:guardfile_contents]
|
||||
UI.info "Using inline Guardfile."
|
||||
UI.info 'Using inline Guardfile.'
|
||||
@@options[:guardfile_path] = 'Inline Guardfile'
|
||||
|
||||
elsif @@options[:guardfile]
|
||||
if File.exist?(@@options[:guardfile])
|
||||
read_guardfile(@@options[:guardfile])
|
||||
UI.info "Using Guardfile at #{@@options[:guardfile]}."
|
||||
UI.info "Using Guardfile at #{ @@options[:guardfile] }."
|
||||
else
|
||||
UI.error "No Guardfile exists at #{@@options[:guardfile]}."
|
||||
UI.error "No Guardfile exists at #{ @@options[:guardfile] }."
|
||||
exit 1
|
||||
end
|
||||
|
||||
@ -65,86 +163,209 @@ module Guard
|
||||
if File.exist?(guardfile_default_path)
|
||||
read_guardfile(guardfile_default_path)
|
||||
else
|
||||
UI.error "No Guardfile found, please create one with `guard init`."
|
||||
UI.error 'No Guardfile found, please create one with `guard init`.'
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
unless guardfile_contents_usable?
|
||||
UI.error "The command file(#{@@options[:guardfile]}) seems to be empty."
|
||||
UI.error "The command file(#{ @@options[:guardfile] }) seems to be empty."
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
# Get the content of the `Guardfile`.
|
||||
#
|
||||
# @return [String] the Guardfile content
|
||||
#
|
||||
def guardfile_contents
|
||||
@@options ? @@options[:guardfile_contents] : ""
|
||||
@@options ? @@options[:guardfile_contents] : ''
|
||||
end
|
||||
|
||||
# Get the content of the `Guardfile` and the global
|
||||
# user configuration file.
|
||||
#
|
||||
# @see #user_config_path
|
||||
#
|
||||
# @return [String] the Guardfile content
|
||||
#
|
||||
def guardfile_contents_with_user_config
|
||||
config = File.read(user_config_path) if File.exist?(user_config_path)
|
||||
[guardfile_contents, config].join("\n")
|
||||
end
|
||||
|
||||
# Get the file path to the project `Guardfile`.
|
||||
#
|
||||
# @return [String] the path to the Guardfile
|
||||
#
|
||||
def guardfile_path
|
||||
@@options ? @@options[:guardfile_path] : ""
|
||||
@@options ? @@options[:guardfile_path] : ''
|
||||
end
|
||||
|
||||
# Tests if the current `Guardfile` content is usable.
|
||||
#
|
||||
# @return [Boolean] if the Guardfile is usable
|
||||
#
|
||||
def guardfile_contents_usable?
|
||||
guardfile_contents && guardfile_contents.size >= 'guard :a'.size # smallest guard-definition
|
||||
guardfile_contents && guardfile_contents.size >= 'guard :a'.size # Smallest Guard definition
|
||||
end
|
||||
|
||||
# Gets the default path of the `Guardfile`. This returns the `Guardfile`
|
||||
# from the current directory when existing, or the global `.Guardfile`
|
||||
# at the home directory.
|
||||
#
|
||||
# @return [String] the path to the Guardfile
|
||||
#
|
||||
def guardfile_default_path
|
||||
File.exist?(local_guardfile_path) ? local_guardfile_path : home_guardfile_path
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
# The path to the `Guardfile` that is located at
|
||||
# the directory, where Guard has been started from.
|
||||
#
|
||||
# @param [String] the path to the local Guardfile
|
||||
#
|
||||
def local_guardfile_path
|
||||
File.join(Dir.pwd, "Guardfile")
|
||||
File.join(Dir.pwd, 'Guardfile')
|
||||
end
|
||||
|
||||
# The path to the `.Guardfile` that is located at
|
||||
# the users home directory.
|
||||
#
|
||||
# @param [String] the path to ~/.Guardfile
|
||||
#
|
||||
def home_guardfile_path
|
||||
File.expand_path(File.join("~", ".Guardfile"))
|
||||
File.expand_path(File.join('~', '.Guardfile'))
|
||||
end
|
||||
|
||||
# The path to the user configuration `.guard.rb`
|
||||
# that is located at the users home directory.
|
||||
#
|
||||
# @param [String] the path to ~/.guard.rb
|
||||
#
|
||||
def user_config_path
|
||||
File.expand_path(File.join("~", ".guard.rb"))
|
||||
File.expand_path(File.join('~', '.guard.rb'))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def group(name, options = {}, &guard_definition)
|
||||
# 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
|
||||
name = name.to_sym
|
||||
|
||||
if guard_definition && (@groups.empty? || @groups.map(&:to_sym).include?(name))
|
||||
::Guard.add_group(name.to_s.downcase.to_sym, options)
|
||||
if block_given? && (@groups.empty? || @groups.map(&:to_sym).include?(name))
|
||||
::Guard.add_group(name.to_s.downcase, options)
|
||||
@current_group = name
|
||||
guard_definition.call
|
||||
|
||||
yield if block_given?
|
||||
|
||||
@current_group = nil
|
||||
end
|
||||
end
|
||||
|
||||
def guard(name, options = {}, &watch_and_callback_definition)
|
||||
# Declare a guard to be used when running `guard start`.
|
||||
#
|
||||
# The name parameter is usually the name of the gem without
|
||||
# the 'guard-' prefix.
|
||||
#
|
||||
# The available options are different for each Guard implementation.
|
||||
#
|
||||
# @example Declare a Guard
|
||||
#
|
||||
# guard 'rspec' do
|
||||
# end
|
||||
#
|
||||
# @param [String] name the Guard name
|
||||
# @param [Hash] options the options accepted by the Guard
|
||||
# @yield a block where you can declare several watch patterns and actions
|
||||
#
|
||||
# @see Guard.add_guard
|
||||
# @see Dsl#group
|
||||
# @see Dsl#watch
|
||||
# @see Guard::DslDescriber
|
||||
#
|
||||
def guard(name, options = {})
|
||||
@watchers = []
|
||||
@callbacks = []
|
||||
watch_and_callback_definition.call if watch_and_callback_definition
|
||||
|
||||
yield if block_given?
|
||||
|
||||
options.update(:group => (@current_group || :default))
|
||||
::Guard.add_guard(name.to_s.downcase.to_sym, @watchers, @callbacks, options)
|
||||
::Guard.add_guard(name.to_s.downcase, @watchers, @callbacks, options)
|
||||
end
|
||||
|
||||
# Define a pattern to be watched in order to run actions on file modification.
|
||||
#
|
||||
# @example Declare watchers for a Guard
|
||||
#
|
||||
# guard 'rspec' do
|
||||
# watch('spec/spec_helper.rb')
|
||||
# watch(%r{^.+_spec.rb})
|
||||
# watch(%r{^app/controllers/(.+).rb}) { |m| 'spec/acceptance/#{m[1]}s_spec.rb' }
|
||||
# end
|
||||
#
|
||||
# @param [String, Regexp] pattern the pattern to be watched by the guard
|
||||
# @yield a block to be run when the pattern is matched
|
||||
# @yieldparam [MatchData] m matches of the pattern
|
||||
# @yieldreturn a directory, a filename, an array of directories / filenames, or nothing (can be an arbitrary command)
|
||||
#
|
||||
# @see Guard::Watcher
|
||||
# @see Dsl#guard
|
||||
#
|
||||
def watch(pattern, &action)
|
||||
@watchers << ::Guard::Watcher.new(pattern, action)
|
||||
end
|
||||
|
||||
# Define a callback to execute arbitrary code before or after any of
|
||||
# the `start`, `stop`, `reload`, `run_all` and `run_on_change` guards' method.
|
||||
#
|
||||
# @param [Array] args the callback arguments
|
||||
# @yield a block with listeners
|
||||
#
|
||||
# @see Guard::Hook
|
||||
#
|
||||
def callback(*args, &listener)
|
||||
listener, events = args.size > 1 ? args : [listener, args[0]]
|
||||
@callbacks << { :events => events, :listener => listener }
|
||||
end
|
||||
|
||||
# Ignore certain paths globally.
|
||||
#
|
||||
# @example Ignore some paths
|
||||
# ignore_paths ".git", ".svn"
|
||||
#
|
||||
# @param [Array] paths the list of paths to ignore
|
||||
#
|
||||
# @see Guard::Listener
|
||||
#
|
||||
def ignore_paths(*paths)
|
||||
UI.info "Ignoring paths: #{paths.join(', ')}"
|
||||
UI.info "Ignoring paths: #{ paths.join(', ') }"
|
||||
::Guard.listener.ignore_paths.push(*paths)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -1,28 +1,60 @@
|
||||
require 'guard/dsl'
|
||||
|
||||
module Guard
|
||||
|
||||
# The DslDescriber overrides methods to create an internal structure
|
||||
# of the Guardfile that is used in some inspection utility methods
|
||||
# like the CLI commands `show` and `list`.
|
||||
#
|
||||
# @see Guard::Dsl
|
||||
# @see Guard::CLI
|
||||
#
|
||||
class DslDescriber < Dsl
|
||||
|
||||
@@guardfile_structure = [ { :guards => [] } ]
|
||||
|
||||
class << self
|
||||
|
||||
# Get the Guardfile structure.
|
||||
#
|
||||
# @return [Array<Hash>] the structure
|
||||
#
|
||||
def guardfile_structure
|
||||
@@guardfile_structure
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def group(name, &guard_definition)
|
||||
@@guardfile_structure << { :group => name.to_sym, :guards => [] }
|
||||
|
||||
# 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
|
||||
guard_definition.call
|
||||
|
||||
yield if block_given?
|
||||
|
||||
@group = false
|
||||
end
|
||||
|
||||
def guard(name, options = {}, &watch_definition)
|
||||
# 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,21 +1,39 @@
|
||||
module Guard
|
||||
|
||||
# Main class that every Guard implementation must subclass.
|
||||
#
|
||||
# Guard will trigger the `start`, `stop`, `reload`, `run_all` and `run_on_change`
|
||||
# methods depending on user interaction and file modification.
|
||||
#
|
||||
# Each Guard should provide a template Guardfile located within the Gem
|
||||
# at `lib/guard/guard-name/templates/Guardfile`.
|
||||
#
|
||||
class Guard
|
||||
include Hook
|
||||
|
||||
attr_accessor :watchers, :options, :group
|
||||
|
||||
# Initialize a Guard.
|
||||
#
|
||||
# @param [Array<Guard::Watcher>] watchers the Guard file watchers
|
||||
# @param [Hash] options the custom Guard options.
|
||||
#
|
||||
def initialize(watchers = [], options = {})
|
||||
@group = options.delete(:group) || :default
|
||||
@watchers, @options = watchers, options
|
||||
end
|
||||
|
||||
# Guardfile template needed inside guard gem
|
||||
# Initialize the Guard. This will copy the Guardfile template inside the Guard gem.
|
||||
# The template Guardfile must be located within the Gem at `lib/guard/guard-name/templates/Guardfile`.
|
||||
#
|
||||
# @param [String] name the name of the Guard
|
||||
#
|
||||
def self.init(name)
|
||||
if ::Guard::Dsl.guardfile_include?(name)
|
||||
::Guard::UI.info "Guardfile already includes #{name} guard"
|
||||
::Guard::UI.info "Guardfile already includes #{ name } guard"
|
||||
else
|
||||
content = File.read('Guardfile')
|
||||
guard = File.read("#{::Guard.locate_guard(name)}/lib/guard/#{name}/templates/Guardfile")
|
||||
guard = File.read("#{ ::Guard.locate_guard(name) }/lib/guard/#{ name }/templates/Guardfile")
|
||||
File.open('Guardfile', 'wb') do |f|
|
||||
f.puts(content)
|
||||
f.puts("")
|
||||
@ -25,31 +43,43 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# ================
|
||||
# = Guard method =
|
||||
# ================
|
||||
|
||||
# Call once when guard starts
|
||||
# Please override initialize method to init stuff
|
||||
# Call once when Guard starts. Please override initialize method to init stuff.
|
||||
#
|
||||
# @return [Boolean] Whether the start action was successful or not
|
||||
#
|
||||
def start
|
||||
true
|
||||
end
|
||||
|
||||
# Call once when guard quit
|
||||
# Call once when Guard quit.
|
||||
#
|
||||
# @return [Boolean] Whether the stop action was successful or not
|
||||
#
|
||||
def stop
|
||||
true
|
||||
end
|
||||
|
||||
# Should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
||||
# Should be used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
||||
#
|
||||
# @return [Boolean] Whether the reload action was successful or not
|
||||
#
|
||||
def reload
|
||||
true
|
||||
end
|
||||
|
||||
# Should be principally used for long action like running all specs/tests/...
|
||||
# Should be used for long action like running all specs/tests/...
|
||||
#
|
||||
# @return [Boolean] Whether the run_all action was successful or not
|
||||
#
|
||||
def run_all
|
||||
true
|
||||
end
|
||||
|
||||
# Will be triggered when a file change matched a watcher.
|
||||
#
|
||||
# @param [Array<String>] paths the changes files or paths
|
||||
# @return [Boolean] Whether the run_all action was successful or not
|
||||
#
|
||||
def run_on_change(paths)
|
||||
true
|
||||
end
|
||||
|
@ -1,51 +1,82 @@
|
||||
module Guard
|
||||
|
||||
# Guard has a hook mechanism that allows you to insert callbacks for individual Guards.
|
||||
# By default, each of the Guard instance methods has a "_begin" and an "_end" hook.
|
||||
# For example, the Guard::Guard#start method has a :start_begin hook that is runs immediately
|
||||
# before Guard::Guard#start, and a :start_end hook that runs immediately after Guard::Guard#start.
|
||||
#
|
||||
# Read more about [hooks and callbacks on the wiki](https://github.com/guard/guard/wiki/Hooks-and-callbacks).
|
||||
#
|
||||
module Hook
|
||||
|
||||
# The Hook module gets included.
|
||||
#
|
||||
# @param [Class] base the class that includes the module
|
||||
#
|
||||
def self.included(base)
|
||||
base.send :include, InstanceMethods
|
||||
end
|
||||
|
||||
# Instance methods that gets included in the base class.
|
||||
#
|
||||
module InstanceMethods
|
||||
# When +event+ is a Symbol, #hook will generate a hook name
|
||||
# by concatenating the method name from where #hook is called
|
||||
|
||||
# When event is a Symbol, {#hook} will generate a hook name
|
||||
# by concatenating the method name from where {#hook} is called
|
||||
# with the given Symbol.
|
||||
# Example:
|
||||
#
|
||||
# @example Add a hook with a Symbol
|
||||
#
|
||||
# def run_all
|
||||
# hook :foo
|
||||
# end
|
||||
# Here, when #run_all is called, #hook will notify callbacks
|
||||
#
|
||||
# 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
|
||||
# When event is a String, {#hook} will directly turn the String
|
||||
# into a Symbol.
|
||||
# Example:
|
||||
#
|
||||
# @example Add a hook with a String
|
||||
#
|
||||
# def run_all
|
||||
# hook "foo_bar"
|
||||
# end
|
||||
# Here, when #run_all is called, #hook will notify callbacks
|
||||
#
|
||||
# When {Guard::Guard#run_all} is called, {#hook} will notify callbacks
|
||||
# registered for the "foo_bar" event.
|
||||
#
|
||||
# +args+ parameter is passed as is to the callbacks registered
|
||||
# for the given 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
|
||||
calling_method = caller[0][/`([^']*)'/, 1]
|
||||
"#{ calling_method }_#{ event }"
|
||||
else
|
||||
event
|
||||
end.to_sym
|
||||
|
||||
UI.debug "Hook :#{hook_name} executed for #{self.class}"
|
||||
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|
|
||||
@ -53,19 +84,34 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# Checks if a callback has been registered.
|
||||
#
|
||||
# @param [Block] listener the listener to notify
|
||||
# @param [Guard::Guard] guard_class the Guard class to add the callback
|
||||
# @param [Symbol] event the event to look for
|
||||
#
|
||||
def has_callback?(listener, guard_class, event)
|
||||
callbacks[[guard_class, event]].include?(listener)
|
||||
end
|
||||
|
||||
# Notify a callback.
|
||||
#
|
||||
# @param [Guard::Guard] guard_class the Guard class to add the callback
|
||||
# @param [Symbol] event the event to trigger
|
||||
# @param [Array] args the arguments for the listener
|
||||
#
|
||||
def notify(guard_class, event, *args)
|
||||
callbacks[[guard_class, event]].each do |listener|
|
||||
listener.call(guard_class, event, *args)
|
||||
end
|
||||
end
|
||||
|
||||
# Reset all callbacks.
|
||||
#
|
||||
def reset_callbacks!
|
||||
@callbacks = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,39 +1,76 @@
|
||||
module Guard
|
||||
|
||||
# The interactor reads user input and triggers
|
||||
# specific action upon them unless its locked.
|
||||
#
|
||||
# Currently the following actions are implemented:
|
||||
#
|
||||
# - stop, quit, exit, s, q, e => Exit Guard
|
||||
# - reload, r, z => Reload Guard
|
||||
# - pause, p => Pause Guard
|
||||
# - Everything else => Run all
|
||||
#
|
||||
class Interactor
|
||||
|
||||
class LockException < Exception; end
|
||||
class UnlockException < Exception; end
|
||||
|
||||
attr_reader :locked
|
||||
|
||||
# Initialize the interactor in unlocked state.
|
||||
#
|
||||
def initialize
|
||||
@locked = false
|
||||
end
|
||||
|
||||
# Start the interactor in its own thread.
|
||||
#
|
||||
def start
|
||||
return if ENV["GUARD_ENV"] == 'test'
|
||||
Thread.new do
|
||||
|
||||
@thread = Thread.new do
|
||||
loop do
|
||||
if (entry = $stdin.gets) && !@locked
|
||||
entry.gsub! /\n/, ''
|
||||
case entry
|
||||
when 'stop', 'quit', 'exit', 's', 'q', 'e'
|
||||
::Guard.stop
|
||||
when 'reload', 'r', 'z'
|
||||
::Guard.reload
|
||||
when 'pause', 'p'
|
||||
::Guard.pause
|
||||
else
|
||||
::Guard.run_all
|
||||
begin
|
||||
if !@locked && (entry = $stdin.gets)
|
||||
entry.gsub! /\n/, ''
|
||||
case entry
|
||||
when 'stop', 'quit', 'exit', 's', 'q', 'e'
|
||||
::Guard.stop
|
||||
when 'reload', 'r', 'z'
|
||||
::Guard.reload
|
||||
when 'pause', 'p'
|
||||
::Guard.pause
|
||||
else
|
||||
::Guard.run_all
|
||||
end
|
||||
end
|
||||
rescue LockException
|
||||
lock
|
||||
rescue UnlockException
|
||||
unlock
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Lock the interactor.
|
||||
#
|
||||
def lock
|
||||
@locked = true
|
||||
if !@thread || @thread == Thread.current
|
||||
@locked = true
|
||||
else
|
||||
@thread.raise(LockException)
|
||||
end
|
||||
end
|
||||
|
||||
# Unlock the interactor.
|
||||
#
|
||||
def unlock
|
||||
@locked = false
|
||||
if !@thread || @thread == Thread.current
|
||||
@locked = false
|
||||
else
|
||||
@thread.raise(UnlockException)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -8,40 +8,63 @@ module Guard
|
||||
autoload :Windows, 'guard/listeners/windows'
|
||||
autoload :Polling, 'guard/listeners/polling'
|
||||
|
||||
# The Listener is the base class for all listener
|
||||
# implementations.
|
||||
#
|
||||
# @abstract
|
||||
#
|
||||
class Listener
|
||||
|
||||
DefaultIgnorePaths = %w[. .. .bundle .git log tmp vendor]
|
||||
# Default paths that gets ignored by the listener
|
||||
DEFAULT_IGNORE_PATHS = %w[. .. .bundle .git log tmp vendor]
|
||||
|
||||
attr_accessor :changed_files
|
||||
attr_reader :directory, :ignore_paths, :locked
|
||||
|
||||
def self.select_and_init(*a)
|
||||
# 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?
|
||||
Darwin.new(*a)
|
||||
Darwin.new(*args)
|
||||
elsif linux? && Linux.usable?
|
||||
Linux.new(*a)
|
||||
Linux.new(*args)
|
||||
elsif windows? && Windows.usable?
|
||||
Windows.new(*a)
|
||||
Windows.new(*args)
|
||||
else
|
||||
UI.info "Using polling (Please help us to support your system better than that.)"
|
||||
Polling.new(*a)
|
||||
UI.info 'Using polling (Please help us to support your system better than that).'
|
||||
Polling.new(*args)
|
||||
end
|
||||
end
|
||||
|
||||
# Initialize the listener.
|
||||
#
|
||||
# @param [String] directory the root directory to listen to
|
||||
# @param [Hash] options the listener options
|
||||
# @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 = {}
|
||||
@sha1_checksums_hash = { }
|
||||
@relativize_paths = options.fetch(:relativize_paths, true)
|
||||
@changed_files = []
|
||||
@locked = false
|
||||
@ignore_paths = DefaultIgnorePaths
|
||||
@ignore_paths |= options[:ignore_paths] if options[:ignore_paths]
|
||||
@ignore_paths = DEFAULT_IGNORE_PATHS
|
||||
@ignore_paths |= options[:ignore_paths] if options[:ignore_paths]
|
||||
|
||||
update_last_event
|
||||
start_reactor
|
||||
end
|
||||
|
||||
# Start the listener thread.
|
||||
#
|
||||
def start_reactor
|
||||
return if ENV["GUARD_ENV"] == 'test'
|
||||
|
||||
Thread.new do
|
||||
loop do
|
||||
if @changed_files != [] && !@locked
|
||||
@ -55,33 +78,54 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# Start watching the root directory.
|
||||
#
|
||||
def start
|
||||
watch(@directory)
|
||||
end
|
||||
|
||||
# Stop listening for events.
|
||||
#
|
||||
def stop
|
||||
end
|
||||
|
||||
# Lock the listener to ignore change events.
|
||||
#
|
||||
def lock
|
||||
@locked = true
|
||||
end
|
||||
|
||||
# Unlock the listener to listen again to change events.
|
||||
#
|
||||
def unlock
|
||||
@locked = false
|
||||
end
|
||||
|
||||
# Clear the list of changed files.
|
||||
#
|
||||
def clear_changed_files
|
||||
@changed_files.clear
|
||||
end
|
||||
|
||||
# Store a listener callback.
|
||||
#
|
||||
# @param [Block] callback the callback to store
|
||||
#
|
||||
def on_change(&callback)
|
||||
@callback = callback
|
||||
end
|
||||
|
||||
# Updates the timestamp of the last event.
|
||||
#
|
||||
def update_last_event
|
||||
@last_event = Time.now
|
||||
end
|
||||
|
||||
# Get the modified files.
|
||||
#
|
||||
# @param [Array<String>] dirs the watched directories
|
||||
# @param [Hash] options the listener options
|
||||
#
|
||||
def modified_files(dirs, options = {})
|
||||
last_event = @last_event
|
||||
update_last_event
|
||||
@ -89,41 +133,64 @@ module Guard
|
||||
relativize_paths(files)
|
||||
end
|
||||
|
||||
def worker
|
||||
raise NotImplementedError, "should respond to #watch"
|
||||
end
|
||||
|
||||
# register a directory to watch. must be implemented by the subclasses
|
||||
# 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
|
||||
# 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}/}, '')
|
||||
path.gsub(%r{^#{ @directory }/}, '')
|
||||
end
|
||||
end
|
||||
|
||||
# Use paths relative to the current directory.
|
||||
#
|
||||
# @return [Boolean] whether to use relative or absolute paths
|
||||
#
|
||||
def relativize_paths?
|
||||
!!@relativize_paths
|
||||
end
|
||||
|
||||
# return children of the passed dirs that are not in the ignore_paths list
|
||||
# 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
|
||||
private
|
||||
|
||||
def potentially_modified_files(dirs, options={})
|
||||
# Gets a list of files that are in the modified directories.
|
||||
#
|
||||
# @param [Array<String>] dirs the list of directories
|
||||
# @param [Hash] options the options
|
||||
# @option options [Symbol] all whether to include all files
|
||||
#
|
||||
def potentially_modified_files(dirs, options = {})
|
||||
paths = exclude_ignored_paths(dirs)
|
||||
|
||||
if options[:all]
|
||||
@ -131,7 +198,7 @@ module Guard
|
||||
if File.file?(path)
|
||||
array << path
|
||||
else
|
||||
array += Dir.glob("#{path}/**/*", File::FNM_DOTMATCH).select { |p| File.file?(p) }
|
||||
array += Dir.glob("#{ path }/**/*", File::FNM_DOTMATCH).select { |p| File.file?(p) }
|
||||
end
|
||||
array
|
||||
end
|
||||
@ -140,9 +207,17 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# Test if the file content has changed.
|
||||
#
|
||||
# Depending on the filesystem, mtime/ctime is probably only precise to the second, so round
|
||||
# both values down to the second for the comparison.
|
||||
#
|
||||
# ctime is used only on == comparison to always catches Rails 3.1 Assets pipelined on Mac OSX
|
||||
#
|
||||
# @param [String] path the file path
|
||||
# @param [Time] last_event the time of the last event
|
||||
# @return [Boolean] Whether the file content has changed or not.
|
||||
#
|
||||
def file_modified?(path, last_event)
|
||||
ctime = File.ctime(path).to_i
|
||||
mtime = File.mtime(path).to_i
|
||||
@ -158,6 +233,12 @@ module Guard
|
||||
false
|
||||
end
|
||||
|
||||
# Tests if the file content has been modified by
|
||||
# comparing the SHA1 checksum.
|
||||
#
|
||||
# @param [String] path the file path
|
||||
# @param [String] sha1_checksum the checksum of the file
|
||||
#
|
||||
def file_content_modified?(path, sha1_checksum)
|
||||
if @sha1_checksums_hash[path] != sha1_checksum
|
||||
set_sha1_checksums_hash(path, sha1_checksum)
|
||||
@ -167,22 +248,44 @@ module Guard
|
||||
end
|
||||
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
|
||||
|
||||
# Calculates the SHA1 checksum of a file.
|
||||
#
|
||||
# @param [String] path the path to the file
|
||||
# @return [String] the SHA1 checksum
|
||||
#
|
||||
def sha1_checksum(path)
|
||||
Digest::SHA1.file(path).to_s
|
||||
end
|
||||
|
||||
# Test if the OS is Mac OS X.
|
||||
#
|
||||
# @return [Boolean] Whether the OS is Mac OS X
|
||||
#
|
||||
def self.mac?
|
||||
RbConfig::CONFIG['target_os'] =~ /darwin/i
|
||||
end
|
||||
|
||||
# Test if the OS is Linux.
|
||||
#
|
||||
# @return [Boolean] Whether the OS is Linux
|
||||
#
|
||||
def self.linux?
|
||||
RbConfig::CONFIG['target_os'] =~ /linux/i
|
||||
end
|
||||
|
||||
# Test if the OS is Windows.
|
||||
#
|
||||
# @return [Boolean] Whether the OS is Windows
|
||||
#
|
||||
def self.windows?
|
||||
RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
||||
end
|
||||
|
@ -1,41 +1,60 @@
|
||||
module Guard
|
||||
|
||||
# Listener implementation for Mac OS X `FSEvents`.
|
||||
#
|
||||
class Darwin < Listener
|
||||
|
||||
# Initialize the Listener.
|
||||
#
|
||||
def initialize(*)
|
||||
super
|
||||
@fsevent = FSEvent.new
|
||||
end
|
||||
|
||||
def worker
|
||||
@fsevent
|
||||
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-fsevent'
|
||||
if !defined?(FSEvent::VERSION) || (defined?(Gem::Version) &&
|
||||
Gem::Version.new(FSEvent::VERSION) < Gem::Version.new('0.4.0'))
|
||||
UI.info "Please update rb-fsevent (>= 0.4.0)"
|
||||
UI.info 'Please update rb-fsevent (>= 0.4.0)'
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
rescue LoadError
|
||||
UI.info "Please install rb-fsevent gem for Mac OSX FSEvents support"
|
||||
UI.info 'Please install rb-fsevent gem for Mac OSX FSEvents support'
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
# Get the listener worker.
|
||||
#
|
||||
def worker
|
||||
@fsevent
|
||||
end
|
||||
|
||||
# Watch the given directory for file changes.
|
||||
#
|
||||
# @param [String] directory the directory to watch
|
||||
#
|
||||
def watch(directory)
|
||||
worker.watch(directory) do |modified_dirs|
|
||||
files = modified_files(modified_dirs)
|
||||
|
@ -1,6 +1,11 @@
|
||||
module Guard
|
||||
|
||||
# Listener implementation for Linux `inotify`.
|
||||
#
|
||||
class Linux < Listener
|
||||
|
||||
# Initialize the Listener.
|
||||
#
|
||||
def initialize(*)
|
||||
super
|
||||
@inotify = INotify::Notifier.new
|
||||
@ -8,44 +13,53 @@ module Guard
|
||||
@latency = 0.5
|
||||
end
|
||||
|
||||
# Start the listener.
|
||||
#
|
||||
def start
|
||||
@stop = false
|
||||
super
|
||||
watch_change unless watch_change?
|
||||
end
|
||||
|
||||
# Stop the listener.
|
||||
#
|
||||
def stop
|
||||
super
|
||||
@stop = true
|
||||
sleep(@latency)
|
||||
end
|
||||
|
||||
# Check if the listener is usable on the current OS.
|
||||
#
|
||||
# @return [Boolean] whether usable or not
|
||||
#
|
||||
def self.usable?
|
||||
require 'rb-inotify'
|
||||
if !defined?(INotify::VERSION) || (defined?(Gem::Version) &&
|
||||
Gem::Version.new(INotify::VERSION.join('.')) < Gem::Version.new('0.8.5'))
|
||||
UI.info "Please update rb-inotify (>= 0.8.5)"
|
||||
UI.info 'Please update rb-inotify (>= 0.8.5)'
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
rescue LoadError
|
||||
UI.info "Please install rb-inotify gem for Linux inotify support"
|
||||
UI.info 'Please install rb-inotify gem for Linux inotify support'
|
||||
false
|
||||
end
|
||||
|
||||
def watch_change?
|
||||
!!@watch_change
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
# Get the listener worker.
|
||||
#
|
||||
def worker
|
||||
@inotify
|
||||
end
|
||||
|
||||
# Watch the given directory for file changes.
|
||||
#
|
||||
# @param [String] directory the directory to watch
|
||||
#
|
||||
def watch(directory)
|
||||
# The event selection is based on https://github.com/guard/guard/wiki/Analysis-of-inotify-events-for-different-editors
|
||||
worker.watch(directory, :recursive, :attrib, :create, :move_self, :close_write) do |event|
|
||||
unless event.name == "" # Event on root directory
|
||||
@files << event.absolute_name
|
||||
@ -54,6 +68,16 @@ module Guard
|
||||
rescue Interrupt
|
||||
end
|
||||
|
||||
# Test if inotify is watching for changes.
|
||||
#
|
||||
# @return [Boolean] whether inotify is active or not
|
||||
#
|
||||
def watch_change?
|
||||
!!@watch_change
|
||||
end
|
||||
|
||||
# Watch for file system changes.
|
||||
#
|
||||
def watch_change
|
||||
@watch_change = true
|
||||
until @stop
|
||||
|
@ -1,24 +1,46 @@
|
||||
module Guard
|
||||
|
||||
# Polling listener that works cross-platform and
|
||||
# has no dependencies. This is the listener that
|
||||
# uses the most CPU processing power and has higher
|
||||
# file IO that the other implementations.
|
||||
#
|
||||
class Polling < Listener
|
||||
|
||||
# Initialize the Listener.
|
||||
#
|
||||
def initialize(*)
|
||||
super
|
||||
@latency = 1.5
|
||||
end
|
||||
|
||||
# Start the listener.
|
||||
#
|
||||
def start
|
||||
@stop = false
|
||||
super
|
||||
watch_change
|
||||
end
|
||||
|
||||
# Stop the listener.
|
||||
#
|
||||
def stop
|
||||
super
|
||||
@stop = true
|
||||
end
|
||||
|
||||
private
|
||||
# Watch the given directory for file changes.
|
||||
#
|
||||
# @param [String] directory the directory to watch
|
||||
#
|
||||
def watch(directory)
|
||||
@existing = all_files
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Watch for file system changes.
|
||||
#
|
||||
def watch_change
|
||||
until @stop
|
||||
start = Time.now.to_f
|
||||
@ -29,9 +51,5 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
def watch(directory)
|
||||
@existing = all_files
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -1,35 +1,48 @@
|
||||
module Guard
|
||||
|
||||
# Listener implementation for Windows `fchange`.
|
||||
#
|
||||
class Windows < Listener
|
||||
|
||||
# Initialize the Listener.
|
||||
#
|
||||
def initialize(*)
|
||||
super
|
||||
@fchange = FChange::Notifier.new
|
||||
end
|
||||
|
||||
# Start the listener.
|
||||
#
|
||||
def start
|
||||
super
|
||||
worker.run
|
||||
end
|
||||
|
||||
# Stop the listener.
|
||||
#
|
||||
def stop
|
||||
super
|
||||
worker.stop
|
||||
end
|
||||
|
||||
# Check if the listener is usable on the current OS.
|
||||
#
|
||||
# @return [Boolean] whether usable or not
|
||||
#
|
||||
def self.usable?
|
||||
require 'rb-fchange'
|
||||
true
|
||||
rescue LoadError
|
||||
UI.info "Please install rb-fchange gem for Windows file events support"
|
||||
UI.info 'Please install rb-fchange gem for Windows file events support'
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def worker
|
||||
@fchange
|
||||
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)]
|
||||
@ -38,5 +51,11 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# Get the listener worker.
|
||||
#
|
||||
def worker
|
||||
@fchange
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -3,13 +3,29 @@ require 'pathname'
|
||||
require 'guard/ui'
|
||||
|
||||
module Guard
|
||||
|
||||
# The notifier class handles cross-platform system notifications that supports:
|
||||
#
|
||||
# - Growl on Mac OS X
|
||||
# - Libnotify on Linux
|
||||
# - Notifu on Windows
|
||||
#
|
||||
module Notifier
|
||||
|
||||
# Application name as shown in the specific notification settings
|
||||
APPLICATION_NAME = "Guard"
|
||||
|
||||
# Turn notifications off.
|
||||
#
|
||||
def self.turn_off
|
||||
ENV["GUARD_NOTIFY"] = 'false'
|
||||
end
|
||||
|
||||
# Turn notifications on. This tries to load the platform
|
||||
# specific notification library.
|
||||
#
|
||||
# @return [Boolean] whether the notification could be enabled.
|
||||
#
|
||||
def self.turn_on
|
||||
ENV["GUARD_NOTIFY"] = 'true'
|
||||
case RbConfig::CONFIG['target_os']
|
||||
@ -22,6 +38,15 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# Show a message with the system notification.
|
||||
#
|
||||
# @see .image_path
|
||||
#
|
||||
# @param [String] the message to show
|
||||
# @param [Hash] options the notification options
|
||||
# @option options [Symbol, String] image the image symbol or path to an image
|
||||
# @option options [String] title the notification title
|
||||
#
|
||||
def self.notify(message, options = {})
|
||||
if enabled?
|
||||
image = options.delete(:image) || :success
|
||||
@ -38,12 +63,23 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# Test if the notifications are enabled and available.
|
||||
#
|
||||
# @return [Boolean] whether the notifications are available
|
||||
#
|
||||
def self.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 self.notify_mac(title, message, image, options)
|
||||
require_growl # need for guard-rspec formatter that is called out of guard scope
|
||||
|
||||
@ -61,18 +97,43 @@ module Guard
|
||||
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 self.notify_linux(title, message, image, options)
|
||||
require_libnotify # need for guard-rspec formatter that is called out of guard scope
|
||||
default_options = { :body => message, :summary => title, :icon_path => image_path(image), :transient => true }
|
||||
Libnotify.show default_options.merge(options) if enabled?
|
||||
end
|
||||
|
||||
# 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 self.notify_windows(title, message, image, options)
|
||||
require_rbnotifu # need for guard-rspec formatter that is called out of guard scope
|
||||
default_options = { :message => message, :title => title, :type => image_level(image), :time => 3 }
|
||||
Notifu.show default_options.merge(options) if enabled?
|
||||
end
|
||||
|
||||
# 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 self.image_path(image)
|
||||
images_path = Pathname.new(File.dirname(__FILE__)).join('../../images')
|
||||
case image
|
||||
@ -88,6 +149,11 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# The notification level type for the given image.
|
||||
#
|
||||
# @param [Symbol] image the image
|
||||
# @return [Symbol] the level
|
||||
#
|
||||
def self.image_level(image)
|
||||
case image
|
||||
when :failed
|
||||
@ -101,6 +167,9 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# Try to safely load growl and turns notifications
|
||||
# off on load failure.
|
||||
#
|
||||
def self.require_growl
|
||||
begin
|
||||
require 'growl_notify'
|
||||
@ -119,6 +188,9 @@ module Guard
|
||||
UI.info "Please install growl_notify or growl gem for Mac OS X notification support and add it to your Gemfile"
|
||||
end
|
||||
|
||||
# Try to safely load libnotify and turns notifications
|
||||
# off on load failure.
|
||||
#
|
||||
def self.require_libnotify
|
||||
require 'libnotify'
|
||||
rescue LoadError
|
||||
@ -126,11 +198,15 @@ module Guard
|
||||
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 self.require_rbnotifu
|
||||
require 'rb-notifu'
|
||||
rescue LoadError
|
||||
turn_off
|
||||
UI.info "Please install rb-notifu gem for Windows notification support and add it to your Gemfile"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
176
lib/guard/ui.rb
176
lib/guard/ui.rb
@ -1,88 +1,92 @@
|
||||
module Guard
|
||||
|
||||
# The UI class helps to format messages for the user.
|
||||
#
|
||||
module UI
|
||||
|
||||
ANSI_ESCAPE_BRIGHT = "1"
|
||||
|
||||
ANSI_ESCAPE_BLACK = "30"
|
||||
ANSI_ESCAPE_RED = "31"
|
||||
ANSI_ESCAPE_GREEN = "32"
|
||||
ANSI_ESCAPE_YELLOW = "33"
|
||||
ANSI_ESCAPE_BLUE = "34"
|
||||
ANSI_ESCAPE_MAGENTA = "35"
|
||||
ANSI_ESCAPE_CYAN = "36"
|
||||
ANSI_ESCAPE_WHITE = "37"
|
||||
|
||||
ANSI_ESCAPE_BGBLACK = "40"
|
||||
ANSI_ESCAPE_BGRED = "41"
|
||||
ANSI_ESCAPE_BGGREEN = "42"
|
||||
ANSI_ESCAPE_BGYELLOW = "43"
|
||||
ANSI_ESCAPE_BGBLUE = "44"
|
||||
ANSI_ESCAPE_BGMAGENTA = "45"
|
||||
ANSI_ESCAPE_BGCYAN = "46"
|
||||
ANSI_ESCAPE_BGWHITE = "47"
|
||||
|
||||
class << self
|
||||
|
||||
color_enabled = nil
|
||||
|
||||
def info(message, options = {})
|
||||
unless ENV["GUARD_ENV"] == "test"
|
||||
# Show an info message.
|
||||
#
|
||||
# @param [String] message the message to show
|
||||
# @param [Hash] options the options
|
||||
# @option options [Boolean] reset whether to clean the output before
|
||||
#
|
||||
def info(message, options = { })
|
||||
unless ENV['GUARD_ENV'] == 'test'
|
||||
reset_line if options[:reset]
|
||||
puts color(message) if message != ''
|
||||
end
|
||||
end
|
||||
|
||||
def error(message, options={})
|
||||
unless ENV["GUARD_ENV"] == "test"
|
||||
# Show a red error message that is prefixed with ERROR.
|
||||
#
|
||||
# @param [String] message the message to show
|
||||
# @param [Hash] options the options
|
||||
# @option options [Boolean] reset whether to clean the output before
|
||||
#
|
||||
def error(message, options = { })
|
||||
unless ENV['GUARD_ENV'] == 'test'
|
||||
reset_line if options[:reset]
|
||||
puts color('ERROR: ', :red) + message
|
||||
end
|
||||
end
|
||||
|
||||
def deprecation(message, options = {})
|
||||
unless ENV["GUARD_ENV"] == "test"
|
||||
# Show a red deprecation message that is prefixed with DEPRECATION.
|
||||
#
|
||||
# @param [String] message the message to show
|
||||
# @param [Hash] options the options
|
||||
# @option options [Boolean] reset whether to clean the output before
|
||||
#
|
||||
def deprecation(message, options = { })
|
||||
unless ENV['GUARD_ENV'] == 'test'
|
||||
reset_line if options[:reset]
|
||||
puts color('DEPRECATION: ', :red) + message
|
||||
end
|
||||
end
|
||||
|
||||
def debug(message, options={})
|
||||
unless ENV["GUARD_ENV"] == "test"
|
||||
# Show a debug message that is prefixed with DEBUG and a timestamp.
|
||||
#
|
||||
# @param [String] message the message to show
|
||||
# @param [Hash] options the options
|
||||
# @option options [Boolean] reset whether to clean the output before
|
||||
#
|
||||
def debug(message, options = { })
|
||||
unless ENV['GUARD_ENV'] == 'test'
|
||||
reset_line if options[:reset]
|
||||
puts color("DEBUG (#{Time.now.strftime('%T')}): ", :yellow) + message if ::Guard.options && ::Guard.options[:debug]
|
||||
end
|
||||
end
|
||||
|
||||
# Reset a line.
|
||||
#
|
||||
def reset_line
|
||||
print(color_enabled? ? "\r\e[0m" : "\r\n")
|
||||
end
|
||||
|
||||
# Clear the output.
|
||||
#
|
||||
def clear
|
||||
system("clear;")
|
||||
system('clear;')
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
# Reset a color sequence.
|
||||
#
|
||||
# @deprecated
|
||||
# @param [String] text the text
|
||||
#
|
||||
def reset_color(text)
|
||||
deprecation('UI.reset_color(text) is deprecated, please use color(text, "") instead.')
|
||||
color(text, "")
|
||||
end
|
||||
|
||||
def color(text, *color_options)
|
||||
color_code = ""
|
||||
color_options.each do |color_option|
|
||||
color_option = color_option.to_s
|
||||
if color_option != ""
|
||||
if !(color_option =~ /\d+/)
|
||||
color_option = const_get("ANSI_ESCAPE_#{color_option.upcase}")
|
||||
end
|
||||
color_code += ";" + color_option
|
||||
end
|
||||
end
|
||||
color_enabled? ? "\e[0#{color_code}m#{text}\e[0m" : text
|
||||
deprecation('UI.reset_color(text) is deprecated, please use color(text, ' ') instead.')
|
||||
color(text, '')
|
||||
end
|
||||
|
||||
# Checks if color output can be enabled.
|
||||
#
|
||||
# @return [Boolean] whether color is enabled or not
|
||||
#
|
||||
def color_enabled?
|
||||
if @color_enabled.nil?
|
||||
if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
||||
@ -102,9 +106,87 @@ module Guard
|
||||
@color_enabled = true
|
||||
end
|
||||
end
|
||||
|
||||
@color_enabled
|
||||
end
|
||||
|
||||
# Colorizes a text message. See the constant in the UI class for possible
|
||||
# color_options parameters. You can pass optionally :bright, a foreground
|
||||
# color and a background color.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# color('Hello World', :red, :bright)
|
||||
#
|
||||
# @param [String] the text to colorize
|
||||
# @param [Array] color_options the color options
|
||||
#
|
||||
def color(text, *color_options)
|
||||
color_code = ''
|
||||
color_options.each do |color_option|
|
||||
color_option = color_option.to_s
|
||||
if color_option != ''
|
||||
if !(color_option =~ /\d+/)
|
||||
color_option = const_get("ANSI_ESCAPE_#{ color_option.upcase }")
|
||||
end
|
||||
color_code += ';' + color_option
|
||||
end
|
||||
end
|
||||
color_enabled? ? "\e[0#{ color_code }m#{ text }\e[0m" : text
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Brighten the color
|
||||
ANSI_ESCAPE_BRIGHT = '1'
|
||||
|
||||
# Black foreground color
|
||||
ANSI_ESCAPE_BLACK = '30'
|
||||
|
||||
# Red foreground color
|
||||
ANSI_ESCAPE_RED = '31'
|
||||
|
||||
# Green foreground color
|
||||
ANSI_ESCAPE_GREEN = '32'
|
||||
|
||||
# Yellow foreground color
|
||||
ANSI_ESCAPE_YELLOW = '33'
|
||||
|
||||
# Blue foreground color
|
||||
ANSI_ESCAPE_BLUE = '34'
|
||||
|
||||
# Magenta foreground color
|
||||
ANSI_ESCAPE_MAGENTA = '35'
|
||||
|
||||
# Cyan foreground color
|
||||
ANSI_ESCAPE_CYAN = '36'
|
||||
|
||||
# White foreground color
|
||||
ANSI_ESCAPE_WHITE = '37'
|
||||
|
||||
# Black background color
|
||||
ANSI_ESCAPE_BGBLACK = '40'
|
||||
|
||||
# Red background color
|
||||
ANSI_ESCAPE_BGRED = '41'
|
||||
|
||||
# Green background color
|
||||
ANSI_ESCAPE_BGGREEN = '42'
|
||||
|
||||
# Yellow background color
|
||||
ANSI_ESCAPE_BGYELLOW = '43'
|
||||
|
||||
# Blue background color
|
||||
ANSI_ESCAPE_BGBLUE = '44'
|
||||
|
||||
# Magenta background color
|
||||
ANSI_ESCAPE_BGMAGENTA = '45'
|
||||
|
||||
# Cyan background color
|
||||
ANSI_ESCAPE_BGCYAN = '46'
|
||||
|
||||
# White background color
|
||||
ANSI_ESCAPE_BGWHITE = '47'
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,6 @@
|
||||
module Guard
|
||||
VERSION = "0.7.0" unless defined? Guard::VERSION
|
||||
unless defined? Guard::VERSION
|
||||
# The current gem version of Guard
|
||||
VERSION = '0.7.0'
|
||||
end
|
||||
end
|
||||
|
@ -1,7 +1,18 @@
|
||||
module Guard
|
||||
|
||||
# The watcher defines a RegExp that will be matched against file system modifications.
|
||||
# When a watcher matches a change, an optional action block is executed to enable
|
||||
# processing the file system change result.
|
||||
#
|
||||
class Watcher
|
||||
|
||||
attr_accessor :pattern, :action
|
||||
|
||||
# Initialize a file watcher.
|
||||
#
|
||||
# @param [String, Regexp] pattern the pattern to be watched by the guard
|
||||
# @param [Block] action the action to execute before passing the result to the Guard
|
||||
#
|
||||
def initialize(pattern, action = nil)
|
||||
@pattern, @action = pattern, action
|
||||
@@warning_printed ||= false
|
||||
@ -10,14 +21,25 @@ module Guard
|
||||
if @pattern.is_a?(String) && @pattern =~ /(^(\^))|(>?(\\\.)|(\.\*))|(\(.*\))|(\[.*\])|(\$$)/
|
||||
unless @@warning_printed
|
||||
UI.info "*"*20 + "\nDEPRECATION WARNING!\n" + "*"*20
|
||||
UI.info "You have strings in your Guardfile's watch patterns that seem to represent regexps.\nGuard matchs String with == and Regexp with Regexp#match.\nYou should either use plain String (without Regexp special characters) or real Regexp.\n"
|
||||
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"
|
||||
|
||||
UI.info "\"#{@pattern}\" has been converted to #{ Regexp.new(@pattern).inspect }\n"
|
||||
@pattern = Regexp.new(@pattern)
|
||||
end
|
||||
end
|
||||
|
||||
# Finds the files that matches a Guard.
|
||||
#
|
||||
# @param [Guard::Guard] guard the guard which watchers are used
|
||||
# @param [Array<String>] files the changed files
|
||||
# @return [Array<String>] the matched files
|
||||
#
|
||||
def self.match_files(guard, files)
|
||||
guard.watchers.inject([]) do |paths, watcher|
|
||||
files.each do |file|
|
||||
@ -30,10 +52,17 @@ module Guard
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
paths.flatten.map { |p| p.to_s }
|
||||
end
|
||||
end
|
||||
|
||||
# Test if a file would be matched by any of the Guards watchers.
|
||||
#
|
||||
# @param [Array<Guard::Guard>] guards the guards to use the watchers from
|
||||
# @param [Array<String>] files the files to test
|
||||
# @return [Boolean] Whether a file matches
|
||||
#
|
||||
def self.match_files?(guards, files)
|
||||
guards.any? do |guard|
|
||||
guard.watchers.any? do |watcher|
|
||||
@ -42,6 +71,11 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# Test the watchers pattern against a file.
|
||||
#
|
||||
# @param [String] file the file to test
|
||||
# @return [Boolean] whether the given file is matched
|
||||
#
|
||||
def match_file?(file)
|
||||
if @pattern.is_a?(Regexp)
|
||||
file.match(@pattern)
|
||||
@ -50,15 +84,25 @@ module Guard
|
||||
end
|
||||
end
|
||||
|
||||
# Test if any of the files is the Guardfile.
|
||||
#
|
||||
# @param [Array<String>] the files to test
|
||||
# @return [Boolean] whether one of these files is the Guardfile
|
||||
#
|
||||
def self.match_guardfile?(files)
|
||||
files.any? { |file| "#{Dir.pwd}/#{file}" == Dsl.guardfile_path }
|
||||
files.any? { |file| "#{ Dir.pwd }/#{ file }" == Dsl.guardfile_path }
|
||||
end
|
||||
|
||||
# Executes a watcher action.
|
||||
#
|
||||
# @param [String, MatchData] the matched path or the match from the Regex
|
||||
# @return [String] the final paths
|
||||
#
|
||||
def call_action(matches)
|
||||
begin
|
||||
@action.arity > 0 ? @action.call(matches) : @action.call
|
||||
rescue Exception => e
|
||||
UI.error "Problem with watch action!\n#{e.message}\n\n#{e.backtrace.join("\n")}"
|
||||
UI.error "Problem with watch action!\n#{ e.message }\n\n#{ e.backtrace.join("\n") }"
|
||||
end
|
||||
end
|
||||
|
||||
|
188
man/guard
188
man/guard
@ -1,188 +0,0 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "GUARD" "" "September 2011" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBguard\fR
|
||||
.
|
||||
.P
|
||||
\fI!DOCTYPE html\fR
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
<a href="#NAME">NAME</a>
|
||||
<a href="#NAME">NAME</a>
|
||||
<a href="#NAME">NAME</a>
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
<li class=\'tl\'>guard</li>
|
||||
<li class=\'tc\'></li>
|
||||
<li class=\'tr\'>guard</li>
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
\fBguard\fR
|
||||
.
|
||||
.P
|
||||
\fI!DOCTYPE html\fR
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
<a href="#NAME">NAME</a>
|
||||
<a href="#NAME">NAME</a>
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
<li class=\'tl\'>guard</li>
|
||||
<li class=\'tc\'></li>
|
||||
<li class=\'tr\'>guard</li>
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
\fBguard\fR
|
||||
.
|
||||
.P
|
||||
\fI!DOCTYPE html\fR
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
<a href="#NAME">NAME</a>
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
<li class=\'tl\'>guard</li>
|
||||
<li class=\'tc\'></li>
|
||||
<li class=\'tr\'>guard</li>
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
\fBguard\fR
|
||||
.
|
||||
.P
|
||||
\&\.\e" generated with Ronn/v0\.7\.3 \.\e" http://github\.com/rtomayko/ronn/tree/0\.7\.3 \. \.TH "GUARD" "1" "August 2011" "" "" \. \.SH "NAME" \efBguard\efR \- Guard keeps an eye on your file modifications\. \. \.SH "DESCRIPTION" Guard is a command line tool that easily handle events on files modifications\. \. \.SH "SYNOPSIS" \efBguard \fICOMMAND\fR \fIOPTIONS\fR\efR \. \.SH "COMMANDS" \. \.SS "start" Starts Guard\. This is the default command if none is provided\. \. \.P The following options are available: \. \.P \efB\-c\efR, \efB\-\-clear\efR Clears the Shell after each change\. \. \.P \efB\-n\efR, \efB\-\-notify\efR \efIFLAG\efR Disable notifications (Growl or Libnotify depending on your system)\. Notifications can be disabled globally by setting a GUARD_NOTIFY environment variable to false\. FLAG can be \efBtrue\efR/\efBfalse\efR or \efBt\efR/\efBf\efR\. \. \.P \efB\-d\efR, \efB\-\-debug\efR Runs Guard in debug mode\. \. \.P \efB\-g\efR, \efB\-\-group\efR \efIGROUP1\efR \efIGROUP2\efR\.\.\. Runs only the groups specified by GROUP1, GROUP2 etc\. Groups name should be separated by spaces\. Guards that don\e\'t belong to a group are considered global and are always run\. \. \.P \efB\-w\efR, \efB\-\-watchdir\efR \efIPATH\efR \. \.P Tells Guard to watch PATH instead of \efB\./\efR\. \. \.P \efB\-G\efR, \efB\-\-guardfile\efR \efIFILE\efR Tells Guard to use FILE as its Guardfile instead of \efB\./Guardfile\efR or \efB~/\.Guardfile\efR\. \. \.SS "init [GUARD]" If no Guardfile is present in the current directory, creates an empty Guardfile\. \. \.P If \efIGUARD\efR is present, add its default Guardfile configuration to the current Guardfile\. Note that \efIGUARD\efR is the guard\e\'s name without the \efBguard\-\efR prefix\. For instance to initialize guard\-rspec, run \efBguard init rspec\efR\. \. \.SS "list" Lists guards that can be used with the \efBinit\efR command\. \. \.SS "\-T, show" List defined groups and guards for the current Guardfile\. \. \.SS "\-h, help [COMMAND]" List all of Guard\e\'s available commands\. \. \.P If \efICOMMAND\efR is given, displays a specific help for \efITASK\efR\. \. \.SH "EXAMPLES" Initialize Guard and a specific guard at the same time: \. \.P \efB[bundle exec] guard init [rspec]\efR \. \.P Run Guard: \. \.P \efB[bundle exec] guard [start] \-\-watchdir ~/dev \-\-guardfile ~/env/Guardfile \-\-clear \-\-group backend frontend \-\-notify false \-\-debug\efR \. \.P or in a more concise way: \. \.P \efB[bundle exec] guard [start] \-w ~/dev \-G ~/env/Guardfile \-c \-g backend frontend \-n f \-d\efR \. \.SH "AUTHORS / CONTRIBUTORS" Thibaud Guillaume\-Gentil is the main author\. \. \.P A list of contributors based on all commits can be found here: https://github\.com/guard/guard/contributors \. \.P For an exhaustive list of all the contributors, please see the CHANGELOG: https://github\.com/guard/guard/blob/master/CHANGELOG\.md \. \.P This manual has been written by Remy Coutable\. \. \.SH "WWW" https://github\.com/guard/guard
|
||||
.
|
||||
.P
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
<li class=\'tl\'></li>
|
||||
<li class=\'tc\'>September 2011</li>
|
||||
<li class=\'tr\'>guard</li>
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
<li class=\'tl\'></li>
|
||||
<li class=\'tc\'>September 2011</li>
|
||||
<li class=\'tr\'>guard</li>
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
<li class=\'tl\'></li>
|
||||
<li class=\'tc\'>September 2011</li>
|
||||
<li class=\'tr\'>guard</li>
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
.
|
||||
.P
|
||||
|
@ -41,7 +41,7 @@ Tells Guard to watch PATH instead of \fB\./\fR\.
|
||||
.P
|
||||
\fB\-G\fR, \fB\-\-guardfile\fR \fIFILE\fR Tells Guard to use FILE as its Guardfile instead of \fB\./Guardfile\fR or \fB~/\.Guardfile\fR\.
|
||||
.
|
||||
.SS "init <a href=\"guard\.html\">GUARD</a>"
|
||||
.SS "init [GUARD]"
|
||||
If no Guardfile is present in the current directory, creates an empty Guardfile\.
|
||||
.
|
||||
.P
|
||||
|
@ -112,7 +112,7 @@
|
||||
<p><code>-G</code>, <code>--guardfile</code> <var>FILE</var>
|
||||
Tells Guard to use FILE as its Guardfile instead of <code>./Guardfile</code> or <code>~/.Guardfile</code>.</p>
|
||||
|
||||
<h3 id="init-GUARD">init <a href="guard.html">GUARD</a></h3>
|
||||
<h3 id="init-GUARD-">init [GUARD]</h3>
|
||||
|
||||
<p>If no Guardfile is present in the current directory, creates an empty Guardfile.</p>
|
||||
|
||||
|
192
man/guard.html
192
man/guard.html
@ -56,7 +56,6 @@
|
||||
<a href="#NAME">NAME</a>
|
||||
<a href="#NAME">NAME</a>
|
||||
<a href="#NAME">NAME</a>
|
||||
<a href="#NAME">NAME</a>
|
||||
</div>
|
||||
|
||||
<ol class='man-decor man-head man head'>
|
||||
@ -130,7 +129,6 @@
|
||||
|
||||
<pre><code><a href="#NAME">NAME</a>
|
||||
<a href="#NAME">NAME</a>
|
||||
<a href="#NAME">NAME</a>
|
||||
</code></pre>
|
||||
|
||||
<p> </p>
|
||||
@ -151,125 +149,15 @@
|
||||
</p>
|
||||
|
||||
|
||||
<p><var>!DOCTYPE html</var>
|
||||
|
||||
|
||||
<p><p<var>!DOCTYPE html</var>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" value="text/html;charset=utf8" />
|
||||
<meta value="text/html;charset=utf8" http-equiv="content-type" />
|
||||
<meta name="generator" value="Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)" />
|
||||
<title>guard</title>
|
||||
<style type="text/css" media="all">
|
||||
/<em> style: man </em>/
|
||||
body#manpage {margin:0}
|
||||
.mp {max-width:100ex;padding:0 9ex 1ex 4ex}
|
||||
.mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
|
||||
.mp h2 {margin:10px 0 0 0}
|
||||
.mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
|
||||
.mp h3 {margin:0 0 0 4ex}
|
||||
.mp dt {margin:0;clear:left}
|
||||
.mp dt.flush {float:left;width:8ex}
|
||||
.mp dd {margin:0 0 0 9ex}
|
||||
.mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
|
||||
.mp pre {margin-bottom:20px}
|
||||
.mp pre+h2,.mp pre+h3 {margin-top:22px}
|
||||
.mp h2+pre,.mp h3+pre {margin-top:5px}
|
||||
.mp img {display:block;margin:auto}
|
||||
.mp h1.man-title {display:none}
|
||||
.mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
|
||||
.mp h2 {font-size:16px;line-height:1.25}
|
||||
.mp h1 {font-size:20px;line-height:2}
|
||||
.mp {text-align:justify;background:#fff}
|
||||
.mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
|
||||
.mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
|
||||
.mp u {text-decoration:underline}
|
||||
.mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
|
||||
.mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
|
||||
.mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
|
||||
.mp b.man-ref {font-weight:normal;color:#434241}
|
||||
.mp pre {padding:0 4ex}
|
||||
.mp pre code {font-weight:normal;color:#434241}
|
||||
.mp h2+pre,h3+pre {padding-left:0}
|
||||
ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
|
||||
ol.man-decor {width:100%}
|
||||
ol.man-decor li.tl {text-align:left}
|
||||
ol.man-decor li.tc {text-align:center;letter-spacing:4px}
|
||||
ol.man-decor li.tr {text-align:right;float:right}
|
||||
</style>
|
||||
</head></html></p>
|
||||
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
The following styles are deprecated and will be removed at some point:
|
||||
div#man, div#man ol.man, div#man ol.head, div#man ol.man.
|
||||
|
||||
The .man-page, .man-decor, .man-head, .man-foot, .man-title, and
|
||||
.man-navigation should be used instead.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
<p><body id="manpage">
|
||||
<div class="mp" id="man" /></body></p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p> <div class="man-navigation" style="display:none" /></p>
|
||||
|
||||
|
||||
|
||||
|
||||
<pre><code><a href="#NAME">NAME</a>
|
||||
<a href="#NAME">NAME</a>
|
||||
</code></pre>
|
||||
|
||||
|
||||
|
||||
|
||||
<p> </p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p> <ol class="man-decor man-head man head" /></p>
|
||||
|
||||
|
||||
|
||||
|
||||
<pre><code><li class='tl'>guard</li>
|
||||
<li class='tc'></li>
|
||||
<li class='tr'>guard</li>
|
||||
</code></pre>
|
||||
|
||||
|
||||
|
||||
|
||||
<p> </p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p> <h2 id="NAME">NAME</h2></p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p class="man-name">
|
||||
<code>guard</code>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p><var>!DOCTYPE html</var>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" value="text/html;charset=utf8" />
|
||||
<meta name="generator" value="Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)" />
|
||||
<title>guard</title>
|
||||
<style type="text/css" media="all">
|
||||
<style media="all" type="text/css">
|
||||
/<em> style: man </em>/
|
||||
body#manpage {margin:0}
|
||||
.mp {max-width:100ex;padding:0 9ex 1ex 4ex}
|
||||
@ -374,6 +262,11 @@
|
||||
|
||||
|
||||
|
||||
<blockquote><blockquote><blockquote><blockquote><blockquote><blockquote><blockquote><p>master</p></blockquote></blockquote></blockquote></blockquote></blockquote></blockquote></blockquote>
|
||||
|
||||
|
||||
|
||||
|
||||
<p>.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
@ -496,6 +389,71 @@ https://github.com/guard/guard</p>
|
||||
|
||||
|
||||
|
||||
<p><var><<<<<< HEAD</var></p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p><p <ol class="man-decor man-foot man foot" /></p>
|
||||
|
||||
|
||||
|
||||
|
||||
<pre><code><li class='tl'></li>
|
||||
<li class='tc'>September 2011</li>
|
||||
<li class='tr'>guard</li>
|
||||
</code></pre>
|
||||
|
||||
|
||||
|
||||
|
||||
<p> </p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p>=======</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<blockquote><blockquote><blockquote><blockquote><blockquote><blockquote><blockquote><p>master</p></blockquote></blockquote></blockquote></blockquote></blockquote></blockquote></blockquote>
|
||||
|
||||
|
||||
|
||||
|
||||
<p> <ol class="man-decor man-foot man foot" /></p>
|
||||
|
||||
|
||||
|
||||
|
||||
<pre><code><li class='tl'></li>
|
||||
<li class='tc'>September 2011</li>
|
||||
<li class='tr'>guard</li>
|
||||
</code></pre>
|
||||
|
||||
|
||||
|
||||
|
||||
<p> </p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p> <ol class="man-decor man-foot man foot" /></p>
|
||||
|
||||
|
||||
|
@ -235,17 +235,17 @@ describe Guard::Dsl do
|
||||
disable_user_config
|
||||
|
||||
it "evaluates only the specified string group" do
|
||||
::Guard.should_receive(:add_guard).with(:pow, [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :w })
|
||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :w })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => ['w'])
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:w])
|
||||
|
||||
::Guard.groups.should eql [{ :name => :default, :options => {} }, { :name => :w, :options => {} }]
|
||||
end
|
||||
|
||||
it "evaluates only the specified symbol group" do
|
||||
::Guard.should_receive(:add_guard).with(:pow, [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :w })
|
||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :w })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:w])
|
||||
|
||||
@ -253,10 +253,10 @@ describe Guard::Dsl do
|
||||
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 })
|
||||
::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 })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:x, :y])
|
||||
|
||||
@ -264,8 +264,8 @@ describe Guard::Dsl do
|
||||
end
|
||||
|
||||
it "evaluates always guard outside any group (even when a group is given)" do
|
||||
::Guard.should_receive(:add_guard).with(:pow, [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :w })
|
||||
::Guard.should_receive(:add_guard).with('pow', [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :w })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string, :group => [:w])
|
||||
|
||||
@ -273,11 +273,11 @@ describe Guard::Dsl do
|
||||
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 })
|
||||
::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 })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => valid_guardfile_string)
|
||||
|
||||
@ -290,31 +290,31 @@ describe Guard::Dsl do
|
||||
disable_user_config
|
||||
|
||||
it "loads a guard specified as a quoted string from the DSL" do
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :default })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => "guard 'test'")
|
||||
end
|
||||
|
||||
it "loads a guard specified as a double quoted string from the DSL" do
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :default })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => 'guard "test"')
|
||||
end
|
||||
|
||||
it "loads a guard specified as a symbol from the DSL" do
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :default })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => "guard :test")
|
||||
end
|
||||
|
||||
it "loads a guard specified as a symbol and called with parens from the DSL" do
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :group => :default })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => "guard(:test)")
|
||||
end
|
||||
|
||||
it "receives options when specified, from normal arg" do
|
||||
::Guard.should_receive(:add_guard).with(:test, [], [], { :opt_a => 1, :opt_b => 'fancy', :group => :default })
|
||||
::Guard.should_receive(:add_guard).with('test', [], [], { :opt_a => 1, :opt_b => 'fancy', :group => :default })
|
||||
|
||||
subject.evaluate_guardfile(:guardfile_contents => "guard 'test', :opt_a => 1, :opt_b => 'fancy'")
|
||||
end
|
||||
@ -324,7 +324,7 @@ describe Guard::Dsl do
|
||||
disable_user_config
|
||||
|
||||
it "should receive watchers when specified" do
|
||||
::Guard.should_receive(:add_guard).with(:dummy, anything, anything, { :group => :default }) do |name, watchers, callbacks, options|
|
||||
::Guard.should_receive(:add_guard).with('dummy', anything, anything, { :group => :default }) do |name, watchers, callbacks, options|
|
||||
watchers.size.should == 2
|
||||
watchers[0].pattern.should == 'a'
|
||||
watchers[0].action.call.should == proc { 'b' }.call
|
||||
@ -347,7 +347,7 @@ describe Guard::Dsl do
|
||||
end
|
||||
end
|
||||
|
||||
::Guard.should_receive(:add_guard).with(:dummy, anything, anything, { :group => :default }) do |name, watchers, callbacks, options|
|
||||
::Guard.should_receive(:add_guard).with('dummy', anything, anything, { :group => :default }) do |name, watchers, callbacks, options|
|
||||
callbacks.should have(2).items
|
||||
callbacks[0][:events].should == :start_end
|
||||
callbacks[0][:listener].call(Guard::Dummy, :start_end, 'foo').should == "Guard::Dummy executed 'start_end' hook with foo!"
|
||||
@ -373,17 +373,17 @@ private
|
||||
"
|
||||
guard :pow
|
||||
|
||||
group 'w' do
|
||||
guard 'test'
|
||||
group :w do
|
||||
guard :test
|
||||
end
|
||||
|
||||
group :x, :halt_on_fail => true do
|
||||
guard 'rspec'
|
||||
guard :rspec
|
||||
guard :ronn
|
||||
end
|
||||
|
||||
group 'y' do
|
||||
guard 'less'
|
||||
group :y do
|
||||
guard :less
|
||||
end
|
||||
"
|
||||
end
|
||||
|
@ -4,20 +4,22 @@ describe Guard::Interactor do
|
||||
subject { Guard::Interactor.new }
|
||||
|
||||
describe "#initialize" do
|
||||
it "un-lock by default" do
|
||||
it "unlocks the interactor by default" do
|
||||
subject.locked.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe "#lock" do
|
||||
it "locks" do
|
||||
it "locks the interactor" do
|
||||
subject.start
|
||||
subject.lock
|
||||
subject.locked.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "#unlock" do
|
||||
it "unlocks" do
|
||||
it "unlocks the interactor" do
|
||||
subject.start
|
||||
subject.unlock
|
||||
subject.locked.should be_false
|
||||
end
|
||||
|
@ -172,7 +172,7 @@ describe Guard::Listener do
|
||||
|
||||
describe "#ignore_paths" do
|
||||
it "defaults to the default ignore paths" do
|
||||
subject.new.ignore_paths.should == Guard::Listener::DefaultIgnorePaths
|
||||
subject.new.ignore_paths.should == Guard::Listener::DEFAULT_IGNORE_PATHS
|
||||
end
|
||||
|
||||
it "can be added to via :ignore_paths option" do
|
||||
|
Loading…
Reference in New Issue
Block a user