Browse files

Refactor: Split up the command argument parsing

This changeset spit out the argument parsing from
using a centralized argument parsing, to one where
each operation parse its own argument set. By doing
so it makes it easier for us to change parameters
for _one_ of the commands, instead of having to
figure out the order (and impact) on other commands.

The patch renames the old "argument" class into
Operations and subclass that interface with a
specialized class per operation.

The "dict" style of parameters is dropped in this
commit, since we're going to refactor each of the
operations over to a dict in the future so that
they look like the signature in bucket.js.

Change-Id: I973f9dccd4f74a774a82f1a56cf8696a530fd03e
Reviewed-on: http://review.couchbase.org/23909
Reviewed-by: Matt Ingenthron <matt@couchbase.com>
Reviewed-by: Mark Getz <mark.getz@kellpro.com>
Tested-by: Trond Norbye <trond.norbye@gmail.com>
  • Loading branch information...
1 parent 0f6c405 commit 10c7655d6cface0d97d9202f6f519705b5919397 @trondn trondn committed Jan 11, 2013
Showing with 739 additions and 719 deletions.
  1. +1 −1 Makefile
  2. +0 −1 binding.gyp
  3. +32 −16 lib/bucket.js
  4. +0 −345 src/args.cc
  5. +0 −136 src/args.h
  6. +96 −35 src/couchbase_impl.cc
  7. +8 −32 src/couchbase_impl.h
  8. +417 −153 src/operations.cc
  9. +185 −0 src/operations.h
