File::ChangeNotify::Watcher::Default - Fallback default watcher subclass


File-ChangeNotify documentation Contained in the File-ChangeNotify distribution.

Index


Code Index:

NAME

Top

File::ChangeNotify::Watcher::Default - Fallback default watcher subclass

VERSION

Top

version 0.20

DESCRIPTION

Top

This class implements watching by comparing two snapshots of the filesystem tree. It if inefficient and dumb, and so it is the subclass of last resort.

Its $watcher->wait_for_events() method sleeps between comparisons of the filesystem snapshot it takes.

AUTHOR

Top

Dave Rolsky <autarch@urth.org>

COPYRIGHT AND LICENSE

Top


File-ChangeNotify documentation Contained in the File-ChangeNotify distribution.

package File::ChangeNotify::Watcher::Default;
BEGIN {
  $File::ChangeNotify::Watcher::Default::VERSION = '0.20';
}

use strict;
use warnings;
use namespace::autoclean;

use File::Find qw( finddepth );
use File::Spec;
use Time::HiRes qw( sleep );

# Trying to import this just blows up on Win32, and checking
# Time::HiRes::d_hires_stat() _also_ blows up on Win32.
BEGIN {
    eval { Time::HiRes->import('stat') };
}

use Moose;
use MooseX::SemiAffordanceAccessor;

extends 'File::ChangeNotify::Watcher';

has _map => (
    is      => 'rw',
    isa     => 'HashRef',
    default => sub { {} },
);

sub sees_all_events {0}

sub BUILD {
    my $self = shift;

    $self->_set_map( $self->_build_map() );
}

sub _build_map {
    my $self = shift;

    my %map;

    File::Find::find(
        {
            wanted => sub {
                my $path = $File::Find::name;

                if ( $self->_path_is_excluded($path) ) {
                    $File::Find::prune = 1;
                    return;
                }

                my $entry = $self->_entry_for_map($path) or return;
                $map{$path} = $entry;
            },
            follow_fast => ( $self->follow_symlinks() ? 1 : 0 ),
            no_chdir    => 1,
            follow_skip => 2,
        },
        @{ $self->directories() },
    );

    return \%map;
}

sub _entry_for_map {
    my $self = shift;
    my $path = shift;

    my $is_dir = -d $path ? 1 : 0;

    return if -l $path && !$is_dir;

    unless ($is_dir) {
        my $filter = $self->filter();
        return unless ( File::Spec->splitpath($path) )[2] =~ /$filter/;
    }

    return {
        is_dir => $is_dir,
        mtime  => _mtime(*_),
        size   => ( $is_dir ? 0 : -s _ ),
    };
}

# It seems that Time::HiRes's stat does not act exactly like the
# built-in, so if I do ( stat _ )[9] it will not work (grr).
sub _mtime {
    my @stat = stat;

    return $stat[9];
}

sub wait_for_events {
    my $self = shift;

    while (1) {
        my @events = $self->_interesting_events();
        return @events if @events;

        sleep $self->sleep_interval();
    }
}

sub _interesting_events {
    my $self = shift;

    my @interesting;

    my $old_map = $self->_map();
    my $new_map = $self->_build_map();

    for my $path ( sort keys %{$old_map} ) {
        if ( !exists $new_map->{$path} ) {
            if ( $old_map->{$path}{is_dir} ) {
                $self->_remove_directory($path);
            }

            push @interesting, $self->event_class()->new(
                path => $path,
                type => 'delete',
            );
        }
        elsif (
            !$old_map->{$path}{is_dir}
            && (   $old_map->{$path}{mtime} != $new_map->{$path}{mtime}
                || $old_map->{$path}{size} != $new_map->{$path}{size} )
            ) {
            push @interesting, $self->event_class()->new(
                path => $path,
                type => 'modify',
            );
        }
    }

    for my $path ( sort grep { !exists $old_map->{$_} } keys %{$new_map} ) {
        if ( -d $path ) {
            push @interesting, $self->event_class()->new(
                path => $path,
                type => 'create',
                ),
                ;
        }
        else {
            push @interesting, $self->event_class()->new(
                path => $path,
                type => 'create',
            );
        }
    }

    $self->_set_map($new_map);

    return @interesting;
}

__PACKAGE__->meta()->make_immutable();

1;

# ABSTRACT: Fallback default watcher subclass




__END__