guard 0.3.4 → 0.4.0.rc
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +80 -32
- data/lib/guard.rb +8 -6
- data/lib/guard/dsl.rb +76 -14
- data/lib/guard/interactor.rb +41 -16
- data/lib/guard/listener.rb +88 -76
- data/lib/guard/listeners/darwin.rb +17 -9
- data/lib/guard/listeners/linux.rb +19 -15
- data/lib/guard/listeners/polling.rb +10 -8
- data/lib/guard/listeners/windows.rb +44 -0
- data/lib/guard/notifier.rb +55 -24
- data/lib/guard/ui.rb +26 -2
- data/lib/guard/version.rb +1 -1
- data/lib/guard/watcher.rb +1 -1
- metadata +9 -10
data/README.markdown
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Guard
|
1
|
+
Guard [![Build Status](http://travis-ci.org/guard/guard.png)](http://travis-ci.org/guard/guard)
|
2
2
|
=====
|
3
3
|
|
4
4
|
Guard is a command line tool that easily handle events on files modifications.
|
@@ -8,20 +8,23 @@ Features
|
|
8
8
|
|
9
9
|
* [FSEvent](http://en.wikipedia.org/wiki/FSEvents) support on Mac OS X 10.5+ (without RubyCocoa!, [rb-fsevent gem, >= 0.3.5](https://rubygems.org/gems/rb-fsevent) required).
|
10
10
|
* [Inotify](http://en.wikipedia.org/wiki/Inotify) support on Linux ([rb-inotify gem, >= 0.5.1](https://rubygems.org/gems/rb-inotify) required).
|
11
|
+
* [Directory Change Notification](http://msdn.microsoft.com/en-us/library/aa365261\(VS.85\).aspx) support on Windows ([rb-fchange, >= 0.0.2](https://rubygems.org/gems/rb-fchange) required).
|
11
12
|
* Polling on the other operating systems (help us to support more OS).
|
12
13
|
* Automatic & Super fast (when polling is not used) files modifications detection (even new files are detected).
|
13
14
|
* Growl notifications ([growlnotify](http://growl.info/documentation/growlnotify.php) & [growl gem](https://rubygems.org/gems/growl) required).
|
14
15
|
* Libnotify notifications ([libnotify gem](https://rubygems.org/gems/libnotify) required).
|
15
|
-
* Tested on Ruby 1.8.
|
16
|
+
* Tested on Ruby 1.8.7, 1.9.2 && ree.
|
16
17
|
|
17
18
|
Install
|
18
19
|
-------
|
19
20
|
|
20
21
|
Install the gem:
|
21
22
|
|
22
|
-
|
23
|
+
``` bash
|
24
|
+
$ gem install guard
|
25
|
+
```
|
23
26
|
|
24
|
-
Add it to your Gemfile (inside the
|
27
|
+
Add it to your Gemfile (inside the `test` group):
|
25
28
|
|
26
29
|
``` ruby
|
27
30
|
gem 'guard'
|
@@ -29,7 +32,11 @@ gem 'guard'
|
|
29
32
|
|
30
33
|
Generate an empty Guardfile with:
|
31
34
|
|
32
|
-
|
35
|
+
``` bash
|
36
|
+
$ guard init
|
37
|
+
```
|
38
|
+
|
39
|
+
You may optionally place this Guardfile in your home directory to use it across multiple projects.
|
33
40
|
|
34
41
|
Add the guards you need to your Guardfile (see the existing guards below).
|
35
42
|
|
@@ -37,11 +44,15 @@ Add the guards you need to your Guardfile (see the existing guards below).
|
|
37
44
|
|
38
45
|
Install the rb-fsevent gem for [FSEvent](http://en.wikipedia.org/wiki/FSEvents) support:
|
39
46
|
|
40
|
-
|
47
|
+
``` bash
|
48
|
+
$ gem install rb-fsevent
|
49
|
+
```
|
41
50
|
|
42
51
|
Install the Growl gem if you want notification support:
|
43
52
|
|
44
|
-
|
53
|
+
``` bash
|
54
|
+
$ gem install growl
|
55
|
+
```
|
45
56
|
|
46
57
|
And add it to you Gemfile:
|
47
58
|
|
@@ -53,11 +64,15 @@ gem 'growl'
|
|
53
64
|
|
54
65
|
Install the rb-inotify gem for [inotify](http://en.wikipedia.org/wiki/Inotify) support:
|
55
66
|
|
56
|
-
|
67
|
+
``` bash
|
68
|
+
$ gem install rb-inotify
|
69
|
+
```
|
57
70
|
|
58
71
|
Install the Libnotify gem if you want notification support:
|
59
72
|
|
60
|
-
|
73
|
+
``` bash
|
74
|
+
$ gem install libnotify
|
75
|
+
```
|
61
76
|
|
62
77
|
And add it to you Gemfile:
|
63
78
|
|
@@ -65,47 +80,71 @@ And add it to you Gemfile:
|
|
65
80
|
gem 'libnotify'
|
66
81
|
```
|
67
82
|
|
83
|
+
### On Windows
|
84
|
+
|
85
|
+
Install the rb-fchange gem for [Directory Change Notification](http://msdn.microsoft.com/en-us/library/aa365261\(VS.85\).aspx) support:
|
86
|
+
|
87
|
+
``` bash
|
88
|
+
$ gem install rb-fchange
|
89
|
+
```
|
90
|
+
|
68
91
|
Usage
|
69
92
|
-----
|
70
93
|
|
71
94
|
Just launch Guard inside your Ruby / Rails project with:
|
72
95
|
|
73
|
-
|
96
|
+
``` bash
|
97
|
+
$ guard [start]
|
98
|
+
```
|
74
99
|
|
75
100
|
or if you use Bundler, to run the Guard executable specific to your bundle:
|
76
101
|
|
77
|
-
|
102
|
+
``` bash
|
103
|
+
$ bundle exec guard
|
104
|
+
```
|
105
|
+
|
106
|
+
Guard will look for a Guardfile in your current directory. If it does not find one, it will look in your home directory for one.
|
78
107
|
|
79
108
|
Command line options
|
80
109
|
--------------------
|
81
110
|
|
82
111
|
Shell can be cleared after each change with:
|
83
112
|
|
84
|
-
|
85
|
-
|
113
|
+
``` bash
|
114
|
+
$ guard --clear
|
115
|
+
$ guard -c # shortcut
|
116
|
+
```
|
86
117
|
|
87
118
|
Notifications (growl/libnotify) can be disabled with:
|
88
119
|
|
89
|
-
|
90
|
-
|
120
|
+
``` bash
|
121
|
+
$ guard --notify false
|
122
|
+
$ guard -n f # shortcut
|
123
|
+
```
|
124
|
+
|
125
|
+
Notifications can also be disabled by setting a `GUARD_NOTIFY` environment variable to `false`
|
91
126
|
|
92
|
-
The guards to start can be specified by group (see the Guardfile DSL below) specifying the
|
127
|
+
The guards to start can be specified by group (see the Guardfile DSL below) specifying the `--group` (or `-g`) option:
|
93
128
|
|
94
|
-
|
95
|
-
|
129
|
+
``` bash
|
130
|
+
$ guard --group group_name another_group_name
|
131
|
+
$ guard -g group_name another_group_name # shortcut
|
132
|
+
```
|
96
133
|
|
97
134
|
Options list is available with:
|
98
135
|
|
99
|
-
|
136
|
+
``` bash
|
137
|
+
$ guard help [TASK]
|
138
|
+
```
|
100
139
|
|
101
140
|
Signal handlers
|
102
141
|
---------------
|
103
142
|
|
104
143
|
Signal handlers are used to interact with Guard:
|
105
144
|
|
106
|
-
*
|
107
|
-
*
|
108
|
-
*
|
145
|
+
* `Ctrl-C` - Calls each guard's `stop` method, in the same order they are declared in the Guardfile, and then quits Guard itself.
|
146
|
+
* `Ctrl-\` - Calls each guard's `run_all` method, in the same order they are declared in the Guardfile.
|
147
|
+
* `Ctrl-Z` - Calls each guard's `reload` method, in the same order they are declared in the Guardfile.
|
109
148
|
|
110
149
|
Available Guards
|
111
150
|
----------------
|
@@ -114,7 +153,7 @@ Available Guards
|
|
114
153
|
|
115
154
|
### Add a guard to your Guardfile
|
116
155
|
|
117
|
-
Add it to your Gemfile (inside the
|
156
|
+
Add it to your Gemfile (inside the `test` group):
|
118
157
|
|
119
158
|
``` ruby
|
120
159
|
gem '<guard-name>'
|
@@ -122,21 +161,25 @@ gem '<guard-name>'
|
|
122
161
|
|
123
162
|
Insert default guard's definition to your Guardfile by running this command:
|
124
163
|
|
125
|
-
|
164
|
+
``` bash
|
165
|
+
$ guard init <guard-name>
|
166
|
+
```
|
126
167
|
|
127
168
|
You are good to go!
|
128
169
|
|
129
170
|
Guardfile DSL
|
130
171
|
-------------
|
131
172
|
|
132
|
-
The Guardfile DSL consists of just three simple methods:
|
173
|
+
The Guardfile DSL consists of just three simple methods: `guard`, `watch` & `group`.
|
133
174
|
|
134
175
|
Required:
|
135
|
-
|
136
|
-
* The
|
176
|
+
|
177
|
+
* The `guard` method allows you to add a guard with an optional hash of options.
|
178
|
+
* The `watch` method allows you to define which files are supervised by this guard. An optional block can be added to overwrite the paths sent to the `run_on_change` guard method or to launch any arbitrary command.
|
137
179
|
|
138
180
|
Optional:
|
139
|
-
|
181
|
+
|
182
|
+
* The `group` method allows you to group several guards together. Groups to be run can be specified with the Guard DSL option `--group` (or `-g`). This comes in handy especially when you have a huge Guardfile and want to focus your development on a certain part.
|
140
183
|
|
141
184
|
Example:
|
142
185
|
|
@@ -172,7 +215,7 @@ end
|
|
172
215
|
Create a new guard
|
173
216
|
------------------
|
174
217
|
|
175
|
-
Creating a new guard is very easy, just create a new gem (
|
218
|
+
Creating a new guard is very easy, just create a new gem (`bundle gem` if you use Bundler) with this basic structure:
|
176
219
|
|
177
220
|
lib/
|
178
221
|
guard/
|
@@ -181,7 +224,7 @@ Creating a new guard is very easy, just create a new gem (<tt>bundle gem</tt> if
|
|
181
224
|
Guardfile (needed for guard init <guard-name>)
|
182
225
|
guard-name.rb
|
183
226
|
|
184
|
-
|
227
|
+
`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. Example:
|
185
228
|
|
186
229
|
``` ruby
|
187
230
|
require 'guard'
|
@@ -219,7 +262,7 @@ module Guard
|
|
219
262
|
true
|
220
263
|
end
|
221
264
|
|
222
|
-
# Called on Ctrl
|
265
|
+
# Called on Ctrl-\ signal
|
223
266
|
# This method should be principally used for long action like running all specs/tests/...
|
224
267
|
def run_all
|
225
268
|
true
|
@@ -242,7 +285,7 @@ Alternatively, a new guard can be added inline to a Guardfile with this basic st
|
|
242
285
|
require 'guard/guard'
|
243
286
|
|
244
287
|
module ::Guard
|
245
|
-
class
|
288
|
+
class InlineGuard < ::Guard::Guard
|
246
289
|
def run_all
|
247
290
|
true
|
248
291
|
end
|
@@ -267,3 +310,8 @@ Author
|
|
267
310
|
------
|
268
311
|
|
269
312
|
[Thibaud Guillaume-Gentil](https://github.com/thibaudgg)
|
313
|
+
|
314
|
+
Contributors
|
315
|
+
------
|
316
|
+
|
317
|
+
https://github.com/guard/guard/contributors
|
data/lib/guard.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'bundler'
|
2
|
-
|
3
1
|
module Guard
|
4
2
|
|
5
3
|
autoload :UI, 'guard/ui'
|
@@ -18,7 +16,7 @@ module Guard
|
|
18
16
|
@listener = Listener.select_and_init
|
19
17
|
@guards = []
|
20
18
|
|
21
|
-
|
19
|
+
@options[:notify] && ENV["GUARD_NOTIFY"] != 'false' ? Notifier.turn_on : Notifier.turn_off
|
22
20
|
|
23
21
|
self
|
24
22
|
end
|
@@ -62,7 +60,7 @@ module Guard
|
|
62
60
|
guard.send(task_to_supervise, *args)
|
63
61
|
rescue Exception
|
64
62
|
UI.error("#{guard.class.name} guard failed to achieve its <#{task_to_supervise.to_s}> command: #{$!}")
|
65
|
-
|
63
|
+
guards.delete guard
|
66
64
|
UI.info("Guard #{guard.class.name} has just been fired")
|
67
65
|
return $!
|
68
66
|
end
|
@@ -84,7 +82,7 @@ module Guard
|
|
84
82
|
|
85
83
|
def get_guard_class(name)
|
86
84
|
try_to_load_gem name
|
87
|
-
self.const_get(self.constants.find{|klass_name| klass_name.to_s.downcase == name.downcase })
|
85
|
+
self.const_get(self.constants.find{ |klass_name| klass_name.to_s.downcase == name.to_s.downcase.gsub('-', '') })
|
88
86
|
rescue TypeError
|
89
87
|
UI.error "Could not find load find gem 'guard-#{name}' or find class Guard::#{name}"
|
90
88
|
end
|
@@ -95,7 +93,11 @@ module Guard
|
|
95
93
|
end
|
96
94
|
|
97
95
|
def locate_guard(name)
|
98
|
-
Gem.
|
96
|
+
if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
|
97
|
+
Gem::Specification.find_by_name("guard-#{name}").full_gem_path
|
98
|
+
else
|
99
|
+
Gem.source_index.find_name("guard-#{name}").last.full_gem_path
|
100
|
+
end
|
99
101
|
rescue
|
100
102
|
UI.error "Could not find 'guard-#{name}' gem path."
|
101
103
|
end
|
data/lib/guard/dsl.rb
CHANGED
@@ -1,30 +1,92 @@
|
|
1
1
|
module Guard
|
2
2
|
class Dsl
|
3
|
-
|
4
3
|
class << self
|
4
|
+
@@options = nil
|
5
|
+
|
5
6
|
def evaluate_guardfile(options = {})
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
options.is_a?(Hash) or raise ArgumentError.new("evaluate_guardfile not passed a Hash!")
|
8
|
+
|
9
|
+
@@options = options.dup
|
10
|
+
instance_eval_guardfile(fetch_guardfile_contents)
|
11
|
+
end
|
12
|
+
|
13
|
+
def instance_eval_guardfile(contents)
|
14
|
+
begin
|
15
|
+
new.instance_eval(contents, @@options[:guardfile_path], 1)
|
16
|
+
rescue
|
17
|
+
UI.error "Invalid Guardfile, original error is:\n#{$!}"
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def guardfile_include?(guard_name)
|
23
|
+
guardfile_contents.match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/)
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_guardfile(guardfile_path)
|
27
|
+
begin
|
28
|
+
@@options[:guardfile_path] = guardfile_path
|
29
|
+
@@options[:guardfile_contents] = File.read(guardfile_path)
|
30
|
+
rescue
|
31
|
+
UI.error("Error reading file #{guardfile_path}")
|
32
|
+
exit 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def fetch_guardfile_contents
|
37
|
+
# TODO: do we need .rc file interaction?
|
38
|
+
if @@options.has_key?(:guardfile_contents)
|
39
|
+
UI.info "Using inline Guardfile."
|
40
|
+
@@options[:guardfile_path] = 'Inline Guardfile'
|
41
|
+
|
42
|
+
elsif @@options.has_key?(:guardfile)
|
43
|
+
if File.exist?(@@options[:guardfile])
|
44
|
+
read_guardfile(@@options[:guardfile])
|
45
|
+
UI.info "Using Guardfile at #{@@options[:guardfile]}."
|
46
|
+
else
|
47
|
+
UI.error "No Guardfile exists at #{@@options[:guardfile]}."
|
13
48
|
exit 1
|
14
49
|
end
|
50
|
+
|
15
51
|
else
|
16
|
-
|
52
|
+
if File.exist?(guardfile_default_path)
|
53
|
+
read_guardfile(guardfile_default_path)
|
54
|
+
else
|
55
|
+
UI.error "No Guardfile found, please create one with `guard init`."
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
unless guardfile_contents_usable?
|
61
|
+
UI.error "The command file(#{@@options[:guardfile]}) seems to be empty."
|
17
62
|
exit 1
|
18
63
|
end
|
64
|
+
|
65
|
+
guardfile_contents
|
19
66
|
end
|
20
67
|
|
21
|
-
def
|
22
|
-
|
68
|
+
def guardfile_contents
|
69
|
+
@@options ? @@options[:guardfile_contents] : ""
|
23
70
|
end
|
24
|
-
|
25
|
-
def
|
26
|
-
|
71
|
+
|
72
|
+
def guardfile_contents_usable?
|
73
|
+
guardfile_contents && guardfile_contents.size >= 'guard :a'.size # smallest guard-definition
|
27
74
|
end
|
75
|
+
|
76
|
+
def guardfile_default_path
|
77
|
+
File.exist?(local_guardfile_path) ? local_guardfile_path : home_guardfile_path
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def local_guardfile_path
|
83
|
+
File.join(Dir.pwd, "Guardfile")
|
84
|
+
end
|
85
|
+
|
86
|
+
def home_guardfile_path
|
87
|
+
File.expand_path(File.join("~", "Guardfile"))
|
88
|
+
end
|
89
|
+
|
28
90
|
end
|
29
91
|
|
30
92
|
def group(name, &guard_definition)
|
data/lib/guard/interactor.rb
CHANGED
@@ -1,29 +1,54 @@
|
|
1
1
|
module Guard
|
2
2
|
module Interactor
|
3
|
-
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def run_all
|
6
|
+
::Guard.run do
|
7
|
+
::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :run_all) }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def stop
|
12
|
+
UI.info "Bye bye...", :reset => true
|
13
|
+
::Guard.listener.stop
|
14
|
+
::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :stop) }
|
15
|
+
abort
|
16
|
+
end
|
17
|
+
|
18
|
+
def reload
|
19
|
+
::Guard.run do
|
20
|
+
::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :reload) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
4
24
|
def self.init_signal_traps
|
5
25
|
# Run all (Ctrl-\)
|
6
|
-
Signal.
|
7
|
-
|
8
|
-
|
26
|
+
if Signal.list.has_key?('QUIT')
|
27
|
+
Signal.trap('QUIT') do
|
28
|
+
run_all
|
9
29
|
end
|
30
|
+
else
|
31
|
+
UI.info "Your system doesn't support QUIT signal, so Ctrl-\\ (Run all) won't work"
|
10
32
|
end
|
11
|
-
|
33
|
+
|
12
34
|
# Stop (Ctrl-C)
|
13
|
-
Signal.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
35
|
+
if Signal.list.has_key?('INT')
|
36
|
+
Signal.trap('INT') do
|
37
|
+
stop
|
38
|
+
end
|
39
|
+
else
|
40
|
+
UI.info "Your system doesn't support INT signal, so Ctrl-C (stop) won't work"
|
18
41
|
end
|
19
|
-
|
42
|
+
|
20
43
|
# Reload (Ctrl-Z)
|
21
|
-
Signal.
|
22
|
-
|
23
|
-
|
44
|
+
if Signal.list.has_key?('TSTP')
|
45
|
+
Signal.trap('TSTP') do
|
46
|
+
reload
|
24
47
|
end
|
48
|
+
else
|
49
|
+
UI.info "Your system doesn't support TSTP signal, so Ctrl-Z (Reload) won't work"
|
25
50
|
end
|
51
|
+
|
26
52
|
end
|
27
|
-
|
28
53
|
end
|
29
|
-
end
|
54
|
+
end
|
data/lib/guard/listener.rb
CHANGED
@@ -1,38 +1,88 @@
|
|
1
1
|
require 'rbconfig'
|
2
|
+
require 'digest/sha1'
|
2
3
|
|
3
4
|
module Guard
|
4
5
|
|
5
6
|
autoload :Darwin, 'guard/listeners/darwin'
|
6
7
|
autoload :Linux, 'guard/listeners/linux'
|
8
|
+
autoload :Windows, 'guard/listeners/windows'
|
7
9
|
autoload :Polling, 'guard/listeners/polling'
|
8
10
|
|
9
11
|
class Listener
|
10
|
-
attr_reader :last_event
|
12
|
+
attr_reader :last_event, :sha1_checksums_hash, :directory, :callback
|
11
13
|
|
12
|
-
def self.select_and_init
|
14
|
+
def self.select_and_init(*a)
|
13
15
|
if mac? && Darwin.usable?
|
14
|
-
Darwin.new
|
16
|
+
Darwin.new(*a)
|
15
17
|
elsif linux? && Linux.usable?
|
16
|
-
Linux.new
|
18
|
+
Linux.new(*a)
|
19
|
+
elsif windows? && Windows.usable?
|
20
|
+
Windows.new(*a)
|
17
21
|
else
|
18
22
|
UI.info "Using polling (Please help us to support your system better than that.)"
|
19
|
-
Polling.new
|
23
|
+
Polling.new(*a)
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
23
|
-
def initialize
|
27
|
+
def initialize(directory=Dir.pwd, options={})
|
28
|
+
@directory = directory.to_s
|
29
|
+
@sha1_checksums_hash = {}
|
30
|
+
@relativate_paths = options.fetch(:relativate_paths, true)
|
24
31
|
update_last_event
|
25
32
|
end
|
26
33
|
|
34
|
+
def start
|
35
|
+
watch directory
|
36
|
+
end
|
37
|
+
|
38
|
+
def stop
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_change(&callback)
|
42
|
+
@callback = callback
|
43
|
+
end
|
44
|
+
|
27
45
|
def update_last_event
|
28
46
|
@last_event = Time.now
|
29
47
|
end
|
30
48
|
|
31
49
|
def modified_files(dirs, options = {})
|
32
|
-
|
33
|
-
|
50
|
+
# <<<<<<< HEAD
|
51
|
+
# files = potentially_modified_files(dirs, options).select { |path| File.file?(path) && file_modified?(path) }
|
52
|
+
# files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
|
53
|
+
# =======
|
54
|
+
files = potentially_modified_files(dirs, options).select { |path| File.file?(path) && file_modified?(path) }
|
55
|
+
relativate_paths files
|
56
|
+
end
|
57
|
+
|
58
|
+
def worker
|
59
|
+
raise NotImplementedError, "should respond to #watch"
|
60
|
+
end
|
61
|
+
|
62
|
+
# register a directory to watch. must be implemented by the subclasses
|
63
|
+
def watch(directory)
|
64
|
+
raise NotImplementedError, "do whatever you want here, given the directory as only argument"
|
65
|
+
end
|
66
|
+
|
67
|
+
def all_files
|
68
|
+
potentially_modified_files [directory + '/'], :all => true
|
69
|
+
# >>>>>>> b12769d2bf385b3c69973721144cae3d5d8fbed9
|
70
|
+
end
|
71
|
+
|
72
|
+
# scopes all given paths to the current #directory
|
73
|
+
def relativate_paths(paths)
|
74
|
+
return paths unless relativate_paths?
|
75
|
+
paths.map do |path|
|
76
|
+
path.gsub(%r~^#{directory}/~, '')
|
77
|
+
end
|
34
78
|
end
|
35
79
|
|
80
|
+
attr_writer :relativate_paths
|
81
|
+
def relativate_paths?
|
82
|
+
!!@relativate_paths
|
83
|
+
end
|
84
|
+
|
85
|
+
|
36
86
|
private
|
37
87
|
|
38
88
|
def potentially_modified_files(dirs, options = {})
|
@@ -40,12 +90,36 @@ module Guard
|
|
40
90
|
Dir.glob(dirs.map { |dir| "#{dir}#{match}" })
|
41
91
|
end
|
42
92
|
|
43
|
-
|
44
|
-
|
93
|
+
# Depending on the filesystem, mtime is probably only precise to the second, so round
|
94
|
+
# both values down to the second for the comparison.
|
95
|
+
def file_modified?(path)
|
96
|
+
if File.mtime(path).to_i == last_event.to_i
|
97
|
+
file_content_modified?(path, sha1_checksum(path))
|
98
|
+
elsif File.mtime(path).to_i > last_event.to_i
|
99
|
+
set_sha1_checksums_hash(path, sha1_checksum(path))
|
100
|
+
true
|
101
|
+
end
|
45
102
|
rescue
|
46
103
|
false
|
47
104
|
end
|
48
105
|
|
106
|
+
def file_content_modified?(path, sha1_checksum)
|
107
|
+
if sha1_checksums_hash[path] != sha1_checksum
|
108
|
+
set_sha1_checksums_hash(path, sha1_checksum)
|
109
|
+
true
|
110
|
+
else
|
111
|
+
false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def set_sha1_checksums_hash(path, sha1_checksum)
|
116
|
+
@sha1_checksums_hash[path] = sha1_checksum
|
117
|
+
end
|
118
|
+
|
119
|
+
def sha1_checksum(path)
|
120
|
+
Digest::SHA1.file(path).to_s
|
121
|
+
end
|
122
|
+
|
49
123
|
def self.mac?
|
50
124
|
Config::CONFIG['target_os'] =~ /darwin/i
|
51
125
|
end
|
@@ -54,71 +128,9 @@ module Guard
|
|
54
128
|
Config::CONFIG['target_os'] =~ /linux/i
|
55
129
|
end
|
56
130
|
|
131
|
+
def self.windows?
|
132
|
+
Config::CONFIG['target_os'] =~ /mswin|mingw/i
|
133
|
+
end
|
134
|
+
|
57
135
|
end
|
58
136
|
end
|
59
|
-
|
60
|
-
# require 'rbconfig'
|
61
|
-
#
|
62
|
-
# module Guard
|
63
|
-
#
|
64
|
-
# autoload :Darwin, 'guard/listeners/darwin'
|
65
|
-
# autoload :Linux, 'guard/listeners/linux'
|
66
|
-
# autoload :Polling, 'guard/listeners/polling'
|
67
|
-
#
|
68
|
-
# class Listener
|
69
|
-
# attr_accessor :last_event, :changed_files
|
70
|
-
#
|
71
|
-
# def self.select_and_init
|
72
|
-
# if mac? && Darwin.usable?
|
73
|
-
# Darwin.new
|
74
|
-
# elsif linux? && Linux.usable?
|
75
|
-
# Linux.new
|
76
|
-
# else
|
77
|
-
# UI.info "Using polling (Please help us to support your system better than that.)"
|
78
|
-
# Polling.new
|
79
|
-
# end
|
80
|
-
# end
|
81
|
-
#
|
82
|
-
# def initialize
|
83
|
-
# @changed_files = []
|
84
|
-
# update_last_event
|
85
|
-
# end
|
86
|
-
#
|
87
|
-
# def get_and_clear_changed_files
|
88
|
-
# files = changed_files.dup
|
89
|
-
# changed_files.clear
|
90
|
-
# files.uniq
|
91
|
-
# end
|
92
|
-
#
|
93
|
-
# private
|
94
|
-
#
|
95
|
-
# def find_changed_files(dirs, options = {})
|
96
|
-
# files = potentially_changed_files(dirs, options).select { |path| File.file?(path) && changed_file?(path) }
|
97
|
-
# files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
|
98
|
-
# end
|
99
|
-
#
|
100
|
-
# def potentially_changed_files(dirs, options = {})
|
101
|
-
# match = options[:all] ? "**/*" : "*"
|
102
|
-
# Dir.glob(dirs.map { |dir| "#{dir}#{match}" })
|
103
|
-
# end
|
104
|
-
#
|
105
|
-
# def changed_file?(file)
|
106
|
-
# File.mtime(file) >= last_event
|
107
|
-
# rescue
|
108
|
-
# false
|
109
|
-
# end
|
110
|
-
#
|
111
|
-
# def update_last_event
|
112
|
-
# @last_event = Time.now
|
113
|
-
# end
|
114
|
-
#
|
115
|
-
# def self.mac?
|
116
|
-
# Config::CONFIG['target_os'] =~ /darwin/i
|
117
|
-
# end
|
118
|
-
#
|
119
|
-
# def self.linux?
|
120
|
-
# Config::CONFIG['target_os'] =~ /linux/i
|
121
|
-
# end
|
122
|
-
#
|
123
|
-
# end
|
124
|
-
# end
|
@@ -2,25 +2,23 @@ module Guard
|
|
2
2
|
class Darwin < Listener
|
3
3
|
attr_reader :fsevent
|
4
4
|
|
5
|
-
def initialize
|
5
|
+
def initialize(*)
|
6
6
|
super
|
7
7
|
@fsevent = FSEvent.new
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
11
|
-
@fsevent
|
12
|
-
files = modified_files(modified_dirs)
|
13
|
-
update_last_event
|
14
|
-
callback.call(files)
|
15
|
-
end
|
10
|
+
def worker
|
11
|
+
@fsevent
|
16
12
|
end
|
17
13
|
|
18
14
|
def start
|
19
|
-
|
15
|
+
super
|
16
|
+
fsevent.run
|
20
17
|
end
|
21
18
|
|
22
19
|
def stop
|
23
|
-
|
20
|
+
super
|
21
|
+
fsevent.stop
|
24
22
|
end
|
25
23
|
|
26
24
|
def self.usable?
|
@@ -36,5 +34,15 @@ module Guard
|
|
36
34
|
false
|
37
35
|
end
|
38
36
|
|
37
|
+
private
|
38
|
+
|
39
|
+
def watch(directory)
|
40
|
+
worker.watch directory do |modified_dirs|
|
41
|
+
files = modified_files(modified_dirs)
|
42
|
+
update_last_event
|
43
|
+
callback.call(files)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
39
47
|
end
|
40
48
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Guard
|
2
2
|
class Linux < Listener
|
3
|
-
attr_reader :inotify, :files, :latency
|
3
|
+
attr_reader :inotify, :files, :latency
|
4
4
|
|
5
|
-
def initialize
|
5
|
+
def initialize(*)
|
6
6
|
super
|
7
7
|
|
8
8
|
@inotify = INotify::Notifier.new
|
@@ -12,24 +12,16 @@ module Guard
|
|
12
12
|
|
13
13
|
def start
|
14
14
|
@stop = false
|
15
|
+
super
|
15
16
|
watch_change unless watch_change?
|
16
17
|
end
|
17
18
|
|
18
19
|
def stop
|
20
|
+
super
|
19
21
|
@stop = true
|
20
22
|
sleep latency
|
21
23
|
end
|
22
24
|
|
23
|
-
def on_change(&callback)
|
24
|
-
@callback = callback
|
25
|
-
inotify.watch(Dir.pwd, :recursive, :modify, :create, :delete, :move) do |event|
|
26
|
-
unless event.name == "" # Event on root directory
|
27
|
-
@files << event.absolute_name
|
28
|
-
end
|
29
|
-
end
|
30
|
-
rescue Interrupt
|
31
|
-
end
|
32
|
-
|
33
25
|
def self.usable?
|
34
26
|
require 'rb-inotify'
|
35
27
|
if !defined?(INotify::VERSION) || Gem::Version.new(INotify::VERSION.join('.')) < Gem::Version.new('0.5.1')
|
@@ -49,9 +41,22 @@ module Guard
|
|
49
41
|
|
50
42
|
private
|
51
43
|
|
44
|
+
def worker
|
45
|
+
@inotify
|
46
|
+
end
|
47
|
+
|
48
|
+
def watch(directory)
|
49
|
+
worker.watch(directory, :recursive, :modify, :create) do |event|
|
50
|
+
unless event.name == "" # Event on root directory
|
51
|
+
@files << event.absolute_name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
rescue Interrupt
|
55
|
+
end
|
56
|
+
|
52
57
|
def watch_change
|
53
58
|
@watch_change = true
|
54
|
-
|
59
|
+
until @stop
|
55
60
|
if Config::CONFIG['build'] =~ /java/ || IO.select([inotify.to_io], [], [], latency)
|
56
61
|
break if @stop
|
57
62
|
|
@@ -61,8 +66,7 @@ module Guard
|
|
61
66
|
|
62
67
|
unless files.empty?
|
63
68
|
files.uniq!
|
64
|
-
|
65
|
-
callback.call(files)
|
69
|
+
callback.call( relativate_paths(files) )
|
66
70
|
files.clear
|
67
71
|
end
|
68
72
|
end
|
@@ -1,37 +1,39 @@
|
|
1
1
|
module Guard
|
2
2
|
class Polling < Listener
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :latency
|
4
4
|
|
5
|
-
def initialize
|
5
|
+
def initialize(*)
|
6
6
|
super
|
7
7
|
@latency = 1.5
|
8
8
|
end
|
9
9
|
|
10
|
-
def on_change(&callback)
|
11
|
-
@callback = callback
|
12
|
-
end
|
13
|
-
|
14
10
|
def start
|
15
11
|
@stop = false
|
12
|
+
super
|
16
13
|
watch_change
|
17
14
|
end
|
18
15
|
|
19
16
|
def stop
|
17
|
+
super
|
20
18
|
@stop = true
|
21
19
|
end
|
22
20
|
|
23
21
|
private
|
24
22
|
|
25
23
|
def watch_change
|
26
|
-
|
24
|
+
until @stop
|
27
25
|
start = Time.now.to_f
|
28
26
|
files = modified_files([Dir.pwd + '/'], :all => true)
|
29
27
|
update_last_event
|
30
|
-
callback.call(files) unless files.empty?
|
28
|
+
callback.call(relativate_paths(files)) unless files.empty?
|
31
29
|
nap_time = latency - (Time.now.to_f - start)
|
32
30
|
sleep(nap_time) if nap_time > 0
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
34
|
+
def watch(directory)
|
35
|
+
@existing = all_files
|
36
|
+
end
|
37
|
+
|
36
38
|
end
|
37
39
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Guard
|
2
|
+
class Windows < Listener
|
3
|
+
attr_reader :fchange
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
@fchange = FChange::Notifier.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def start
|
11
|
+
super
|
12
|
+
@fchange.run
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop
|
16
|
+
super
|
17
|
+
@fchange.stop
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.usable?
|
21
|
+
require 'rb-fchange'
|
22
|
+
true
|
23
|
+
rescue LoadError
|
24
|
+
UI.info "Please install rb-fchange gem for Windows file events support"
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def worker
|
31
|
+
@fchange
|
32
|
+
end
|
33
|
+
|
34
|
+
def watch(directory)
|
35
|
+
worker.watch(directory, :all_events, :recursive) do |event|
|
36
|
+
paths = [File.expand_path(event.watcher.path) + '/']
|
37
|
+
files = modified_files(paths, {:all => true})
|
38
|
+
update_last_event
|
39
|
+
callback.call(files)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
data/lib/guard/notifier.rb
CHANGED
@@ -1,30 +1,48 @@
|
|
1
1
|
require 'rbconfig'
|
2
2
|
require 'pathname'
|
3
|
+
require 'guard/ui'
|
3
4
|
|
4
5
|
module Guard
|
5
6
|
module Notifier
|
6
7
|
|
7
8
|
def self.turn_off
|
8
|
-
|
9
|
+
ENV["GUARD_NOTIFY"] = 'false'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.turn_on
|
13
|
+
ENV["GUARD_NOTIFY"] = 'true'
|
14
|
+
case Config::CONFIG['target_os']
|
15
|
+
when /darwin/i
|
16
|
+
require_growl
|
17
|
+
when /linux/i
|
18
|
+
require_libnotify
|
19
|
+
when /mswin|mingw/i
|
20
|
+
require_rbnotifu
|
21
|
+
end
|
9
22
|
end
|
10
23
|
|
11
24
|
def self.notify(message, options = {})
|
12
|
-
|
25
|
+
if enabled?
|
13
26
|
image = options[:image] || :success
|
14
27
|
title = options[:title] || "Guard"
|
15
28
|
case Config::CONFIG['target_os']
|
16
29
|
when /darwin/i
|
17
|
-
|
18
|
-
|
19
|
-
end
|
30
|
+
require_growl # need for guard-rspec formatter that is called out of guard scope
|
31
|
+
Growl.notify message, :title => title, :icon => image_path(image), :name => "Guard"
|
20
32
|
when /linux/i
|
21
|
-
|
22
|
-
|
23
|
-
|
33
|
+
require_libnotify # need for guard-rspec formatter that is called out of guard scope
|
34
|
+
Libnotify.show :body => message, :summary => title, :icon_path => image_path(image)
|
35
|
+
when /mswin|mingw/i
|
36
|
+
require_rbnotifu
|
37
|
+
Notifu.show :message => message, :title => title, :type => image_level(image), :time => 3
|
24
38
|
end
|
25
39
|
end
|
26
40
|
end
|
27
41
|
|
42
|
+
def self.enabled?
|
43
|
+
ENV["GUARD_NOTIFY"] == 'true'
|
44
|
+
end
|
45
|
+
|
28
46
|
private
|
29
47
|
|
30
48
|
def self.image_path(image)
|
@@ -42,25 +60,38 @@ module Guard
|
|
42
60
|
end
|
43
61
|
end
|
44
62
|
|
45
|
-
def self.
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
63
|
+
def self.image_level(image)
|
64
|
+
case image
|
65
|
+
when :failed
|
66
|
+
:error
|
67
|
+
when :pending
|
68
|
+
:warn
|
69
|
+
when :success
|
70
|
+
:info
|
71
|
+
else
|
72
|
+
:info
|
52
73
|
end
|
53
74
|
end
|
75
|
+
|
76
|
+
def self.require_growl
|
77
|
+
require 'growl'
|
78
|
+
rescue LoadError
|
79
|
+
turn_off
|
80
|
+
UI.info "Please install growl gem for Mac OS X notification support and add it to your Gemfile"
|
81
|
+
end
|
54
82
|
|
55
|
-
def self.
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
UI.info "Please install libnotify gem for Linux notification support and add it to your Gemfile"
|
61
|
-
false
|
62
|
-
end
|
83
|
+
def self.require_libnotify
|
84
|
+
require 'libnotify'
|
85
|
+
rescue LoadError
|
86
|
+
turn_off
|
87
|
+
UI.info "Please install libnotify gem for Linux notification support and add it to your Gemfile"
|
63
88
|
end
|
64
89
|
|
90
|
+
def self.require_rbnotifu
|
91
|
+
require 'rb-notifu'
|
92
|
+
rescue LoadError
|
93
|
+
turn_off
|
94
|
+
UI.info "Please install rb-notifu gem for Windows notification support and add it to your Gemfile"
|
95
|
+
end
|
65
96
|
end
|
66
|
-
end
|
97
|
+
end
|
data/lib/guard/ui.rb
CHANGED
@@ -24,7 +24,11 @@ module Guard
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def reset_line
|
27
|
-
|
27
|
+
if color_enabled?
|
28
|
+
print "\r\e[0m"
|
29
|
+
else
|
30
|
+
print "\r\n"
|
31
|
+
end
|
28
32
|
end
|
29
33
|
|
30
34
|
def clear
|
@@ -38,7 +42,27 @@ module Guard
|
|
38
42
|
end
|
39
43
|
|
40
44
|
def color(text, color_code)
|
41
|
-
|
45
|
+
if color_enabled?
|
46
|
+
return "#{color_code}#{text}\e[0m"
|
47
|
+
else
|
48
|
+
return text
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def color_enabled?
|
53
|
+
@color_enabled ||= if Config::CONFIG['target_os'] =~ /mswin|mingw/i
|
54
|
+
unless ENV['ANSICON']
|
55
|
+
begin
|
56
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
57
|
+
require 'Win32/Console/ANSI'
|
58
|
+
rescue LoadError
|
59
|
+
info "You must 'gem install win32console' to use color on Windows"
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
else
|
64
|
+
true
|
65
|
+
end
|
42
66
|
end
|
43
67
|
|
44
68
|
end
|
data/lib/guard/version.rb
CHANGED
data/lib/guard/watcher.rb
CHANGED
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: guard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
version: 0.
|
4
|
+
prerelease: 6
|
5
|
+
version: 0.4.0.rc
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Thibaud Guillaume-Gentil
|
@@ -10,8 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
14
|
-
default_executable:
|
13
|
+
date: 2011-05-28 00:00:00 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: bundler
|
@@ -19,9 +18,9 @@ dependencies:
|
|
19
18
|
requirement: &id001 !ruby/object:Gem::Requirement
|
20
19
|
none: false
|
21
20
|
requirements:
|
22
|
-
- -
|
21
|
+
- - ">="
|
23
22
|
- !ruby/object:Gem::Version
|
24
|
-
version:
|
23
|
+
version: "0"
|
25
24
|
type: :development
|
26
25
|
version_requirements: *id001
|
27
26
|
- !ruby/object:Gem::Dependency
|
@@ -32,7 +31,7 @@ dependencies:
|
|
32
31
|
requirements:
|
33
32
|
- - ~>
|
34
33
|
- !ruby/object:Gem::Version
|
35
|
-
version: 2.
|
34
|
+
version: 2.6.0
|
36
35
|
type: :development
|
37
36
|
version_requirements: *id002
|
38
37
|
- !ruby/object:Gem::Dependency
|
@@ -43,7 +42,7 @@ dependencies:
|
|
43
42
|
requirements:
|
44
43
|
- - ~>
|
45
44
|
- !ruby/object:Gem::Version
|
46
|
-
version: 0.
|
45
|
+
version: 0.3.1
|
47
46
|
type: :development
|
48
47
|
version_requirements: *id003
|
49
48
|
- !ruby/object:Gem::Dependency
|
@@ -79,6 +78,7 @@ files:
|
|
79
78
|
- lib/guard/listeners/darwin.rb
|
80
79
|
- lib/guard/listeners/linux.rb
|
81
80
|
- lib/guard/listeners/polling.rb
|
81
|
+
- lib/guard/listeners/windows.rb
|
82
82
|
- lib/guard/notifier.rb
|
83
83
|
- lib/guard/templates/Guardfile
|
84
84
|
- lib/guard/ui.rb
|
@@ -87,7 +87,6 @@ files:
|
|
87
87
|
- lib/guard.rb
|
88
88
|
- LICENSE
|
89
89
|
- README.markdown
|
90
|
-
has_rdoc: true
|
91
90
|
homepage: http://rubygems.org/gems/guard
|
92
91
|
licenses: []
|
93
92
|
|
@@ -111,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
110
|
requirements: []
|
112
111
|
|
113
112
|
rubyforge_project: guard
|
114
|
-
rubygems_version: 1.
|
113
|
+
rubygems_version: 1.8.4
|
115
114
|
signing_key:
|
116
115
|
specification_version: 3
|
117
116
|
summary: Guard keep an eye on your files modifications.
|