joshbuddy-guard 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/CHANGELOG.md +370 -0
  2. data/LICENSE +20 -0
  3. data/README.md +470 -0
  4. data/bin/fsevent_watch_guard +0 -0
  5. data/bin/guard +6 -0
  6. data/images/failed.png +0 -0
  7. data/images/pending.png +0 -0
  8. data/images/success.png +0 -0
  9. data/lib/guard.rb +463 -0
  10. data/lib/guard/cli.rb +125 -0
  11. data/lib/guard/dsl.rb +370 -0
  12. data/lib/guard/dsl_describer.rb +150 -0
  13. data/lib/guard/group.rb +37 -0
  14. data/lib/guard/guard.rb +129 -0
  15. data/lib/guard/hook.rb +118 -0
  16. data/lib/guard/interactor.rb +116 -0
  17. data/lib/guard/listener.rb +351 -0
  18. data/lib/guard/listeners/darwin.rb +60 -0
  19. data/lib/guard/listeners/linux.rb +91 -0
  20. data/lib/guard/listeners/polling.rb +55 -0
  21. data/lib/guard/listeners/windows.rb +61 -0
  22. data/lib/guard/notifier.rb +290 -0
  23. data/lib/guard/templates/Guardfile +2 -0
  24. data/lib/guard/ui.rb +193 -0
  25. data/lib/guard/version.rb +6 -0
  26. data/lib/guard/watcher.rb +114 -0
  27. data/lib/vendor/darwin/Gemfile +6 -0
  28. data/lib/vendor/darwin/Guardfile +8 -0
  29. data/lib/vendor/darwin/LICENSE +20 -0
  30. data/lib/vendor/darwin/README.rdoc +254 -0
  31. data/lib/vendor/darwin/Rakefile +21 -0
  32. data/lib/vendor/darwin/ext/extconf.rb +61 -0
  33. data/lib/vendor/darwin/ext/fsevent/fsevent_watch.c +226 -0
  34. data/lib/vendor/darwin/lib/rb-fsevent.rb +2 -0
  35. data/lib/vendor/darwin/lib/rb-fsevent/fsevent.rb +105 -0
  36. data/lib/vendor/darwin/lib/rb-fsevent/version.rb +3 -0
  37. data/lib/vendor/darwin/rb-fsevent.gemspec +24 -0
  38. data/lib/vendor/darwin/spec/fixtures/folder1/file1.txt +0 -0
  39. data/lib/vendor/darwin/spec/fixtures/folder1/folder2/file2.txt +0 -0
  40. data/lib/vendor/darwin/spec/rb-fsevent/fsevent_spec.rb +75 -0
  41. data/lib/vendor/darwin/spec/spec_helper.rb +24 -0
  42. data/lib/vendor/linux/MIT-LICENSE +20 -0
  43. data/lib/vendor/linux/README.md +66 -0
  44. data/lib/vendor/linux/Rakefile +54 -0
  45. data/lib/vendor/linux/VERSION +1 -0
  46. data/lib/vendor/linux/lib/rb-inotify.rb +17 -0
  47. data/lib/vendor/linux/lib/rb-inotify/event.rb +139 -0
  48. data/lib/vendor/linux/lib/rb-inotify/native.rb +31 -0
  49. data/lib/vendor/linux/lib/rb-inotify/native/flags.rb +89 -0
  50. data/lib/vendor/linux/lib/rb-inotify/notifier.rb +308 -0
  51. data/lib/vendor/linux/lib/rb-inotify/watcher.rb +83 -0
  52. data/lib/vendor/linux/rb-inotify.gemspec +53 -0
  53. data/lib/vendor/windows/Gemfile +4 -0
  54. data/lib/vendor/windows/README.md +34 -0
  55. data/lib/vendor/windows/Rakefile +18 -0
  56. data/lib/vendor/windows/lib/rb-fchange.rb +14 -0
  57. data/lib/vendor/windows/lib/rb-fchange/event.rb +29 -0
  58. data/lib/vendor/windows/lib/rb-fchange/native.rb +45 -0
  59. data/lib/vendor/windows/lib/rb-fchange/native/flags.rb +78 -0
  60. data/lib/vendor/windows/lib/rb-fchange/notifier.rb +149 -0
  61. data/lib/vendor/windows/lib/rb-fchange/version.rb +3 -0
  62. data/lib/vendor/windows/lib/rb-fchange/watcher.rb +99 -0
  63. data/lib/vendor/windows/rb-fchange.gemspec +34 -0
  64. data/lib/vendor/windows/spec/fixtures/folder1/file1.txt +0 -0
  65. data/lib/vendor/windows/spec/fixtures/folder1/folder2/file2.txt +0 -0
  66. data/lib/vendor/windows/spec/rb-fchange/fchange_spec.rb +119 -0
  67. data/lib/vendor/windows/spec/spec_helper.rb +21 -0
  68. data/man/guard.1 +96 -0
  69. data/man/guard.1.html +181 -0
  70. metadata +193 -0
