middleman-core 3.0.0.alpha.7 → 3.0.0.alpha.8
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/features/preview_changes.feature +12 -1
- data/lib/middleman-core/base.rb +1 -1
- data/lib/middleman-core/cli/server.rb +1 -1
- data/lib/middleman-core/core_extensions/file_watcher.rb +2 -0
- data/lib/middleman-core/core_extensions/front_matter.rb +0 -1
- data/lib/middleman-core/core_extensions/rendering.rb +8 -1
- data/lib/middleman-core/renderers/erb.rb +10 -0
- data/lib/middleman-core/step_definitions/middleman_steps.rb +10 -8
- data/lib/middleman-core/step_definitions/server_steps.rb +4 -3
- data/lib/middleman-core/vendor/rb-fsevent-0.4.3.1/LICENSE +20 -0
- data/lib/middleman-core/vendor/rb-fsevent-0.4.3.1/README.rdoc +254 -0
- data/lib/middleman-core/vendor/rb-fsevent-0.4.3.1/ext/extconf.rb +61 -0
- data/lib/middleman-core/vendor/rb-fsevent-0.4.3.1/ext/fsevent/fsevent_watch.c +226 -0
- data/lib/middleman-core/vendor/rb-fsevent-0.4.3.1/lib/rb-fsevent/fsevent.rb +105 -0
- data/lib/middleman-core/vendor/rb-fsevent-0.4.3.1/lib/rb-fsevent/version.rb +3 -0
- data/lib/middleman-core/vendor/rb-fsevent-0.4.3.1/lib/rb-fsevent.rb +2 -0
- data/lib/middleman-core/vendor/rb-inotify-0.8.8/.yardopts +4 -0
- data/lib/middleman-core/vendor/rb-inotify-0.8.8/MIT-LICENSE +20 -0
- data/lib/middleman-core/vendor/rb-inotify-0.8.8/README.md +66 -0
- data/lib/middleman-core/vendor/rb-inotify-0.8.8/Rakefile +54 -0
- data/lib/middleman-core/vendor/rb-inotify-0.8.8/VERSION +1 -0
- data/lib/middleman-core/vendor/rb-inotify-0.8.8/lib/rb-inotify/event.rb +139 -0
- data/lib/middleman-core/vendor/rb-inotify-0.8.8/lib/rb-inotify/native/flags.rb +89 -0
- data/lib/middleman-core/vendor/rb-inotify-0.8.8/lib/rb-inotify/native.rb +31 -0
- data/lib/middleman-core/vendor/rb-inotify-0.8.8/lib/rb-inotify/notifier.rb +308 -0
- data/lib/middleman-core/vendor/rb-inotify-0.8.8/lib/rb-inotify/watcher.rb +83 -0
- data/lib/middleman-core/vendor/rb-inotify-0.8.8/lib/rb-inotify.rb +17 -0
- data/lib/middleman-core/vendor/rb-inotify-0.8.8/rb-inotify.gemspec +53 -0
- data/lib/middleman-core/version.rb +1 -1
- data/lib/middleman-core/{guard.rb → watcher.rb} +62 -58
- data/lib/middleman-core.rb +1 -1
- data/middleman-core.gemspec +1 -1
- metadata +29 -10
@@ -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,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')}"
|
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,20 @@
|
|
1
|
+
Copyright (c) 2009 Nathan Weizenbaum
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# rb-inotify
|
2
|
+
|
3
|
+
This is a simple wrapper over the [inotify](http://en.wikipedia.org/wiki/Inotify) Linux kernel subsystem
|
4
|
+
for monitoring changes to files and directories.
|
5
|
+
It uses the [FFI](http://wiki.github.com/ffi/ffi) gem to avoid having to compile a C extension.
|
6
|
+
|
7
|
+
[API documentation is available on rdoc.info](http://rdoc.info/projects/nex3/rb-inotify).
|
8
|
+
|
9
|
+
## Basic Usage
|
10
|
+
|
11
|
+
The API is similar to the inotify C API, but with a more Rubyish feel.
|
12
|
+
First, create a notifier:
|
13
|
+
|
14
|
+
notifier = INotify::Notifier.new
|
15
|
+
|
16
|
+
Then, tell it to watch the paths you're interested in
|
17
|
+
for the events you care about:
|
18
|
+
|
19
|
+
notifier.watch("path/to/foo.txt", :modify) {puts "foo.txt was modified!"}
|
20
|
+
notifier.watch("path/to/bar", :moved_to, :create) do |event|
|
21
|
+
puts "#{event.name} is now in path/to/bar!"
|
22
|
+
end
|
23
|
+
|
24
|
+
Inotify can watch directories or individual files.
|
25
|
+
It can pay attention to all sorts of events;
|
26
|
+
for a full list, see [the inotify man page](http://www.tin.org/bin/man.cgi?section=7&topic=inotify).
|
27
|
+
|
28
|
+
Finally, you get at the events themselves:
|
29
|
+
|
30
|
+
notifier.run
|
31
|
+
|
32
|
+
This will loop infinitely, calling the appropriate callbacks when the files are changed.
|
33
|
+
If you don't want infinite looping,
|
34
|
+
you can also block until there are available events,
|
35
|
+
process them all at once,
|
36
|
+
and then continue on your merry way:
|
37
|
+
|
38
|
+
notifier.process
|
39
|
+
|
40
|
+
## Advanced Usage
|
41
|
+
|
42
|
+
Sometimes it's necessary to have finer control over the underlying IO operations
|
43
|
+
than is provided by the simple callback API.
|
44
|
+
The trick to this is that the \{INotify::Notifier#to_io Notifier#to_io} method
|
45
|
+
returns a fully-functional IO object,
|
46
|
+
with a file descriptor and everything.
|
47
|
+
This means, for example, that it can be passed to `IO#select`:
|
48
|
+
|
49
|
+
# Wait 10 seconds for an event then give up
|
50
|
+
if IO.select([notifier.to_io], [], [], 10)
|
51
|
+
notifier.process
|
52
|
+
end
|
53
|
+
|
54
|
+
It can even be used with EventMachine:
|
55
|
+
|
56
|
+
require 'eventmachine'
|
57
|
+
|
58
|
+
EM.run do
|
59
|
+
EM.watch notifier.to_io do
|
60
|
+
notifier.process
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Unfortunately, this currently doesn't work under JRuby.
|
65
|
+
JRuby currently doesn't use native file descriptors for the IO object,
|
66
|
+
so we can't use the notifier's file descriptor as a stand-in.
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "rb-inotify"
|
8
|
+
gem.summary = "A Ruby wrapper for Linux's inotify, using FFI"
|
9
|
+
gem.description = gem.summary
|
10
|
+
gem.email = "nex342@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/nex3/rb-inotify"
|
12
|
+
gem.authors = ["Nathan Weizenbaum"]
|
13
|
+
gem.add_dependency "ffi", ">= 0.5.0"
|
14
|
+
gem.add_development_dependency "yard", ">= 0.4.0"
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
task(:permissions) {sh %{chmod -R a+r .}}
|
22
|
+
Rake::Task[:build].prerequisites.unshift('permissions')
|
23
|
+
|
24
|
+
module Jeweler::VersionHelper::PlaintextExtension
|
25
|
+
def write_with_inotify
|
26
|
+
write_without_inotify
|
27
|
+
filename = File.join(File.dirname(__FILE__), "lib/rb-inotify.rb")
|
28
|
+
text = File.read(filename)
|
29
|
+
File.open(filename, 'w') do |f|
|
30
|
+
f.write text.gsub(/^( VERSION = ).*/, '\1' + [major, minor, patch].inspect)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias_method :write_without_inotify, :write
|
34
|
+
alias_method :write, :write_with_inotify
|
35
|
+
end
|
36
|
+
|
37
|
+
class Jeweler::Commands::Version::Base
|
38
|
+
def commit_version_with_inotify
|
39
|
+
return unless self.repo
|
40
|
+
self.repo.add(File.join(File.dirname(__FILE__), "lib/rb-inotify.rb"))
|
41
|
+
commit_version_without_inotify
|
42
|
+
end
|
43
|
+
alias_method :commit_version_without_inotify, :commit_version
|
44
|
+
alias_method :commit_version, :commit_version_with_inotify
|
45
|
+
end
|
46
|
+
|
47
|
+
begin
|
48
|
+
require 'yard'
|
49
|
+
YARD::Rake::YardocTask.new
|
50
|
+
rescue LoadError
|
51
|
+
task :yardoc do
|
52
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
0.8.8
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module INotify
|
2
|
+
# An event caused by a change on the filesystem.
|
3
|
+
# Each {Watcher} can fire many events,
|
4
|
+
# which are passed to that watcher's callback.
|
5
|
+
class Event
|
6
|
+
# A list of other events that are related to this one.
|
7
|
+
# Currently, this is only used for files that are moved within the same directory:
|
8
|
+
# the `:moved_from` and the `:moved_to` events will be related.
|
9
|
+
#
|
10
|
+
# @return [Array<Event>]
|
11
|
+
attr_reader :related
|
12
|
+
|
13
|
+
# The name of the file that the event occurred on.
|
14
|
+
# This is only set for events that occur on files in directories;
|
15
|
+
# otherwise, it's `""`.
|
16
|
+
# Similarly, if the event is being fired for the directory itself
|
17
|
+
# the name will be `""`
|
18
|
+
#
|
19
|
+
# This pathname is relative to the enclosing directory.
|
20
|
+
# For the absolute pathname, use \{#absolute\_name}.
|
21
|
+
# Note that when the `:recursive` flag is passed to {Notifier#watch},
|
22
|
+
# events in nested subdirectories will still have a `#name` field
|
23
|
+
# relative to their immediately enclosing directory.
|
24
|
+
# For example, an event on the file `"foo/bar/baz"`
|
25
|
+
# will have name `"baz"`.
|
26
|
+
#
|
27
|
+
# @return [String]
|
28
|
+
attr_reader :name
|
29
|
+
|
30
|
+
# The {Notifier} that fired this event.
|
31
|
+
#
|
32
|
+
# @return [Notifier]
|
33
|
+
attr_reader :notifier
|
34
|
+
|
35
|
+
# An integer specifying that this event is related to some other event,
|
36
|
+
# which will have the same cookie.
|
37
|
+
#
|
38
|
+
# Currently, this is only used for files that are moved within the same directory.
|
39
|
+
# Both the `:moved_from` and the `:moved_to` events will have the same cookie.
|
40
|
+
#
|
41
|
+
# @private
|
42
|
+
# @return [Fixnum]
|
43
|
+
attr_reader :cookie
|
44
|
+
|
45
|
+
# The {Watcher#id id} of the {Watcher} that fired this event.
|
46
|
+
#
|
47
|
+
# @private
|
48
|
+
# @return [Fixnum]
|
49
|
+
attr_reader :watcher_id
|
50
|
+
|
51
|
+
# Returns the {Watcher} that fired this event.
|
52
|
+
#
|
53
|
+
# @return [Watcher]
|
54
|
+
def watcher
|
55
|
+
@watcher ||= @notifier.watchers[@watcher_id]
|
56
|
+
end
|
57
|
+
|
58
|
+
# The absolute path of the file that the event occurred on.
|
59
|
+
#
|
60
|
+
# This is actually only as absolute as the path passed to the {Watcher}
|
61
|
+
# that created this event.
|
62
|
+
# However, it is relative to the working directory,
|
63
|
+
# assuming that hasn't changed since the watcher started.
|
64
|
+
#
|
65
|
+
# @return [String]
|
66
|
+
def absolute_name
|
67
|
+
return watcher.path if name.empty?
|
68
|
+
return File.join(watcher.path, name)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the flags that describe this event.
|
72
|
+
# This is generally similar to the input to {Notifier#watch},
|
73
|
+
# except that it won't contain options flags nor `:all_events`,
|
74
|
+
# and it may contain one or more of the following flags:
|
75
|
+
#
|
76
|
+
# `:unmount`
|
77
|
+
# : The filesystem containing the watched file or directory was unmounted.
|
78
|
+
#
|
79
|
+
# `:ignored`
|
80
|
+
# : The \{#watcher watcher} was closed, or the watched file or directory was deleted.
|
81
|
+
#
|
82
|
+
# `:isdir`
|
83
|
+
# : The subject of this event is a directory.
|
84
|
+
#
|
85
|
+
# @return [Array<Symbol>]
|
86
|
+
def flags
|
87
|
+
@flags ||= Native::Flags.from_mask(@native[:mask])
|
88
|
+
end
|
89
|
+
|
90
|
+
# Constructs an {Event} object from a string of binary data,
|
91
|
+
# and destructively modifies the string to get rid of the initial segment
|
92
|
+
# used to construct the Event.
|
93
|
+
#
|
94
|
+
# @private
|
95
|
+
# @param data [String] The string to be modified
|
96
|
+
# @param notifier [Notifier] The {Notifier} that fired the event
|
97
|
+
# @return [Event, nil] The event, or `nil` if the string is empty
|
98
|
+
def self.consume(data, notifier)
|
99
|
+
return nil if data.empty?
|
100
|
+
ev = new(data, notifier)
|
101
|
+
data.replace data[ev.size..-1]
|
102
|
+
ev
|
103
|
+
end
|
104
|
+
|
105
|
+
# Creates an event from a string of binary data.
|
106
|
+
# Differs from {Event.consume} in that it doesn't modify the string.
|
107
|
+
#
|
108
|
+
# @private
|
109
|
+
# @param data [String] The data string
|
110
|
+
# @param notifier [Notifier] The {Notifier} that fired the event
|
111
|
+
def initialize(data, notifier)
|
112
|
+
ptr = FFI::MemoryPointer.from_string(data)
|
113
|
+
@native = Native::Event.new(ptr)
|
114
|
+
@related = []
|
115
|
+
@cookie = @native[:cookie]
|
116
|
+
@name = data[@native.size, @native[:len]].gsub(/\0+$/, '')
|
117
|
+
@notifier = notifier
|
118
|
+
@watcher_id = @native[:wd]
|
119
|
+
|
120
|
+
raise Exception.new("inotify event queue has overflowed.") if @native[:mask] & Native::Flags::IN_Q_OVERFLOW != 0
|
121
|
+
end
|
122
|
+
|
123
|
+
# Calls the callback of the watcher that fired this event,
|
124
|
+
# passing in the event itself.
|
125
|
+
#
|
126
|
+
# @private
|
127
|
+
def callback!
|
128
|
+
watcher.callback!(self)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns the size of this event object in bytes,
|
132
|
+
# including the \{#name} string.
|
133
|
+
#
|
134
|
+
# @return [Fixnum]
|
135
|
+
def size
|
136
|
+
@native.size + @native[:len]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|