rb-fsevent 0.9.3 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 73ff82cfa0945136952fd4b6f5d350de06023c02
4
+ data.tar.gz: c5b0b37f14237d9d9c3c3c75f07b78fd1729f397
5
+ SHA512:
6
+ metadata.gz: 91a5b8a87e7dc4e8694100df01d027cbf182508dbd829ad38c3f63482ba489ea50f3f00c8757bc0d263ba203aa2584bfb2225d99d21d2c44f82e26dc9feadfa9
7
+ data.tar.gz: 736de2562e74149e3e0979d0da8b6ebe73d69b851dfbe2f5fd7eb9833919846159d75d330cb136ac685e0b019659c6f38810c52b1f9b224e2bc8a74c79282f45
@@ -0,0 +1,13 @@
1
+ *.gem
2
+ .DS_Store
3
+ .Trashes
4
+ .bundle
5
+ .com.apple.timemachine.supported
6
+ .fseventsd
7
+ .idea
8
+ .rbx
9
+ /ext/build
10
+ Desktop DB
11
+ Desktop DF
12
+ Gemfile.lock
13
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at http://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r(^spec/(.*)_spec.rb))
6
+ watch(%r(^lib/(.*)\.rb)) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { 'spec' }
8
+ end
@@ -1,4 +1,6 @@
1
- Copyright (c) 2010-2012 Thibaud Guillaume-Gentil & Travis Tilley
1
+ Copyright (c) 2010-2014 Thibaud Guillaume-Gentil & Travis Tilley
2
+
3
+ MIT License
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
@@ -0,0 +1,245 @@
1
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/thibaudgg/rb-fsevent)
2
+ [![endorse](https://api.coderwall.com/ttilley/endorsecount.png)](https://coderwall.com/ttilley)
3
+
4
+ # rb-fsevent
5
+
6
+ Very simple & usable Mac OSX FSEvents API
7
+
8
+ * RubyCocoa not required!
9
+ * Signals are working (really)
10
+ * Tested on MRI 1.8.7 & 1.9.3, RBX 2.0.0dev, JRuby
11
+ * Tested on 10.6 -> 10.8 (though 10.5 should work just as well)
12
+
13
+ ## Install
14
+
15
+ gem install rb-fsevent
16
+
17
+ ### re-compilation
18
+
19
+ rb-fsevent comes with a pre-compiled fsevent\_watch binary supporting x86\_64 and i386 on 10.6 and above. The binary is codesigned with my (Travis Tilley) Developer ID as an extra precaution when distributing pre-compiled code and contains an embedded plist describing its build environment. This should be sufficient for most users, but if you need to use rb-fsevent on 10.5 and/or on PPC then recompilation is necessary. This can be done by entering the installed gem's ext directory and running:
20
+
21
+ MACOSX_DEPLOYMENT_TARGET="10.5" CC=/usr/bin/gcc-4.2 rake ppc replace_exe
22
+
23
+ The following ENV vars are recognized:
24
+
25
+ * CC
26
+ * CFLAGS
27
+ * ARCHFLAGS
28
+ * MACOSX\_DEPLOYMENT\_TARGET
29
+ * FWDEBUG (enables debug mode, printing an obscene number of informational
30
+ messages to STDERR)
31
+
32
+ ### embedded plist
33
+
34
+ You can retrieve the values in the embedded plist via the CLI:
35
+
36
+ fsevent_watch --show-plist
37
+
38
+ The output is essentially formatted as `"#{key}:\n #{value}\n"` to make it easier to read than plist style xml. The result looks like this:
39
+
40
+ DTSDKName:
41
+ macosx10.5
42
+ FSEWBuildTriple:
43
+ i386-apple-darwin10.8.0
44
+ FSEWCC:
45
+ /usr/bin/gcc-4.2
46
+ DTSDKPath:
47
+ /Developer/SDKs/MacOSX10.5.sdk
48
+ FSEWCCVersion:
49
+ i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3)
50
+ FSEWCFLAGS:
51
+ -fconstant-cfstrings -fno-strict-aliasing -Wall -mmacosx-version-min=10.5 -O3
52
+
53
+ If, for some perverse reason, you prefer to look at the xml... it can be retrieved via:
54
+
55
+ otool -s __TEXT __info_plist ./bin/fsevent_watch | grep ^0 | xxd -r -
56
+
57
+ ### codesign
58
+
59
+ You can verify code signing information for a specific fsevent\_watch via:
60
+
61
+ codesign -d -vvv ./bin/fsevent_watch
62
+
63
+ If you're using the pre-compiled binary, then the output should contain something to the effect of:
64
+
65
+ Authority=Developer ID Application: Travis Tilley
66
+ Authority=Developer ID Certification Authority
67
+ Authority=Apple Root CA
68
+ Timestamp=Dec 31, 2012 12:49:13 PM
69
+
70
+ ## Usage
71
+
72
+ ### Singular path
73
+
74
+ ```ruby
75
+ require 'rb-fsevent'
76
+
77
+ fsevent = FSEvent.new
78
+ fsevent.watch Dir.pwd do |directories|
79
+ puts "Detected change inside: #{directories.inspect}"
80
+ end
81
+ fsevent.run
82
+ ```
83
+
84
+ ### Multiple paths
85
+
86
+ ```ruby
87
+ require 'rb-fsevent'
88
+
89
+ paths = ['/tmp/path/one', '/tmp/path/two', Dir.pwd]
90
+
91
+ fsevent = FSEvent.new
92
+ fsevent.watch paths do |directories|
93
+ puts "Detected change inside: #{directories.inspect}"
94
+ end
95
+ fsevent.run
96
+ ```
97
+
98
+ ### Multiple paths and additional options as a Hash
99
+
100
+ ```ruby
101
+ require 'rb-fsevent'
102
+
103
+ paths = ['/tmp/path/one', '/tmp/path/two', Dir.pwd]
104
+ options = {:latency => 1.5, :no_defer => true }
105
+
106
+ fsevent = FSEvent.new
107
+ fsevent.watch paths, options do |directories|
108
+ puts "Detected change inside: #{directories.inspect}"
109
+ end
110
+ fsevent.run
111
+ ```
112
+
113
+ ### Multiple paths and additional options as an Array
114
+
115
+ ```ruby
116
+ require 'rb-fsevent'
117
+
118
+ paths = ['/tmp/path/one', '/tmp/path/two', Dir.pwd]
119
+ options = ['--latency', 1.5, '--no-defer']
120
+
121
+ fsevent = FSEvent.new
122
+ fsevent.watch paths, options do |directories|
123
+ puts "Detected change inside: #{directories.inspect}"
124
+ end
125
+ fsevent.run
126
+ ```
127
+
128
+ ## Options
129
+
130
+ When defining options using a hash or hash-like object, it gets checked for validity and converted to the appropriate fsevent\_watch commandline arguments array when the FSEvent class is instantiated. This is obviously the safest and preferred method of passing in options.
131
+
132
+ You may, however, choose to pass in an array of commandline arguments as your options value and it will be passed on, unmodified, to the fsevent\_watch binary when called.
133
+
134
+ So far, the following options are supported:
135
+
136
+ * :latency => 0.5 # in seconds
137
+ * :no\_defer => true
138
+ * :watch\_root => true
139
+ * :since\_when => 18446744073709551615 # an FSEventStreamEventId
140
+ * :file\_events => true
141
+
142
+ ### Latency
143
+
144
+ The :latency parameter determines how long the service should wait after the first event before passing that information along to the client. If your latency is set to 4 seconds, and 300 changes occur in the first three, then the callback will be fired only once. If latency is set to 0.1 in the exact same scenario, you will see that callback fire somewhere closer to between 25 and 30 times.
145
+
146
+ Setting a higher latency value allows for more effective temporal coalescing, resulting in fewer callbacks and greater overall efficiency... at the cost of apparent responsiveness. Setting this to a reasonably high value (and NOT setting :no\_defer) is particularly well suited for background, daemon, or batch processing applications.
147
+
148
+ Implementation note: It appears that FSEvents will only coalesce events from a maximum of 32 distinct subpaths, making the above completely accurate only when events are to fewer than 32 subpaths. Creating 300 files in one directory, for example, or 30 files in 10 subdirectories, but not 300 files within 300 subdirectories. In the latter case, you may receive 31 callbacks in one go after the latency period. As this appears to be an implementation detail, the number could potentially differ across OS revisions. It is entirely possible that this number is somehow configurable, but I have not yet discovered an accepted method of doing so.
149
+
150
+ ### NoDefer
151
+
152
+ The :no\_defer option changes the behavior of the latency parameter completely. Rather than waiting for $latency period of time before sending along events in an attempt to coalesce a potential deluge ahead of time, that first event is sent along to the client immediately and is followed by a $latency period of silence before sending along any additional events that occurred within that period.
153
+
154
+ This behavior is particularly useful for interactive applications where that feeling of apparent responsiveness is most important, but you still don't want to get overwhelmed by a series of events that occur in rapid succession.
155
+
156
+ ### WatchRoot
157
+
158
+ The :watch\_root option allows for catching the scenario where you start watching "~/src/demo\_project" and either it is later renamed to "~/src/awesome\_sauce\_3000" or the path changes in such a manner that the original directory is now at "~/clients/foo/iteration4/demo\_project".
159
+
160
+ Unfortunately, while this behavior is somewhat supported in the fsevent\_watch binary built as part of this project, support for passing across detailed metadata is not (yet). As a result, you would not receive the appropriate RootChanged event and be able to react appropriately. Also, since the C code doesn't open watched directories and retain that file descriptor as part of path-specific callback metadata, we are unable to issue an F\_GETPATH fcntl() to determine the directory's new path.
161
+
162
+ Please do not use this option until proper support is added (or, even better, add it and submit a pull request).
163
+
164
+ ### SinceWhen
165
+
166
+ The FSEventStreamEventId passed in to :since\_when is used as a base for reacting to historic events. Unfortunately, not only is the metadata for transitioning from historic to live events not currently passed along, but it is incorrectly passed as a change event on the root path, and only per-host event streams are currently supported. When using per-host event streams, the event IDs are not guaranteed to be unique or contiguous when shared volumes (firewire/USB/net/etc) are used on multiple macs.
167
+
168
+ Please do not use this option until proper support is added, unless it's acceptable for you to receive that one fake event that's handled incorrectly when events transition from historical to live. Even in that scenario, there's no metadata available for determining the FSEventStreamEventId of the last received event.
169
+
170
+ WARNING: passing in 0 as the parameter to :since\_when will return events for every directory modified since "the beginning of time".
171
+
172
+ ### FileEvents ###
173
+
174
+ Prepare yourself for an obscene number of callbacks. Realistically, an "Atomic Save" could easily fire maybe 6 events for the combination of creating the new file, changing metadata/permissions, writing content, swapping out the old file for the new may itself result in multiple events being fired, and so forth. By the time you get the event for the temporary file being created as part of the atomic save, it will already be gone and swapped with the original file. This and issues of a similar nature have prevented me from adding the option to the ruby code despite the fsevent\_watch binary supporting file level events for quite some time now. Mountain Lion seems to be better at coalescing needless events, but that might just be my imagination.
175
+
176
+ ## Debugging output
177
+
178
+ If the gem is re-compiled with the environment variable FWDEBUG set, then fsevent\_watch will be built with its various DEBUG sections defined, and the output to STDERR is truly verbose (and hopefully helpful in debugging your application and not just fsevent\_watch itself). If enough people find this to be directly useful when developing code that makes use of rb-fsevent, then it wouldn't be hard to clean this up and make it a feature enabled by a commandline argument instead. Until somebody files an issue, however, I will assume otherwise.
179
+
180
+ append_path called for: /tmp/moo/cow/
181
+ resolved path to: /private/tmp/moo/cow
182
+
183
+ config.sinceWhen 18446744073709551615
184
+ config.latency 0.300000
185
+ config.flags 00000000
186
+ config.paths
187
+ /private/tmp/moo/cow
188
+
189
+ FSEventStreamRef @ 0x100108540:
190
+ allocator = 0x7fff705a4ee0
191
+ callback = 0x10000151e
192
+ context = {0, 0x0, 0x0, 0x0, 0x0}
193
+ numPathsToWatch = 1
194
+ pathsToWatch = 0x7fff705a4ee0
195
+ pathsToWatch[0] = '/private/tmp/moo/cow'
196
+ latestEventId = -1
197
+ latency = 300000 (microseconds)
198
+ flags = 0x00000000
199
+ runLoop = 0x0
200
+ runLoopMode = 0x0
201
+
202
+ FSEventStreamCallback fired!
203
+ numEvents: 32
204
+ event path: /private/tmp/moo/cow/1/a/
205
+ event flags: 00000000
206
+ event ID: 1023767
207
+ event path: /private/tmp/moo/cow/1/b/
208
+ event flags: 00000000
209
+ event ID: 1023782
210
+ event path: /private/tmp/moo/cow/1/c/
211
+ event flags: 00000000
212
+ event ID: 1023797
213
+ event path: /private/tmp/moo/cow/1/d/
214
+ event flags: 00000000
215
+ event ID: 1023812
216
+ [etc]
217
+
218
+
219
+ ## Note about FFI
220
+
221
+ rb-fsevent doesn't use [ruby-ffi](http://github.com/ffi/ffi) anymore because it sadly doesn't allow for catching Signals. You can still see the code in the [ffi branch](http://github.com/thibaudgg/rb-fsevent/tree/ffi).
222
+
223
+ ## Development
224
+
225
+ * Source hosted at [GitHub](http://github.com/thibaudgg/rb-fsevent)
226
+ * Report issues/Questions/Feature requests on [GitHub Issues](http://github.com/thibaudgg/rb-fsevent/issues)
227
+
228
+ Pull requests are quite welcome! Please ensure that your commits are in a topic branch for each individual changeset that can be reasonably isolated. It is also important to ensure that your changes are well tested... whether that means new tests, modified tests, or fixing a scenario where the existing tests currently fail. If you have rbenv and ruby-build, we have a helper task for running the testsuite in all of them:
229
+
230
+ rake spec:portability
231
+
232
+ The list of tested targets is currently:
233
+
234
+ %w[1.8.7-p371 1.9.3-p362 2.0.0-dev rbx-2.0.0-dev jruby-1.7.1]
235
+
236
+ ## Donations
237
+
238
+ rb-fsevent is truly free software. The license is quite liberal (you don't even have to contribute back your changes). If, however, you'd like to donate as a way of showing support for the project and its continued development:
239
+
240
+ [![Donate Bitcoins](https://d2o7j92jk8qjiw.cloudfront.net/assets/buttons/donation_small-2d08f8cd93c98acf496e0411cc6a5262.png)](https://coinbase.com/checkouts/5233986321e2217499bd6ef91f679aa4?c=rb-fsevent)
241
+
242
+ ## Authors
243
+
244
+ * [Travis Tilley](http://github.com/ttilley)
245
+ * [Thibaud Guillaume-Gentil](http://github.com/thibaudgg)
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ task :default => :spec
8
+
9
+ namespace(:spec) do
10
+ desc "Run all specs on multiple ruby versions"
11
+ task(:portability) do
12
+ versions = %w[1.8.7-p371 1.9.3-p362 2.0.0-dev rbx-2.0.0-dev jruby-1.7.1]
13
+ versions.each do |version|
14
+ # system <<-BASH
15
+ # bash -c 'source ~/.rvm/scripts/rvm;
16
+ # rvm #{version};
17
+ # echo "--------- version #{version} ----------\n";
18
+ # bundle install;
19
+ # rake spec'
20
+ # BASH
21
+ system <<-BASH
22
+ bash -c 'export PATH="$HOME/.rbenv/bin:$PATH";
23
+ [[ `which rbenv` ]] && eval "$(rbenv init -)";
24
+ [[ ! -a $HOME/.rbenv/versions/#{version} ]] && rbenv install #{version};
25
+ rbenv shell #{version};
26
+ rbenv which bundle 2> /dev/null || gem install bundler;
27
+ bundle install;
28
+ rake spec;'
29
+ BASH
30
+ end
31
+ end
32
+ end
Binary file
Binary file
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 Travis Tilley
1
+ Copyright (c) 2011-2013 Travis Tilley
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -6,6 +6,7 @@ const char* cli_info_usage = "Usage: fsevent_watch [OPTIONS]... [PATHS]...";
6
6
  const char* cli_info_help[] = {
7
7
  " -h, --help you're looking at it",
8
8
  " -V, --version print version number and exit",
9
+ " -p, --show-plist display the embedded Info.plist values",
9
10
  " -s, --since-when=EventID fire historical events since ID",
10
11
  " -l, --latency=seconds latency period (default='0.5')",
11
12
  " -n, --no-defer enable no-defer latency modifier",
@@ -25,6 +26,7 @@ static void default_args (struct cli_info* args_info)
25
26
  args_info->watch_root_flag = false;
26
27
  args_info->ignore_self_flag = false;
27
28
  args_info->file_events_flag = false;
29
+ args_info->mark_self_flag = false;
28
30
  args_info->format_arg = kFSEventWatchOutputFormatClassic;
29
31
  }
30
32
 
@@ -56,12 +58,46 @@ void cli_parser_free (struct cli_info* args_info)
56
58
  cli_parser_release(args_info);
57
59
  }
58
60
 
61
+ static void cli_print_info_dict (const void *key,
62
+ const void *value,
63
+ void *context)
64
+ {
65
+ CFStringRef entry = CFStringCreateWithFormat(NULL, NULL,
66
+ CFSTR("%@:\n %@"), key, value);
67
+ if (entry) {
68
+ CFShow(entry);
69
+ CFRelease(entry);
70
+ }
71
+ }
72
+
73
+ void cli_show_plist (void)
74
+ {
75
+ CFBundleRef mainBundle = CFBundleGetMainBundle();
76
+ CFRetain(mainBundle);
77
+ CFDictionaryRef mainBundleDict = CFBundleGetInfoDictionary(mainBundle);
78
+ if (mainBundleDict) {
79
+ CFRetain(mainBundleDict);
80
+ printf("Embedded Info.plist metadata:\n\n");
81
+ CFDictionaryApplyFunction(mainBundleDict, cli_print_info_dict, NULL);
82
+ CFRelease(mainBundleDict);
83
+ }
84
+ CFRelease(mainBundle);
85
+ printf("\n");
86
+ }
87
+
59
88
  void cli_print_version (void)
60
89
  {
61
- printf("%s %s\n", CLI_NAME, CLI_VERSION);
90
+ printf("%s %s\n\n", CLI_NAME, CLI_VERSION);
62
91
  #ifdef COMPILED_AT
63
- printf("Compiled %s\n", COMPILED_AT);
92
+ printf("Compiled at: %s\n", COMPILED_AT);
93
+ #endif
94
+ #ifdef COMPILER
95
+ printf("Compiled with: %s\n", COMPILER);
96
+ #endif
97
+ #ifdef TARGET_CPU
98
+ printf("Compiled for: %s\n", TARGET_CPU);
64
99
  #endif
100
+ printf("\n");
65
101
  }
66
102
 
67
103
  void cli_print_help (void)
@@ -83,17 +119,19 @@ int cli_parser (int argc, const char** argv, struct cli_info* args_info)
83
119
  static struct option longopts[] = {
84
120
  { "help", no_argument, NULL, 'h' },
85
121
  { "version", no_argument, NULL, 'V' },
122
+ { "show-plist", no_argument, NULL, 'p' },
86
123
  { "since-when", required_argument, NULL, 's' },
87
124
  { "latency", required_argument, NULL, 'l' },
88
125
  { "no-defer", no_argument, NULL, 'n' },
89
126
  { "watch-root", no_argument, NULL, 'r' },
90
127
  { "ignore-self", no_argument, NULL, 'i' },
91
128
  { "file-events", no_argument, NULL, 'F' },
129
+ { "mark-self", no_argument, NULL, 'm' },
92
130
  { "format", required_argument, NULL, 'f' },
93
131
  { 0, 0, 0, 0 }
94
132
  };
95
133
 
96
- const char* shortopts = "hVs:l:nriFf:";
134
+ const char* shortopts = "hVps:l:nriFf:";
97
135
 
98
136
  int c = -1;
99
137
 
@@ -117,6 +155,9 @@ int cli_parser (int argc, const char** argv, struct cli_info* args_info)
117
155
  case 'F': // file-events
118
156
  args_info->file_events_flag = true;
119
157
  break;
158
+ case 'm': // mark-self
159
+ args_info->mark_self_flag = true;
160
+ break;
120
161
  case 'f': // format
121
162
  if (strcmp(optarg, "classic") == 0) {
122
163
  args_info->format_arg = kFSEventWatchOutputFormatClassic;
@@ -134,6 +175,9 @@ int cli_parser (int argc, const char** argv, struct cli_info* args_info)
134
175
  case 'V': // version
135
176
  cli_print_version();
136
177
  exit(EXIT_SUCCESS);
178
+ case 'p': // show-plist
179
+ cli_show_plist();
180
+ exit(EXIT_SUCCESS);
137
181
  case 'h': // help
138
182
  case '?': // invalid option
139
183
  case ':': // missing argument
@@ -23,6 +23,7 @@ struct cli_info {
23
23
  bool watch_root_flag;
24
24
  bool ignore_self_flag;
25
25
  bool file_events_flag;
26
+ bool mark_self_flag;
26
27
  enum FSEventWatchOutputFormat format_arg;
27
28
 
28
29
  char** inputs;