guard 0.3.4 → 0.4.0.rc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 [](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.
|