Add process.watchFile() process.unwatchFile()
authorRyan Dahl <ry@tinyclouds.org>
Tue, 17 Nov 2009 13:07:48 +0000 (14:07 +0100)
committerRyan Dahl <ry@tinyclouds.org>
Tue, 17 Nov 2009 13:07:48 +0000 (14:07 +0100)
This is an interface to libev's ev_stat watcher.

doc/api.txt
src/node.cc
src/node.js
src/node_stat.cc [new file with mode: 0644]
src/node_stat.h [new file with mode: 0644]
test/mjsunit/test-stat-handler.js [new file with mode: 0644]
wscript

index 18a5eb3..2801452 100644 (file)
@@ -124,6 +124,13 @@ Send a signal to a process. +pid+ is the process id and +signal+ is the
 signal to send; for example, "SIGINT" or "SIGUSR1".  See kill(2) for more
 information.
 
++process.watchFile(filename, listener)+::
+Watch for changes on +filename+. The callback +listener+ will be called each
+time the file changes.
+
++process.unwatchFile(filename)+::
+Stop watching for changes on +filename+.
+
 +process.compile(source, scriptOrigin)+::
 Just like +eval()+ except that you can specify a +scriptOrigin+ for better
 error reporting.
index 04f55f5..5185fc5 100644 (file)
@@ -16,6 +16,7 @@
 #include <node_file.h>
 #include <node_http.h>
 #include <node_signal_handler.h>
+#include <node_stat.h>
 #include <node_timer.h>
 #include <node_child_process.h>
 #include <node_constants.h>
