diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5df5866 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/doc diff --git a/Cakefile b/Cakefile new file mode 100644 index 0000000..70692e5 --- /dev/null +++ b/Cakefile @@ -0,0 +1,52 @@ +color = (str, color) -> + "\033[0;#{color}m#{str}\033[0m" + +run = (a0, a1=[], options={}) -> + {spawn} = require 'child_process' + + console.log color("$ #{a0} #{a1.join(' ')}", 32) + + proc = spawn a0, a1 + proc.stdout.on 'data', (d) -> console.log "#{d}" + proc.stderr.on 'data', (d) -> console.warn "#{d}" + proc.on 'exit', (status) -> + if status > 0 + options.onerror status if typeof options.onerror is 'function' + process.exit(status) + + options.onsuccess() if typeof options.onsuccess is 'function' + +task 'doc:build', 'Build documentation using jsdoc', build = (callback) -> + path = process.env['JSDOC_PATH'] + + unless path? + console.warn color("Error:", 31), "You need jsdoc-toolkit." + console.warn + console.warn "Download it from http://code.google.com/p/jsdoc-toolkit/, then install" + console.warn "it to a path of your choice. Then run:" + console.warn "" + console.warn "$ JSDOC_PATH=/path/to/jsdoc-toolkit cake doc:build" + console.warn "" + process.exit 256 + + run 'java', [ + '-jar', "#{path}/jsrun.jar" + "#{path}/app/run.js" + "--template=#{path}/templates/jsdoc" + '--directory=doc' + "lib/cassandra.js" + ], onsuccess: callback + +task 'doc:open', 'Opens documentation in your browser', -> + build -> + run 'open', ['doc/index.html'] + +task 'doc:deploy', 'Deploys documentation to gh-pages', -> + build -> + repo = process.env['GITHUB_REPO'] || 'yukim/node_cassandra' + + run 'git', + "update-ghpages #{repo} -i doc --branch gh-pages".split(' '), + onerror: (status) -> + console.warn color("Error:", 31), "You need git-update-ghpages." + console.warn "See: http://github.com/rstacruz/git-update-ghpages" diff --git a/lib/cassandra.js b/lib/cassandra.js index 35921d1..cb989b3 100644 --- a/lib/cassandra.js +++ b/lib/cassandra.js @@ -19,16 +19,51 @@ var sys = require('sys'), ttype = require('../gen-nodejs/cassandra_types'); /** - * node_cassandra, Apache Cassandra Client for node.js - * * @constructor + * A connection to a Cassandra cluster. + * + * @param {String} host A "host:port" pair. * @api public + * + * @class + * A connection to a Cassandra cluster. + * + * @description + *

To connect to a Cassandra server, pass a "host:port" to the constructor then + * use Client#connect.

+ * + *

This is an EventEmitter that you may bind events to. + * See the NodeJS documentation for information on EventEmitter. + * (Link)

+ * + * @example + * # Connect to given host and keyspace. + * var connection = new cassandra.Client("localhost:9160"); + * connection.connect("keyspaceName"); */ var Client = function(host) { var pair = host.split(/:/); + + /** + * The hostname of the Cassandra server. + * @type String + */ this.host = pair[0]; + + /** + * The port number as a string. + * @type String + */ this.port = pair[1]; - // default consistency level + + /** + * The default consistency level used in transactions. + * @type Object + * + * @description + * This is an object with read and write properties, + * each being a ConsistencyLevel. + */ this.defaultCL = { read: ttype.ConsistencyLevel.QUORUM, write: ttype.ConsistencyLevel.QUORUM @@ -37,11 +72,42 @@ var Client = function(host) { sys.inherits(Client, process.EventEmitter); /** - * Connect to Cassandra cluster + * @name Client#error + * @event + * @description + * An error. * - * @param keyspace keyspace name to use - * @param credential if given, try login into cassandra + * @example + * # Trap errors. + * connection.on('error', function(err) { + * console.warn("An error occured."); + * console.warn(err); + * }); + */ + +/** + * @name Client#keyspaceSet + * @event + * @description Called when a keyspace is set. + */ + +/** + * Connects to a Cassandra cluster. + * + * @param {String} keyspace The name of the keyspace to use (optional) + * @param {Object} credentials If given, log into the cluster with the given username and password * @api public + * + * @example + * # Connect to `localhost:9160` with the keyspace name `keyspaceName`. + * var connection = new cassandra.Client("localhost:9160"); + * connection.connect("keyspaceName"); + * + * @example + * # This example uses the given login credentials. + * var connection = new cassandra.Client("localhost:9160"); + * connection.connect("keyspaceName", {user: 'root', password: 'thrift'}); + * */ Client.prototype.connect = function() { var args = Array.prototype.slice.call(arguments); @@ -103,10 +169,19 @@ Client.prototype.connect = function() { }; /** - * set which keyspace to use + * Sets which keyspace to use. + * + * @param {String} keyspace The name of the keyspace to use + * @param {Function} callback (Optional) Callback function to be called after + * @api public * - * @param keyspace Keyspace to use - * @param callback + * @description + * You should probably use {connect} instead. + * + * @example + * var connection = new cassandra.Client("localhost:9160"); + * connection.use("keyspaceName"); + * connection.connect(); */ Client.prototype.use = function(keyspace, callback) { var args = Array.prototype.slice.call(arguments); @@ -144,11 +219,24 @@ Client.prototype.use = function(keyspace, callback) { }; /** - * Set or get default consistency level + * Set or get default consistency level. * - * @param consistencyLevel An object which has write and read consistency level. + * @param {Object} consistencyLevel (Optional) An object which has write and read consistency level. * If given, sets default consistency level. + * @api public * + * @description + * To set a consistency level, pass a consistencyLevel argument. + * + * @example + * # Sets the read/write consistency levels + * client.consistencyLevel({ + * write: CL.ONE, + * read: CL.ONE + * }); + * + * # Gets + * cl = client.consistencyLevel(); //=> { write: CL.ONE, read: CL.ONE } */ Client.prototype.consistencyLevel = function() { if (arguments.length == 0) { @@ -161,9 +249,19 @@ Client.prototype.consistencyLevel = function() { }; /** - * add keyspace + * Creates a keyspace with the given name. + * + * @param {String} keyspaceName The name of the keyspace to be created + * @param {Function} callback (Optional) Callback function to be called after + * @api public + * + * @description + * TODO: This method is a work in progress. * - * TODO work in progress + * @example + * client.addKeySpace('metrics', function() { + * // ... + * }); */ Client.prototype.addKeySpace = function(ksdef, callback) { var args = Array.prototype.slice.call(arguments); @@ -176,9 +274,19 @@ Client.prototype.addKeySpace = function(ksdef, callback) { }; /** - * drop keyspace + * Drops a given keyspace. + * + * @description + * Deletes the keyspace with the given name and drops all data inside it. * - * TODO work in progress + * TODO: This method is a work in progress. + * + * @param {String} keyspaceName The name of the keyspace to be dropped + * @param {Function} callback (Optional) Callback function to be called after + * @api public + * + * @example + * client.dropKeySpace('metrics'); */ Client.prototype.dropKeySpace = function(keyspace, callback) { var args = Array.prototype.slice.call(arguments); @@ -191,24 +299,29 @@ Client.prototype.dropKeySpace = function(keyspace, callback) { }; /** - * Get column family to perform query or mutation + * Get column family to perform query or mutation. + * + * @param {String} name The name of the column family to retrieve + * @return [ColumnFamily] + * @api public * - * @param name ColumnFamily name to get - * @return An instance of ColumnFamily + * @see ColumnFamily */ Client.prototype.getColumnFamily = function(name) { return new ColumnFamily(this, name); }; /** - * Close connection + * Closes the connection. + * + * @api public */ Client.prototype.close = function() { this.connection.end(); }; /** - * @api {private} + * @api private */ Client.prototype.dispatch = function() { if (this.ready) { @@ -221,17 +334,42 @@ Client.prototype.dispatch = function() { }; /** - * - * @param client Client - * @param name name of this Column Family * @constructor + * A column family. + * + * @param {Client} client the Client + * @param {String} name the name of the column family + * @api public + * + * @class + * A column family. + * + * @description + *

This class can represent either a column family or a super column family. To + * check if the object pertains to a super column family, use the isSuper + * attribute.

+ * + *

To retrieve a column family, use Client#getColumnFamily.

+ * + * @property {String} name The name of the column family. + * @property {String} column_type The type of the column family ('Super' or 'Standard'). + * @property {Boolean} ready The state of the column family. + * @property {Boolean} isSuper True if the column family is a super column family. + * + * @example + * client = new cassandra.Client("localhost:9160"); + * client.connect("keyspaceName"); + * + * family = client.getColumnFamily('pages'); + * family.set(...); + * + * @see Client#getColumnFamily */ var ColumnFamily = function(client, name) { this.name = name; this.queue = []; this.ready = false; this.client_ = client; - var self = this; this.client_.on('keyspaceSet', function(cfdef) { // check to see if column name is valid @@ -247,6 +385,7 @@ var ColumnFamily = function(client, name) { self[prop] = cf[prop]; } } + self.isSuper = self.column_type === 'Super'; self.ready = true; @@ -255,12 +394,24 @@ var ColumnFamily = function(client, name) { }; /** - * Get data from cassandra + * Retrieves data for a given key. * * @param keys row keys to fetch - * @param columns optional. which columns to retrieve - * @param options optional. valid params are start, finish, reversed, count - * @param callback callback function which called after data retrieval. + * @param columns (optional) which columns to retrieve + * @param options (optional) valid params are start, finish, reversed, count + * @param {Function} callback Callback function to be called after + * @api public + * + * @description + * You may supply options as an object literal. It can have one or more of the + * following keys: + * + * * start + * * finish + * * reversed (boolean) + * * count (number) + * * consistencyLevel (ConsistencyLevel) + * */ ColumnFamily.prototype.get = function() { var args = Array.prototype.slice.call(arguments); @@ -327,12 +478,15 @@ ColumnFamily.prototype.get = function() { }; /** - * Get column count from cassandra + * Retrieves the column count of a given key. * - * @param keys row keys to fetch - * @param columns optional. which columns to retrieve - * @param options optional. valid params are start, finish, reversed, count - * @param callback callback function which called after data retrieval. + * @param {Array} keys row keys to fetch. This can be a string or array. + * @param {Object} columns (Optional) which columns to retrieve + * @param {Object} options (Optional) options + * @param {Function} callback Callback function to be called after + * + * @description + * For description on the options parameter, see ColumnFamily#get. */ ColumnFamily.prototype.count = function() { var args = Array.prototype.slice.call(arguments); @@ -381,13 +535,40 @@ ColumnFamily.prototype.count = function() { /** * slice data + * @api private */ ColumnFamily.prototype.slice = function() { this.client_.emit('error', new Error('slice(get_range_slices, get_indexed_slices) not supported.')); }; /** - * set (insert or update) data + * Sets data through insert or delete. + * + * @param {String} key + * @param {Object} values + * @param {Object} options + * @param {Function} callback (Optional) Callback function to be called after + * @api public + * + * @description + * For super column families, values should be 2 levels down. + * + * @example + * # Sets some data for a normal column family. + * names = client.getColumnFamily('names'); + * names.set("fruits", { "a": "apple" }); + * + * @example + * # Sets some data for a super column family. + * metrics = client.getColumnFamily('metrics'); + * if (metrics.isSuper) { + * metrics.set( + * "august_2011", + * { + * "10": { visits: 29, pageViews: 84 }, + * "11": { visits: 14, pageViews: 29 } + * }); + * } */ ColumnFamily.prototype.set = function() { var args = Array.prototype.slice.call(arguments); @@ -458,7 +639,16 @@ ColumnFamily.prototype.set = function() { }; /** - * remove data + * Remove data. + * + * @param {String} key + * @param {Object} columns optional. which columns to retrieve + * @param {Object} options optional. valid params are start, finish, reversed, count + * @param {Function} callback (Optional) Callback function to be called after + * @api public + * + * @description + * For description on the options parameter, see ColumnFamily.get. */ ColumnFamily.prototype.remove = function() { var args = Array.prototype.slice.call(arguments); @@ -498,7 +688,11 @@ ColumnFamily.prototype.remove = function() { }; /** - * truncate this column family + * Removes all rows in a given column. + * + * @param {String} name The name. + * @param {Function} callback (Optional) Callback function to be called after + * @api public **/ ColumnFamily.prototype.truncate = function() { var args = Array.prototype.slice.call(arguments); @@ -643,7 +837,42 @@ ColumnFamily.prototype.parseArgumentsForStandardCF_ = function(args) { options.consistencyLevel]; } -/** module exports */ - exports.Client = Client; + +/** + * @name ConsistencyLevel + * @namespace Consistency levels. + * ONE, QUORUM, LOCAL_QUORUM, EACH_QUORUM, ALL, ANY + **/ + exports.ConsistencyLevel = ttype.ConsistencyLevel; + +/** + * @name ConsistencyLevel.ONE + * @constant One + */ + +/** + * @name ConsistencyLevel.QUORUM + * @constant Quorum + */ + +/** + * @name ConsistencyLevel.LOCAL_QUORUM + * @constant Local Quorum + */ + +/** + * @name ConsistencyLevel.EACH_QUORUM + * @constant Each Quorum + */ + +/** + * @name ConsistencyLevel.ALL + * @constant All + */ + +/** + * @name ConsistencyLevel.ANY + * @constant Any + */