# Phusion Passenger - http://www.modrails.com/# Copyright (c) 2010 Phusion## "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.## Permission is hereby granted, free of charge, to any person obtaining a copy# of this software and associated documentation files (the "Software"), to deal# in the Software without restriction, including without limitation the rights# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell# copies of the Software, and to permit persons to whom the Software is# furnished to do so, subject to the following conditions:## The above copyright notice and this permission notice shall be included in# all copies or substantial portions of the Software.## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN# THE SOFTWARE.require'phusion_passenger/native_support'modulePhusionPassengermoduleUtils# Watches changes on one or more files or directories. To use this class,# construct an object, passing it file or directory names to watch, then# call #wait_for_change. #wait_for_change waits until one of the following# events has happened since the constructor was called:## - One of the specified files has been renamed, deleted, or its access# revoked. This will cause +true+ to be returned.# - One of the specified directories has been modified, renamed, deleted,# or its access revoked. This will cause +true+ to be returned.# - +termination_pipe+ (as passed to the constructor) becomes readable.# This will cause +nil+ to be returned.# - The thread is interrupted. This will cause +nil+ to be returned.# # The constructor will attempt to stat and possible also open all specified# files/directories. If one of them cannot be statted or opened, then# +false+ will be returned by #wait_for_change.## #wait_for_change may only be called once. After calling it one should# create a new object if one wishes to watch the filesystem again.## Always call #close when a FileSystemWatcher object is no longer needed# in order to free resources.## This class tries to use kqueue for efficient filesystem watching on# platforms that support it. On other platforms it'll fallback to stat# polling instead.ifdefined?(NativeSupport::FileSystemWatcher)FileSystemWatcher=NativeSupport::FileSystemWatcherFileSystemWatcher.class_evaldodefself.new(filenames,termination_pipe=nil)# Default parameter values, type conversion and exception# handling in C is too much of a pain.filenames=filenames.mapdo|filename|filename.to_sendreturn_new(filenames,termination_pipe)enddefself.opens_files?returntrueendendelseclassFileSystemWatcherattr_accessor:poll_intervaldefself.opens_files?returnfalseenddefinitialize(filenames,termination_pipe=nil)@poll_interval=3@termination_pipe=termination_pipe@dirs=[]@files=[]beginfilenames.eachdo|filename|stat=File.stat(filename)ifstat.directory?@dirs<<DirInfo.new(filename,stat)else@files<<FileInfo.new(filename,stat)endendrescueErrno::EACCES,Errno::ENOENT@dirs=@files=nilendenddefwait_for_changeif!@dirsreturnfalseendwhiletrueifchanged?returntrueelsifselect([@termination_pipe],nil,nil,@poll_interval)returnnilendendenddefcloseendprivateclassDirInfoDOT="."DOTDOT=".."definitialize(filename,stat)@filename=filename@stat=stat@subfiles={}Dir.foreach(filename)do|entry|nextifentry==DOT||entry==DOTDOTsubfilename="#{filename}/#{entry}"@subfiles[entry]=FileInfo.new(subfilename,File.stat(subfilename))endenddefchanged?new_stat=File.stat(@filename)if@stat.ino!=new_stat.ino||!new_stat.directory?||@stat.mtime!=new_stat.mtimereturntrueendcount=0Dir.foreach(@filename)do|entry|nextifentry==DOT||entry==DOTDOTsubfilename="#{@filename}/#{entry}"file_info=@subfiles[entry]if!file_info||file_info.changed?(false)returntrueelsecount+=1endendreturncount!=@subfiles.sizerescueErrno::EACCES,Errno::ENOENTreturntrueendendclassFileInfodefinitialize(filename,stat)@filename=filename@stat=statenddefchanged?(check_mtime=true)new_stat=File.stat(@filename)ifcheck_mtimemtime_changed=@stat.mtime!=new_stat.mtime||@stat.size!=new_stat.sizeelsemtime_changed=falseendreturn@stat.ino!=new_stat.ino||@stat.ftype!=new_stat.ftype||mtime_changedrescueErrno::EACCES,Errno::ENOENTreturntrueendenddefchanged?return@dirs.any?{|dir_info|dir_info.changed?}||@files.any?{|file_info|file_info.changed?}endendendend# module Utilsend# module PhusionPassenger