This repository has been archived by the owner on Nov 3, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit ed6aa4d
Showing
12 changed files
with
481 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
*.gem | ||
.bundle | ||
.project | ||
.loadpath | ||
Gemfile.lock | ||
pkg/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
source "http://rubygems.org" | ||
|
||
# Specify your gem's dependencies in rb-fchange.gemspec | ||
gemspec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# rb-fchange | ||
|
||
Code not redy. It's just proof of concept | ||
This is a simple wrapper over the Windows Kernel functions for monitoring the specified directory or subtree. | ||
|
||
Example | ||
|
||
require 'rb-fchange' | ||
|
||
notifier = FChange::Notifier.new | ||
notifier.watch("test", :all_events, :recursive) do |event| | ||
p Time.now.utc | ||
end | ||
|
||
Signal.trap('INT') do | ||
p "Bye bye...", | ||
notifier.stop | ||
abort("\n") | ||
end | ||
|
||
notifier.run |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
require 'bundler' | ||
Bundler::GemHelper.install_tasks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# The root module of the library, which is laid out as so: | ||
# | ||
# * {Notifier} -- The main class, where the notifications are set up | ||
# * {Watcher} -- A watcher for a single file or directory | ||
# * {Event} -- An filesystem event notification | ||
module FChange | ||
|
||
require "rb-fchange/native" | ||
require "rb-fchange/native/flags" | ||
require "rb-fchange/notifier" | ||
require "rb-fchange/watcher" | ||
require "rb-fchange/event" | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
module FChange | ||
# An event caused by a change on the filesystem. | ||
# Each {Watcher} can fire many events, | ||
# which are passed to that watcher's callback. | ||
class Event | ||
# The {Watcher} that fired this event. | ||
# | ||
# @return [Watcher] | ||
attr_reader :watcher | ||
|
||
# Creates an event from a string of binary data. | ||
# Differs from {Event.consume} in that it doesn't modify the string. | ||
# | ||
# @private | ||
# @param watcher [Watcher] The {Watcher} that fired the event | ||
def initialize(watcher) | ||
@watcher = watcher | ||
end | ||
|
||
# Calls the callback of the watcher that fired this event, | ||
# passing in the event itself. | ||
# | ||
# @private | ||
def callback! | ||
@watcher.callback!(self) | ||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
require 'Win32API' | ||
|
||
module FChange | ||
# This module contains the low-level foreign-function. | ||
# It's an implementation detail, and not meant for users to deal with. | ||
# | ||
# @private | ||
module Native | ||
|
||
# | ||
INFINITE = 0xFFFFFFFF | ||
|
||
# | ||
WAIT_OBJECT_0 = 0x00000000 | ||
|
||
# HANDLE FindFirstChangeNotification( | ||
# LPCTSTR lpPathName, // directory name | ||
# BOOL bWatchSubtree, // monitoring option | ||
# DWORD dwNotifyFilter // filter conditions | ||
#); | ||
@_k32FindFirstChangeNotification = Win32API.new("kernel32", | ||
"FindFirstChangeNotification", ['P', 'I', 'L'], 'L') | ||
|
||
# @return handle | ||
def k32FindFirstChangeNotification(path, recursive, flag) | ||
@_k32FindFirstChangeNotification.call(path, recursive ? 1 : 0, flag) | ||
end | ||
|
||
module_function "k32FindFirstChangeNotification" | ||
|
||
# BOOL FindNextChangeNotification( | ||
# HANDLE hChangeHandle // handle to change notification | ||
# ); | ||
@_k32FindNextChangeNotification = Win32API.new("kernel32", | ||
"FindNextChangeNotification", ['L'], 'I') | ||
|
||
def k32FindNextChangeNotification(handle) | ||
@_k32FindNextChangeNotification.call(handle) | ||
end | ||
|
||
module_function "k32FindNextChangeNotification" | ||
|
||
# DWORD WaitForMultipleObjects( | ||
# DWORD nCount, // number of handles in array | ||
# CONST HANDLE *lpHandles, // object-handle array | ||
# BOOL bWaitAll, // wait option | ||
# DWORD dwMilliseconds // time-out interval | ||
# ); | ||
@_k32WaitForMultipleObjects = Win32API.new("kernel32", | ||
"WaitForMultipleObjects", ['L', 'P', 'I', 'L'], 'L') | ||
|
||
def k32WaitForMultipleObjects(count, handles, wait_all, time) | ||
@_k32WaitForMultipleObjects.call(count, handles, wait_all, time) | ||
end | ||
|
||
module_function "k32WaitForMultipleObjects" | ||
|
||
# BOOL FindCloseChangeNotification( | ||
# HANDLE hChangeHandle | ||
# ); | ||
@_k32FindCloseChangeNotification = Win32API.new("kernel32", | ||
"FindCloseChangeNotification", ['L'], 'I') | ||
|
||
def k32FindCloseChangeNotification(handle) | ||
@_k32FindCloseChangeNotification.call(handle) | ||
end | ||
|
||
module_function "k32FindCloseChangeNotification" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
module FChange | ||
module Native | ||
# A module containing all the flags to be passed to {Notifier#watch}. | ||
# @see http://msdn.microsoft.com/en-us/library/aa364417(v=VS.85).aspx | ||
# | ||
# @private | ||
module Flags | ||
|
||
# Any file name change in the watched directory or subtree causes a change | ||
# notification wait operation to return. Changes include renaming, | ||
# creating, or deleting a file name. | ||
FILE_NOTIFY_CHANGE_FILE_NAME = 0x00000001 | ||
|
||
# Any directory-name change in the watched directory or subtree causes a | ||
# change notification wait operation to return. Changes include creating | ||
# or deleting a directory. | ||
FILE_NOTIFY_CHANGE_DIR_NAME = 0x00000002 | ||
|
||
# Any attribute change in the watched directory or subtree causes a change | ||
# notification wait operation to return. | ||
FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x00000004 | ||
|
||
# Any file-size change in the watched directory or subtree causes a change | ||
# notification wait operation to return. The operating system detects a | ||
# change in file size only when the file is written to the disk. | ||
# For operating systems that use extensive caching, detection occurs only | ||
# when the cache is sufficiently flushed. | ||
FILE_NOTIFY_CHANGE_SIZE = 0x00000008 | ||
|
||
# Any change to the last write-time of files in the watched directory or | ||
# subtree causes a change notification wait operation to return. | ||
# The operating system detects a change to the last write-time only when | ||
# the file is written to the disk. For operating systems that use | ||
# extensive caching, detection occurs only when the cache is sufficiently | ||
# flushed | ||
FILE_NOTIFY_CHANGE_LAST_WRITE = 0x00000010 | ||
|
||
# Any change to the last access time of files in the watched directory or | ||
# subtree causes a change notification wait operation to return. | ||
FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x00000020 | ||
|
||
# Any change to the creation time of files in the watched directory or | ||
# subtree causes a change notification wait operation to return. | ||
FILE_NOTIFY_CHANGE_CREATION = 0x00000040 | ||
|
||
# Any security-descriptor change in the watched directory or subtree | ||
# causes a change notification wait operation to return. | ||
FILE_NOTIFY_CHANGE_SECURITY = 0x00000100 | ||
|
||
FILE_NOTIFY_CHANGE_ALL_EVENTS = ( | ||
FILE_NOTIFY_CHANGE_ATTRIBUTES | | ||
FILE_NOTIFY_CHANGE_CREATION | | ||
FILE_NOTIFY_CHANGE_DIR_NAME | | ||
FILE_NOTIFY_CHANGE_FILE_NAME | | ||
FILE_NOTIFY_CHANGE_LAST_ACCESS | | ||
FILE_NOTIFY_CHANGE_LAST_WRITE | | ||
FILE_NOTIFY_CHANGE_SECURITY | | ||
FILE_NOTIFY_CHANGE_SIZE | ||
) | ||
|
||
# Converts a list of flags to the bitmask that the C API expects. | ||
# | ||
# @param flags [Array<Symbol>] | ||
# @return [Fixnum] | ||
def self.to_mask(flags) | ||
flags.map {|flag| const_get("FILE_NOTIFY_CHANGE_#{flag.to_s.upcase}")}. | ||
inject(0) {|mask, flag| mask | flag} | ||
end | ||
|
||
# Converts a bitmask from the C API into a list of flags. | ||
# | ||
# @param mask [Fixnum] | ||
# @return [Array<Symbol>] | ||
def self.from_mask(mask) | ||
constants.map {|c| c.to_s}.select do |c| | ||
next false unless c =~ /^FILE_NOTIFY_CHANGE_/ | ||
const_get(c) & mask != 0 | ||
end.map {|c| c.sub("FILE_NOTIFY_CHANGE_", "").downcase.to_sym} - [:all_events] | ||
end | ||
|
||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
module FChange | ||
# Notifier wraps a single instance of FChange. | ||
# It's possible to have more than one instance, | ||
# but usually unnecessary. | ||
# | ||
# @example | ||
# # Create the notifier | ||
# notifier = FChange::Notifier.new | ||
# | ||
# # Run this callback whenever the file path/to/foo.txt is read | ||
# notifier.watch("path/to/foo/", :all_events) do | ||
# puts "foo was accessed!" | ||
# end | ||
# | ||
# # Nothing happens until you run the notifier! | ||
# notifier.run | ||
class Notifier | ||
# A hash from {Watcher} ids to the instances themselves. | ||
# | ||
# @private | ||
# @return [{Fixnum => Watcher}] | ||
attr_reader :watchers | ||
|
||
attr_reader :dwChangeHandles | ||
attr_reader :lp_dwChangeHandles | ||
|
||
# Creates a new {Notifier}. | ||
# | ||
# @return [Notifier] | ||
def initialize | ||
@watchers = {} | ||
@dwChangeHandles = [] | ||
@lp_dwChangeHandles = 0 | ||
end | ||
|
||
# Adds a new {Watcher} to the queue. | ||
def add_watcher(watcher) | ||
|
||
@watchers[watcher.id] = watcher | ||
|
||
@dwChangeHandles.push watcher.id | ||
|
||
# Create storage for event handles (LONG) | ||
@lp_dwChangeHandles = " " * 4 * dwChangeHandles.count | ||
|
||
# Pack event handles into newly created storage area | ||
# to be used for Win32 call | ||
@lp_dwChangeHandles = dwChangeHandles.pack("L" * dwChangeHandles.count) | ||
|
||
end | ||
|
||
# Watches a file or directory for changes, | ||
# calling the callback when there are. | ||
# This is only activated once \{#process} or \{#run} is called. | ||
# | ||
# **Note that by default, this does not recursively watch subdirectories | ||
# of the watched directory**. | ||
# To do so, use the `:recursive` flag. | ||
# | ||
# `:recursive` | ||
# : Recursively watch any subdirectories that are created. | ||
# | ||
# @param path [String] The path to the file or directory | ||
# @param flags [Array<Symbol>] Which events to watch for | ||
# @yield [event] A block that will be called | ||
# whenever one of the specified events occur | ||
# @yieldparam event [Event] The Event object containing information | ||
# about the event that occured | ||
# @return [Watcher] A Watcher set up to watch this path for these events | ||
# @raise [SystemCallError] if the file or directory can't be watched, | ||
# e.g. if the file isn't found, read access is denied, | ||
# or the flags don't contain any events | ||
def watch(path, *flags, &callback) | ||
recursive = flags.include?(:recursive) | ||
flags = flags - [:recursive] | ||
@flags = flags.freeze | ||
Watcher.new(self, path, recursive, *flags, &callback) | ||
end | ||
|
||
# Starts the notifier watching for filesystem events. | ||
# Blocks until \{#stop} is called. | ||
# | ||
# @see #process | ||
def run | ||
@stop = false | ||
process until @stop | ||
end | ||
|
||
# Stop watching for filesystem events. | ||
# That is, if we're in a \{#run} loop, | ||
# exit out as soon as we finish handling the events. | ||
def stop | ||
@stop = true | ||
end | ||
|
||
# Blocks until there are one or more filesystem events | ||
# that this notifier has watchers registered for. | ||
# Once there are events, the appropriate callbacks are called | ||
# and this function returns. | ||
# | ||
# @see #run | ||
def process | ||
read_events.each {|event| event.callback!} | ||
end | ||
|
||
# Blocks until there are one or more filesystem events | ||
# that this notifier has watchers registered for. | ||
# Once there are events, returns their {Event} objects. | ||
# | ||
# @private | ||
def read_events | ||
|
||
dwWaitStatus = Native.k32WaitForMultipleObjects(@dwChangeHandles.count, | ||
@lp_dwChangeHandles, 0, Native::INFINITE) | ||
|
||
events = [] | ||
|
||
@dwChangeHandles.each_index do |index| | ||
if dwWaitStatus == Native::WAIT_OBJECT_0 + index | ||
|
||
ev = Event.new(@watchers[@dwChangeHandles[index]]) | ||
events << ev | ||
|
||
r = Native.k32FindNextChangeNotification(@dwChangeHandles[index]) | ||
if r == 0 | ||
raise SystemCallError.new("Failed to watch", r) | ||
end | ||
end | ||
end | ||
events | ||
end | ||
|
||
def close | ||
|
||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module FChange | ||
VERSION = "0.0.1" | ||
end |
Oops, something went wrong.