@@ -695,6 +696,7 @@ static Local<Object> Load(int argc, char *argv[]) {
   Stdio::Initialize(process);                  // stdio.cc
   Timer::Initialize(process);                  // timer.cc
   SignalHandler::Initialize(process);          // signal_handler.cc
+  Stat::Initialize(process);                   // stat.cc
   ChildProcess::Initialize(process);           // child_process.cc
   DefineConstants(process);                    // constants.cc
   // Create node.dns
index 6876a22..a471160 100644 (file)
@@ -332,6 +332,32 @@ process.addListener("newListener", function (event) {
 });
 
 
+// Stat Change Watchers
+
+var statWatchers = {};
+
+process.watchFile = function (filename, listener) {
+  var stat;
+  if (filename in statWatchers) {
+    stat = statWatchers[filename];
+  } else {
+    statWatchers[filename] = new process.Stat();
+    stat = statWatchers[filename];
+    stat.start(filename, true);
+  }
+  stat.addListener("change", listener);
+  return stat;
+};
+
+process.unwatchFile = function (filename) {
+  if (filename in statWatchers) {
+    stat = statWatchers[filename];
+    stat.stop();
+    statWatchers[filename] = undefined;
+  }
+};
+
+
 
 // Timers
 
diff --git a/src/node_stat.cc b/src/node_stat.cc
new file mode 100644 (file)
index 0000000..282252f
--- /dev/null
@@ -0,0 +1,95 @@
+// Copyright 2009 Ryan Dahl <ry@tinyclouds.org>
+#include <node_stat.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+namespace node {
+
+using namespace v8;
+
+Persistent<FunctionTemplate> Stat::constructor_template;
+
+void Stat::Initialize(Handle<Object> target) {
+  HandleScope scope;
+
+  Local<FunctionTemplate> t = FunctionTemplate::New(Stat::New);
+  constructor_template = Persistent<FunctionTemplate>::New(t);
+  constructor_template->Inherit(EventEmitter::constructor_template);
+  constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
+  constructor_template->SetClassName(String::NewSymbol("Stat"));
+
+  NODE_SET_PROTOTYPE_METHOD(constructor_template, "start", Stat::Start);
+  NODE_SET_PROTOTYPE_METHOD(constructor_template, "stop", Stat::Stop);
+
+  target->Set(String::NewSymbol("Stat"), constructor_template->GetFunction());
+}
+
+
+void Stat::Callback(EV_P_ ev_stat *watcher, int revents) {
+  assert(revents == EV_STAT);
+  Stat *handler = static_cast<Stat*>(watcher->data);
+  assert(watcher == &handler->watcher_);
+  HandleScope scope;
+  handler->Emit("change", 0, NULL);
+}
+
+
+Handle<Value> Stat::New(const Arguments& args) {
+  HandleScope scope;
+  Stat *s = new Stat();
+  s->Wrap(args.Holder());
+  return args.This();
+}
+
+
+Handle<Value> Stat::Start(const Arguments& args) {
+  HandleScope scope;
+
+  if (args.Length() < 1 || !args[0]->IsString()) {
+    return ThrowException(Exception::TypeError(String::New("Bad arguments")));
+  }
+
+  Stat *handler = ObjectWrap::Unwrap<Stat>(args.Holder());
+  String::Utf8Value path(args[0]->ToString());
+
+  assert(handler->path_ == NULL);
+  handler->path_ = strdup(*path);
+
+  ev_stat_set(&handler->watcher_, handler->path_, 0.);
+  ev_stat_start(EV_DEFAULT_UC_ &handler->watcher_);
+
+  handler->persistent_ = args[1]->IsTrue();
+
+  if (!handler->persistent_) {
+    ev_unref(EV_DEFAULT_UC);
+  }
+
+  handler->Attach();
+
+  return Undefined();
+}
+
+
+Handle<Value> Stat::Stop(const Arguments& args) {
+  HandleScope scope;
+  Stat *handler = ObjectWrap::Unwrap<Stat>(args.Holder());
+  handler->Emit("stop", 0, NULL);
+  handler->Stop();
+  return Undefined();
+}
+
+
+void Stat::Stop () {
+  if (watcher_.active) {
+    if (!persistent_) ev_ref(EV_DEFAULT_UC);
+    ev_stat_stop(EV_DEFAULT_UC_ &watcher_);
+    free(path_);
+    path_ = NULL;
+    Detach();
+  }
+}
+
+
+}  // namespace node
diff --git a/src/node_stat.h b/src/node_stat.h
new file mode 100644 (file)
index 0000000..bb6a20d
--- /dev/null
@@ -0,0 +1,46 @@
+// Copyright 2009 Ryan Dahl <ry@tinyclouds.org>
+#ifndef NODE_STAT_H_
+#define NODE_STAT_H_
+
+#include <node.h>
+#include <node_events.h>
+#include <ev.h>
+
+namespace node {
+
+class Stat : EventEmitter {
+ public:
+  static void Initialize(v8::Handle<v8::Object> target);
+
+ protected:
+  static v8::Persistent<v8::FunctionTemplate> constructor_template;
+
+  Stat() : EventEmitter() {
+    persistent_ = false;
+    path_ = NULL;
+    ev_init(&watcher_, Stat::Callback);
+    watcher_.data = this;
+  }
+
+  ~Stat() {
+    Stop();
+    assert(path_ == NULL);
+  }
+
+  static v8::Handle<v8::Value> New(const v8::Arguments& args);
+  static v8::Handle<v8::Value> Start(const v8::Arguments& args);
+  static v8::Handle<v8::Value> Stop(const v8::Arguments& args);
+
+ private:
+  static void Callback(EV_P_ ev_stat *watcher, int revents);
+
+  void Stop();
+
+  ev_stat watcher_;
+  bool persistent_;
+  char *path_;
+};
+
+}  // namespace node
+#endif  // NODE_STAT_H_
+
diff --git a/test/mjsunit/test-stat-handler.js b/test/mjsunit/test-stat-handler.js
new file mode 100644 (file)
index 0000000..32b1fe8
--- /dev/null
@@ -0,0 +1,25 @@
+process.mixin(require("./common"));
+
+var posix = require("posix");
+var path = require("path");
+
+var f = path.join(fixturesDir, "x.txt");
+var f2 = path.join(fixturesDir, "x2.txt");
+
+var changes = 0;
+process.watchFile(f, function () {
+  puts(f + " change");
+  changes++;
+});
+
+
+setTimeout(function () {
+  posix.rename(f, f2).wait();
+  posix.rename(f2, f).wait();
+  process.unwatchFile(f);
+}, 10);
+
+
+process.addListener("exit", function () {
+  assertTrue(changes > 0);
+});
diff --git a/wscript b/wscript
index a78e69e..3bc403b 100644 (file)
--- a/wscript
+++ b/wscript
@@ -321,6 +321,7 @@ def build(bld):
     src/node_http.cc
     src/node_net.cc
     src/node_signal_handler.cc
+    src/node_stat.cc
     src/node_stdio.cc
     src/node_timer.cc
   """