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 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.6, 1.8.7 & 1.9.2.
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
- $ gem install guard
23
+ ``` bash
24
+ $ gem install guard
25
+ ```
23
26
 
24
- Add it to your Gemfile (inside the <tt>test</tt> group):
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
- $ guard init
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
- $ gem install rb-fsevent
47
+ ``` bash
48
+ $ gem install rb-fsevent
49
+ ```
41
50
 
42
51
  Install the Growl gem if you want notification support:
43
52
 
44
- $ gem install growl
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
- $ gem install rb-inotify
67
+ ``` bash
68
+ $ gem install rb-inotify
69
+ ```
57
70
 
58
71
  Install the Libnotify gem if you want notification support:
59
72
 
60
- $ gem install libnotify
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
- $ guard [start]
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
- $ bundle exec guard
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
- $ guard --clear
85
- $ guard -c # shortcut
113
+ ``` bash
114
+ $ guard --clear
115
+ $ guard -c # shortcut
116
+ ```
86
117
 
87
118
  Notifications (growl/libnotify) can be disabled with:
88
119
 
89
- $ guard --notify false
90
- $ guard -n false # shortcut
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 <tt>--group</tt> (or <tt>-g</tt>) option:
127
+ The guards to start can be specified by group (see the Guardfile DSL below) specifying the `--group` (or `-g`) option:
93
128
 
94
- $ guard --group group_name another_group_name
95
- $ guard -g group_name another_group_name # shortcut
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
- $ guard help [TASK]
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
- * <tt>Ctrl-C</tt> - Calls each guard's <tt>stop</tt> method, in the same order they are declared in the Guardfile, and then quits Guard itself.
107
- * <tt>Ctrl-\\</tt> - Calls each guard's <tt>run_all</tt> method, in the same order they are declared in the Guardfile.
108
- * <tt>Ctrl-Z</tt> - Calls each guard's <tt>reload</tt> method, in the same order they are declared in the Guardfile.
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 <tt>test</tt> group):
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
- $ guard init <guard-name>
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: <tt>guard</tt>, <tt>watch</tt> & <tt>group</tt>.
173
+ The Guardfile DSL consists of just three simple methods: `guard`, `watch` & `group`.
133
174
 
134
175
  Required:
135
- * The <tt>guard</tt> method allows you to add a guard with an optional hash of options.
136
- * The <tt>watch</tt> 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 <tt>run_on_change</tt> guard method or to launch any arbitrary command.
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
- * The <tt>group</tt> method allows you to group several guards together. Groups to be run can be specified with the Guard DSL option <tt>--group</tt> (or <tt>-g</tt>). This comes in handy especially when you have a huge Guardfile and want to focus your development on a certain part.
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 (<tt>bundle gem</tt> if you use Bundler) with this basic structure:
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
- <tt>Guard::GuardName</tt> (in <tt>lib/guard/guard-name.rb</tt>) must inherit from <tt>Guard::Guard</tt> and should overwrite at least one of the five basic <tt>Guard::Guard</tt> instance methods. Example:
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-/ signal
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 Example < ::Guard::Guard
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
- Notifier.turn_off unless options[:notify]
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
- ::Guard.guards.delete guard
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.source_index.find_name("guard-#{name}").last.full_gem_path
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
- @@options = options
7
-
8
- if File.exists?(guardfile_path)
9
- begin
10
- new.instance_eval(File.read(guardfile_path), guardfile_path, 1)
11
- rescue
12
- UI.error "Invalid Guardfile, original error is:\n#{$!}"
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
- UI.error "No Guardfile in current folder, please create one."
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 guardfile_include?(guard_name)
22
- File.read(guardfile_path).match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/)
68
+ def guardfile_contents
69
+ @@options ? @@options[:guardfile_contents] : ""
23
70
  end
24
-
25
- def guardfile_path
26
- File.join(Dir.pwd, 'Guardfile')
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)
@@ -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.trap('QUIT') do
7
- ::Guard.run do
8
- ::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :run_all) }
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.trap('INT') do
14
- UI.info "Bye bye...", :reset => true
15
- ::Guard.listener.stop
16
- ::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :stop) }
17
- abort("\n")
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.trap('TSTP') do
22
- ::Guard.run do
23
- ::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :reload) }
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
@@ -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
- files = potentially_modified_files(dirs, options).select { |path| File.file?(path) && recent_file?(path) }
33
- files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
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
- def recent_file?(file)
44
- File.mtime(file) >= last_event
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 on_change(&callback)
11
- @fsevent.watch Dir.pwd do |modified_dirs|
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
- @fsevent.run
15
+ super
16
+ fsevent.run
20
17
  end
21
18
 
22
19
  def stop
23
- @fsevent.stop
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, :callback
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
- while !@stop
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
- files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
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 :callback, :latency
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
- while !@stop
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
@@ -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
- @disable = true
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
- unless @disable || ENV["GUARD_ENV"] == "test"
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
- if growl_installed?
18
- Growl.notify message, :title => title, :icon => image_path(image), :name => "Guard"
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
- if libnotify_installed?
22
- Libnotify.show :body => message, :summary => title, :icon_path => image_path(image)
23
- end
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.growl_installed?
46
- @installed ||= begin
47
- require 'growl'
48
- true
49
- rescue LoadError
50
- UI.info "Please install growl gem for Mac OS X notification support and add it to your Gemfile"
51
- false
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.libnotify_installed?
56
- @installed ||= begin
57
- require 'libnotify'
58
- true
59
- rescue LoadError
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
- print "\r\e[0m"
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
- "#{color_code}#{text}\e[0m"
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
@@ -1,3 +1,3 @@
1
1
  module Guard
2
- VERSION = "0.3.4"
2
+ VERSION = "0.4.0.rc"
3
3
  end
data/lib/guard/watcher.rb CHANGED
@@ -59,4 +59,4 @@ module Guard
59
59
  end
60
60
 
61
61
  end
62
- end
62
+ end
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.3.4
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-04-25 00:00:00 +02:00
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: 1.0.7
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.5.0
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.2.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.6.2
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.