ruby-fsevent 0.1.0
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/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.md +46 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/examples/print_changes.rb +17 -0
- data/ext/extconf.rb +4 -0
- data/ext/fsevent.c +124 -0
- data/ruby-fsevent.gemspec +58 -0
- data/spec/fsevent_spec.rb +31 -0
- data/spec/spec_helper.rb +4 -0
- metadata +77 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Sandro Turriate
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
ruby-fsevent
|
2
|
+
============
|
3
|
+
|
4
|
+
A native extension exposing the OS X FSEvent API.
|
5
|
+
Register directories you want to watch and a callback will fire whenever a
|
6
|
+
change occurs in the registered directories.
|
7
|
+
|
8
|
+
I got tired of reinstalling RubyCocoa every time I installed a new version of
|
9
|
+
Ruby just to rspactor working. Watchr has changed the game by allowing generic
|
10
|
+
file handlers but I started waiting 4-5 seconds for an event to fire. With a
|
11
|
+
generic, native interface to the FSEvent API we can harness the speed of
|
12
|
+
FSEvents without depending on RubyCocoa.
|
13
|
+
|
14
|
+
Demo
|
15
|
+
----
|
16
|
+
|
17
|
+
1. cd ext/
|
18
|
+
2. ruby extconf.rb
|
19
|
+
3. make
|
20
|
+
4. cd ../
|
21
|
+
5. ruby examples/print_changes.rb
|
22
|
+
6. Notice that the examples directory and the /tmp directories are being monitored
|
23
|
+
7. Make a change to either directory and watch the callback fire
|
24
|
+
|
25
|
+
TODO
|
26
|
+
----
|
27
|
+
|
28
|
+
* Add ability to register a block as a callback handler, on_change would then
|
29
|
+
call the block. This removes the need for subclassing.
|
30
|
+
|
31
|
+
Note on Patches/Pull Requests
|
32
|
+
-----------------------------
|
33
|
+
|
34
|
+
* Fork the project.
|
35
|
+
* Make your feature addition or bug fix.
|
36
|
+
* Add tests for it. This is important so I don't break it in a
|
37
|
+
future version unintentionally.
|
38
|
+
* Commit, do not mess with rakefile, version, or history.
|
39
|
+
(if you want to have your own version, that is fine but
|
40
|
+
bump version in a commit by itself I can ignore when I pull)
|
41
|
+
* Send me a pull request. Bonus points for topic branches.
|
42
|
+
|
43
|
+
Copyright
|
44
|
+
---------
|
45
|
+
|
46
|
+
Copyright (c) 2009 Sandro Turriate. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "ruby-fsevent"
|
8
|
+
gem.summary = %Q{A native extension exposing the OS X FSEvent API.}
|
9
|
+
gem.description = %Q{
|
10
|
+
A native extension exposing the OS X FSEvent API. Register directories you want to watch and a callback will fire whenever a change occurs in the registered directories.
|
11
|
+
}
|
12
|
+
gem.email = "sandro.turriate@gmail.com"
|
13
|
+
gem.homepage = "http://github.com/sandro/ruby-fsevent"
|
14
|
+
gem.authors = ["Sandro Turriate"]
|
15
|
+
gem.add_development_dependency "rspec"
|
16
|
+
gem.extensions << 'ext/extconf.rb'
|
17
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
|
+
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'spec/rake/spectask'
|
25
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
26
|
+
spec.libs << 'lib' << 'spec'
|
27
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
28
|
+
end
|
29
|
+
|
30
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
31
|
+
spec.libs << 'lib' << 'spec'
|
32
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
33
|
+
spec.rcov = true
|
34
|
+
end
|
35
|
+
|
36
|
+
task :spec => :check_dependencies
|
37
|
+
|
38
|
+
task :default => :spec
|
39
|
+
|
40
|
+
require 'rake/rdoctask'
|
41
|
+
Rake::RDocTask.new do |rdoc|
|
42
|
+
if File.exist?('VERSION')
|
43
|
+
version = File.read('VERSION')
|
44
|
+
else
|
45
|
+
version = ""
|
46
|
+
end
|
47
|
+
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = "ruby-fsevent #{version}"
|
50
|
+
rdoc.rdoc_files.include('README*')
|
51
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../ext/fsevent'
|
2
|
+
|
3
|
+
class PrintChange < FSEvent
|
4
|
+
def on_change(directories)
|
5
|
+
puts "Detected change in: #{directories.inspect}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def run
|
9
|
+
puts "watching #{registered_directories.join(", ")} for changes"
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
printer = PrintChange.new
|
15
|
+
printer.latency = 0.2
|
16
|
+
printer.watch_directories %W(#{Dir.pwd} /tmp)
|
17
|
+
printer.run
|
data/ext/extconf.rb
ADDED
data/ext/fsevent.c
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <stdio.h>
|
3
|
+
#include <stdlib.h>
|
4
|
+
#include <unistd.h>
|
5
|
+
#include <signal.h>
|
6
|
+
#include <CoreServices/CoreServices.h>
|
7
|
+
|
8
|
+
/*
|
9
|
+
* Thanks to fswatch.c for providing a starting point
|
10
|
+
* http://github.com/alandipert/fswatch
|
11
|
+
*/
|
12
|
+
|
13
|
+
|
14
|
+
void callback(
|
15
|
+
ConstFSEventStreamRef streamRef,
|
16
|
+
void *context,
|
17
|
+
size_t numEvents,
|
18
|
+
void *eventPaths,
|
19
|
+
const FSEventStreamEventFlags eventFlags[],
|
20
|
+
const FSEventStreamEventId eventIds[]
|
21
|
+
) {
|
22
|
+
VALUE self = (VALUE)context;
|
23
|
+
int i;
|
24
|
+
char **paths = eventPaths;
|
25
|
+
VALUE rb_paths[numEvents];
|
26
|
+
for (i = 0; i < numEvents; i++) {
|
27
|
+
VALUE name = rb_str_new2(paths[i]);
|
28
|
+
rb_paths[i] = name;
|
29
|
+
}
|
30
|
+
rb_funcall(self, rb_intern("on_change"), 1, rb_ary_new4(numEvents, rb_paths));
|
31
|
+
}
|
32
|
+
|
33
|
+
void watch_directory(VALUE self) {
|
34
|
+
VALUE rb_registered_directories = rb_iv_get(self, "@registered_directories");
|
35
|
+
int i, dir_size;
|
36
|
+
dir_size = RARRAY(rb_registered_directories)->len;
|
37
|
+
|
38
|
+
VALUE *rb_dir_names = RARRAY(rb_registered_directories)->ptr;
|
39
|
+
CFStringRef dir_names[dir_size];
|
40
|
+
for (i = 0; i < dir_size; i++) {
|
41
|
+
dir_names[i] = CFStringCreateWithCString(NULL, (char *)RSTRING(rb_dir_names[i])->ptr, kCFStringEncodingUTF8);
|
42
|
+
}
|
43
|
+
|
44
|
+
CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&dir_names, dir_size, NULL);
|
45
|
+
|
46
|
+
|
47
|
+
VALUE rb_latency = rb_iv_get(self, "@latency");
|
48
|
+
CFAbsoluteTime latency = NUM2DBL(rb_latency);
|
49
|
+
|
50
|
+
FSEventStreamContext context;
|
51
|
+
context.version = 0;
|
52
|
+
context.info = (VALUE *)self;
|
53
|
+
context.retain = NULL;
|
54
|
+
context.release = NULL;
|
55
|
+
context.copyDescription = NULL;
|
56
|
+
|
57
|
+
FSEventStreamRef stream;
|
58
|
+
stream = FSEventStreamCreate(NULL,
|
59
|
+
&callback,
|
60
|
+
&context,
|
61
|
+
pathsToWatch,
|
62
|
+
kFSEventStreamEventIdSinceNow,
|
63
|
+
latency,
|
64
|
+
kFSEventStreamCreateFlagNone
|
65
|
+
);
|
66
|
+
|
67
|
+
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
68
|
+
FSEventStreamStart(stream);
|
69
|
+
CFRunLoopRun();
|
70
|
+
}
|
71
|
+
|
72
|
+
static VALUE t_init(VALUE self) {
|
73
|
+
rb_iv_set(self, "@latency", rb_float_new(0.5));
|
74
|
+
return self;
|
75
|
+
}
|
76
|
+
|
77
|
+
static VALUE t_on_change(VALUE self, VALUE original_directory_name) {
|
78
|
+
return Qnil;
|
79
|
+
}
|
80
|
+
|
81
|
+
static VALUE t_watch_directories(VALUE self, VALUE directories) {
|
82
|
+
if (TYPE(directories) == T_ARRAY) {
|
83
|
+
rb_iv_set(self, "@registered_directories", rb_ary_new4(RARRAY(directories)->len, RARRAY(directories)->ptr));
|
84
|
+
}
|
85
|
+
else {
|
86
|
+
rb_iv_set(self, "@registered_directories", rb_ary_new3(1, directories));
|
87
|
+
}
|
88
|
+
VALUE rb_registered_directories = rb_iv_get(self, "@registered_directories");
|
89
|
+
return rb_registered_directories;
|
90
|
+
}
|
91
|
+
|
92
|
+
int pid, status;
|
93
|
+
static VALUE t_run(VALUE self) {
|
94
|
+
VALUE rb_registered_directories = rb_iv_get(self, "@registered_directories");
|
95
|
+
Check_Type(rb_registered_directories, T_ARRAY);
|
96
|
+
if (pid = fork()) {
|
97
|
+
wait(&status);
|
98
|
+
}
|
99
|
+
else {
|
100
|
+
watch_directory(self);
|
101
|
+
}
|
102
|
+
return self;
|
103
|
+
}
|
104
|
+
|
105
|
+
void kill_watcher() {
|
106
|
+
if (pid) {
|
107
|
+
kill(pid, SIGKILL);
|
108
|
+
printf("\n");
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
VALUE fsevent_class;
|
113
|
+
void Init_fsevent() {
|
114
|
+
fsevent_class = rb_define_class("FSEvent", rb_cObject);
|
115
|
+
rb_define_method(fsevent_class, "initialize", t_init, 0);
|
116
|
+
rb_define_method(fsevent_class, "on_change", t_on_change, 1);
|
117
|
+
rb_define_method(fsevent_class, "watch_directories", t_watch_directories, 1);
|
118
|
+
rb_define_method(fsevent_class, "run", t_run, 0);
|
119
|
+
|
120
|
+
rb_define_attr(fsevent_class, "latency", 1, 1);
|
121
|
+
rb_define_attr(fsevent_class, "registered_directories", 1, 1);
|
122
|
+
|
123
|
+
atexit(kill_watcher);
|
124
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{ruby-fsevent}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Sandro Turriate"]
|
12
|
+
s.date = %q{2009-10-15}
|
13
|
+
s.description = %q{
|
14
|
+
A native extension exposing the OS X FSEvent API. Register directories you want to watch and a callback will fire whenever a change occurs in the registered directories.
|
15
|
+
}
|
16
|
+
s.email = %q{sandro.turriate@gmail.com}
|
17
|
+
s.extensions = ["ext/extconf.rb"]
|
18
|
+
s.extra_rdoc_files = [
|
19
|
+
"LICENSE",
|
20
|
+
"README.md"
|
21
|
+
]
|
22
|
+
s.files = [
|
23
|
+
".gitignore",
|
24
|
+
"LICENSE",
|
25
|
+
"README.md",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"examples/print_changes.rb",
|
29
|
+
"ext/extconf.rb",
|
30
|
+
"ext/fsevent.c",
|
31
|
+
"ruby-fsevent.gemspec",
|
32
|
+
"spec/fsevent_spec.rb",
|
33
|
+
"spec/spec_helper.rb"
|
34
|
+
]
|
35
|
+
s.homepage = %q{http://github.com/sandro/ruby-fsevent}
|
36
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubygems_version = %q{1.3.5}
|
39
|
+
s.summary = %q{A native extension exposing the OS X FSEvent API.}
|
40
|
+
s.test_files = [
|
41
|
+
"spec/fsevent_spec.rb",
|
42
|
+
"spec/spec_helper.rb",
|
43
|
+
"examples/print_changes.rb"
|
44
|
+
]
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
51
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
54
|
+
end
|
55
|
+
else
|
56
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe FSEvent do
|
4
|
+
describe "accessors" do
|
5
|
+
it "reads and writes registered_directories" do
|
6
|
+
subject.registered_directories = %w(one two)
|
7
|
+
subject.registered_directories.should == %w(one two)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "reads and writes latency" do
|
11
|
+
subject.latency = 1.5
|
12
|
+
subject.latency.should == 1.5
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#watch_directories" do
|
17
|
+
it "register a single directory" do
|
18
|
+
subject.watch_directories '/Users'
|
19
|
+
subject.registered_directories.should == ['/Users']
|
20
|
+
end
|
21
|
+
it "registers an array of directories" do
|
22
|
+
subject.watch_directories %w(/Users /tmp)
|
23
|
+
subject.registered_directories.should == %w(/Users /tmp)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "API" do
|
28
|
+
it { should respond_to(:on_change) }
|
29
|
+
it { should respond_to(:run) }
|
30
|
+
end
|
31
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-fsevent
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sandro Turriate
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-15 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: "\n A native extension exposing the OS X FSEvent API. Register directories you want to watch and a callback will fire whenever a change occurs in the registered directories.\n "
|
26
|
+
email: sandro.turriate@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions:
|
30
|
+
- ext/extconf.rb
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.md
|
34
|
+
files:
|
35
|
+
- .gitignore
|
36
|
+
- LICENSE
|
37
|
+
- README.md
|
38
|
+
- Rakefile
|
39
|
+
- VERSION
|
40
|
+
- examples/print_changes.rb
|
41
|
+
- ext/extconf.rb
|
42
|
+
- ext/fsevent.c
|
43
|
+
- ruby-fsevent.gemspec
|
44
|
+
- spec/fsevent_spec.rb
|
45
|
+
- spec/spec_helper.rb
|
46
|
+
has_rdoc: true
|
47
|
+
homepage: http://github.com/sandro/ruby-fsevent
|
48
|
+
licenses: []
|
49
|
+
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options:
|
52
|
+
- --charset=UTF-8
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.3.5
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: A native extension exposing the OS X FSEvent API.
|
74
|
+
test_files:
|
75
|
+
- spec/fsevent_spec.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
- examples/print_changes.rb
|