@@ -0,0 +1,226 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <unistd.h>
4
+
5
+ #include <CoreServices/CoreServices.h>
6
+
7
+
8
+ // Structure for storing metadata parsed from the commandline
9
+ static struct {
10
+ FSEventStreamEventId sinceWhen;
11
+ CFTimeInterval latency;
12
+ FSEventStreamCreateFlags flags;
13
+ CFMutableArrayRef paths;
14
+ } config = {
15
+ (UInt64) kFSEventStreamEventIdSinceNow,
16
+ (double) 0.3,
17
+ (UInt32) kFSEventStreamCreateFlagNone,
18
+ NULL
19
+ };
20
+
21
+ // Prototypes
22
+ static void append_path(const char *path);
23
+ static inline void parse_cli_settings(int argc, const char *argv[]);
24
+ static void callback(FSEventStreamRef streamRef,
25
+ void *clientCallBackInfo,
26
+ size_t numEvents,
27
+ void *eventPaths,
28
+ const FSEventStreamEventFlags eventFlags[],
29
+ const FSEventStreamEventId eventIds[]);
30
+
31
+
32
+ // Resolve a path and append it to the CLI settings structure
33
+ // The FSEvents API will, internally, resolve paths using a similar scheme.
34
+ // Performing this ahead of time makes things less confusing, IMHO.
35
+ static void append_path(const char *path)
36
+ {
37
+ #ifdef DEBUG
38
+ fprintf(stderr, "\n");
39
+ fprintf(stderr, "append_path called for: %s\n", path);
40
+ #endif
41
+
42
+ char fullPath[PATH_MAX];
43
+
44
+ if (realpath(path, fullPath) == NULL) {
45
+ #ifdef DEBUG
46
+ fprintf(stderr, " realpath not directly resolvable from path\n");
47
+ #endif
48
+
49
+ if (path[0] != '/') {
50
+ #ifdef DEBUG
51
+ fprintf(stderr, " passed path is not absolute\n");
52
+ #endif
53
+ size_t len;
54
+ getcwd(fullPath, sizeof(fullPath));
55
+ #ifdef DEBUG
56
+ fprintf(stderr, " result of getcwd: %s\n", fullPath);
57
+ #endif
58
+ len = strlen(fullPath);
59
+ fullPath[len] = '/';
60
+ strlcpy(&fullPath[len + 1], path, sizeof(fullPath) - (len + 1));
61
+ } else {
62
+ #ifdef DEBUG
63
+ fprintf(stderr, " assuming path does not YET exist\n");
64
+ #endif
65
+ strlcpy(fullPath, path, sizeof(fullPath));
66
+ }
67
+ }
68
+
69
+ #ifdef DEBUG
70
+ fprintf(stderr, " resolved path to: %s\n", fullPath);
71
+ fprintf(stderr, "\n");
72
+ fflush(stderr);
73
+ #endif
74
+
75
+ CFStringRef pathRef = CFStringCreateWithCString(kCFAllocatorDefault,
76
+ fullPath,
77
+ kCFStringEncodingUTF8);
78
+ CFArrayAppendValue(config.paths, pathRef);
79
+ CFRelease(pathRef);
80
+ }
81
+
82
+ // Parse commandline settings
83
+ static inline void parse_cli_settings(int argc, const char *argv[])
84
+ {
85
+ config.paths = CFArrayCreateMutable(NULL,
86
+ (CFIndex)0,
87
+ &kCFTypeArrayCallBacks);
88
+
89
+ for (int i = 1; i < argc; i++) {
90
+ if (strcmp(argv[i], "--since-when") == 0) {
91
+ config.sinceWhen = strtoull(argv[++i], NULL, 0);
92
+ } else if (strcmp(argv[i], "--latency") == 0) {
93
+ config.latency = strtod(argv[++i], NULL);
94
+ } else if (strcmp(argv[i], "--no-defer") == 0) {
95
+ config.flags |= kFSEventStreamCreateFlagNoDefer;
96
+ } else if (strcmp(argv[i], "--watch-root") == 0) {
97
+ config.flags |= kFSEventStreamCreateFlagWatchRoot;
98
+ } else if (strcmp(argv[i], "--ignore-self") == 0) {
99
+ #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
100
+ config.flags |= kFSEventStreamCreateFlagIgnoreSelf;
101
+ #else
102
+ fprintf(stderr, "MacOSX10.6.sdk is required for --ignore-self\n");
103
+ #endif
104
+ } else {
105
+ append_path(argv[i]);
106
+ }
107
+ }
108
+
109
+ if (CFArrayGetCount(config.paths) == 0) {
110
+ append_path(".");
111
+ }
112
+
113
+ #ifdef DEBUG
114
+ fprintf(stderr, "config.sinceWhen %llu\n", config.sinceWhen);
115
+ fprintf(stderr, "config.latency %f\n", config.latency);
116
+ fprintf(stderr, "config.flags %#.8x\n", config.flags);
117
+ fprintf(stderr, "config.paths\n");
118
+
119
+ long numpaths = CFArrayGetCount(config.paths);
120
+
121
+ for (long i = 0; i < numpaths; i++) {
122
+ char path[PATH_MAX];
123
+ CFStringGetCString(CFArrayGetValueAtIndex(config.paths, i),
124
+ path,
125
+ PATH_MAX,
126
+ kCFStringEncodingUTF8);
127
+ fprintf(stderr, " %s\n", path);
128
+ }
129
+
130
+ fprintf(stderr, "\n");
131
+ fflush(stderr);
132
+ #endif
133
+ }
134
+
135
+ static void callback(FSEventStreamRef streamRef,
136
+ void *clientCallBackInfo,
137
+ size_t numEvents,
138
+ void *eventPaths,
139
+ const FSEventStreamEventFlags eventFlags[],
140
+ const FSEventStreamEventId eventIds[])
141
+ {
142
+ char **paths = eventPaths;
143
+
144
+ #ifdef DEBUG
145
+ fprintf(stderr, "\n");
146
+ fprintf(stderr, "FSEventStreamCallback fired!\n");
147
+ fprintf(stderr, " numEvents: %lu\n", numEvents);
148
+
149
+ for (size_t i = 0; i < numEvents; i++) {
150
+ fprintf(stderr, " event path: %s\n", paths[i]);
151
+ fprintf(stderr, " event flags: %#.8x\n", eventFlags[i]);
152
+ fprintf(stderr, " event ID: %llu\n", eventIds[i]);
153
+ }
154
+
155
+ fprintf(stderr, "\n");
156
+ fflush(stderr);
157
+ #endif
158
+
159
+ for (size_t i = 0; i < numEvents; i++) {
160
+ fprintf(stdout, "%s", paths[i]);
161
+ fprintf(stdout, ":");
162
+ }
163
+
164
+ fprintf(stdout, "\n");
165
+ fflush(stdout);
166
+ }
167
+
168
+ int main(int argc, const char *argv[])
169
+ {
170
+ /*
171
+ * a subprocess will initially inherit the process group of its parent. the
172
+ * process group may have a control terminal associated with it, which would
173
+ * be the first tty device opened by the group leader. typically the group
174
+ * leader is your shell and the control terminal is your login device. a
175
+ * subset of signals triggered on the control terminal are sent to all members
176
+ * of the process group, in large part to facilitate sane and consistent
177
+ * cleanup (ex: control terminal was closed).
178
+ *
179
+ * so why the overly descriptive lecture style comment?
180
+ * 1. SIGINT and SIGQUIT are among the signals with this behavior
181
+ * 2. a number of applications gank the above for their own use
182
+ * 3. ruby's insanely useful "guard" is one of these applications
183
+ * 4. despite having some level of understanding of POSIX signals and a few
184
+ * of the scenarios that might cause problems, i learned this one only
185
+ * after reading ruby 1.9's process.c
186
+ * 5. if left completely undocumented, even slightly obscure bugfixes
187
+ * may be removed as cruft by a future maintainer
188
+ *
189
+ * hindsight is 20/20 addition: if you're single-threaded and blocking on IO
190
+ * with a subprocess, then handlers for deferrable signals might not get run
191
+ * when you expect them to. In the case of Ruby 1.8, that means making use of
192
+ * IO::select, which will preserve correct signal handling behavior.
193
+ */
194
+ if (setpgid(0,0) < 0) {
195
+ fprintf(stderr, "Unable to set new process group.\n");
196
+ return 1;
197
+ }
198
+
199
+ parse_cli_settings(argc, argv);
200
+
201
+ FSEventStreamContext context = {0, NULL, NULL, NULL, NULL};
202
+ FSEventStreamRef stream;
203
+ stream = FSEventStreamCreate(kCFAllocatorDefault,
204
+ (FSEventStreamCallback)&callback,
205
+ &context,
206
+ config.paths,
207
+ config.sinceWhen,
208
+ config.latency,
209
+ config.flags);
210
+
211
+ #ifdef DEBUG
212
+ FSEventStreamShow(stream);
213
+ fprintf(stderr, "\n");
214
+ fflush(stderr);
215
+ #endif
216
+
217
+ FSEventStreamScheduleWithRunLoop(stream,
218
+ CFRunLoopGetCurrent(),
219
+ kCFRunLoopDefaultMode);
220
+ FSEventStreamStart(stream);
221
+ CFRunLoopRun();
222
+ FSEventStreamFlushSync(stream);
223
+ FSEventStreamStop(stream);
224
+
225
+ return 0;
226
+ }
@@ -0,0 +1,2 @@
1
+ require 'rb-fsevent/fsevent'
2
+ require 'rb-fsevent/version'
@@ -0,0 +1,105 @@
1
+ class FSEvent
2
+ class << self
3
+ class_eval <<-END
4
+ def root_path
5
+ "#{File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..'))}"
6
+ end
7
+ END
8
+ class_eval <<-END
9
+ def watcher_path
10
+ "#{File.join(FSEvent.root_path, 'bin', 'fsevent_watch_guard')}"
11
+ end
12
+ END
13
+ end
14
+
15
+ attr_reader :paths, :callback
16
+
17
+ def watch(watch_paths, options=nil, &block)
18
+ @paths = watch_paths.kind_of?(Array) ? watch_paths : [watch_paths]
19
+ @callback = block
20
+
21
+ if options.kind_of?(Hash)
22
+ @options = parse_options(options)
23
+ elsif options.kind_of?(Array)
24
+ @options = options
25
+ else
26
+ @options = []
27
+ end
28
+ end
29
+
30
+ def run
31
+ @running = true
32
+ # please note the use of IO::select() here, as it is used specifically to
33
+ # preserve correct signal handling behavior in ruby 1.8.
34
+ while @running && IO::select([pipe], nil, nil, nil)
35
+ if line = pipe.readline
36
+ modified_dir_paths = line.split(":").select { |dir| dir != "\n" }
37
+ callback.call(modified_dir_paths)
38
+ end
39
+ end
40
+ rescue Interrupt, IOError
41
+ ensure
42
+ stop
43
+ end
44
+
45
+ def stop
46
+ if pipe
47
+ Process.kill("KILL", pipe.pid)
48
+ pipe.close
49
+ end
50
+ rescue IOError
51
+ ensure
52
+ @pipe = @running = nil
53
+ end
54
+
55
+ if RUBY_VERSION < '1.9'
56
+ def pipe
57
+ @pipe ||= IO.popen("#{self.class.watcher_path} #{options_string} #{shellescaped_paths}")
58
+ end
59
+
60
+ private
61
+
62
+ def options_string
63
+ @options.join(' ')
64
+ end
65
+
66
+ def shellescaped_paths
67
+ @paths.map {|path| shellescape(path)}.join(' ')
68
+ end
69
+
70
+ # for Ruby 1.8.6 support
71
+ def shellescape(str)
72
+ # An empty argument will be skipped, so return empty quotes.
73
+ return "''" if str.empty?
74
+
75
+ str = str.dup
76
+
77
+ # Process as a single byte sequence because not all shell
78
+ # implementations are multibyte aware.
79
+ str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/n, "\\\\\\1")
80
+
81
+ # A LF cannot be escaped with a backslash because a backslash + LF
82
+ # combo is regarded as line continuation and simply ignored.
83
+ str.gsub!(/\n/, "'\n'")
84
+
85
+ return str
86
+ end
87
+ else
88
+ def pipe
89
+ @pipe ||= IO.popen([self.class.watcher_path] + @options + @paths)
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ def parse_options(options={})
96
+ opts = []
97
+ opts.concat(['--since-when', options[:since_when]]) if options[:since_when]
98
+ opts.concat(['--latency', options[:latency]]) if options[:latency]
99
+ opts.push('--no-defer') if options[:no_defer]
100
+ opts.push('--watch-root') if options[:watch_root]
101
+ # ruby 1.9's IO.popen(array-of-stuff) syntax requires all items to be strings
102
+ opts.map {|opt| "#{opt}"}
103
+ end
104
+
105
+ end
@@ -0,0 +1,3 @@
1
+ class FSEvent
2
+ VERSION = "0.4.3.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rb-fsevent/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rb-fsevent"
7
+ s.version = FSEvent::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ['Thibaud Guillaume-Gentil', 'Travis Tilley']
10
+ s.email = ['thibaud@thibaud.me', 'ttilley@gmail.com']
11
+ s.homepage = "http://rubygems.org/gems/rb-fsevent"
12
+ s.summary = "Very simple & usable FSEvents API"
13
+ s.description = "FSEvents API with Signals catching (without RubyCocoa)"
14
+
15
+ s.rubyforge_project = "rb-fsevent"
16
+
17
+ s.add_development_dependency 'bundler', '~> 1.0.10'
18
+ s.add_development_dependency 'rspec', '~> 2.5.0'
19
+ s.add_development_dependency 'guard-rspec', '~> 0.1.9'
20
+
21
+ s.files = Dir.glob('{lib,ext}/**/*') + %w[LICENSE README.rdoc]
22
+ s.extensions = ['ext/extconf.rb']
23
+ s.require_path = 'lib'
24
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe FSEvent do
4
+
5
+ before(:each) do
6
+ @results = []
7
+ @fsevent = FSEvent.new
8
+ @fsevent.watch @fixture_path.to_s, {:latency => 0.5} do |paths|
9
+ @results += paths
10
+ end
11
+ end
12
+
13
+ it "should have a watcher_path that resolves to an executable file" do
14
+ File.exists?(FSEvent.watcher_path).should be_true
15
+ File.executable?(FSEvent.watcher_path).should be_true
16
+ end
17
+
18
+ it "should work with path with an apostrophe" do
19
+ custom_path = @fixture_path.join("custom 'path")
20
+ file = custom_path.join("newfile.rb").to_s
21
+ File.delete file if File.exists? file
22
+ @fsevent.watch custom_path.to_s do |paths|
23
+ @results += paths
24
+ end
25
+ @fsevent.paths.should == ["#{custom_path}"]
26
+ run
27
+ FileUtils.touch file
28
+ stop
29
+ File.delete file
30
+ @results.should == [custom_path.to_s + '/']
31
+ end
32
+
33
+ it "should catch new file" do
34
+ file = @fixture_path.join("newfile.rb")
35
+ File.delete file if File.exists? file
36
+ run
37
+ FileUtils.touch file
38
+ stop
39
+ File.delete file
40
+ @results.should == [@fixture_path.to_s + '/']
41
+ end
42
+
43
+ it "should catch file update" do
44
+ file = @fixture_path.join("folder1/file1.txt")
45
+ File.exists?(file).should be_true
46
+ run
47
+ FileUtils.touch file
48
+ stop
49
+ @results.should == [@fixture_path.join("folder1/").to_s]
50
+ end
51
+
52
+ it "should catch files update" do
53
+ file1 = @fixture_path.join("folder1/file1.txt")
54
+ file2 = @fixture_path.join("folder1/folder2/file2.txt")
55
+ File.exists?(file1).should be_true
56
+ File.exists?(file2).should be_true
57
+ run
58
+ FileUtils.touch file1
59
+ FileUtils.touch file2
60
+ stop
61
+ @results.should == [@fixture_path.join("folder1/").to_s, @fixture_path.join("folder1/folder2/").to_s]
62
+ end
63
+
64
+ def run
65
+ sleep 1
66
+ Thread.new { @fsevent.run }
67
+ sleep 1
68
+ end
69
+
70
+ def stop
71
+ sleep 1
72
+ @fsevent.stop
73
+ end
74
+
75
+ end
@@ -0,0 +1,24 @@
1
+ require 'rspec'
2
+ require 'rb-fsevent'
3
+
4
+ RSpec.configure do |config|
5
+ config.color_enabled = true
6
+ config.filter_run :focus => true
7
+ config.run_all_when_everything_filtered = true
8
+
9
+ config.before(:each) do
10
+ @fixture_path = Pathname.new(File.expand_path('../fixtures/', __FILE__))
11
+ end
12
+
13
+ config.before(:all) do
14
+ system "cd ext; ruby extconf.rb"
15
+ puts "fsevent_watch compiled"
16
+ end
17
+
18
+ config.after(:all) do
19
+ gem_root = Pathname.new(File.expand_path('../../', __FILE__))
20
+ system "rm -rf #{gem_root.join('bin')}"
21
+ system "rm #{gem_root.join('ext/Makefile')}"
22
+ end
23
+
24
+ end