Subject: | Improvements to Filesys::Notify::Simple (with patch) |
Hello,
Here is a patch with some improvements I made to Filesys::Notify::Simple. These are the
changes:
* Adds options to the constructor, including:
* Details: Set to 0 to get a single notification of some change, instead of one notification
for each change
* Latency: Set the time to wait for notification. Sets the FSEvents latency, or the sleep time
for the timer method
* Method: Choose a method, instead of auto-detecting.
* Adds documentation of these options
* Pass object to the various wait_* handlers, so they can see the object's properties
Thanks!
Subject: | filesys-notify-simple-sg.patch |
--- Simple.pm 2011-01-13 14:32:18.000000000 -0500
+++ /Users/sgifford/perl/lib/perl5/site_perl/5.8.9/Filesys/Notify/Simple.pm 2011-08-18 16:54:13.000000000 -0400
@@ -9,13 +9,29 @@
use constant NO_OPT => $ENV{PERL_FNS_NO_OPT};
sub new {
- my($class, $path) = @_;
+ my($class, $path, %opt) = @_;
unless (ref $path eq 'ARRAY') {
Carp::croak('Usage: Filesys::Notify::Simple->new([ $path1, $path2 ])');
}
my $self = bless { paths => $path }, $class;
+ if (defined($opt{Detail})) {
+ $self->{detail} = $opt{Detail};
+ } else {
+ $self->{detail} = 1;
+ }
+
+ if (defined($opt{Latency})) {
+ $self->{latency} = $opt{Latency};
+ } else {
+ $self->{latency} = 2;
+ }
+
+ if (defined($opt{Method})) {
+ $self->{method} = $opt{Method};
+ }
+
$self->init;
$self;
@@ -24,7 +40,7 @@
sub wait {
my($self, $cb) = @_;
- $self->{watcher} ||= $self->{watcher_cb}->(@{$self->{paths}});
+ $self->{watcher} ||= $self->{watcher_cb}->($self, @{$self->{paths}});
$self->{watcher}->($cb);
}
@@ -32,16 +48,31 @@
my $self = shift;
local $@;
- if ($^O eq 'linux' && !NO_OPT && eval { require Linux::Inotify2; 1 }) {
+ if (!$self->{method}) {
+ if ($^O eq 'linux' && !NO_OPT && eval { require Linux::Inotify2; 1 }) {
+ $self->{method} = 'inotify2';
+ } elsif ($^O eq 'darwin' && !NO_OPT && eval { require Mac::FSEvents; 1 }) {
+ $self->{method} = 'fsevents';
+ } else {
+ $self->{method} = 'timer';
+ }
+ }
+
+ if ($self->{method} eq 'inotify2') {
+ require Linux::Inotify2;
$self->{watcher_cb} = \&wait_inotify2;
- } elsif ($^O eq 'darwin' && !NO_OPT && eval { require Mac::FSEvents; 1 }) {
- $self->{watcher_cb} = \&wait_fsevents;
- } else {
+ } elsif ($self->{method} eq 'fsevents') {
+ require Mac::FSEvents;
+ $self->{watcher_cb} = \&wait_fsevents;
+ } elsif ($self->{method} eq 'timer') {
$self->{watcher_cb} = \&wait_timer;
+ } else {
+ die "Invalid method requested\n";
}
}
sub wait_inotify2 {
+ my $obj = shift;
my @path = @_;
Linux::Inotify2->import;
@@ -62,14 +93,18 @@
sub wait_fsevents {
require IO::Select;
+ my $obj = shift;
my @path = @_;
- my $fs = _full_scan(@path);
+ my $fs;
+ if ($obj->{detail} > 0) {
+ $fs = _full_scan(@path);
+ }
my $sel = IO::Select->new;
my %events;
for my $path (@path) {
- my $fsevents = Mac::FSEvents->new({ path => $path, latency => 1 });
+ my $fsevents = Mac::FSEvents->new({ path => $path, latency => $obj->{latency} });
my $fh = $fsevents->watch;
$sel->add($fh);
$events{fileno $fh} = $fsevents;
@@ -84,11 +119,14 @@
my $fsevents = $events{fileno $fh};
my %uniq;
my @path = grep !$uniq{$_}++, map { $_->path } $fsevents->read_events;
-
- my $new_fs = _full_scan(@path);
- my $old_fs = +{ map { ($_ => $fs->{$_}) } keys %$new_fs };
- _compare_fs($old_fs, $new_fs, sub { push @events, { path => $_[0] } });
- $fs->{$_} = $new_fs->{$_} for keys %$new_fs;
+ if ($obj->{details} > 0) {
+ my $new_fs = _full_scan(@path);
+ my $old_fs = +{ map { ($_ => $fs->{$_}) } keys %$new_fs };
+ _compare_fs($old_fs, $new_fs, sub { push @events, { path => $_[0] } });
+ $fs->{$_} = $new_fs->{$_} for keys %$new_fs;
+ } else {
+ push(@events,map { { path => $_ } } @path);
+ }
last if @events;
}
@@ -97,6 +135,7 @@
}
sub wait_timer {
+ my $obj = shift;
my @path = @_;
my $fs = _full_scan(@path);
@@ -105,7 +144,7 @@
my $cb = shift;
my @events;
while (1) {
- sleep 2;
+ sleep $obj->{latency};
my $new_fs = _full_scan(@path);
_compare_fs($fs, $new_fs, sub { push @events, { path => $_[0] } });
$fs = $new_fs;
@@ -220,6 +259,81 @@
modules. Platform specific optimizations with L<Linux::Inotify2> and
L<Mac::FSEvents> are truely optional.
+=head1 CONSTRUCTOR
+
+=over 4
+
+=item new ( [ @dirs ], opt1 => val1, ... )
+
+Creates a new filesystem monitor. C<@dirs> is a list of directories
+to monitor, given as an array reference. Additional named parameters
+can be passed after that, including:
+
+=over 4
+
+=item Detail
+
+Set to 1 (the default) or higher to get a notification for each change
+to the filesystem with the full path to each changed file. Set to 0
+to get just a notification of some change to the monitored
+directories, without information about individual changes. Note that
+if the individual changes have been detected already, they may be sent.
+
+=item Latency
+
+Requested time to wait between notifications, in seconds. The default
+is 2 seconds. This is advisory, and you may get notifications more or
+less frequently than this.
+
+=item Method
+
+Method to use to monitor for changes. The default is to auto-detect
+the most efficient method based on your OS and installed modules.
+Values are:
+
+=over 4
+
+=item timer
+
+Periodically scan the entire filesystem for changes. See the
+C<Latency> option to control how often files are checked.
+
+=item inotify2
+
+Use Linux inotify, version 2. Requires L<Linux::Inotify2>.
+
+=item fsevent
+
+Use MacOS X FS Events. Requires L<Mac::FSEvents>.
+
+=back
+
+=back
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item wait ( ¬ify_sub )
+
+Callback sub which should be called whenever a modified file is
+changed. It will be passed a single parameter containing an array
+reference; each item in the array is a hash reference with the
+following values:
+
+=over 4
+
+=item path
+
+The path of the file that changed, or the monitored directory if
+C<Detail> was set to 0 in the constructor.
+
+=back
+
+=back
+
=head1 AUTHOR
Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>