View
2 Makefile
@@ -1,4 +1,4 @@
-SOURCE = src/couchbase_impl.cc src/couchbase_impl.h src/args.cc src/notify.cc \
+SOURCE = src/couchbase_impl.cc src/couchbase_impl.h src/notify.cc \
src/namemap.cc src/operations.cc src/namemap.h src/cas.cc \
src/cas.h
View
1 binding.gyp
@@ -65,7 +65,6 @@
}],
],
'sources': [
- 'src/args.cc',
'src/couchbase_impl.cc',
'src/namemap.cc',
'src/notify.cc',
View
48 lib/bucket.js
@@ -5,12 +5,23 @@ var qs = require("querystring");
exports.create = function(connection, config, ready) {
// We aren't using prototype inheritance here for two reasons:
- // 1) even though they may be faster, this constructor won't be called frequently
- // 2) doing it this way means we get better inspectability in the repl, etc.
+ //
+ // 1) even though they may be faster, this constructor won't be
+ // called frequently
+ //
+ // 2) doing it this way means we get better inspectability in
+ // the repl, etc.
var doJsonConversions = config.doJsonConversions != false;
var getHandler = doJsonConversions ? getParsedHandler : getRawHandler;
+ var OPERATION_ADD = 1;
+ var OPERATION_REPLACE = 2;
+ var OPERATION_SET = 3;
+ var OPERATION_APPEND = 4;
+ var OPERATION_PREPEND = 5;
+
+
function on(event, callback) {
if (config.debug) console.log("couchbase.bucket.on()");
connection.on(event, callback);
@@ -50,7 +61,7 @@ exports.create = function(connection, config, ready) {
}
callsRemaining = keys.length;
- connection.get(keys, undefined, getHandler, [handleValue,this]);
+ connection.get(keys, getHandler, [handleValue,this]);
} else {
if (spooledCallback) {
spooledCallback(null, [], []);
@@ -70,7 +81,7 @@ exports.create = function(connection, config, ready) {
}
}
- connection.get(key, undefined, getHandler, [handleValue,this]);
+ connection.get(key, getHandler, [handleValue,this]);
}
function get(key, callback, spooledCallback) {
@@ -91,9 +102,11 @@ exports.create = function(connection, config, ready) {
requiredArgs(key, doc, callback);
if (config.debug) console.log("couchbase.bucket.set("+key+")");
if (doJsonConversions) doc = makeDoc(doc);
- connection.set(key,
+ connection.store(OPERATION_SET,
+ key,
doc,
meta.expiry || 0,
+ meta.flags || 0,
meta.cas,
setHandler,
[callback,this]);
@@ -110,7 +123,6 @@ exports.create = function(connection, config, ready) {
meta.offset || 1,
meta.defaultValue || 0,
meta.expiry || 0,
- meta.cas,
arithmeticHandler,
[callback,this]);
}
@@ -126,7 +138,6 @@ exports.create = function(connection, config, ready) {
(meta.offset || 1) *-1,
meta.defaultValue || 0,
meta.expiry || 0,
- meta.cas,
arithmeticHandler,
[callback,this]);
}
@@ -138,10 +149,7 @@ exports.create = function(connection, config, ready) {
}
if (config.debug) console.log("couchbase.bucket.observe("+key+")");
- connection.observe(key,
- undefined,
- observeHandler,
- [callback,this]);
+ connection.observe(key, observeHandler, [callback,this]);
}
function endure(key, meta, callback) {
@@ -239,9 +247,11 @@ exports.create = function(connection, config, ready) {
requiredArgs(key, doc, callback);
if (config.debug) console.log("couchbase.bucket.replace("+key+")");
if (doJsonConversions) doc = makeDoc(doc);
- connection.replace(key,
+ connection.store(OPERATION_REPLACE,
+ key,
doc,
meta.expiry || 0,
+ meta.flags || 0,
meta.cas,
setHandler,
[callback,this]);
@@ -255,9 +265,11 @@ exports.create = function(connection, config, ready) {
requiredArgs(key, doc, callback);
if (config.debug) console.log("couchbase.bucket.add("+key+")");
if (doJsonConversions) doc = makeDoc(doc);
- connection.add(key,
+ connection.store(OPERATION_ADD,
+ key,
doc,
meta.expiry || 0,
+ meta.flags || 0,
meta.cas,
setHandler,
[callback,this]);
@@ -272,9 +284,11 @@ exports.create = function(connection, config, ready) {
requiredArgs(key, doc, callback);
if (config.debug) console.log("couchbase.bucket.append("+key+')');
if (doJsonConversions) doc = makeDoc(doc);
- connection.append(key,
+ connection.store(OPERATION_APPEND,
+ key,
doc,
meta.expiry || 0,
+ meta.flags || 0,
meta.cas || null ,
appendHandler,
[callback, this]);
@@ -289,9 +303,11 @@ exports.create = function(connection, config, ready) {
requiredArgs(key, doc, callback);
if (config.debug) console.log("couchbase.bucket.prepend("+key+')');
if (doJsonConversions) doc = makeDoc(doc);
- connection.prepend(key,
+ connection.store(OPERATION_PREPEND,
+ key,
doc,
meta.expiry || 0,
+ meta.flags || 0,
meta.cas || null ,
prependHandler,
[callback, this]);
@@ -557,4 +573,4 @@ function appendHandler(data, errorCode, key, cas) {
function prependHandler(data, errorCode, key, cas) {
var error = makeError(data[1], errorCode);
data[0](error, {id: key, cas: cas})
-}
+}
View
345 src/args.cc
@@ -1,345 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-#include "couchbase_impl.h"
-#include <cstring>
-#include <cstdio>
-#include <cassert>
-
-using namespace Couchnode;
-using namespace std;
-
-static bool get_string(const v8::Handle<v8::Value> &jv,
- char **strp,
- size_t *szp)
-{
- if (!jv->IsString()) {
-#ifdef COUCHNODE_DEBUG
- printf("Not a string..\n");
-#endif
- return false;
- }
-
- v8::Local<v8::String> s = jv->ToString();
- *szp = s->Utf8Length();
- if (!*szp) {
- return false;
- }
-
- *strp = new char[*szp];
- int nw = s->WriteUtf8(*strp, -1, NULL, v8::String::NO_NULL_TERMINATION);
- return true;
-}
-
-#define GET_DPARAM(lval, bidx) \
- if (!dict.IsEmpty()) { \
- lval = dict->Get(NameMap::names[NameMap::##bidx]); \
- }
-
-
-static inline bool isFalseValue(const v8::Handle<v8::Value> &v)
-{
- return (v.IsEmpty() || v->BooleanValue() == false);
-}
-
-CommonArgs::CommonArgs(const v8::Arguments &argv, int pmax, int reqmax)
- : args(argv), key(NULL), params_max(pmax), required_max(reqmax), stale(false)
-{
-}
-
-bool CommonArgs::parse()
-{
- if (use_dictparams) {
- if (args.Length() < required_max + 2) {
- throw Couchnode::Exception("Bad arguments");
- }
- if (args.Length() == required_max + 3) {
- if (!args[required_max + 2]->IsObject()) {
- throw Couchnode::Exception(
- "Have last argument, but it's not an Object",
- args[required_max + 2]);
- }
- dict = args[required_max + 2]->ToObject();
- }
- } else if (args.Length() < (params_max + 2)) {
- throw Couchnode::Exception("Bad arguments");
- }
-
- if (!extractKey()) {
- return false;
- }
-
- if (!extractUdata()) {
- return false;
- }
- return true;
-}
-
-bool CommonArgs::extractKey()
-{
- if (!get_string(args[0], &key, &nkey)) {
-#ifdef COUCHNODE_DEBUG
- printf("Arg at pos %d\n", 0);
-#endif
- throw Couchnode::Exception("Couldn't extract string");
- }
- return true;
-}
-
-bool CommonArgs::extractUdata()
-{
- // { "key", .. params_max ..., function() { .. }, "Data" }
- // Index should be 1 + params_max
-
- int ix;
- if (use_dictparams) {
- ix = required_max + 1;
- } else {
- ix = params_max + 1;
- }
-
- ucb = v8::Local<v8::Function>::Cast(args[ix]);
- if (!ucb->IsFunction()) {
-#ifdef COUCHNODE_DEBUG
- printf("Not a function at index %d\n", ix);
-#endif
- throw Couchnode::Exception("Not a function", args[ix]);
- }
-
- getParam(ix + 1, NameMap::DATA, &udata);
- return true;
-}
-
-void CommonArgs::extractCas(const v8::Handle<v8::Value> &arg,
- lcb_cas_t *cas)
-{
- if (isFalseValue(arg)) {
- *cas = 0;
- } else if (arg->IsObject()) {
- *cas = Cas::GetCas(arg->ToObject());
- } else {
- throw Couchnode::Exception("Couldn't parse CAS", arg);
- }
-}
-
-void CommonArgs::extractExpiry(const v8::Handle<v8::Value> &arg, time_t *exp)
-{
- if (isFalseValue(arg)) {
- *exp = 0;
- } else if (arg->IsNumber()) {
- *exp = arg->Uint32Value();
- } else {
- throw Couchnode::Exception("Couldn't extract expiration", arg);
- }
-}
-
-CouchbaseCookie *CommonArgs::makeCookie()
-{
- return new CouchbaseCookie(args.This(), ucb, udata, 1);
-}
-
-void CommonArgs::bailout(CouchbaseCookie *cookie, lcb_error_t err)
-{
- cookie->result(err, key, nkey);
-}
-
-CommonArgs::~CommonArgs()
-{
- if (stale) {
- return;
- }
-
- if (key) {
- delete[] key;
- key = NULL;
- }
-}
-
-// store(key, value, exp, cas, cb, data)
-StorageArgs::StorageArgs(const v8::Arguments &argv, int vparams)
- : CommonArgs(argv, vparams + 3, 1),
-
- data(NULL), ndata(0), exp(0), cas(0)
-{
-}
-
-bool StorageArgs::parse()
-{
-
- if (!CommonArgs::parse()) {
- return false;
- }
-
- if (!extractValue()) {
- return false;
- }
-
- v8::Local<v8::Value> arg_exp, arg_cas;
- getParam(params_max, NameMap::CAS, &arg_cas);
- getParam(params_max - 1, NameMap::EXPIRY, &arg_exp);
-
- extractExpiry(arg_exp, &exp);
- extractCas(arg_cas, &cas);
- return true;
-}
-
-bool StorageArgs::extractValue()
-{
- if (!get_string(args[1], &data, &ndata)) {
- throw Couchnode::Exception("Bad value", args[1]);
- }
-
- return true;
-}
-
-StorageArgs::~StorageArgs()
-{
- if (stale) {
- return;
- }
-
- if (data) {
- delete[] data;
- data = NULL;
- }
-}
-
-MGetArgs::MGetArgs(const v8::Arguments &args, int nkparams)
- : CommonArgs(args, nkparams),
- kcount(0), single_exp(0), keys(NULL), sizes(NULL), exps(NULL)
-{
-}
-
-bool MGetArgs::extractKey()
-{
-
- if (args[0]->IsString()) {
- if (!CommonArgs::extractKey()) {
- return false;
- }
-
- kcount = 1;
- v8::Local<v8::Value> arg_exp;
- getParam(1, NameMap::EXPIRY, &arg_exp);
-
- extractExpiry(arg_exp, &single_exp);
- assert(key);
-
- keys = &key;
- sizes = &nkey;
-
- if (single_exp) {
- exps = &single_exp;
- }
-
- return true;
- }
-
- exps = NULL;
-
- if (!args[0]->IsArray()) {
- return false;
- }
-
- v8::Local<v8::Array> karry = v8::Local<v8::Array>::Cast(args[0]);
- kcount = karry->Length();
-
- keys = new char*[kcount];
- sizes = new size_t[kcount];
-
- memset(keys, 0, sizeof(char *) * kcount);
- memset(sizes, 0, sizeof(size_t) * kcount);
-
- for (unsigned ii = 0; ii < karry->Length(); ii++) {
- if (!get_string(karry->Get(ii), keys + ii, sizes + ii)) {
- return false;
- }
- }
- return true;
-}
-
-void MGetArgs::bailout(CouchbaseCookie *cookie, lcb_error_t err)
-{
- for (unsigned ii = 0; ii < kcount; ii++) {
- cookie->result(err, keys[ii], sizes[ii]);
- }
-}
-
-MGetArgs::~MGetArgs()
-{
- if (stale) {
- return;
- }
-
- if (exps && exps != &single_exp) {
- delete[] exps;
- }
-
- if (sizes && sizes != &nkey) {
- delete[] sizes;
- }
-
- if (keys && keys != &key) {
- for (unsigned ii = 0; ii < kcount; ii++) {
- delete[] keys[ii];
- }
- delete[] keys;
- }
-
- exps = NULL;
- sizes = NULL;
- keys = NULL;
-}
-
-KeyopArgs::KeyopArgs(const v8::Arguments &args)
- : CommonArgs(args, 1), cas(0)
-{
-}
-
-bool KeyopArgs::parse()
-{
- if (!CommonArgs::parse()) {
- return false;
- }
-
- v8::Local<v8::Value> arg_cas;
- getParam(1, NameMap::CAS, &arg_cas);
-
- extractCas(arg_cas, &cas);
- return true;
-}
-
-// arithmetic(key, delta, initial, exp, cas, ...)
-ArithmeticArgs::ArithmeticArgs(const v8::Arguments &args)
- : StorageArgs(args, 1),
- delta(0), initial(0), create(false)
-{
-}
-
-bool ArithmeticArgs::extractValue()
-{
- if (args[1]->IsNumber() == false) {
- throw Couchnode::Exception("Delta must be numeric", args[1]);
- }
-
- delta = args[1]->IntegerValue();
-
- v8::Local<v8::Value> arg_initial;
- getParam(2, NameMap::INITIAL, &arg_initial);
-
- if (!arg_initial.IsEmpty()) {
- if (arg_initial->IsNumber()) {
- initial = arg_initial->IntegerValue();
- create = true;
- } else {
- if (!arg_initial->IsUndefined()) {
- throw Couchnode::Exception("Initial value must be numeric",
- arg_initial);
- }
- create = false;
- }
-
- } else {
- create = false;
- }
-
- return true;
-}
View
136 src/args.h
@@ -1,136 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-#ifndef COUCHNODE_ARGS_H
-#define COUCHNODE_ARGS_H 1
-#ifndef COUCHBASE_H
-#error "Include couchbase.h before including this file"
-#endif
-
-namespace Couchnode
-{
- class CommonArgs
- {
- public:
-
- CommonArgs(const v8::Arguments &, int pmax = 0, int reqmax = 0);
- virtual ~CommonArgs();
-
- virtual bool parse();
-
- virtual CouchbaseCookie *makeCookie();
-
- virtual bool extractKey();
-
- bool extractUdata();
- void extractExpiry(const v8::Handle<v8::Value> &, time_t *);
- void extractCas(const v8::Handle<v8::Value> &, lcb_cas_t *);
-
- void getParam(int aix, int dcix, v8::Handle<v8::Value> *vp) {
- if (use_dictparams) {
- if (dict.IsEmpty() == false) {
- *vp = dict->Get(NameMap::names[dcix]);
- }
- } else if (args.Length() >= aix - 1) {
- *vp = args[aix];
- }
- }
-
- virtual void bailout(CouchbaseCookie *, lcb_error_t);
-
- void invalidate() {
- stale = true;
- }
-
- // Hooks for deep-copy post assignment.. (manual step)..
- virtual void sync(const CommonArgs &) { }
-
- const v8::Arguments &args;
- v8::Handle<v8::Value> excerr;
- v8::Local<v8::Function> ucb;
- v8::Local<v8::Value> udata;
- char *key;
- size_t nkey;
-
- // last index for operation-specific parameters, this is the length
- // of all arguments minus the key (at the beginning) and callback data
- // (at the end)
- int params_max;
- int required_max;
- bool use_dictparams;
- v8::Local<v8::Object> dict;
- bool stale;
-
- };
-
- class StorageArgs : public CommonArgs
- {
- public:
- StorageArgs(const v8::Arguments &, int nvparams = 0);
- virtual ~StorageArgs();
-
- virtual bool parse();
- virtual bool extractValue();
-
- char *data;
- size_t ndata;
- time_t exp;
- uint64_t cas;
- lcb_storage_t storop;
- };
-
- class MGetArgs : public CommonArgs
- {
- public:
- MGetArgs(const v8::Arguments &, int nkparams = 1);
- virtual ~MGetArgs();
-
- virtual CouchbaseCookie *makeCookie() {
- return new CouchbaseCookie(args.This(), ucb, udata, kcount);
- }
-
- virtual void bailout(CouchbaseCookie *, lcb_error_t);
-
- virtual void sync(const MGetArgs &other) {
- if (other.keys == &other.key) {
- keys = &key;
- }
- if (other.sizes == &other.nkey) {
- sizes = &nkey;
- }
- if (other.exps == &other.single_exp) {
- exps = &single_exp;
- }
- }
-
- size_t kcount;
- time_t single_exp;
-
- char **keys;
- size_t *sizes;
- time_t *exps;
-
- virtual bool extractKey();
- };
-
- class KeyopArgs : public CommonArgs
- {
-
- public:
- KeyopArgs(const v8::Arguments &);
- virtual bool parse();
- uint64_t cas;
- };
-
- class ArithmeticArgs : public StorageArgs
- {
- public:
- ArithmeticArgs(const v8::Arguments &);
-
- virtual bool extractValue();
-
- int64_t delta;
- uint64_t initial;
- bool create;
- };
-
-} // namespace Couchnode
-#endif // COUCHNODE_ARGS_H
View
131 src/couchbase_impl.cc
@@ -105,17 +105,12 @@ void CouchbaseImpl::Init(v8::Handle<v8::Object> target)
NODE_SET_PROTOTYPE_METHOD(s_ct, "isSynchronous", IsSynchronous);
NODE_SET_PROTOTYPE_METHOD(s_ct, "getLastError", GetLastError);
NODE_SET_PROTOTYPE_METHOD(s_ct, "get", Get);
- NODE_SET_PROTOTYPE_METHOD(s_ct, "set", Set);
- NODE_SET_PROTOTYPE_METHOD(s_ct, "add", Add);
- NODE_SET_PROTOTYPE_METHOD(s_ct, "replace", Replace);
- NODE_SET_PROTOTYPE_METHOD(s_ct, "append", Append);
- NODE_SET_PROTOTYPE_METHOD(s_ct, "prepend", Prepend);
+ NODE_SET_PROTOTYPE_METHOD(s_ct, "store", Store);
NODE_SET_PROTOTYPE_METHOD(s_ct, "on", On);
NODE_SET_PROTOTYPE_METHOD(s_ct, "arithmetic", Arithmetic);
NODE_SET_PROTOTYPE_METHOD(s_ct, "remove", Remove);
NODE_SET_PROTOTYPE_METHOD(s_ct, "touch", Touch);
NODE_SET_PROTOTYPE_METHOD(s_ct, "observe", Observe);
- NODE_SET_PROTOTYPE_METHOD(s_ct, "_opCallStyle", OpCallStyle);
target->Set(v8::String::NewSymbol("CouchbaseImpl"), s_ct->GetFunction());
@@ -160,33 +155,6 @@ v8::Handle<v8::Value> CouchbaseImpl::on(const v8::Arguments &args)
return scope.Close(v8::True());
}
-v8::Handle<v8::Value> CouchbaseImpl::OpCallStyle(const v8::Arguments &args)
-{
- CouchbaseImpl *me = ObjectWrap::Unwrap<CouchbaseImpl>(args.This());
-
- v8::Handle<v8::String> rv = me->useHashtableParams ?
- NameMap::names[NameMap::OPSTYLE_HASHTABLE] :
- NameMap::names[NameMap::OPSTYLE_POSITIONAL];
-
- if (!args.Length()) {
- return rv;
- }
-
- if (args.Length() != 1 || args[0]->IsString() == false) {
- return ThrowException("First (and only) argument must be a string");
- }
-
- if (NameMap::names[NameMap::OPSTYLE_HASHTABLE]->Equals(args[0])) {
- me->useHashtableParams = true;
- } else if (NameMap::names[NameMap::OPSTYLE_POSITIONAL]->Equals(args[0])) {
- me->useHashtableParams = false;
- } else {
- return ThrowException("Unrecognized call style");
- }
-
- return rv;
-}
-
v8::Handle<v8::Value> CouchbaseImpl::New(const v8::Arguments &args)
{
v8::HandleScope scope;
@@ -361,7 +329,7 @@ void CouchbaseImpl::errorCallback(lcb_error_t err, const char *errinfo)
if (!connected) {
// Time to fail out all the commands..
connected = true;
- runScheduledCommands();
+ runScheduledOperations();
}
if (err == LCB_ETIMEDOUT && onTimeout()) {
@@ -382,7 +350,7 @@ void CouchbaseImpl::onConnect(lcb_configuration_t config)
if (config == LCB_CONFIGURATION_NEW) {
if (!connected) {
connected = true;
- runScheduledCommands();
+ runScheduledOperations();
}
}
lcb_set_configuration_callback(instance, NULL);
@@ -407,3 +375,96 @@ bool CouchbaseImpl::onTimeout(void)
return false;
}
+
+static inline v8::Handle<v8::Value> makeOperation(CouchbaseImpl *me,
+ const v8::Arguments &arguments,
+ Operation *op)
+{
+ try {
+ op->parse(arguments);
+ } catch (std::string &error) {
+ delete op;
+ return ThrowException(error.c_str());
+ }
+
+ lcb_error_t error = op->execute(me->getLibcouchbaseHandle());
+ if (error == LCB_SUCCESS) {
+ delete op;
+ } else {
+ me->setLastError(error);
+ if (me->isConnected()) {
+ op->cancel(error);
+ return v8::False();
+ }
+
+ me->scheduleOperation(op);
+ }
+
+ return v8::True();
+}
+
+void CouchbaseImpl::runScheduledOperations(void)
+{
+ QueuedOperationList::iterator iter = queuedOperations.begin();
+ for (; iter != queuedOperations.end(); iter++) {
+
+ lcb_error_t err = (*iter)->execute(instance);
+ if (err != LCB_SUCCESS) {
+ lastError = err;
+ (*iter)->cancel(err);
+ }
+ delete *iter;
+ }
+ queuedOperations.clear();
+}
+
+/*******************************************
+ ** Entry point for all of the operations **
+ ******************************************/
+v8::Handle<v8::Value> CouchbaseImpl::Store(const v8::Arguments &args)
+{
+ v8::HandleScope scope;
+ CouchbaseImpl *me = ObjectWrap::Unwrap<CouchbaseImpl>(args.This());
+ StoreOperation *op = new StoreOperation;
+ return makeOperation(me, args, op);
+}
+
+v8::Handle<v8::Value> CouchbaseImpl::Get(const v8::Arguments &args)
+{
+ v8::HandleScope scope;
+ CouchbaseImpl *me = ObjectWrap::Unwrap<CouchbaseImpl>(args.This());
+ GetOperation *op = new GetOperation;
+ return makeOperation(me, args, op);
+}
+
+v8::Handle<v8::Value> CouchbaseImpl::Touch(const v8::Arguments &args)
+{
+ v8::HandleScope scope;
+ CouchbaseImpl *me = ObjectWrap::Unwrap<CouchbaseImpl>(args.This());
+ TouchOperation *op = new TouchOperation;
+ return makeOperation(me, args, op);
+}
+
+v8::Handle<v8::Value> CouchbaseImpl::Observe(const v8::Arguments &args)
+{
+ v8::HandleScope scope;
+ CouchbaseImpl *me = ObjectWrap::Unwrap<CouchbaseImpl>(args.This());
+ ObserveOperation *op = new ObserveOperation;
+ return makeOperation(me, args, op);
+}
+
+v8::Handle<v8::Value> CouchbaseImpl::Arithmetic(const v8::Arguments &args)
+{
+ v8::HandleScope scope;
+ CouchbaseImpl *me = ObjectWrap::Unwrap<CouchbaseImpl>(args.This());
+ ArithmeticOperation *op = new ArithmeticOperation;
+ return makeOperation(me, args, op);
+}
+
+v8::Handle<v8::Value> CouchbaseImpl::Remove(const v8::Arguments &args)
+{
+ v8::HandleScope scope;
+ CouchbaseImpl *me = ObjectWrap::Unwrap<CouchbaseImpl>(args.This());
+ RemoveOperation *op = new RemoveOperation;
+ return makeOperation(me, args, op);
+}
View
40 src/couchbase_impl.h
@@ -52,7 +52,7 @@
#include <libcouchbase/couchbase.h>
#include "namemap.h"
#include "cookie.h"
-#include "args.h"
+#include "operations.h"
#include "cas.h"
namespace Couchnode
@@ -78,16 +78,11 @@ namespace Couchnode
static v8::Handle<v8::Value> SetHandler(const v8::Arguments &);
static v8::Handle<v8::Value> GetLastError(const v8::Arguments &);
static v8::Handle<v8::Value> Get(const v8::Arguments &);
- static v8::Handle<v8::Value> Set(const v8::Arguments &);
- static v8::Handle<v8::Value> Add(const v8::Arguments &);
- static v8::Handle<v8::Value> Replace(const v8::Arguments &);
- static v8::Handle<v8::Value> Append(const v8::Arguments &);
- static v8::Handle<v8::Value> Prepend(const v8::Arguments &);
+ static v8::Handle<v8::Value> Store(const v8::Arguments &);
static v8::Handle<v8::Value> Arithmetic(const v8::Arguments &);
static v8::Handle<v8::Value> Remove(const v8::Arguments &);
static v8::Handle<v8::Value> Touch(const v8::Arguments &);
static v8::Handle<v8::Value> Observe(const v8::Arguments &);
- static v8::Handle<v8::Value> OpCallStyle(const v8::Arguments &);
// Setting up the event emitter
static v8::Handle<v8::Value> On(const v8::Arguments &);
v8::Handle<v8::Value> on(const v8::Arguments &);
@@ -98,11 +93,11 @@ namespace Couchnode
void errorCallback(lcb_error_t err, const char *errinfo);
- void scheduleCommand(QueuedCommand &cmd) {
- queued_commands.push_back(cmd);
+ void scheduleOperation(Operation *op) {
+ queuedOperations.push_back(op);
}
- void runScheduledCommands(void);
+ void runScheduledOperations(void);
void setLastError(lcb_error_t err) {
lastError = err;
@@ -125,8 +120,9 @@ namespace Couchnode
lcb_t instance;
lcb_error_t lastError;
- typedef std::vector<QueuedCommand> QueuedCommandList;
- QueuedCommandList queued_commands;
+ // @todo why not use a std::queue?
+ typedef std::vector<Operation*> QueuedOperationList;
+ QueuedOperationList queuedOperations;
typedef std::map<std::string, v8::Persistent<v8::Function> > EventMap;
EventMap events;
@@ -138,26 +134,6 @@ namespace Couchnode
#endif
};
- class QueuedCommand
- {
- public:
- typedef lcb_error_t (*operfunc)(
- lcb_t, CommonArgs *, CouchbaseCookie *);
-
- QueuedCommand(CouchbaseCookie *c, CommonArgs *a, operfunc f) :
- cookie(c), args(a), ofn(f) { }
-
- virtual ~QueuedCommand() { }
-
- void setDone(void) {
- delete args;
- }
-
- CouchbaseCookie *cookie;
- CommonArgs *args;
- operfunc ofn;
- };
-
/**
* Base class of the Exceptions thrown by the internals of
* Couchnode
View
570 src/operations.cc
@@ -15,211 +15,475 @@
* limitations under the License.
*/
#include "couchbase_impl.h"
-using namespace Couchnode;
-/**
- * These are simplistic functions which each take a CommonArgs object
- * and unpack it into the appropriate libcouchbase entry point.
- */
+#include <cstdlib>
+#include <sstream>
-static lcb_error_t doGet(lcb_t instance,
- CommonArgs *args,
- CouchbaseCookie *cookie)
+using namespace Couchnode;
+
+static void getString(const v8::Handle<v8::Value> &jv, char* &str, size_t &len)
{
- MGetArgs *cargs = static_cast<MGetArgs *>(args);
-
- // @todo move this over to the MGetArgs structs
- lcb_get_cmd_t **commands = new lcb_get_cmd_t*[cargs->kcount];
- for (size_t ii = 0; ii < cargs->kcount; ++ii) {
- if (cargs->exps == NULL) {
- commands[ii] = new lcb_get_cmd_t(cargs->keys[ii],
- cargs->sizes[ii]);
- } else {
- // @todo how do the single thing work?
- commands[ii] = new lcb_get_cmd_t(cargs->keys[ii],
- cargs->sizes[ii],
- cargs->exps[ii]);
+ if (!jv->IsString()) {
+ throw std::string("Not a string");
+ }
+
+ v8::Local<v8::String> s = jv->ToString();
+ len = s->Utf8Length();
+ if (len == 0) {
+ str = NULL;
+ } else {
+ str = new char[len];
+ int nw = s->WriteUtf8(str, -1, NULL, v8::String::NO_NULL_TERMINATION);
+ if (nw != (int)len) {
+ delete []str;
+ throw std::string("Incomplete conversion");
}
}
+}
- lcb_error_t ret = lcb_get(instance, cookie, cargs->kcount, commands);
- for (size_t ii = 0; ii < cargs->kcount; ++ii) {
- delete commands[ii];
+static int getInteger(const v8::Handle<v8::Value> &val)
+{
+ if (!val->IsNumber()) {
+ throw std::string("Not a number");
}
- delete []commands;
- return ret;
+ return val->IntegerValue();
}
-static lcb_error_t doSet(lcb_t instance,
- CommonArgs *args,
- CouchbaseCookie *cookie)
+static inline bool isFalseValue(const v8::Handle<v8::Value> &v)
{
- StorageArgs *cargs = static_cast<StorageArgs *>(args);
+ return (v.IsEmpty() || v->BooleanValue() == false);
+}
- lcb_store_cmd_t cmd(cargs->storop, cargs->key, cargs->nkey,
- cargs->data, cargs->ndata, 0, cargs->exp,
- cargs->cas);
- lcb_store_cmd_t *commands[] = { &cmd };
- return lcb_store(instance, cookie, 1, commands);
+static lcb_cas_t getCas(const v8::Handle<v8::Value> &val)
+{
+ if (isFalseValue(val)) {
+ return 0;
+ } else if (val->IsObject()) {
+ return Cas::GetCas(val->ToObject());
+ } else {
+ throw std::string("Invalid cas specified");
+ }
}
-static lcb_error_t doTouch(lcb_t instance,
- CommonArgs *args,
- CouchbaseCookie *cookie)
+/**
+ * The argument layout of the Store command is:
+ * store(operation, key, data, expiry, flags, cas, callback, cookie);
+ *
+ */
+void StoreOperation::parse(const v8::Arguments &arguments)
{
- MGetArgs *cargs = static_cast<MGetArgs *>(args);
-
-
- // @todo move this over to the MGetArgs structs
- lcb_touch_cmd_t **commands = new lcb_touch_cmd_t*[cargs->kcount];
- for (size_t ii = 0; ii < cargs->kcount; ++ii) {
- if (cargs->exps == NULL) {
- commands[ii] = new lcb_touch_cmd_t(cargs->keys[ii],
- cargs->sizes[ii]);
- } else {
- // @todo how do the single thing work?
- commands[ii] = new lcb_touch_cmd_t(cargs->keys[ii],
- cargs->sizes[ii],
- cargs->exps[ii]);
- }
+ const int idxOp = 0;
+ const int idxKey = idxOp + 1;
+ const int idxValue = idxKey + 1;
+ const int idxExp = idxValue + 1;
+ const int idxFlags = idxExp + 1;
+ const int idxCas = idxFlags + 1;
+ const int idxCallback = idxCas + 1;
+ const int idxCookie = idxCallback + 1;
+ const int idxLast = idxCookie + 1;
+
+ if (arguments.Length() != idxLast) {
+ std::stringstream ss;
+ ss << "Incorrect number of arguments passed to store()." << std::endl
+ << " usage: store(type, key, data, expiry, flags, cas, "
+ << "callback, cookie)" << std::endl
+ << " Expected " << idxLast << "arguments, got: "
+ << arguments.Length() << std::endl;
+ throw ss.str();
}
- lcb_error_t ret = lcb_touch(instance, cookie, cargs->kcount, commands);
- for (size_t ii = 0; ii < cargs->kcount; ++ii) {
- delete commands[ii];
+ try {
+ cmd.v.v0.operation = (lcb_storage_t)getInteger(arguments[idxOp]);
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as parameter #" << idxOp
+ << " to store(). Expected a number";
+ throw ss.str();
}
- delete []commands;
- return ret;
-}
+ try {
+ char *data;
+ size_t len;
+ getString(arguments[idxKey], data, len);
+ cmd.v.v0.key = (void*)data;
+ cmd.v.v0.nkey = (lcb_uint16_t)len;
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Failed to parse key argument (#" << idxKey << ") to store: "
+ << ex;
+ throw ss.str();
+ }
-static lcb_error_t doObserve(lcb_t instance,
- CommonArgs *args,
- CouchbaseCookie *cookie)
-{
- lcb_observe_cmd_t cmd(args->key, args->nkey);
- lcb_observe_cmd_t *commands[] = { &cmd };
- return lcb_observe(instance, cookie, 1, commands);
-}
+ try {
+ char *data;
+ size_t len;
+ getString(arguments[idxValue], data, len);
+ cmd.v.v0.bytes = (void*)data;
+ cmd.v.v0.nbytes = (lcb_uint16_t)len;
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Failed to parse value argument (# " << idxValue
+ << ")to store: " << ex;
+ throw ss.str();
+ }
-static lcb_error_t doArithmetic(lcb_t instance,
- CommonArgs *args,
- CouchbaseCookie *cookie)
-{
- ArithmeticArgs *cargs = static_cast<ArithmeticArgs *>(args);
- lcb_arithmetic_cmd_t cmd(cargs->key, cargs->nkey, cargs->delta,
- cargs->create, cargs->initial, cargs->exp);
- lcb_arithmetic_cmd_t *commands[] = { &cmd };
- return lcb_arithmetic(instance, cookie, 1, commands);
-}
+ try {
+ cmd.v.v0.exptime = (lcb_time_t)getInteger(arguments[idxExp]);
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as expiry parameter (#" << idxExp
+ << "). Expected a number";
+ throw ss.str();
+ }
-static lcb_error_t doRemove(lcb_t instance,
- CommonArgs *args,
- CouchbaseCookie *cookie)
-{
- KeyopArgs *cargs = static_cast<KeyopArgs *>(args);
- lcb_remove_cmd_t cmd(cargs->key, cargs->nkey, cargs->cas);
- lcb_remove_cmd_t *commands[] = { &cmd };
- return lcb_remove(instance, cookie, 1, commands);
+ try {
+ cmd.v.v0.flags = (lcb_uint32_t)getInteger(arguments[idxFlags]);
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as flags parameter (#" << idxFlags
+ << "). Expected a number";
+ throw ss.str();
+ }
+
+ try {
+ cmd.v.v0.cas = getCas(arguments[idxCas]);
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as cas parameter (#" << idxCas
+ << "): " << ex;
+ throw ss.str();
+ }
+
+ // callback function to follow
+ if (!arguments[idxCallback]->IsFunction()) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as callback parameter (#" << idxCallback
+ << "). Expected a function";
+ throw ss.str();
+ }
+
+ cookie = new CouchbaseCookie(arguments.This(),
+ v8::Local<v8::Function>::Cast(arguments[idxCallback]),
+ arguments[idxCookie], 1);
}
/**
- * Entry point for API calls.
+ * The argument layout of the Get command is:
+ * get(key, callback, cookie);
+ * or
+ * get(keys[], callback, cookie);
*/
-template <class Targs>
-static inline v8::Handle<v8::Value> makeOperation(CouchbaseImpl *me,
- Targs *cargs,
- QueuedCommand::operfunc ofn)
+void GetOperation::parse(const v8::Arguments &arguments)
{
- cargs->use_dictparams = me->isUsingHashtableParams();
- try {
- if (!cargs->parse()) {
- return cargs->excerr;
- }
- } catch (Couchnode::Exception &exp) {
- return ThrowException(exp.getMessage().c_str());
+ const int idxKey = 0;
+ const int idxCallback = idxKey + 1;
+ const int idxCookie = idxCallback + 1;
+ const int idxLast = idxCookie + 1;
+
+ if (arguments.Length() < idxLast) {
+ std::stringstream ss;
+ ss << "Incorrect number of arguments passed to get()." << std::endl
+ << " usage: get(key, callback, cookie)" << std::endl
+ << " Expected " << idxLast << "arguments, got: "
+ << arguments.Length() << std::endl;
+ throw ss.str();
}
- CouchbaseCookie *cookie = cargs->makeCookie();
+ bool multiget = arguments[idxKey]->IsArray();
+ if (multiget) {
+ // argv[0] is an arry containing the keys
+ v8::Local<v8::Array> keys = v8::Local<v8::Array>::Cast(arguments[idxKey]);
+ numCommands = keys->Length();;
+ cmds = new lcb_get_cmd_t* [numCommands];
+ memset(cmds, NULL, sizeof(lcb_get_cmd_t*) * numCommands);
- lcb_error_t err = ofn(me->getLibcouchbaseHandle(), cargs, cookie);
+ for (int ii = 0; ii < numCommands; ++ii) {
+ lcb_get_cmd_t *cmd = new lcb_get_cmd_t();
+ cmds[ii] = cmd;
+ try {
+ char *data;
+ size_t len;
+ getString(keys->Get(ii), data, len);
+ cmd->v.v0.key = (void*)data;
+ cmd->v.v0.nkey = (lcb_uint16_t)len;
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Failed to parse key #" << ii << ": " << ex;
+ throw ss.str();
+ }
+ }
+ } else {
+ numCommands = 1;
+ cmds = new lcb_get_cmd_t* [numCommands];
+ lcb_get_cmd_t *cmd = new lcb_get_cmd_t();
+ cmds[0] = cmd;
- if (err != LCB_SUCCESS) {
- me->setLastError(err);
- if (me->isConnected()) {
- cargs->bailout(cookie, err);
- return v8::False();
+ try {
+ char *data;
+ size_t len;
+ getString(arguments[idxKey], data, len);
+ cmd->v.v0.key = (void*)data;
+ cmd->v.v0.nkey = (lcb_uint16_t)len;
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Failed to parse key argument (#" << idxKey << ") to get: "
+ << ex;
+ throw ss.str();
}
- Targs *newargs = new Targs(*cargs);
- newargs->sync(*cargs);
- cargs->invalidate();
- QueuedCommand cmd(cookie, newargs, ofn);
- me->scheduleCommand(cmd);
}
- return v8::True();
+ // callback function to follow
+ if (!arguments[idxCallback]->IsFunction()) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as callback parameter (#"
+ << idxCallback << "). Expected a function";
+ throw ss.str();
+ }
+
+ cookie = new CouchbaseCookie(arguments.This(),
+ v8::Local<v8::Function>::Cast(arguments[idxCallback]),
+ arguments[idxCookie], numCommands);
}
-void CouchbaseImpl::runScheduledCommands(void)
+/**
+ * The argument layout of the Touch command is:
+ * touch(key, exptime, callback, cookie);
+ */
+void TouchOperation::parse(const v8::Arguments &arguments)
{
- for (QueuedCommandList::iterator iter = queued_commands.begin();
- iter != queued_commands.end(); iter++) {
- lcb_error_t err = iter->ofn(instance, iter->args, iter->cookie);
- if (err != LCB_SUCCESS) {
- lastError = err;
- iter->args->bailout(iter->cookie, err);
- }
- iter->setDone();
+ const int idxKey = 0;
+ const int idxExp = idxKey + 1;
+ const int idxCallback = idxExp + 1;
+ const int idxCookie = idxCallback + 1;
+ const int idxLast = idxCookie + 1;
+
+ if (arguments.Length() < idxLast) {
+ std::stringstream ss;
+ ss << "Incorrect number of arguments passed to touch()." << std::endl
+ << " usage: touch(key, exptime, callback, cookie)" << std::endl
+ << " Expected " << idxLast << "arguments, got: "
+ << arguments.Length() << std::endl;
+ throw ss.str();
+ }
+
+ try {
+ char *data;
+ size_t len;
+ getString(arguments[idxKey], data, len);
+ cmd.v.v0.key = (void*)data;
+ cmd.v.v0.nkey = (lcb_uint16_t)len;
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Failed to parse key argument (#" << idxKey << ") to get: "
+ << ex;
+ throw ss.str();
}
- queued_commands.clear();
-}
-#define COUCHNODE_API_INIT_COMMON(argcls) \
- v8::HandleScope scope; \
- CouchbaseImpl *me = ObjectWrap::Unwrap<CouchbaseImpl>(args.This()); \
- argcls cargs = argcls(args);
+ try {
+ cmd.v.v0.exptime = (lcb_time_t)getInteger(arguments[idxExp]);
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as expiry parameter (#" << idxExp
+ << "). Expected a number";
+ throw ss.str();
+ }
-v8::Handle<v8::Value> CouchbaseImpl::Get(const v8::Arguments &args)
-{
- COUCHNODE_API_INIT_COMMON(MGetArgs);
- return makeOperation<MGetArgs>(me, &cargs, doGet);
+ // callback function to follow
+ if (!arguments[idxCallback]->IsFunction()) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as callback parameter (#"
+ << idxCallback << "). Expected a function";
+ throw ss.str();
+ }
+
+ cookie = new CouchbaseCookie(arguments.This(),
+ v8::Local<v8::Function>::Cast(arguments[idxCallback]),
+ arguments[idxCookie], 1);
}
-v8::Handle<v8::Value> CouchbaseImpl::Touch(const v8::Arguments &args)
+/**
+ * The argument layout of the Remove command is:
+ * remove(key, cas, callback, cookie);
+ */
+void RemoveOperation::parse(const v8::Arguments &arguments)
{
- COUCHNODE_API_INIT_COMMON(MGetArgs);
- return makeOperation<MGetArgs>(me, &cargs, doTouch);
+
+ const int idxKey = 0;
+ const int idxCas = idxKey + 1;
+ const int idxCallback = idxCas + 1;
+ const int idxCookie = idxCallback + 1;
+ const int idxLast = idxCookie + 1;
+
+ if (arguments.Length() < idxLast) {
+ std::stringstream ss;
+ ss << "Incorrect number of arguments passed to remove()." << std::endl
+ << " usage: remove(key, cas, callback, cookie)" << std::endl
+ << " Expected " << idxLast << "arguments, got: "
+ << arguments.Length() << std::endl;
+ throw ss.str();
+ }
+
+ try {
+ char *data;
+ size_t len;
+ getString(arguments[idxKey], data, len);
+ cmd.v.v0.key = (void*)data;
+ cmd.v.v0.nkey = (lcb_uint16_t)len;
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Failed to parse key argument (#" << idxKey << ") to get: "
+ << ex;
+ throw ss.str();
+ }
+
+ try {
+ cmd.v.v0.cas = getCas(arguments[idxCas]);
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as cas parameter (#" << idxCas
+ << "): " << ex;
+ throw ss.str();
+ }
+
+ // callback function to follow
+ if (!arguments[idxCallback]->IsFunction()) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as callback parameter (#"
+ << idxCallback << "). Expected a function";
+ throw ss.str();
+ }
+
+ cookie = new CouchbaseCookie(arguments.This(),
+ v8::Local<v8::Function>::Cast(arguments[idxCallback]),
+ arguments[idxCookie], 1);
}
-v8::Handle<v8::Value> CouchbaseImpl::Observe(const v8::Arguments &args)
+/**
+ * The argument layout of the Observe command is:
+ * observe(key, callback, cookie);
+ */
+void ObserveOperation::parse(const v8::Arguments &arguments)
{
- COUCHNODE_API_INIT_COMMON(MGetArgs);
- return makeOperation<MGetArgs>(me, &cargs, doObserve);
-}
+ const int idxKey = 0;
+ const int idxCallback = idxKey + 1;
+ const int idxCookie = idxCallback + 1;
+ const int idxLast = idxCookie + 1;
+
+ if (arguments.Length() < idxLast) {
+ std::stringstream ss;
+ ss << "Incorrect number of arguments passed to observe()." << std::endl
+ << " usage: observe(key, callback, cookie)" << std::endl
+ << " Expected " << idxLast << "arguments, got: "
+ << arguments.Length() << std::endl;
+ throw ss.str();
+ }
-#define COUCHBASE_STOREFN_DEFINE(name, mode) \
- v8::Handle<v8::Value> CouchbaseImpl::name(const v8::Arguments& args) { \
- COUCHNODE_API_INIT_COMMON(StorageArgs); \
- cargs.storop = LCB_##mode; \
- return makeOperation<StorageArgs>(me, &cargs, doSet); \
+ try {
+ char *data;
+ size_t len;
+ getString(arguments[idxKey], data, len);
+ cmd.v.v0.key = (void*)data;
+ cmd.v.v0.nkey = (lcb_uint16_t)len;
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Failed to parse key argument (#" << idxKey << "): "
+ << ex;
+ throw ss.str();
}
-COUCHBASE_STOREFN_DEFINE(Set, SET)
-COUCHBASE_STOREFN_DEFINE(Add, ADD)
-COUCHBASE_STOREFN_DEFINE(Replace, REPLACE)
-COUCHBASE_STOREFN_DEFINE(Append, APPEND)
-COUCHBASE_STOREFN_DEFINE(Prepend, PREPEND)
+ // callback function to follow
+ if (!arguments[idxCallback]->IsFunction()) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as callback parameter (#"
+ << idxCallback << "). Expected a function";
+ throw ss.str();
+ }
-v8::Handle<v8::Value> CouchbaseImpl::Arithmetic(const v8::Arguments &args)
-{
- COUCHNODE_API_INIT_COMMON(ArithmeticArgs);
- return makeOperation<ArithmeticArgs>(me, &cargs, doArithmetic);
+ cookie = new CouchbaseCookie(arguments.This(),
+ v8::Local<v8::Function>::Cast(arguments[idxCallback]),
+ arguments[idxCookie], 1);
}
-v8::Handle<v8::Value> CouchbaseImpl::Remove(const v8::Arguments &args)
+/**
+ * The argument layout of the aritmethic command is:
+ * arithmetic(key, delta, initial, expiry, callback, cookie);
+ */
+void ArithmeticOperation::parse(const v8::Arguments &arguments)
{
- COUCHNODE_API_INIT_COMMON(KeyopArgs);
- return makeOperation<KeyopArgs>(me, &cargs, doRemove);
+
+ const int idxKey = 0;
+ const int idxDelta = idxKey + 1;
+ const int idxInitial = idxDelta + 1;
+ const int idxExp = idxInitial + 1;
+ const int idxCallback = idxExp + 1;
+ const int idxCookie = idxCallback + 1;
+ const int idxLast = idxCookie + 1;
+
+ if (arguments.Length() < idxLast) {
+ std::stringstream ss;
+ ss << "Incorrect number of arguments passed to arithmetic()."
+ << std::endl
+ << " usage: arithmetic(key, delta, initial, expiry, "
+ << "callback, cookie)" << std::endl
+ << " Expected " << idxLast << "arguments, got: "
+ << arguments.Length() << std::endl;
+ throw ss.str();
+ }
+
+ try {
+ char *data;
+ size_t len;
+ getString(arguments[idxKey], data, len);
+ cmd.v.v0.key = (void*)data;
+ cmd.v.v0.nkey = (lcb_uint16_t)len;
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Failed to parse key argument (#" << idxKey << ") to get: "
+ << ex;
+ throw ss.str();
+ }
+
+ try {
+ cmd.v.v0.delta = (lcb_int64_t)getInteger(arguments[idxDelta]);
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as delta parameter (#" << idxExp
+ << "). Expected a number";
+ throw ss.str();
+ }
+
+ try {
+ cmd.v.v0.initial = (lcb_time_t)getInteger(arguments[idxInitial]);
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as initial parameter (#" << idxExp
+ << "). Expected a number";
+ throw ss.str();
+ }
+
+ try {
+ cmd.v.v0.exptime = (lcb_time_t)getInteger(arguments[idxExp]);
+ } catch (std::string &ex) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as expiry parameter (#" << idxExp
+ << "). Expected a number";
+ throw ss.str();
+ }
+
+ // @todo the user should be allowed to disable this!
+ cmd.v.v0.create = 1;
+
+ // callback function to follow
+ if (!arguments[idxCallback]->IsFunction()) {
+ std::stringstream ss;
+ ss << "Incorrect parameter passed as callback parameter (#"
+ << idxCallback << "). Expected a function";
+ throw ss.str();
+ }
+
+ cookie = new CouchbaseCookie(arguments.This(),
+ v8::Local<v8::Function>::Cast(arguments[idxCallback]),
+ arguments[idxCookie], 1);
}
View
185 src/operations.h
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#ifndef COUCHNODE_OPERATIONS_H
+#define COUCHNODE_OPERATIONS_H 1
+#ifndef COUCHBASE_H
+#error "Include couchbase.h before including this file"
+#endif
+
+namespace Couchnode
+{
+ /**
+ * This is an abstract interface for all operations.
+ * The lifecycle for the operation is that after it is created
+ * the parse() method is called for the operation to initialize
+ * itself. Then either the execute method or the cancel method
+ * is called upon the operation before it is deleted.
+ */
+ class Operation {
+ public:
+ /** Release all allocated resources */
+ virtual ~Operation() {
+ // The cookie is refcounted and deletes itself
+ }
+
+ /**
+ * Parse the arguments for the given operation.
+ * @param arguments the argument vector provided by v8
+ * @throws std::string if there is a problem with one of the
+ * arguments
+ */
+ virtual void parse(const v8::Arguments &arguments) = 0;
+
+ /**
+ * Exeucte the operation
+ * @return the error code from libcouchbase
+ */
+ virtual lcb_error_t execute(lcb_t instance) = 0;
+
+ /**
+ * Mark this operation as terminated with the specified
+ * error code
+ */
+ virtual void cancel(lcb_error_t err) = 0;
+ };
+
+ class StoreOperation : public Operation {
+ public:
+ StoreOperation() : cmd(), cookie(NULL) { }
+ virtual ~StoreOperation() {
+ delete [](char*)cmd.v.v0.key;
+ delete [](char*)cmd.v.v0.bytes;
+ }
+ virtual void parse(const v8::Arguments &arguments);
+
+ virtual lcb_error_t execute(lcb_t instance) {
+ const lcb_store_cmd_t * const cmds[] = { &cmd };
+ return lcb_store(instance, cookie, 1, cmds);
+ }
+
+ virtual void cancel(lcb_error_t err) {
+ cookie->result(err, cmd.v.v0.key, cmd.v.v0.nkey);
+ }
+ private:
+ lcb_store_cmd_t cmd;
+ CouchbaseCookie *cookie;
+ };
+
+ class GetOperation : public Operation {
+ public:
+ GetOperation() : numCommands(0), cmds(NULL), cookie(NULL) {}
+ virtual ~GetOperation() {
+ for (int ii = 0; ii < numCommands; ++ii) {
+ delete [](char*)cmds[ii]->v.v0.key;
+ }
+ delete []cmds;
+ }
+
+ virtual void parse(const v8::Arguments &arguments);
+
+ virtual lcb_error_t execute(lcb_t instance) {
+ return lcb_get(instance, cookie, numCommands, cmds);
+ }
+
+ virtual void cancel(lcb_error_t err) {
+ for (int ii = 0; ii < numCommands; ++ii) {
+ cookie->result(err, cmds[ii]->v.v0.key, cmds[ii]->v.v0.nkey);
+ }
+ }
+
+ private:
+ int numCommands;
+ lcb_get_cmd_t ** cmds;
+ CouchbaseCookie *cookie;
+ };
+
+ class TouchOperation : public Operation {
+ public:
+ TouchOperation() : cmd(), cookie(NULL) {}
+ virtual ~TouchOperation() {
+ delete [](char*)cmd.v.v0.key;
+ }
+ virtual void parse(const v8::Arguments &arguments);
+
+ lcb_error_t execute(lcb_t instance) {
+ const lcb_touch_cmd_t * const cmds[] = { &cmd };
+ return lcb_touch(instance, cookie, 1, cmds);
+ }
+
+ virtual void cancel(lcb_error_t err) {
+ cookie->result(err, cmd.v.v0.key, cmd.v.v0.nkey);
+ }
+ private:
+ lcb_touch_cmd_t cmd;
+ CouchbaseCookie *cookie;
+ };
+
+ class ObserveOperation : public Operation {
+ public:
+ ObserveOperation() : cmd(), cookie(NULL) {}
+ virtual ~ObserveOperation() {
+ delete [](char*)cmd.v.v0.key;
+ }
+
+ virtual void parse(const v8::Arguments &arguments);
+
+ lcb_error_t execute(lcb_t instance) {
+ const lcb_observe_cmd_t * const cmds[] = { &cmd };
+ return lcb_observe(instance, cookie, 1, cmds);
+ }
+
+ virtual void cancel(lcb_error_t err) {
+ cookie->result(err, cmd.v.v0.key, cmd.v.v0.nkey);
+ }
+
+ private:
+ lcb_observe_cmd_t cmd;
+ CouchbaseCookie *cookie;
+ };
+
+ class RemoveOperation : public Operation {
+ public:
+ RemoveOperation() : cmd(), cookie(NULL) {}
+ virtual ~RemoveOperation() {
+ delete [](char*)cmd.v.v0.key;
+ }
+
+ virtual void parse(const v8::Arguments &arguments);
+
+ lcb_error_t execute(lcb_t instance) {
+ const lcb_remove_cmd_t * const cmds[] = { &cmd };
+ return lcb_remove(instance, cookie, 1, cmds);
+ }
+
+ virtual void cancel(lcb_error_t err) {
+ cookie->result(err, cmd.v.v0.key, cmd.v.v0.nkey);
+ }
+
+ private:
+ lcb_remove_cmd_t cmd;
+ CouchbaseCookie *cookie;
+ };
+
+ class ArithmeticOperation : public Operation {
+ public:
+ ArithmeticOperation() : cmd(), cookie(NULL) {}
+ virtual ~ArithmeticOperation() {
+ delete [](char*)cmd.v.v0.key;
+ }
+
+ virtual void parse(const v8::Arguments &arguments);
+
+ lcb_error_t execute(lcb_t instance) {
+ const lcb_arithmetic_cmd_t * const cmds[] = { &cmd };
+ return lcb_arithmetic(instance, cookie, 1, cmds);
+ }
+
+ virtual void cancel(lcb_error_t err) {
+ cookie->result(err, cmd.v.v0.key, cmd.v.v0.nkey);
+ }
+ private:
+ lcb_arithmetic_cmd_t cmd;
+ CouchbaseCookie *cookie;
+ };
+} // namespace Couchnode
+
+#endif

0 comments on commit 10c7655

Please sign in to comment.