# Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com># # 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'set'require'build/files/state'moduleBuildmoduleFilesclassHandledefinitialize(monitor,files,&block)@monitor=monitor@state=State.new(files)@on_changed=blockendattr:monitordefcommit!@state.update!enddefdirectories@state.files.rootsenddefremove!monitor.delete(self)enddefchanged!@on_changed.call(@state)if@state.update!endendclassMonitordefinitialize@directories=Hash.new{|hash,key|hash[key]=Set.new}@updated=false@deletions=nilendattr:updated# Notify the monitor that files in these directories have changed.defupdate(directories,*args)delay_deletionsdodirectories.eachdo|directory|# directory = File.realpath(directory)@directories[directory].eachdo|handle|handle.changed!(*args)endendendenddefroots@directories.keysenddefdelete(handle)if@deletions@deletions<<handleelsepurge(handle)endenddeftrack_changes(files,&block)handle=Handle.new(self,files,&block)add(handle)enddefadd(handle)handle.directories.eachdo|directory|@directories[directory]<<handle# We just added the first handle:if@directories[directory].size==1# If the handle already existed, this might trigger unnecessarily.@updated=trueendendhandleenddefrun(options={},&block)default_driver=caseRUBY_PLATFORMwhen/linux/i;:inotifywhen/darwin/i;:fseventelse;:pollingendifdriver=options.fetch(:driver,default_driver)method_name="run_with_#{driver}"Files.send(method_name,self,options,&block)endendprotecteddefdelay_deletions@deletions=[]yield@deletions.eachdo|handle|purge(handle)end@deletions=nilenddefpurge(handle)handle.directories.eachdo|directory|@directories[directory].delete(handle)# Remove the entire record if there are no handles:if@directories[directory].size==0@directories.delete(directory)@updated=trueendendendenddefself.run_with_inotify(monitor,options={},&block)require'rb-inotify'notifier=INotify::Notifier.newcatch(:interrupt)dowhiletruemonitor.roots.eachdo|root|notifier.watchroot,:create,:modify,:deletedo|event|monitor.update([root])yieldifmonitor.updatednotifier.stopendendendnotifier.runendendenddefself.run_with_fsevent(monitor,options={},&block)require'rb-fsevent'notifier=FSEvent.newcatch(:interrupt)dowhiletruenotifier.watchmonitor.rootsdo|directories|directories.collect!{|directory|File.expand_path(directory)}monitor.update(directories)yieldifmonitor.updatednotifier.stopendendnotifier.runendendenddefself.run_with_polling(monitor,options={},&block)catch(:interrupt)dowhiletruemonitor.update(monitor.roots)yieldsleep(options[:latency]||1.0)endendendendend