guard 0.8.8 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/CHANGELOG.md +16 -1
  2. data/README.md +665 -293
  3. data/bin/fsevent_watch_guard +0 -0
  4. data/lib/guard.rb +66 -31
  5. data/lib/guard/cli.rb +9 -3
  6. data/lib/guard/dsl.rb +32 -5
  7. data/lib/guard/dsl_describer.rb +3 -2
  8. data/lib/guard/guard.rb +2 -2
  9. data/lib/guard/interactor.rb +179 -48
  10. data/lib/guard/listener.rb +32 -17
  11. data/lib/guard/listeners/darwin.rb +5 -9
  12. data/lib/guard/listeners/linux.rb +6 -10
  13. data/lib/guard/listeners/windows.rb +4 -2
  14. data/lib/guard/notifier.rb +171 -258
  15. data/lib/guard/notifiers/gntp.rb +114 -0
  16. data/lib/guard/notifiers/growl.rb +98 -0
  17. data/lib/guard/notifiers/growl_notify.rb +91 -0
  18. data/lib/guard/notifiers/libnotify.rb +96 -0
  19. data/lib/guard/notifiers/rb_notifu.rb +101 -0
  20. data/lib/guard/ui.rb +2 -2
  21. data/lib/guard/version.rb +1 -1
  22. data/lib/guard/watcher.rb +1 -1
  23. data/lib/vendor/darwin/Gemfile +6 -0
  24. data/lib/vendor/darwin/Guardfile +8 -0
  25. data/lib/vendor/darwin/LICENSE +20 -0
  26. data/lib/vendor/darwin/README.rdoc +254 -0
  27. data/lib/vendor/darwin/Rakefile +21 -0
  28. data/lib/vendor/darwin/ext/extconf.rb +61 -0
  29. data/lib/vendor/darwin/ext/fsevent/fsevent_watch.c +226 -0
  30. data/lib/vendor/darwin/lib/rb-fsevent.rb +2 -0
  31. data/lib/vendor/darwin/lib/rb-fsevent/fsevent.rb +105 -0
  32. data/lib/vendor/darwin/lib/rb-fsevent/version.rb +3 -0
  33. data/lib/vendor/darwin/rb-fsevent.gemspec +24 -0
  34. data/lib/vendor/darwin/spec/fixtures/folder1/file1.txt +0 -0
  35. data/lib/vendor/darwin/spec/fixtures/folder1/folder2/file2.txt +0 -0
  36. data/lib/vendor/darwin/spec/rb-fsevent/fsevent_spec.rb +75 -0
  37. data/lib/vendor/darwin/spec/spec_helper.rb +24 -0
  38. data/lib/vendor/linux/MIT-LICENSE +20 -0
  39. data/lib/vendor/linux/README.md +66 -0
  40. data/lib/vendor/linux/Rakefile +54 -0
  41. data/lib/vendor/linux/VERSION +1 -0
  42. data/lib/vendor/linux/lib/rb-inotify.rb +17 -0
  43. data/lib/vendor/linux/lib/rb-inotify/event.rb +139 -0
  44. data/lib/vendor/linux/lib/rb-inotify/native.rb +31 -0
  45. data/lib/vendor/linux/lib/rb-inotify/native/flags.rb +89 -0
  46. data/lib/vendor/linux/lib/rb-inotify/notifier.rb +308 -0
  47. data/lib/vendor/linux/lib/rb-inotify/watcher.rb +83 -0
  48. data/lib/vendor/linux/rb-inotify.gemspec +53 -0
  49. data/lib/vendor/windows/Gemfile +4 -0
  50. data/lib/vendor/windows/README.md +34 -0
  51. data/lib/vendor/windows/Rakefile +18 -0
  52. data/lib/vendor/windows/lib/rb-fchange.rb +14 -0
  53. data/lib/vendor/windows/lib/rb-fchange/event.rb +29 -0
  54. data/lib/vendor/windows/lib/rb-fchange/native.rb +45 -0
  55. data/lib/vendor/windows/lib/rb-fchange/native/flags.rb +78 -0
  56. data/lib/vendor/windows/lib/rb-fchange/notifier.rb +149 -0
  57. data/lib/vendor/windows/lib/rb-fchange/version.rb +3 -0
  58. data/lib/vendor/windows/lib/rb-fchange/watcher.rb +99 -0
  59. data/lib/vendor/windows/rb-fchange.gemspec +34 -0
  60. data/lib/vendor/windows/spec/fixtures/folder1/file1.txt +0 -0
  61. data/lib/vendor/windows/spec/fixtures/folder1/folder2/file2.txt +0 -0
  62. data/lib/vendor/windows/spec/rb-fchange/fchange_spec.rb +119 -0
  63. data/lib/vendor/windows/spec/spec_helper.rb +21 -0
  64. metadata +87 -22
  65. data/lib/guard/version.rbc +0 -180
