Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

+ Comments

+ README v0
+ Verify if a file being watched is a dir
+ Default target is current directory
+ Already watching verification
  • Loading branch information...
commit 18a12cddc9cb76499ff774bd46b7bf76167610e5 1 parent d37f9e6
@krlito authored
Showing with 242 additions and 20 deletions.
  1. +8 −0 LICENSE
  2. +153 −0 README.md
  3. +6 −0 index.js
  4. +74 −19 lib/filemonitor.js
  5. +1 −1  package.json
View
8 LICENSE
@@ -0,0 +1,8 @@
+Copyright (c) 2012 Carlos Campo <carlos@campo.com.co>
+
+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.
+
View
153 README.md
@@ -0,0 +1,153 @@
+#filemonitor-node
+
+This node module is a thin wrapper around the [inotifywait][] command. It can be used to monitor and act upon file system events.
+Filemonitor-node can be used to monitor events on files or directories. When a directory is monitored, events are returned
+for the directory itself and its files.
+
+
+##Requirements
+
+Filemonitor-node is an only-Linux module. It depends on [inotify-tools]. To install inotify-tools on a Linux (Debian/Ubuntu) system:
+ sudo apt-get install inotify-tools
+For installing on other linux flavors, go [here][getting-inotify-tools].
+
+
+##Installation
+
+On your project directory:
+ npm install filemonitor
+
+
+##Events
+
+The following events are captured by the module:
+
+- **access**: A watched file or a file within a watched directory was read from.
+- **modify**: A watched file or a file within a watched directory was written to.
+- **attrib**: The metadata of a watched file or a file within a watched directory was modified. This includes timestamps,
+ file permissions, extended attributes etc.
+- **close_write**: A watched file or a file within a watched directory was closed, after being opened in writeable mode.
+ This does not necessarily imply the file was written to.
+- **close_nowrite**: A watched file or a file within a watched directory was closed, after being opened in read-only mode.
+- **close**: A watched file or a file within a watched directory was closed, regardless of how it was opened. Note that this
+ is actually implemented simply by listening for both close_write and close_nowrite, hence all close events received will
+ be output as one of these, not CLOSE.
+- **open**: A watched file or a file within a watched directory was opened.
+- **moved_to**: A file or directory was moved into a watched directory. This event occurs even if the file is simply moved
+ from and to the same directory.
+- **moved_from**: A file or directory was moved from a watched directory. This event occurs even if the file is simply moved
+ from and to the same directory.
+- **move**: A file or directory was moved from or to a watched directory. Note that this is actually implemented simply by
+ listening for both moved_to and moved_from, hence all close events received will be output as one or both of these, not MOVE.
+- **move_self**: A watched file or directory was moved. After this event, the file or directory is no longer being watched.
+- **create**: A file or directory was created within a watched directory.
+- **delete**: A file or directory within a watched directory was deleted.
+- **delete_self**: A watched file or directory was deleted. After this event the file or directory is no longer being watched.
+ Note that this event can occur even if it is not explicitly being listened for.
+- **unmount**: The filesystem on which a watched file or directory resides was unmounted. After this event the file or directory
+ is no longer being watched. Note that this event can occur even if it is not explicitly being listened to.
+
+
+##Usage
+
+To monitor a file is as easy as:
+
+ var filemon = require('filemonitor');
+
+ var onFileEvent = function (ev) {
+ var filetype = ev.isDir ? "directory" : "file";
+ console.log("Event " + ev.eventId + " was captured for " + filetype + " " + ev.filename + " on time: " + ev.timestamp.toString());
+ }
+
+ var options = {
+ target: "./docs/myfile.txt",
+ listeners: {
+ all_events: onFileModified
+ }
+ }
+
+ filemon.watch(options);
+
+First, the module is instantiated. Then, an options object is set including a target file and a callback function in case any event is captured.
+Finally, `filemon.watch(options)` is called to start monitoring.
+
+
+###Event Object
+Each time an event is captured, its assigned callback is called with an event object as argument. This object has the following properties:
+- **filename** is a string with the relative path to the watched file/directory.
+- **eventId** is a string whose value is one of the events names described in *Events* section.
+- **isDir** is `true` if what is being watched is a directory. Otherwise, `false`.
+- **timestamp** is a Date object. It has the datetime at which the event was captured.
+
+
+###Options
+The options to configure filemonitor-node include:
+- **target** is an array of strings containing files/directories to be watched. (default: current directory `["."]`).
+- **recursive** is a boolean. When `true`, nested files in the subdirectories of the *target* directories will be also watched (recursively).
+ Normally, these nested files are excluded from any monitoring. (default: `false`).
+- **exclude** is an array of string containg files/directories to be excluded from watching. This is very useful when watching
+ directories. (default: empty array `[]`).
+- **dismissQueuedEvents** is `true` for dismissing events that are already pending. If a previously captured event has not been. (default: `true`).
+- **listeners** is an object for mapping events to callbacks. Listener's keys can be any of the event names described in section *Events*.
+ Each of these keys must have assigned a callback as a value. The special key `all_events` matches a callback to all event. (default: empty object `{}`).
+
+
+###More examples
+
+For monitoring a complete directory waiting for newly created files:
+
+ var filemon = require('filemonitor');
+
+ var onFileEvent = function (ev) {
+ console.log("File " + ev.filename + " triggered event " + ev.eventId + " on " + ev.timestamp.toString());
+ }
+
+ var onFileCreation = function (ev) {
+ console.log("File " + ev.filename + " was created on " + ev.timestamp.toString());
+ }
+
+ var options = {
+ recursive: false,
+ target: "./docs/",
+ listeners: {
+ all_events: onFileEvent,
+ create: onFileCreation
+ }
+ }
+
+ filemon.watch(options);
+
+
+Watching multiple files. On file creation or modification, then do something.
+
+ var filemon = require('filemonitor');
+
+ var onFileChange = function (ev) {
+ //... Do Something
+ }
+
+ var onFileCreation = function (ev) {
+ //... Do Something
+ }
+
+ var options = {
+ recursive: false,
+ target: ["./docs/", "./config/app.config"],
+ exclude: ["./docs/myfile1.txt", "./docs/myfile10.txt"],
+ listeners: {
+ modify: onFileChange,
+ create: onFileCreation
+ }
+ }
+
+ filemon.watch(options);
+
+
+###Other comments...
+- Once the `watch` function has been called, options cannot be changed.
+- Monitoring can be stopped calling function `filemon.stop()`.
+
+
+[inotifywait]: http://github.com/rvoicilas/inotify-tools/wiki
+[inotify-tools]: http://github.com/rvoicilas/inotify-tools/wiki
+[getting-inotify-tools]: http://github.com/rvoicilas/inotify-tools/wiki/#wiki-getting
View
6 index.js
@@ -1 +1,7 @@
+/*!
+* filemonitor-node
+* Copyright(c) 2012 Carlos Campo <carlos@campo.com.co>
+* MIT Licensed
+*/
+
module.exports = require('./lib/filemonitor.js');
View
93 lib/filemonitor.js
@@ -1,18 +1,41 @@
+/*!
+* filemonitor-node
+* Copyright(c) 2012 Carlos Campo <carlos@campo.com.co>
+* MIT Licensed
+*/
+
+//Dependencies
var spawn = require('child_process').spawn;
+//filemonitor options
var options = {
recursive: false,
listeners: {},
- excludes: [],
- targets: [],
+ exclude: [],
+ target: ["."],
dismissQueuedEvents: true
};
-var inotify, queuedEvents = {};
+
+// ChildProcess instance
+var inotify;
+
+// Event callbacks pending to be run
+var queuedEvents = {};
+
+//inotifywait status flag
+var watching = false;
+
+// PUBLIC EXPORTS //
var filemonitor = module.exports = {
-
+
+ /*
+ * Set filemonitor options
+ */
configure: function (opts) {
+ if (watching) { throw new Error("Already watching."); return; }
+
if (typeof opts.recursive === "boolean") {
options.recursive = opts.recursive;
}
@@ -21,20 +44,20 @@ var filemonitor = module.exports = {
options.listeners = opts.listeners;
}
- if (Array.isArray(opts.excludes)) {
- options.excludes = opts.excludes;
+ if (Array.isArray(opts.exclude)) {
+ options.exclude = opts.excludes;
}
- if (typeof opts.excludes === "string") {
- options.excludes = [ opts.excludes ];
+ if (typeof opts.exclude === "string") {
+ options.exclude = [ opts.excludes ];
}
- if (Array.isArray(opts.targets)) {
- options.targets = opts.targets;
+ if (Array.isArray(opts.target)) {
+ options.target = opts.targets;
}
- if (typeof opts.targets === "string") {
- options.targets = [ opts.targets ];
+ if (typeof opts.target === "string") {
+ options.target = [ opts.targets ];
}
if (typeof opts.dismissQueuedEvents === "boolean") {
@@ -42,22 +65,40 @@ var filemonitor = module.exports = {
}
},
+ /*
+ * Start monitoring
+ */
watch: function (opts) {
+ if (watching) { throw new Error("Already watching."); return; }
if (opts) { filemonitor.configure(opts); }
+ //Call inotifywait
inotify = spawn("inotifywait", inotifywaitArgs());
inotify.stdout.on('data', watchEvents);
inotify.stderr.on('data', function (data) { throw new Error(data.toString()); });
inotify.on('exit', function (code) { if (parseInt(code) !== 0) { throw new Error("Exit code: " + code); } });
+ watching = true;
},
+ /*
+ * Stop monitoring
+ */
stop: function (opts) {
- process.nextTick(inotify.kill);
+ process.nextTick(function () {
+ inotify.kill();
+ watching = false;
+ });
}
};
+
+// PRIVATE FUNCTIONS //
+
+/*
+ * Get inotifywait arguments
+ */
function inotifywaitArgs() {
var listener,
args = ["-m", "-q",
@@ -65,7 +106,7 @@ function inotifywaitArgs() {
"--timefmt",
"%d %b %Y %T %z",
"--format",
- ",{\"filename\": \"%w%f\", \"eventIds\": \"%e\", \"timestamp\": \"%T\"}"];
+ ",{\"filename\": \"%w%f\", \"eventId\": \"%e\", \"timestamp\": \"%T\"}"];
if (!options.listeners.all_events) {
for (listener in options.listeners) {
@@ -75,23 +116,37 @@ function inotifywaitArgs() {
}
}
- options.excludes.forEach(function (exclude) { args.push("@" + exclude); });
+ options.exclude.forEach(function (ex) { args.push("@" + ex); });
- args.push.apply(args, options.targets);
+ args.push.apply(args, options.target);
return args;
}
+/*
+ * Catch events and run registered callbacks
+ */
function watchEvents (data) {
var events = JSON.parse("[" + data.toString().substr(1) + "]");
events.forEach(function (ev) {
- ev.eventIds = ev.eventIds.toLowerCase().split(",");
- ev.timestamp = new Date(ev.timestamp);
+ var eventIds = ev.eventId.toLowerCase().split(","),
+ isdirIdx = eventIds.indexOf("isdir");
+
+ if(isdirIdx === -1) {
+ ev.isDir = false;
+ } else {
+ eventIds = eventIds.splice(isdirIdx, 1);
+ ev.isDir = true;
+ }
- ev.eventIds.forEach(function (eventId) {
+ eventIds.forEach(function (eventId) {
if (!options.dismissQueuedEvents || !queuedEvents[ev.filename + ":" + eventId]) {
queuedEvents[ev.filename + ":" + eventId] = true;
+
+ ev.eventId = eventId;
+ ev.timestamp = new Date(ev.timestamp);
+
process.nextTick(function () {
if (options.listeners.all_events(ev)) { options.listeners.all_events(ev); };
if (options.listeners[eventId]) { options.listeners[eventId](ev); };
View
2  package.json
@@ -1,7 +1,7 @@
{
"name": "filemonitor",
"description": "Monitor filesystem events in node.js. Watch for file changes and run callbacks.",
- "version": "0.1.0",
+ "version": "0.1.1",
"homepage": "http://github.com/krlito/filemonitor-node.git",
"author": "Carlos Campo <carlos@campo.com.co>",
"repository": {
Please sign in to comment.
Something went wrong with that request. Please try again.