Permalink
Browse files

Added increment and append.

  • Loading branch information...
1 parent 617400f commit 949a56ca69af153d9b7b63352de9f4f65b5f1192 @weaver weaver committed Jun 20, 2011
Showing with 265 additions and 6 deletions.
  1. +2 −1 Makefile
  2. +12 −4 README.md
  3. +62 −0 kyoto.js
  4. +1 −1 package.json
  5. +134 −0 src/_kyoto.cc
  6. +54 −0 tests/kyoto.js
View
@@ -16,4 +16,5 @@ test:
expresso -s tests/*.js
clean:
- node-waf clean
+ node-waf clean
+ rm -rf build
View
@@ -44,11 +44,19 @@ Node.JS. For example:
## Installation ##
-Be sure you've installed Kyoto Cabinet first; try to use your
-operating system's package manager. Then:
+Dependencies: Kyoto Cabinet `>= 1.2.57`, Node `>= 0.4.0`
+
+If Kyoto Cabinet isn't already installed, try using your operating
+system's package manager. Otherwise, download and build
+[from source][3]. If you install manually, don't forget to do an
+`ldconfig` afterwards.
+
+To install the Kyoto bindings, use `npm`:
npm install kyoto
+[3]: http://fallabs.com/kyotocabinet/pkg/
+
## Documentation ##
Please refer to `kyoto.js` for documentation of each method. Some
@@ -57,14 +65,14 @@ are forthcoming.
## Related ##
-An alternative to this package is [Kyoto Client][3], an implementation
+An alternative to this package is [Kyoto Client][4], an implementation
of a Kyoto Tycoon client. Tycoon is an out-of-process server that
allows clients to interact with a Kyoto Cabinet over a network
API. Using Kyoto Client is recommended unless you really need
something in-process and plan to implement your own key/value network
API.
-[3]: http://kyoto-client.org/
+[4]: http://kyoto-client.org/
## License ##
View
@@ -224,6 +224,41 @@ KyotoDB.prototype.getBulk = function(keys, atomic, next) {
return this;
};
+// Append to a value in the database.
+//
+// + key - String key
+// + suffix - String value
+// + next - Function(Error, String suffix, String key) callback
+//
+// Returns self.
+KyotoDB.prototype.append = function(key, suffix, next) {
+ return this._modify('append', key, suffix, next);
+};
+
+// Increment an integer value in the database.
+//
+// + key - String key
+// + num - Integer number to increment by
+// + orig - Integer base number if not already set (optional, default: 0)
+// + next - Function(Error, Integer value, String key) callback
+//
+// Returns self.
+KyotoDB.prototype.increment = function(key, num, orig, next) {
+ return this._inc('increment', key, num, orig, next);
+};
+
+// Increment a double value in the database.
+//
+// + key - String key
+// + num - Double number to increment by
+// + orig - Double base number if not already set (optional, default: 0.0)
+// + next - Function(Error, Double value, String key) callback
+//
+// Returns self.
+KyotoDB.prototype.incrementDouble = function(key, num, orig, next) {
+ return this._inc('incrementDouble', key, num, orig, next);
+};
+
// Set a value in the database.
//
// + key - String key
@@ -317,6 +352,11 @@ KyotoDB.prototype.synchronize = function(hard, next) {
return this;
};
+// TODO: cas, setBulk, removeBulk, match_prefix, match_regex
+// MORE: dumpSnapshot, loadSnapshot, clear
+// THEN: count, size, status, merge
+// MAYBE: scan_parallel
+
// A low-level helper method. See add() or set().
KyotoDB.prototype._modify = function(method, key, val, next) {
var self = this;
@@ -334,6 +374,28 @@ KyotoDB.prototype._modify = function(method, key, val, next) {
return this;
};
+// A low-level helper method. See increment().
+KyotoDB.prototype._inc = function(method, key, num, orig, next) {
+ var self = this;
+
+ if (typeof orig != 'number') {
+ next = orig;
+ orig = 0;
+ }
+
+ if (!next)
+ next = noop;
+
+ if (this.db === null)
+ next.call(this, new Error(method + ': database is closed.'));
+ else
+ this.db[method](key, num, orig, function(err, value, key) {
+ next.call(self, err, value, key);
+ });
+
+ return this;
+};
+
// Create a cursor to iterate over items in the database.
//
// Returns Cursor instance.
View
@@ -1,6 +1,6 @@
{
"name": "kyoto",
- "version": "0.1.1",
+ "version": "0.2.0",
"description": "Kyoto Cabinet bindings for Node.JS",
"author": "Ben Weaver <ben@orangesoda.net>"
}
View
@@ -18,9 +18,16 @@ using namespace node;
using namespace v8;
using namespace kyotocabinet;
+// FIXME: is there a way to add a version check against kyotocabinet?
+
// ## Helpful Macros ##
+#define SET_CONSTANT(target, name) \
+ (target)->Set(String::NewSymbol(#name), \
+ Integer::New(name), \
+ static_cast<PropertyAttribute>(ReadOnly|DontDelete)) \
+
#define SET_CLASS_CONSTANT(target, cls, name) \
(target)->Set(String::NewSymbol(#name), \
Integer::New(cls::name), \
@@ -208,12 +215,18 @@ class PolyDBWrap: ObjectWrap {
SET_CLASS_CONSTANT(ctor, PolyDB::Error, SYSTEM);
SET_CLASS_CONSTANT(ctor, PolyDB::Error, MISC);
+ SET_CONSTANT(ctor, INT64MIN);
+ SET_CONSTANT(ctor, INT64MAX);
+
NODE_SET_PROTOTYPE_METHOD(ctor, "open", Open);
NODE_SET_PROTOTYPE_METHOD(ctor, "close", Close);
NODE_SET_PROTOTYPE_METHOD(ctor, "closeSync", CloseSync);
NODE_SET_PROTOTYPE_METHOD(ctor, "set", Set);
NODE_SET_PROTOTYPE_METHOD(ctor, "add", Add);
NODE_SET_PROTOTYPE_METHOD(ctor, "replace", Replace);
+ NODE_SET_PROTOTYPE_METHOD(ctor, "append", Append);
+ NODE_SET_PROTOTYPE_METHOD(ctor, "increment", Increment);
+ NODE_SET_PROTOTYPE_METHOD(ctor, "incrementDouble", IncrementDouble);
NODE_SET_PROTOTYPE_METHOD(ctor, "get", Get);
NODE_SET_PROTOTYPE_METHOD(ctor, "getBulk", GetBulk);
NODE_SET_PROTOTYPE_METHOD(ctor, "remove", Remove);
@@ -458,6 +471,127 @@ class PolyDBWrap: ObjectWrap {
};
+ // ### Append ###
+
+ DEFINE_METHOD(Append, AppendRequest)
+ class AppendRequest: public SetRequest {
+ public:
+ AppendRequest(const Arguments& args) :
+ SetRequest(args)
+ {}
+
+ inline int exec() {
+ PolyDB* db = wrap->db;
+ if (!db->append(*key, key.length(), *value, value.length())) {
+ result = db->error().code();
+ }
+ return 0;
+ }
+ };
+
+
+ // ### Increment ###
+
+ DEFINE_METHOD(Increment, IncrementRequest)
+ class IncrementRequest: public Request {
+ protected:
+ String::Utf8Value key;
+ int64_t num;
+ int64_t orig;
+
+ public:
+ inline static bool validate(const Arguments& args) {
+ return (args.Length() >= 3
+ && args[0]->IsString()
+ && args[1]->IsNumber()
+ && args[2]->IsNumber()
+ && args[3]->IsFunction());
+ }
+
+ IncrementRequest(const Arguments& args):
+ Request(args, 3),
+ key(args[0]->ToString()),
+ num(args[1]->IntegerValue()),
+ orig(args[2]->IntegerValue())
+ {}
+
+ inline int exec() {
+ PolyDB* db = wrap->db;
+
+ num = db->increment(*key, key.length(), num, orig);
+ if (num == INT64MIN) {
+ result = db->error().code();
+ }
+
+ return 0;
+ }
+
+ inline int after() {
+ int argc = 0;
+ Local<Value> argv[2];
+
+ argv[argc++] = error();
+ if (num != INT64MIN) {
+ argv[argc++] = Integer::New(num);
+ }
+
+ callback(argc, argv);
+ return 0;
+ }
+ };
+
+
+ // ### IncrementDouble ###
+
+ DEFINE_METHOD(IncrementDouble, IncrementDoubleRequest)
+ class IncrementDoubleRequest: public Request {
+ protected:
+ String::Utf8Value key;
+ double num;
+ double orig;
+
+ public:
+ inline static bool validate(const Arguments& args) {
+ return (args.Length() >= 3
+ && args[0]->IsString()
+ && args[1]->IsNumber()
+ && args[2]->IsNumber()
+ && args[3]->IsFunction());
+ }
+
+ IncrementDoubleRequest(const Arguments& args):
+ Request(args, 3),
+ key(args[0]->ToString()),
+ num(args[1]->NumberValue()),
+ orig(args[2]->NumberValue())
+ {}
+
+ inline int exec() {
+ PolyDB* db = wrap->db;
+
+ num = db->increment_double(*key, key.length(), num, orig);
+ if (std::isnan(num)) {
+ result = db->error().code();
+ }
+
+ return 0;
+ }
+
+ inline int after() {
+ int argc = 0;
+ Local<Value> argv[2];
+
+ argv[argc++] = error();
+ if (!std::isnan(num)) {
+ argv[argc++] = Number::New(num);
+ }
+
+ callback(argc, argv);
+ return 0;
+ }
+ };
+
+
// ### Get ###
DEFINE_METHOD(Get, GetRequest)
View
@@ -89,6 +89,52 @@ module.exports = {
});
},
+ 'first append': function(done) {
+ db.append('epsilon', 'prefix', function(err) {
+ if (err) throw err;
+ isEqual('epsilon', 'prefix', done);
+ });
+ },
+
+ 'second append': function(done) {
+ db.append('epsilon', '-suffix', function(err) {
+ if (err) throw err;
+ isEqual('epsilon', 'prefix-suffix', done);
+ });
+ },
+
+ 'increment': function(done) {
+ db.increment('iota', 1, function(err, val) {
+ if (err) throw err;
+ Assert.equal(1, val);
+ done();
+ });
+ },
+
+ 'increment again': function(done) {
+ db.increment('iota', 1, function(err, val) {
+ if (err) throw err;
+ Assert.equal(2, val);
+ done();
+ });
+ },
+
+ 'increment double': function(done) {
+ db.incrementDouble('kappa', 0.1, 1.0, function(err, val) {
+ if (err) throw err;
+ Assert.equal(1.1, val);
+ done();
+ });
+ },
+
+ 'increment double again': function(done) {
+ db.incrementDouble('kappa', 0.1, 1.0, function(err, val) {
+ if (err) throw err;
+ Assert.equal(1.2, val);
+ done();
+ });
+ },
+
'close': function(done) {
db.close(function(err) {
if (err) throw err;
@@ -264,6 +310,14 @@ module.exports = {
// ## Helpers ##
+function isEqual(key, expect, done) {
+ db.get(key, function(err, val) {
+ if (err) throw err;
+ Assert.equal(expect, val);
+ done();
+ });
+}
+
function allEqual(done, expect) {
var all = {};

0 comments on commit 949a56c

Please sign in to comment.