@@ -0,0 +1,31 @@
1
+ require 'ffi'
2
+
3
+ module INotify
4
+ # This module contains the low-level foreign-function interface code
5
+ # for dealing with the inotify C APIs.
6
+ # It's an implementation detail, and not meant for users to deal with.
7
+ #
8
+ # @private
9
+ module Native
10
+ extend FFI::Library
11
+ ffi_lib "c"
12
+
13
+ # The C struct describing an inotify event.
14
+ #
15
+ # @private
16
+ class Event < FFI::Struct
17
+ layout(
18
+ :wd, :int,
19
+ :mask, :uint32,
20
+ :cookie, :uint32,
21
+ :len, :uint32)
22
+ end
23
+
24
+ attach_function :inotify_init, [], :int
25
+ attach_function :inotify_add_watch, [:int, :string, :uint32], :int
26
+ attach_function :inotify_rm_watch, [:int, :uint32], :int
27
+
28
+ attach_function :read, [:int, :pointer, :size_t], :ssize_t
29
+ attach_function :close, [:int], :int
30
+ end
31
+ end
@@ -0,0 +1,89 @@
1
+ module INotify
2
+ module Native
3
+ # A module containing all the inotify flags
4
+ # to be passed to {Notifier#watch}.
5
+ #
6
+ # @private
7
+ module Flags
8
+ # File was accessed.
9
+ IN_ACCESS = 0x00000001
10
+ # Metadata changed.
11
+ IN_ATTRIB = 0x00000004
12
+ # Writtable file was closed.
13
+ IN_CLOSE_WRITE = 0x00000008
14
+ # File was modified.
15
+ IN_MODIFY = 0x00000002
16
+ # Unwrittable file closed.
17
+ IN_CLOSE_NOWRITE = 0x00000010
18
+ # File was opened.
19
+ IN_OPEN = 0x00000020
20
+ # File was moved from X.
21
+ IN_MOVED_FROM = 0x00000040
22
+ # File was moved to Y.
23
+ IN_MOVED_TO = 0x00000080
24
+ # Subfile was created.
25
+ IN_CREATE = 0x00000100
26
+ # Subfile was deleted.
27
+ IN_DELETE = 0x00000200
28
+ # Self was deleted.
29
+ IN_DELETE_SELF = 0x00000400
30
+ # Self was moved.
31
+ IN_MOVE_SELF = 0x00000800
32
+
33
+ ## Helper events.
34
+
35
+ # Close.
36
+ IN_CLOSE = (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
37
+ # Moves.
38
+ IN_MOVE = (IN_MOVED_FROM | IN_MOVED_TO)
39
+ # All events which a program can wait on.
40
+ IN_ALL_EVENTS = (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE |
41
+ IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
42
+ IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF)
43
+
44
+
45
+ ## Special flags.
46
+
47
+ # Only watch the path if it is a directory.
48
+ IN_ONLYDIR = 0x01000000
49
+ # Do not follow a sym link.
50
+ IN_DONT_FOLLOW = 0x02000000
51
+ # Add to the mask of an already existing watch.
52
+ IN_MASK_ADD = 0x20000000
53
+ # Only send event once.
54
+ IN_ONESHOT = 0x80000000
55
+
56
+
57
+ ## Events sent by the kernel.
58
+
59
+ # Backing fs was unmounted.
60
+ IN_UNMOUNT = 0x00002000
61
+ # Event queued overflowed.
62
+ IN_Q_OVERFLOW = 0x00004000
63
+ # File was ignored.
64
+ IN_IGNORED = 0x00008000
65
+ # Event occurred against dir.
66
+ IN_ISDIR = 0x40000000
67
+
68
+ # Converts a list of flags to the bitmask that the C API expects.
69
+ #
70
+ # @param flags [Array<Symbol>]
71
+ # @return [Fixnum]
72
+ def self.to_mask(flags)
73
+ flags.map {|flag| const_get("IN_#{flag.to_s.upcase}")}.
74
+ inject(0) {|mask, flag| mask | flag}
75
+ end
76
+
77
+ # Converts a bitmask from the C API into a list of flags.
78
+ #
79
+ # @param mask [Fixnum]
80
+ # @return [Array<Symbol>]
81
+ def self.from_mask(mask)
82
+ constants.map {|c| c.to_s}.select do |c|
83
+ next false unless c =~ /^IN_/
84
+ const_get(c) & mask != 0
85
+ end.map {|c| c.sub("IN_", "").downcase.to_sym} - [:all_events]
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,308 @@
1
+ module INotify
2
+ # Notifier wraps a single instance of inotify.
3
+ # It's possible to have more than one instance,
4
+ # but usually unnecessary.
5
+ #
6
+ # @example
7
+ # # Create the notifier
8
+ # notifier = INotify::Notifier.new
9
+ #
10
+ # # Run this callback whenever the file path/to/foo.txt is read
11
+ # notifier.watch("path/to/foo.txt", :access) do
12
+ # puts "Foo.txt was accessed!"
13
+ # end
14
+ #
15
+ # # Watch for any file in the directory being deleted
16
+ # # or moved out of the directory.
17
+ # notifier.watch("path/to/directory", :delete, :moved_from) do |event|
18
+ # # The #name field of the event object contains the name of the affected file
19
+ # puts "#{event.name} is no longer in the directory!"
20
+ # end
21
+ #
22
+ # # Nothing happens until you run the notifier!
23
+ # notifier.run
24
+ class Notifier
25
+ # A list of directories that should never be recursively watched.
26
+ #
27
+ # * Files in `/dev/fd` sometimes register as directories, but are not enumerable.
28
+ RECURSIVE_BLACKLIST = %w[/dev/fd]
29
+
30
+ # A hash from {Watcher} ids to the instances themselves.
31
+ #
32
+ # @private
33
+ # @return [{Fixnum => Watcher}]
34
+ attr_reader :watchers
35
+
36
+ # The underlying file descriptor for this notifier.
37
+ # This is a valid OS file descriptor, and can be used as such
38
+ # (except under JRuby -- see \{#to\_io}).
39
+ #
40
+ # @return [Fixnum]
41
+ attr_reader :fd
42
+
43
+ # @return [Boolean] Whether or not this Ruby implementation supports
44
+ # wrapping the native file descriptor in a Ruby IO wrapper.
45
+ def self.supports_ruby_io?
46
+ RUBY_PLATFORM !~ /java/
47
+ end
48
+
49
+ # Creates a new {Notifier}.
50
+ #
51
+ # @return [Notifier]
52
+ # @raise [SystemCallError] if inotify failed to initialize for some reason
53
+ def initialize
54
+ @fd = Native.inotify_init
55
+ @watchers = {}
56
+ return unless @fd < 0
57
+
58
+ raise SystemCallError.new(
59
+ "Failed to initialize inotify" +
60
+ case FFI.errno
61
+ when Errno::EMFILE::Errno; ": the user limit on the total number of inotify instances has been reached."
62
+ when Errno::ENFILE::Errno; ": the system limit on the total number of file descriptors has been reached."
63
+ when Errno::ENOMEM::Errno; ": insufficient kernel memory is available."
64
+ else; ""
65
+ end,
66
+ FFI.errno)
67
+ end
68
+
69
+ # Returns a Ruby IO object wrapping the underlying file descriptor.
70
+ # Since this file descriptor is fully functional (except under JRuby),
71
+ # this IO object can be used in any way a Ruby-created IO object can.
72
+ # This includes passing it to functions like `#select`.
73
+ #
74
+ # Note that this always returns the same IO object.
75
+ # Creating lots of IO objects for the same file descriptor
76
+ # can cause some odd problems.
77
+ #
78
+ # **This is not supported under JRuby**.
79
+ # JRuby currently doesn't use native file descriptors for the IO object,
80
+ # so we can't use this file descriptor as a stand-in.
81
+ #
82
+ # @return [IO] An IO object wrapping the file descriptor
83
+ # @raise [NotImplementedError] if this is being called in JRuby
84
+ def to_io
85
+ unless self.class.supports_ruby_io?
86
+ raise NotImplementedError.new("INotify::Notifier#to_io is not supported under JRuby")
87
+ end
88
+ @io ||= IO.new(@fd)
89
+ end
90
+
91
+ # Watches a file or directory for changes,
92
+ # calling the callback when there are.
93
+ # This is only activated once \{#process} or \{#run} is called.
94
+ #
95
+ # **Note that by default, this does not recursively watch subdirectories
96
+ # of the watched directory**.
97
+ # To do so, use the `:recursive` flag.
98
+ #
99
+ # ## Flags
100
+ #
101
+ # `:access`
102
+ # : A file is accessed (that is, read).
103
+ #
104
+ # `:attrib`
105
+ # : A file's metadata is changed (e.g. permissions, timestamps, etc).
106
+ #
107
+ # `:close_write`
108
+ # : A file that was opened for writing is closed.
109
+ #
110
+ # `:close_nowrite`
111
+ # : A file that was not opened for writing is closed.
112
+ #
113
+ # `:modify`
114
+ # : A file is modified.
115
+ #
116
+ # `:open`
117
+ # : A file is opened.
118
+ #
119
+ # ### Directory-Specific Flags
120
+ #
121
+ # These flags only apply when a directory is being watched.
122
+ #
123
+ # `:moved_from`
124
+ # : A file is moved out of the watched directory.
125
+ #
126
+ # `:moved_to`
127
+ # : A file is moved into the watched directory.
128
+ #
129
+ # `:create`
130
+ # : A file is created in the watched directory.
131
+ #
132
+ # `:delete`
133
+ # : A file is deleted in the watched directory.
134
+ #
135
+ # `:delete_self`
136
+ # : The watched file or directory itself is deleted.
137
+ #
138
+ # `:move_self`
139
+ # : The watched file or directory itself is moved.
140
+ #
141
+ # ### Helper Flags
142
+ #
143
+ # These flags are just combinations of the flags above.
144
+ #
145
+ # `:close`
146
+ # : Either `:close_write` or `:close_nowrite` is activated.
147
+ #
148
+ # `:move`
149
+ # : Either `:moved_from` or `:moved_to` is activated.
150
+ #
151
+ # `:all_events`
152
+ # : Any event above is activated.
153
+ #
154
+ # ### Options Flags
155
+ #
156
+ # These flags don't actually specify events.
157
+ # Instead, they specify options for the watcher.
158
+ #
159
+ # `:onlydir`
160
+ # : Only watch the path if it's a directory.
161
+ #
162
+ # `:dont_follow`
163
+ # : Don't follow symlinks.
164
+ #
165
+ # `:mask_add`
166
+ # : Add these flags to the pre-existing flags for this path.
167
+ #
168
+ # `:oneshot`
169
+ # : Only send the event once, then shut down the watcher.
170
+ #
171
+ # `:recursive`
172
+ # : Recursively watch any subdirectories that are created.
173
+ # Note that this is a feature of rb-inotify,
174
+ # rather than of inotify itself, which can only watch one level of a directory.
175
+ # This means that the {Event#name} field
176
+ # will contain only the basename of the modified file.
177
+ # When using `:recursive`, {Event#absolute_name} should always be used.
178
+ #
179
+ # @param path [String] The path to the file or directory
180
+ # @param flags [Array<Symbol>] Which events to watch for
181
+ # @yield [event] A block that will be called
182
+ # whenever one of the specified events occur
183
+ # @yieldparam event [Event] The Event object containing information
184
+ # about the event that occured
185
+ # @return [Watcher] A Watcher set up to watch this path for these events
186
+ # @raise [SystemCallError] if the file or directory can't be watched,
187
+ # e.g. if the file isn't found, read access is denied,
188
+ # or the flags don't contain any events
189
+ def watch(path, *flags, &callback)
190
+ return Watcher.new(self, path, *flags, &callback) unless flags.include?(:recursive)
191
+
192
+ Dir.glob(File.join(path, '*'), File::FNM_DOTMATCH).each do |d|
193
+ binary_d = d.respond_to?(:force_encoding) ? d.dup.force_encoding('BINARY') : d
194
+ next if binary_d =~ /\/\.\.?$/ # Current or parent directory
195
+ watch(d, *flags, &callback) if !RECURSIVE_BLACKLIST.include?(d) && File.directory?(d)
196
+ end
197
+
198
+ rec_flags = [:create, :moved_to]
199
+ return watch(path, *((flags - [:recursive]) | rec_flags)) do |event|
200
+ callback.call(event) if flags.include?(:all_events) || !(flags & event.flags).empty?
201
+ next if (rec_flags & event.flags).empty? || !event.flags.include?(:isdir)
202
+ begin
203
+ watch(event.absolute_name, *flags, &callback)
204
+ rescue Errno::ENOENT
205
+ # If the file has been deleted since the glob was run, we don't want to error out.
206
+ end
207
+ end
208
+ end
209
+
210
+ # Starts the notifier watching for filesystem events.
211
+ # Blocks until \{#stop} is called.
212
+ #
213
+ # @see #process
214
+ def run
215
+ @stop = false
216
+ process until @stop
217
+ end
218
+
219
+ # Stop watching for filesystem events.
220
+ # That is, if we're in a \{#run} loop,
221
+ # exit out as soon as we finish handling the events.
222
+ def stop
223
+ @stop = true
224
+ end
225
+
226
+ # Blocks until there are one or more filesystem events
227
+ # that this notifier has watchers registered for.
228
+ # Once there are events, the appropriate callbacks are called
229
+ # and this function returns.
230
+ #
231
+ # @see #run
232
+ def process
233
+ read_events.each {|event| event.callback!}
234
+ end
235
+
236
+ # Close the notifier.
237
+ #
238
+ # @raise [SystemCallError] if closing the underlying file descriptor fails.
239
+ def close
240
+ return if Native.close(@fd) == 0
241
+
242
+ raise SystemCallError.new("Failed to properly close inotify socket" +
243
+ case FFI.errno
244
+ when Errno::EBADF::Errno; ": invalid or closed file descriptior"
245
+ when Errno::EIO::Errno; ": an I/O error occured"
246
+ end,
247
+ FFI.errno)
248
+ end
249
+
250
+ private
251
+
252
+ # Blocks until there are one or more filesystem events
253
+ # that this notifier has watchers registered for.
254
+ # Once there are events, returns their {Event} objects.
255
+ def read_events
256
+ size = 64 * Native::Event.size
257
+ tries = 1
258
+
259
+ begin
260
+ data = readpartial(size)
261
+ rescue SystemCallError => er
262
+ # EINVAL means that there's more data to be read
263
+ # than will fit in the buffer size
264
+ raise er unless er.errno == Errno::EINVAL::Errno || tries == 5
265
+ size *= 2
266
+ tries += 1
267
+ retry
268
+ end
269
+
270
+ events = []
271
+ cookies = {}
272
+ while ev = Event.consume(data, self)
273
+ events << ev
274
+ next if ev.cookie == 0
275
+ cookies[ev.cookie] ||= []
276
+ cookies[ev.cookie] << ev
277
+ end
278
+ cookies.each {|c, evs| evs.each {|ev| ev.related.replace(evs - [ev]).freeze}}
279
+ events
280
+ end
281
+
282
+ # Same as IO#readpartial, or as close as we need.
283
+ def readpartial(size)
284
+ # Use Ruby's readpartial if possible, to avoid blocking other threads.
285
+ return to_io.readpartial(size) if self.class.supports_ruby_io?
286
+
287
+ tries = 0
288
+ begin
289
+ tries += 1
290
+ buffer = FFI::MemoryPointer.new(:char, size)
291
+ size_read = Native.read(fd, buffer, size)
292
+ return buffer.read_string(size_read) if size_read >= 0
293
+ end while FFI.errno == Errno::EINTR::Errno && tries <= 5
294
+
295
+ raise SystemCallError.new("Error reading inotify events" +
296
+ case FFI.errno
297
+ when Errno::EAGAIN::Errno; ": no data available for non-blocking I/O"
298
+ when Errno::EBADF::Errno; ": invalid or closed file descriptor"
299
+ when Errno::EFAULT::Errno; ": invalid buffer"
300
+ when Errno::EINVAL::Errno; ": invalid file descriptor"
301
+ when Errno::EIO::Errno; ": I/O error"
302
+ when Errno::EISDIR::Errno; ": file descriptor is a directory"
303
+ else; ""
304
+ end,
305
+ FFI.errno)
306
+ end
307
+ end
308
+ end
@@ -0,0 +1,83 @@
1
+ module INotify
2
+ # Watchers monitor a single path for changes,
3
+ # specified by {INotify::Notifier#watch event flags}.
4
+ # A watcher is usually created via \{Notifier#watch}.
5
+ #
6
+ # One {Notifier} may have many {Watcher}s.
7
+ # The Notifier actually takes care of the checking for events,
8
+ # via \{Notifier#run #run} or \{Notifier#process #process}.
9
+ # The main purpose of having Watcher objects
10
+ # is to be able to disable them using \{#close}.
11
+ class Watcher
12
+ # The {Notifier} that this Watcher belongs to.
13
+ #
14
+ # @return [Notifier]
15
+ attr_reader :notifier
16
+
17
+ # The path that this Watcher is watching.
18
+ #
19
+ # @return [String]
20
+ attr_reader :path
21
+
22
+ # The {INotify::Notifier#watch flags}
23
+ # specifying the events that this Watcher is watching for,
24
+ # and potentially some options as well.
25
+ #
26
+ # @return [Array<Symbol>]
27
+ attr_reader :flags
28
+
29
+ # The id for this Watcher.
30
+ # Used to retrieve this Watcher from {Notifier#watchers}.
31
+ #
32
+ # @private
33
+ # @return [Fixnum]
34
+ attr_reader :id
35
+
36
+ # Calls this Watcher's callback with the given {Event}.
37
+ #
38
+ # @private
39
+ # @param event [Event]
40
+ def callback!(event)
41
+ @callback[event]
42
+ end
43
+
44
+ # Disables this Watcher, so that it doesn't fire any more events.
45
+ #
46
+ # @raise [SystemCallError] if the watch fails to be disabled for some reason
47
+ def close
48
+ return if Native.inotify_rm_watch(@notifier.fd, @id) == 0
49
+ raise SystemCallError.new("Failed to stop watching #{path.inspect}", FFI.errno)
50
+ end
51
+
52
+ # Creates a new {Watcher}.
53
+ #
54
+ # @private
55
+ # @see Notifier#watch
56
+ def initialize(notifier, path, *flags, &callback)
57
+ @notifier = notifier
58
+ @callback = callback || proc {}
59
+ @path = path
60
+ @flags = flags.freeze
61
+ @id = Native.inotify_add_watch(@notifier.fd, path.dup,
62
+ Native::Flags.to_mask(flags))
63
+
64
+ unless @id < 0
65
+ @notifier.watchers[@id] = self
66
+ return
67
+ end
68
+
69
+ raise SystemCallError.new(
70
+ "Failed to watch #{path.inspect}" +
71
+ case FFI.errno
72
+ when Errno::EACCES::Errno; ": read access to the given file is not permitted."
73
+ when Errno::EBADF::Errno; ": the given file descriptor is not valid."
74
+ when Errno::EFAULT::Errno; ": path points outside of the process's accessible address space."
75
+ when Errno::EINVAL::Errno; ": the given event mask contains no legal events; or fd is not an inotify file descriptor."
76
+ when Errno::ENOMEM::Errno; ": insufficient kernel memory was available."
77
+ when Errno::ENOSPC::Errno; ": The user limit on the total number of inotify watches was reached or the kernel failed to allocate a needed resource."
78
+ else; ""
79
+ end,
80
+ FFI.errno)
81
+ end
82
+ end
83
+ end