Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

get rid of bundle; support plain arrays

  • Loading branch information...
commit b1f322a91a3633a5ae739b100c6dcd22ae64a2c7 1 parent 2ee1698
Vladimir Dronnikov authored April 21, 2011
5  examples.js
... ...
@@ -1,11 +1,8 @@
1 1
 var sys = require("sys"),
2  
-	redislib = require("./lib/redis-client"),
  2
+	redislib = require("redis"),
3 3
 	redis = redislib.createClient(),
4 4
 	redis2json = require("./lib/redis2json");
5 5
 
6  
-redis2json.redis = redis;
7  
-
8  
-
9 6
 
10 7
 var map = {
11 8
 	postId: ":{postId}",
1  index.js
... ...
@@ -0,0 +1 @@
  1
+module.exports = require('./lib/redis2json.js');
386  lib/async.js
... ...
@@ -1,386 +0,0 @@
1  
-(function(exports){
2  
-
3  
-    //// cross-browser compatiblity functions ////
4  
-
5  
-    var _forEach = function(arr, iterator){
6  
-        if(arr.forEach) return arr.forEach(iterator);
7  
-        for(var i=0; i<arr.length; i++){
8  
-            iterator(arr[i], i, arr);
9  
-        }
10  
-    };
11  
-
12  
-    var _map = function(arr, iterator){
13  
-        if(arr.map) return arr.map(iterator);
14  
-        var results = [];
15  
-        _forEach(arr, function(x, i, a){
16  
-            results.push(iterator(x, i, a));
17  
-        })
18  
-        return results;
19  
-    };
20  
-
21  
-    var _reduce = function(arr, iterator, memo){
22  
-        if(arr.reduce) return arr.reduce(iterator, memo);
23  
-        _forEach(arr, function(x, i, a){
24  
-            memo = iterator(memo, x, i, a);
25  
-        });
26  
-        return memo;
27  
-    };
28  
-
29  
-    var _keys = function(obj){
30  
-        if(Object.keys) return Object.keys(obj);
31  
-        var keys = [];
32  
-        for(var k in obj){
33  
-            if(obj.hasOwnProperty(k)) keys.push(k);
34  
-        }
35  
-        return keys;
36  
-    };
37  
-
38  
-    var _indexOf = function(arr, item){
39  
-        if(arr.indexOf) return arr.indexOf(item);
40  
-        for(var i=0; i<arr.length; i++){
41  
-            if(arr[i] === item) return i;
42  
-        }
43  
-        return -1;
44  
-    };
45  
-
46  
-    //// nextTick implementation with browser-compatible fallback ////
47  
-
48  
-    var _nextTick;
49  
-    if(typeof process === 'undefined' || !process.nextTick){
50  
-        _nextTick = function(fn){
51  
-            setTimeout(fn, 0);
52  
-        };
53  
-    }
54  
-    else _nextTick = process.nextTick;
55  
-
56  
-
57  
-    //// exported async module functions ////
58  
-
59  
-    exports.forEach = function(arr, iterator, callback){
60  
-        if(!arr.length) return callback();
61  
-        var completed = 0;
62  
-        _forEach(arr, function(x){
63  
-            iterator(x, function(err){
64  
-                if(err){
65  
-                    callback(err);
66  
-                    callback = function(){};
67  
-                }
68  
-                else {
69  
-                    completed++;
70  
-                    if(completed == arr.length) callback();
71  
-                }
72  
-            });
73  
-        });
74  
-    };
75  
-
76  
-    exports.forEachSeries = function(arr, iterator, callback){
77  
-        if(!arr.length) return callback();
78  
-        var completed = 0;
79  
-        var iterate = function(){
80  
-            iterator(arr[completed], function(err){
81  
-                if(err){
82  
-                    callback(err);
83  
-                    callback = function(){};
84  
-                }
85  
-                else {
86  
-                    completed++;
87  
-                    if(completed == arr.length) callback();
88  
-                    else iterate();
89  
-                }
90  
-            });
91  
-        };
92  
-        iterate();
93  
-    };
94  
-
95  
-
96  
-    var doParallel = function(fn){
97  
-        return function(){
98  
-            var args = Array.prototype.slice.call(arguments);
99  
-            return fn.apply(null, [exports.forEach].concat(args));
100  
-        };
101  
-    };
102  
-    var doSeries = function(fn){
103  
-        return function(){
104  
-            var args = Array.prototype.slice.call(arguments);
105  
-            return fn.apply(null, [exports.forEachSeries].concat(args));
106  
-        };
107  
-    };
108  
-
109  
-
110  
-    var _asyncMap = function(eachfn, arr, iterator, callback){
111  
-        var results = [];
112  
-        arr = _map(arr, function(x, i){
113  
-            return {index: i, value: x};
114  
-        });
115  
-        eachfn(arr, function(x, callback){
116  
-            iterator(x.value, function(err, v){
117  
-                results[x.index] = v;
118  
-                callback(err);
119  
-            });
120  
-        }, function(err){
121  
-            callback(err, results);
122  
-        });
123  
-    };
124  
-    exports.map = doParallel(_asyncMap);
125  
-    exports.mapSeries = doSeries(_asyncMap);
126  
-
127  
-
128  
-    // reduce only has a series version, as doing reduce in parallel won't
129  
-    // work in many situations.
130  
-    exports.reduce = function(arr, memo, iterator, callback){
131  
-        exports.forEachSeries(arr, function(x, callback){
132  
-            iterator(memo, x, function(err, v){
133  
-                memo = v;
134  
-                callback(err);
135  
-            });
136  
-        }, function(err){
137  
-            callback(err, memo);
138  
-        });
139  
-    };
140  
-    // inject alias
141  
-    exports.inject = exports.reduce;
142  
-    // foldl alias
143  
-    exports.foldl = exports.reduce;
144  
-
145  
-    exports.reduceRight = function(arr, memo, iterator, callback){
146  
-        var reversed = _map(arr, function(x){return x;}).reverse();
147  
-        exports.reduce(reversed, memo, iterator, callback);
148  
-    };
149  
-    // foldr alias
150  
-    exports.foldr = exports.reduceRight;
151  
-
152  
-    var _filter = function(eachfn, arr, iterator, callback){
153  
-        var results = [];
154  
-        arr = _map(arr, function(x, i){
155  
-            return {index: i, value: x};
156  
-        });
157  
-        eachfn(arr, function(x, callback){
158  
-            iterator(x.value, function(v){
159  
-                if(v) results.push(x);
160  
-                callback();
161  
-            });
162  
-        }, function(err){
163  
-            callback(_map(results.sort(function(a,b){
164  
-                return a.index - b.index;
165  
-            }), function(x){
166  
-                return x.value;
167  
-            }));
168  
-        });
169  
-    };
170  
-    exports.filter = doParallel(_filter);
171  
-    exports.filterSeries = doSeries(_filter);
172  
-    // select alias
173  
-    exports.select = exports.filter;
174  
-    exports.selectSeries = exports.filterSeries;
175  
-
176  
-    var _reject = function(eachfn, arr, iterator, callback){
177  
-        var results = [];
178  
-        arr = _map(arr, function(x, i){
179  
-            return {index: i, value: x};
180  
-        });
181  
-        eachfn(arr, function(x, callback){
182  
-            iterator(x.value, function(v){
183  
-                if(!v) results.push(x);
184  
-                callback();
185  
-            });
186  
-        }, function(err){
187  
-            callback(_map(results.sort(function(a,b){
188  
-                return a.index - b.index;
189  
-            }), function(x){
190  
-                return x.value;
191  
-            }));
192  
-        });
193  
-    };
194  
-    exports.reject = doParallel(_reject);
195  
-    exports.rejectSeries = doSeries(_reject);
196  
-
197  
-    var _detect = function(eachfn, arr, iterator, main_callback){
198  
-        eachfn(arr, function(x, callback){
199  
-            iterator(x, function(result){
200  
-                if(result) main_callback(x);
201  
-                else callback();
202  
-            });
203  
-        }, function(err){
204  
-            main_callback();
205  
-        });
206  
-    };
207  
-    exports.detect = doParallel(_detect);
208  
-    exports.detectSeries = doSeries(_detect);
209  
-
210  
-    exports.some = function(arr, iterator, main_callback){
211  
-        exports.forEach(arr, function(x, callback){
212  
-            iterator(x, function(v){
213  
-                if(v){
214  
-                    main_callback(true);
215  
-                    main_callback = function(){};
216  
-                }
217  
-                callback();
218  
-            });
219  
-        }, function(err){
220  
-            main_callback(false);
221  
-        });
222  
-    };
223  
-    // any alias
224  
-    exports.any = exports.some;
225  
-
226  
-    exports.every = function(arr, iterator, main_callback){
227  
-        exports.forEach(arr, function(x, callback){
228  
-            iterator(x, function(v){
229  
-                if(!v){
230  
-                    main_callback(false);
231  
-                    main_callback = function(){};
232  
-                }
233  
-                callback();
234  
-            });
235  
-        }, function(err){
236  
-            main_callback(true);
237  
-        });
238  
-    };
239  
-    // all alias
240  
-    exports.all = exports.every;
241  
-
242  
-    exports.sortBy = function(arr, iterator, callback){
243  
-        exports.map(arr, function(x, callback){
244  
-            iterator(x, function(err, criteria){
245  
-                if(err) callback(err);
246  
-                else callback(null, {value: x, criteria: criteria});
247  
-            });
248  
-        }, function(err, results){
249  
-            if(err) return callback(err);
250  
-            else callback(null, _map(results.sort(function(left, right){
251  
-                var a = left.criteria, b = right.criteria;
252  
-                return a < b ? -1 : a > b ? 1 : 0;
253  
-            }), function(x){return x.value;}));
254  
-        })
255  
-    };
256  
-
257  
-    exports.auto = function(tasks, callback){
258  
-        callback = callback || function(){};
259  
-        var keys = _keys(tasks);
260  
-        if(!keys.length) return callback(null);
261  
-
262  
-        var completed = [];
263  
-
264  
-        var listeners = [];
265  
-        var addListener = function(fn){
266  
-            listeners.unshift(fn);
267  
-        };
268  
-        var removeListener = function(fn){
269  
-            for(var i=0; i<listeners.length; i++){
270  
-                if(listeners[i] === fn){
271  
-                    listeners.splice(i, 1);
272  
-                    return;
273  
-                }
274  
-            }
275  
-        };
276  
-        var taskComplete = function(){
277  
-            _forEach(listeners, function(fn){fn();});
278  
-        };
279  
-
280  
-        addListener(function(){
281  
-            if(completed.length == keys.length){
282  
-                callback(null);
283  
-            }
284  
-        });
285  
-
286  
-        _forEach(keys, function(k){
287  
-            var task = (tasks[k] instanceof Function)? [tasks[k]]: tasks[k];
288  
-            var taskCallback = function(err){
289  
-                if(err){
290  
-                    callback(err);
291  
-                    // stop subsequent errors hitting callback multiple times
292  
-                    callback = function(){};
293  
-                }
294  
-                else {
295  
-                    completed.push(k);
296  
-                    taskComplete();
297  
-                }
298  
-            };
299  
-            var requires = task.slice(0, Math.abs(task.length-1)) || [];
300  
-            var ready = function(){
301  
-                return _reduce(requires, function(a,x){
302  
-                    return (a && _indexOf(completed, x) != -1);
303  
-                }, true);
304  
-            };
305  
-            if(ready()) task[task.length-1](taskCallback);
306  
-            else {
307  
-                var listener = function(){
308  
-                    if(ready()){
309  
-                        removeListener(listener);
310  
-                        task[task.length-1](taskCallback);
311  
-                    }
312  
-                };
313  
-                addListener(listener);
314  
-            }
315  
-        });
316  
-    };
317  
-
318  
-    exports.waterfall = function(tasks, callback){
319  
-        if(!tasks.length) return callback();
320  
-        callback = callback || function(){};
321  
-        var wrapIterator = function(iterator){
322  
-            return function(err){
323  
-                if(err){
324  
-                    callback(err);
325  
-                    callback = function(){};
326  
-                }
327  
-                else {
328  
-                    var args = Array.prototype.slice.call(arguments, 1);
329  
-                    var next = iterator.next();
330  
-                    if(next) args.push(wrapIterator(next));
331  
-                    else     args.push(callback);
332  
-                    _nextTick(function(){iterator.apply(null, args);});
333  
-                }
334  
-            };
335  
-        };
336  
-        wrapIterator(exports.iterator(tasks))();
337  
-    };
338  
-
339  
-    exports.parallel = function(tasks, callback){
340  
-        callback = callback || function(){};
341  
-        exports.map(tasks, function(fn, callback){
342  
-            if(fn){
343  
-                fn(function(err){
344  
-                    var args = Array.prototype.slice.call(arguments,1);
345  
-                    if(args.length <= 1) args = args[0];
346  
-                    callback.call(null, err, args || null);
347  
-                });
348  
-            }
349  
-        }, callback);
350  
-    };
351  
-
352  
-    exports.series = function(tasks, callback){
353  
-        callback = callback || function(){};
354  
-        exports.mapSeries(tasks, function(fn, callback){
355  
-            if(fn){
356  
-                fn(function(err){
357  
-                    var args = Array.prototype.slice.call(arguments,1);
358  
-                    if(args.length <= 1) args = args[0];
359  
-                    callback.call(null, err, args || null);
360  
-                });
361  
-            }
362  
-        }, callback);
363  
-    };
364  
-
365  
-    exports.iterator = function(tasks){
366  
-        var makeCallback = function(index){
367  
-            var fn = function(){
368  
-                if(tasks.length) tasks[index].apply(null, arguments);
369  
-                return fn.next();
370  
-            };
371  
-            fn.next = function(){
372  
-                return (index < tasks.length-1)? makeCallback(index+1): null;
373  
-            };
374  
-            return fn;
375  
-        };
376  
-        return makeCallback(0);
377  
-    };
378  
-
379  
-    exports.apply = function(fn){
380  
-        var args = Array.prototype.slice.call(arguments, 1);
381  
-        return function(){
382  
-            fn.apply(null, args.concat(Array.prototype.slice.call(arguments)));
383  
-        };
384  
-    };
385  
-
386  
-})((typeof exports == 'undefined') ? this['async']={}: exports);
917  lib/redis-client.js
... ...
@@ -1,917 +0,0 @@
1  
-/*
2  
-
3  
-© 2010 by Fictorial LLC
4  
-
5  
-Permission is hereby granted, free of charge, to any person obtaining a copy
6  
-of this software and associated documentation files (the "Software"), to deal
7  
-in the Software without restriction, including without limitation the rights
8  
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  
-copies of the Software, and to permit persons to whom the Software is
10  
-furnished to do so, subject to the following conditions:
11  
-
12  
-The above copyright notice and this permission notice shall be included in
13  
-all copies or substantial portions of the Software.
14  
-
15  
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21  
-THE SOFTWARE.
22  
-
23  
-*/
24  
-
25  
-// To add support for new commands, edit the array called "commands" at the
26  
-// bottom of this file.
27  
-
28  
-// Set this to true to aid in debugging wire protocol input/output,
29  
-// parsing methods, etc.
30  
-
31  
-exports.debugMode = false;
32  
-
33  
-var net = require("net"),
34  
-    sys = require("sys"),
35  
-    Buffer = require('buffer').Buffer,
36  
-    events = require('events'),
37  
-
38  
-    CRLF = "\r\n",
39  
-    CRLF_LEN = 2,
40  
-
41  
-    PLUS      = exports.PLUS      = 0x2B, // +
42  
-    MINUS     = exports.MINUS     = 0x2D, // -
43  
-    DOLLAR    = exports.DOLLAR    = 0x24, // $
44  
-    STAR      = exports.STAR      = 0x2A, // *
45  
-    COLON     = exports.COLON     = 0x3A, // :
46  
-    CR        = exports.CR        = 0x0D, // \r
47  
-    LF        = exports.LF        = 0x0A, // \n
48  
-                                
49  
-    NONE      = exports.NONE      = "NONE",
50  
-    BULK      = exports.BULK      = "BULK",     
51  
-    MULTIBULK = exports.MULTIBULK = "MULTIBULK",
52  
-    INLINE    = exports.INLINE    = "INLINE",   
53  
-    INTEGER   = exports.INTEGER   = "INTEGER",  
54  
-    ERROR     = exports.ERROR     = "ERROR";    
55  
-
56  
-exports.DEFAULT_HOST = '127.0.0.1';
57  
-exports.DEFAULT_PORT = 6379;
58  
-
59  
-exports.COMMAND_ORPHANED_ERROR = "connection lost before reply received";
60  
-exports.NO_CONNECTION_ERROR = "failed to establish a connection to Redis";
61  
-
62  
-function debugFilter(buffer, len) {
63  
-    // Redis is binary-safe but assume for debug display that 
64  
-    // the encoding of textual data is UTF-8.
65  
-
66  
-    var filtered = buffer.utf8Slice(0, len || buffer.length);
67  
-
68  
-    filtered = filtered.replace(/\r\n/g, '<CRLF>');
69  
-    filtered = filtered.replace(/\r/g, '<CR>');
70  
-    filtered = filtered.replace(/\n/g, '<LF>');
71  
-
72  
-    return filtered;
73  
-}
74  
-
75  
-// A fully interruptable, binary-safe Redis reply parser.
76  
-// 'callback' is called with each reply parsed in 'feed'.
77  
-// 'thisArg' is the "thisArg" for the callback "call".
78  
-
79  
-function ReplyParser(callback, thisArg) {
80  
-    this.onReply = callback;
81  
-    this.thisArg = thisArg;
82  
-    this.clearState();
83  
-    this.clearMultiBulkState();
84  
-}
85  
-
86  
-exports.ReplyParser = ReplyParser;
87  
-
88  
-ReplyParser.prototype.clearState = function () {
89  
-    this.type = NONE;
90  
-    this.bulkLengthExpected = null;
91  
-    this.valueBufferLen = 0;
92  
-    this.skip = 0;
93  
-    this.valueBuffer = new Buffer(4096);
94  
-};
95  
-
96  
-ReplyParser.prototype.clearMultiBulkState = function () {
97  
-    this.multibulkReplies = null; 
98  
-    this.multibulkRepliesExpected = null;
99  
-};
100  
-
101  
-ReplyParser.prototype.feed = function (inbound) {
102  
-    for (var i=0; i < inbound.length; ++i) {
103  
-        if (this.skip > 0) {
104  
-            this.skip--;
105  
-            continue;
106  
-        }
107  
-
108  
-        var typeBefore = this.type;
109  
-
110  
-        if (this.type === NONE) {
111  
-            switch (inbound[i]) {
112  
-                case DOLLAR: this.type = BULK;      break;
113  
-                case STAR:   this.type = MULTIBULK; break;
114  
-                case COLON:  this.type = INTEGER;   break;
115  
-                case PLUS:   this.type = INLINE;    break;
116  
-                case MINUS:  this.type = ERROR;     break;
117  
-            }
118  
-        }
119  
-
120  
-        // Just a state transition on '*', '+', etc.?  
121  
-
122  
-        if (typeBefore != this.type)
123  
-            continue;
124  
-
125  
-        // If the reply is a part of a multi-bulk reply.  Save it.  If we have
126  
-        // received all the expected replies of a multi-bulk reply, then
127  
-        // callback.  If the reply is not part of a multi-bulk. Call back
128  
-        // immediately.
129  
-
130  
-        var self = this;
131  
-
132  
-        var maybeCallbackWithReply = function (reply) {
133  
-            if (self.multibulkReplies != null) {
134  
-                self.multibulkReplies.push(reply);
135  
-                if (--self.multibulkRepliesExpected == 0) {
136  
-                    self.onReply.call(self.thisArg, { 
137  
-                        type:  MULTIBULK, 
138  
-                        value: self.multibulkReplies 
139  
-                    });
140  
-                    self.clearMultiBulkState();
141  
-                }
142  
-            } else {
143  
-                self.onReply.call(self.thisArg, reply);
144  
-            }
145  
-            self.clearState();
146  
-            self.skip = 1; // Skip LF
147  
-        };
148  
-
149  
-        switch (inbound[i]) {
150  
-        case CR:
151  
-            switch (this.type) {
152  
-                case INLINE:
153  
-                case ERROR:
154  
-                    // CR denotes end of the inline/error value.  
155  
-                    // +OK\r\n
156  
-                    //    ^
157  
-
158  
-                    var inlineBuf = new Buffer(this.valueBufferLen);
159  
-                    this.valueBuffer.copy(inlineBuf, 0, 0, this.valueBufferLen);
160  
-                    maybeCallbackWithReply({ type:this.type, value:inlineBuf });
161  
-                    break;
162  
-
163  
-                case INTEGER:
164  
-                    // CR denotes the end of the integer value.  
165  
-                    // :42\r\n
166  
-                    //    ^
167  
-
168  
-                    var n = parseInt(this.valueBuffer.asciiSlice(0, this.valueBufferLen), 10);
169  
-                    maybeCallbackWithReply({ type:INTEGER, value:n });
170  
-                    break;
171  
-
172  
-                case BULK:
173  
-                    if (this.bulkLengthExpected == null) {
174  
-                        // CR denotes end of first line of a bulk reply,
175  
-                        // which is the length of the bulk reply value.
176  
-                        // $5\r\nhello\r\n
177  
-                        //   ^
178  
-
179  
-                        var bulkLengthExpected = 
180  
-                            parseInt(this.valueBuffer.asciiSlice(0, this.valueBufferLen), 10);
181  
-
182  
-                        if (bulkLengthExpected <= 0) {
183  
-                            maybeCallbackWithReply({ type:BULK, value:null });
184  
-                        } else {
185  
-                            this.clearState();
186  
-
187  
-                            this.bulkLengthExpected = bulkLengthExpected;
188  
-                            this.type = BULK;
189  
-                            this.skip = 1;  // skip LF
190  
-                        }
191  
-                    } else if (this.valueBufferLen == this.bulkLengthExpected) {
192  
-                        // CR denotes end of the bulk reply value.
193  
-                        // $5\r\nhello\r\n
194  
-                        //            ^
195  
-
196  
-                        var bulkBuf = new Buffer(this.valueBufferLen);
197  
-                        this.valueBuffer.copy(bulkBuf, 0, 0, this.valueBufferLen);
198  
-                        maybeCallbackWithReply({ type:BULK, value:bulkBuf });
199  
-                    } else {
200  
-                        // CR is just an embedded CR and has nothing to do
201  
-                        // with the reply specification.
202  
-                        // $11\r\nhello\rworld\r\n
203  
-                        //             ^
204  
-                        
205  
-                        this.valueBuffer[this.valueBufferLen++] = inbound[i];
206  
-                    }
207  
-                    break;
208  
-
209  
-                case MULTIBULK:
210  
-                    // Parse the count which is the number of expected replies
211  
-                    // in the multi-bulk reply.
212  
-                    // *2\r\n$5\r\nhello\r\n$5\r\nworld\r\n
213  
-                    //   ^
214  
-
215  
-                    var multibulkRepliesExpected = 
216  
-                        parseInt(this.valueBuffer.asciiSlice(0, this.valueBufferLen), 10);
217  
-
218  
-                    if (multibulkRepliesExpected <= 0) {
219  
-                        maybeCallbackWithReply({ type:MULTIBULK, value:null });
220  
-                    } else {
221  
-                        this.clearState();
222  
-                        this.skip = 1;    // skip LF
223  
-                        this.multibulkReplies = [];
224  
-                        this.multibulkRepliesExpected = multibulkRepliesExpected;
225  
-                    }
226  
-                    break;
227  
-            }
228  
-            break;
229  
-
230  
-        default:
231  
-            this.valueBuffer[this.valueBufferLen++] = inbound[i];
232  
-            break;
233  
-        }
234  
-
235  
-        // If the current value buffer is too big, create a new buffer, copy in
236  
-        // the old buffer, and replace the old buffer with the new buffer.
237  
- 
238  
-        if (this.valueBufferLen === this.valueBuffer.length) {
239  
-            var newBuffer = new Buffer(this.valueBuffer.length * 2);
240  
-            this.valueBuffer.copy(newBuffer, 0, 0);
241  
-            this.valueBuffer = newBuffer;
242  
-        }
243  
-    }
244  
-};
245  
-
246  
-/**
247  
- * Emits:
248  
- *
249  
- * - 'connected' when connected (or on a reconnection, reconnected).
250  
- * - 'reconnecting' when about to retry to connect to Redis.
251  
- * - 'reconnected' when connected after a reconnection was established.
252  
- * - 'noconnection' when a connection (or reconnection) cannot be established.
253  
- * - 'drained' when no submitted commands are expecting a reply from Redis.
254  
- *
255  
- * Options: 
256  
- *
257  
- * - maxReconnectionAttempts (default: 10)
258  
- */
259  
-
260  
-function Client(stream, options) {
261  
-    events.EventEmitter.call(this);
262  
-
263  
-    this.stream = stream;
264  
-    this.originalCommands = [];
265  
-    this.queuedOriginalCommands = [];
266  
-    this.queuedRequestBuffers = [];
267  
-    this.channelCallbacks = {};
268  
-    this.requestBuffer = new Buffer(512);
269  
-    this.replyParser = new ReplyParser(this.onReply_, this);
270  
-    this.reconnectionTimer = null;
271  
-    this.maxReconnectionAttempts = 10;
272  
-    this.reconnectionAttempts = 0;
273  
-    this.reconnectionDelay = 500;    // doubles, so starts at 1s delay
274  
-    this.connectionsMade = 0;
275  
-
276  
-    if (options !== undefined) 
277  
-        this.maxReconnectionAttempts = Math.abs(options.maxReconnectionAttempts || 10);
278  
-
279  
-    var client = this;
280  
-
281  
-    stream.addListener("connect", function () {
282  
-        if (exports.debugMode)
283  
-            sys.debug("[CONNECT]");
284  
-
285  
-        stream.setNoDelay();
286  
-        stream.setTimeout(0);
287  
-
288  
-        client.reconnectionAttempts = 0;
289  
-        client.reconnectionDelay = 500;
290  
-        if (client.reconnectionTimer) {
291  
-            clearTimeout(client.reconnectionTimer);
292  
-            client.reconnectionTimer = null;
293  
-        }
294  
-
295  
-        var eventName = client.connectionsMade == 0 
296  
-                      ? 'connected' 
297  
-                      : 'reconnected';
298  
-
299  
-        client.connectionsMade++;
300  
-        client.expectingClose = false;
301  
-
302  
-        // If this a reconnection and there were commands submitted, then they
303  
-        // are gone!  We cannot say with any confidence which were processed by
304  
-        // Redis; perhaps some were processed but we never got the reply, or
305  
-        // perhaps all were processed but Redis is configured with less than
306  
-        // 100% durable writes, etc.  
307  
-        //
308  
-        // We punt to the user by calling their callback with an I/O error.
309  
-        // However, we provide enough information to allow the user to retry
310  
-        // the interrupted operation.  We are certainly not retrying anything
311  
-        // for them as it is too dangerous and application-specific.
312  
-
313  
-        if (client.connectionsMade > 1 && client.originalCommands.length > 0) {
314  
-            if (exports.debug) {
315  
-                sys.debug("[RECONNECTION] some commands orphaned (" + 
316  
-                    client.originalCommands.length + "). notifying...");
317  
-            }
318  
-
319  
-            client.callbackOrphanedCommandsWithError();
320  
-        }
321  
-        
322  
-        client.originalCommands = [];
323  
-        client.flushQueuedCommands();
324  
-
325  
-        client.emit(eventName, client);
326  
-    });
327  
-
328  
-    stream.addListener('error', function (e) {
329  
-        if (exports.debugMode)
330  
-            sys.debug("[ERROR] Connection to redis encountered an error: " + e);
331  
-    });
332  
-
333  
-    stream.addListener("data", function (buffer) {
334  
-        if (exports.debugMode)
335  
-            sys.debug("[RECV] " + debugFilter(buffer));
336  
-
337  
-        client.replyParser.feed(buffer);
338  
-    });
339  
-
340  
-    stream.addListener("error", function (e) {
341  
-	if (exports.debugMode)
342  
-	  sys.debug('[ERROR] ' + e);
343  
-	client.replyParser.clearState();
344  
-	client.maybeReconnect();
345  
-	throw e;
346  
-    });
347  
-
348  
-    stream.addListener("end", function () {
349  
-        if (exports.debugMode && client.originalCommands.length > 0) {
350  
-            sys.debug("Connection to redis closed with " + 
351  
-                      client.originalCommands.length + 
352  
-                      " commands pending replies that will never arrive!");
353  
-        }
354  
-
355  
-        stream.end();
356  
-    });
357  
-
358  
-    stream.addListener("close", function (hadError) {
359  
-        if (exports.debugMode)
360  
-            sys.debug("[NO CONNECTION]");
361  
-
362  
-        client.maybeReconnect();
363  
-    });
364  
-}
365  
-
366  
-sys.inherits(Client, events.EventEmitter);
367  
-
368  
-exports.Client = Client;
369  
-
370  
-exports.createClient = function (port, host, options) {
371  
-    var port = port || exports.DEFAULT_PORT;
372  
-    var host = host || exports.DEFAULT_HOST;
373  
-
374  
-    var client = new Client(net.createConnection(port, host), options);
375  
-
376  
-    client.port = port;
377  
-    client.host = host;
378  
-
379  
-    return client;
380  
-};
381  
-
382  
-Client.prototype.close = function () {
383  
-    this.expectingClose = true;
384  
-    this.stream.end();
385  
-};
386  
-
387  
-Client.prototype.onReply_ = function (reply) {
388  
-    this.flushQueuedCommands();
389  
-
390  
-    if (this.handlePublishedMessage_(reply)) 
391  
-        return;
392  
-
393  
-    var originalCommand = this.originalCommands.shift();
394  
-    var callback = originalCommand[originalCommand.length - 1];
395  
-
396  
-    // Callbacks expect (err, reply) as args.
397  
-
398  
-    if (typeof callback == "function") {
399  
-        if (reply.type == ERROR) {
400  
-            callback(reply.value.utf8Slice(0, reply.value.length), null);
401  
-        } else {
402  
-            callback(null, maybeConvertReplyValue(originalCommand[0], reply));
403  
-        }
404  
-    }
405  
-
406  
-    if (this.originalCommands.length == 0)
407  
-      this.emit('drained', this);
408  
-};
409  
-
410  
-Client.prototype.handlePublishedMessage_ = function (reply) {
411  
-    // We're looking for a multibulk resembling 
412  
-    // ["message", "channelName", messageBuffer]; or
413  
-    // ["pmessage", "matchingPattern", "channelName", messageBuffer]
414  
-    // The latter is sent when the client subscribed to a channel by a pattern;
415  
-    // the former when subscribed to a channel by name.
416  
-    // If the client subscribes by name -and- by pattern and there's some
417  
-    // overlap, the client -will- receive multiple p/message notifications.
418  
-
419  
-    if (reply.type != MULTIBULK || !(reply.value instanceof Array))
420  
-        return false;
421  
-
422  
-    var isMessage = (reply.value.length == 3 &&
423  
-                     reply.value[0].value.length == 7 &&
424  
-                     reply.value[0].value.asciiSlice(0, 7) == 'message');
425  
-
426  
-    var isPMessage = (reply.value.length == 4 &&
427  
-                      reply.value[0].value.length == 8 &&
428  
-                      reply.value[0].value.asciiSlice(0, 8) == 'pmessage');
429  
-
430  
-    if (!isMessage && !isPMessage)
431  
-        return false;
432  
-
433  
-    // This is tricky. We are returning true even though there 
434  
-    // might not be any callback called! This may happen when a
435  
-    // caller subscribes then unsubscribes while a published
436  
-    // message is in transit to us. When the message arrives, no
437  
-    // one is there to consume it. In essence, as long as the 
438  
-    // reply type is a published message (see above), then we've
439  
-    // "handled" the reply.
440  
-        
441  
-    if (Object.getOwnPropertyNames(this.channelCallbacks).length == 0) 
442  
-        return true;
443  
-
444  
-    var channelName, channelPattern, channelCallback, payload;
445  
-
446  
-    if (isMessage) {
447  
-        channelName = reply.value[1].value;
448  
-        channelCallback = this.channelCallbacks[channelName];
449  
-        payload = reply.value[2].value;
450  
-    } else if (isPMessage) {
451  
-        channelPattern = reply.value[1].value;
452  
-        channelName = reply.value[2].value;
453  
-        channelCallback = this.channelCallbacks[channelPattern];
454  
-        payload = reply.value[3].value;
455  
-    } else {
456  
-        return false;
457  
-    }
458  
-
459  
-    if (typeof channelCallback == "function") {
460  
-        channelCallback(channelName, payload, channelPattern);
461  
-        return true;
462  
-    }
463  
-
464  
-    return false;
465  
-}
466  
-
467  
-function maybeAsNumber(str) {
468  
-    var value = parseInt(str, 10);
469  
-
470  
-    if (isNaN(value)) 
471  
-        value = parseFloat(str);
472  
-
473  
-    if (isNaN(value)) 
474  
-        return str;
475  
-
476  
-    return value;
477  
-}
478  
-
479  
-function maybeConvertReplyValue(commandName, reply) {
480  
-    if (reply.value === null)
481  
-        return null;
482  
-
483  
-    // Redis' INFO command returns a BULK reply of the form:
484  
-    // "redis_version:1.3.8
485  
-    // arch_bits:64
486  
-    // multiplexing_api:kqueue
487  
-    // process_id:11604
488  
-    // ..."
489  
-    // 
490  
-    // We convert that to a JS object like:
491  
-    // { redis_version: '1.3.8'
492  
-    // , arch_bits: '64'
493  
-    // , multiplexing_api: 'kqueue'
494  
-    // , process_id: '11604'
495  
-    // , ... }
496  
-
497  
-    if (commandName === 'info' && reply.type === BULK) {
498  
-        var info = {};
499  
-        reply.value.asciiSlice(0, reply.value.length).split(/\r\n/g)
500  
-            .forEach(function (line) {
501  
-                var parts = line.split(':');
502  
-                if (parts.length === 2)
503  
-                    info[parts[0]] = parts[1];
504  
-            });
505  
-        return info;
506  
-    }
507  
-
508  
-    // HGETALL returns a MULTIBULK where each consecutive reply-pair
509  
-    // is a key and value for the Redis HASH.  We convert this into
510  
-    // a JS object.
511  
-
512  
-    if (commandName === 'hgetall' && 
513  
-        reply.type === MULTIBULK &&
514  
-        reply.value.length % 2 === 0) {
515  
-
516  
-        var hash = {};
517  
-        for (var i=0; i<reply.value.length; i += 2) 
518  
-            hash[reply.value[i].value] = reply.value[i + 1].value;
519  
-        return hash;
520  
-    }
521  
-
522  
-    // Redis returns "+OK\r\n" to signify success.
523  
-    // We convert this into a JS boolean with value true.
524  
-    
525  
-    if (reply.type === INLINE && reply.value.asciiSlice(0,2) === 'OK')
526  
-        return true;
527  
-
528  
-    // ZSCORE returns a string representation of a floating point number.
529  
-    // We convert this into a JS number.
530  
-
531  
-    if (commandName === "zscore")
532  
-        return maybeAsNumber(reply.value);
533  
-
534  
-    // Multibulk replies are returned from our reply parser as an
535  
-    // array like: [ {type:BULK, value:"foo"}, {type:BULK, value:"bar"} ]
536  
-    // But, end-users want the value and don't care about the
537  
-    // Redis protocol reply types.  We here extract the value from each
538  
-    // object in the multi-bulk array.
539  
-
540  
-    if (reply.type === MULTIBULK)
541  
-        return reply.value.map(function (element) { return element.value; });
542  
-
543  
-    // Otherwise, we have no conversions to offer.
544  
-
545  
-    return reply.value;
546  
-}
547  
-
548  
-exports.maybeConvertReplyValue_ = maybeConvertReplyValue;
549  
-
550  
-var commands = [ 
551  
-    "append",
552  
-    "auth",
553  
-    "bgsave",
554  
-    "blpop",
555  
-    "brpop",
556  
-    "dbsize",
557  
-    "decr",
558  
-    "decrby",
559  
-    "del",
560  
-    "exists",
561  
-    "expire",
562  
-    "expireat",
563  
-    "flushall",
564  
-    "flushdb",
565  
-    "get",
566  
-    "getset",
567  
-    "hdel",
568  
-    "hexists",
569  
-    "hget",
570  
-    "hgetall",
571  
-    "hincrby",
572  
-    "hkeys",
573  
-    "hlen",
574  
-    "hmget",
575  
-    "hmset",
576  
-    "hset",
577  
-    "hvals",
578  
-    "incr",
579  
-    "incrby",
580  
-    "info",
581  
-    "keys",
582  
-    "lastsave",
583  
-    "len",
584  
-    "lindex",
585  
-    "llen",
586  
-    "lpop",
587  
-    "lpush",
588  
-    "lrange",
589  
-    "lrem",
590  
-    "lset",
591  
-    "ltrim",
592  
-    "mget",
593  
-    "move",
594  
-    "mset",
595  
-    "msetnx",
596  
-    "psubscribe",
597  
-    "publish",
598  
-    "punsubscribe",
599  
-    "randomkey",
600  
-    "rename",
601  
-    "renamenx",
602  
-    "rpop",
603  
-    "rpoplpush",
604  
-    "rpush",
605  
-    "sadd",
606  
-    "save",
607  
-    "scard",
608  
-    "sdiff",
609  
-    "sdiffstore",
610  
-    "select",
611  
-    "set",
612  
-    "setex",
613  
-    "setnx",
614  
-    "shutdown",
615  
-    "sinter",
616  
-    "sinterstore",
617  
-    "sismember",
618  
-    "smembers",
619  
-    "smove",
620  
-    "sort",
621  
-    "spop",
622  
-    "srandmember",
623  
-    "srem",
624  
-    "subscribe",
625  
-    "sunion",
626  
-    "sunionstore",
627  
-    "ttl",
628  
-    "type",
629  
-    "unsubscribe",
630  
-    "zadd",
631  
-    "zcard",
632  
-    "zcount",
633  
-    "zincrby",
634  
-    "zinter",
635  
-    "zrange",
636  
-    "zrangebyscore",
637  
-    "zrank",
638  
-    "zrem",
639  
-    "zrembyrank",
640  
-    "zremrangebyrank",
641  
-    "zremrangebyscore",
642  
-    "zrevrange",
643  
-    "zrevrank",
644  
-    "zscore",
645  
-    "zunion",
646  
-];
647  
-
648  
-// For internal use but maybe useful in rare cases or when the client command
649  
-// set is not 100% up to date with Redis' latest commands.
650  
-// client.sendCommand('GET', 'foo', function (err, value) {...});
651  
-//
652  
-// arguments[0]      = commandName
653  
-// arguments[1..N-2] = Redis command arguments
654  
-// arguments[N-1]    = callback function
655  
-
656  
-Client.prototype.sendCommand = function () {
657  
-    var originalCommand = Array.prototype.slice.call(arguments);
658  
-
659  
-    // If this client has given up trying to connect/reconnect to Redis,
660  
-    // just call the errback (if any). Regardless, don't enqueue the command.
661  
-
662  
-    if (this.noConnection) {
663  
-        if (arguments.length > 0 && typeof arguments[arguments.length - 1] == 'function')
664  
-            arguments[arguments.length - 1](this.makeErrorForCommand(originalCommand, exports.NO_CONNECTION_ERROR));
665  
-        return;
666  
-    }
667  
-
668  
-    this.flushQueuedCommands();
669  
-
670  
-    var commandName = arguments[0].toLowerCase();
671  
-
672  
-    // Invariant: number of queued callbacks == number of commands sent to
673  
-    // Redis whose replies have not yet been received and processed.  Thus,
674  
-    // if no callback was given, we create a dummy callback.
675  
-
676  
-    var argCount = arguments.length;
677  
-    if (typeof arguments[argCount - 1] == 'function')
678  
-        --argCount;
679  
-
680  
-    // All requests are formatted as multi-bulk.
681  
-    // The first line of a multi-bulk request is "*<number of parts to follow>\r\n".
682  
-    // Next is: "$<length of the command name>\r\n<command name>\r\n".
683  
-
684  
-    // Write the request as we go into a request Buffer.  Recall that buffers
685  
-    // are fixed length.  We thus guess at how much space is needed.  If we
686  
-    // need to grow beyond this, we create a new buffer, copy the old one, and
687  
-    // continue.  Once we're ready to write the buffer, we use a 0-copy slice
688  
-    // to send just that which we've written to the buffer.
689  
-    //
690  
-    // We reuse the buffer after each request. When the buffer "grows" to
691  
-    // accomodate a request, it stays that size until it needs to grown again,
692  
-    // which may of course be never.
693  
-
694  
-    var offset = this.requestBuffer.utf8Write('*' + argCount.toString() + CRLF +
695  
-                                              '$' + commandName.length + CRLF +
696  
-                                              commandName + CRLF, 0);
697  
-
698  
-    var self = this;
699  
-
700  
-    function ensureSpaceFor(atLeast) {
701  
-      var currentLength = self.requestBuffer.length;
702  
-
703  
-      if (offset + atLeast > currentLength) {
704  
-        // If we know how much space we need, use that + 10%.
705  
-        // Else double the size of the buffer.
706  
-
707  
-        var bufferLength = Math.max(currentLength * 2, atLeast * 1.1);
708  
-        var newBuffer = new Buffer(Math.round(bufferLength));
709  
-        self.requestBuffer.copy(newBuffer, 0, 0, offset); // target, targetStart, srcStart, srcEnd
710  
-        self.requestBuffer = newBuffer;
711  
-      }
712  
-    }
713  
-
714  
-    // Serialize the arguments into the request buffer
715  
-    // If the request is a Buffer, just copy.  Else if
716  
-    // the arg has a .toString() method, call it and write
717  
-    // it to the request buffer as UTF8.
718  
-
719  
-    var extrasLength = 5;   // '$', '\r\n', '\r\n'
720  
-
721  
-    for (var i=1; i < argCount; ++i) {
722  
-        var arg = arguments[i];
723  
-        if (arg instanceof Buffer) {
724  
-            ensureSpaceFor(arg.length + arg.length.toString().length + extrasLength);
725  
-            offset += this.requestBuffer.asciiWrite('$' + arg.length + CRLF, offset);
726  
-            offset += arg.copy(this.requestBuffer, offset, 0);  // target, targetStart, srcStart
727  
-            offset += this.requestBuffer.asciiWrite(CRLF, offset);
728  
-        } else if (arg.toString) {
729  
-            var asString = arg.toString();
730  
-            var serialized = '$' + Buffer.byteLength(asString, "binary") + CRLF + asString + CRLF;
731  
-            ensureSpaceFor(Buffer.byteLength(serialized, "binary"));
732  
-            offset += this.requestBuffer.binaryWrite(serialized, offset);
733  
-        }
734  
-    }
735  
-
736  
-    // If the stream is writable, write the command.  Else enqueue the command
737  
-    // for when we first establish a connection or reconnect.
738  
-
739  
-    if (this.stream.writable) {
740  
-        this.originalCommands.push(originalCommand);
741  
-        var outBuffer = new Buffer(offset);
742  
-        this.requestBuffer.copy(outBuffer, 0, 0, offset);
743  
-        this.stream.write(outBuffer, 'binary');
744  
-
745  
-        if (exports.debugMode) 
746  
-            sys.debug("[SEND] " + debugFilter(this.requestBuffer, offset) + 
747  
-                " originalCommands = " + this.originalCommands.length);
748  
-    } else {
749  
-        var toEnqueue = new Buffer(offset);
750  
-        this.requestBuffer.copy(toEnqueue, 0, 0, offset);  // dst, dstStart, srcStart, srcEnd
751  
-        this.queuedRequestBuffers.push(toEnqueue);
752  
-        this.queuedOriginalCommands.push(originalCommand);
753  
-
754  
-        if (exports.debugMode) {
755  
-            sys.debug("[ENQUEUE] Not connected. Request queued. There are " + 
756  
-                this.queuedRequestBuffers.length + " requests queued.");
757  
-        }
758  
-    }
759  
-};
760  
-
761  
-commands.forEach(function (commandName) {
762  
-    Client.prototype[commandName] = function () {
763  
-        var args = Array.prototype.slice.call(arguments);
764  
-        // [[1,2,3],function(){}] => [1,2,3,function(){}]
765  
-        if (args.length > 0 && Array.isArray(args[0])) 
766  
-          args = args.shift().concat(args);
767  
-        args.unshift(commandName);
768  
-        this.sendCommand.apply(this, args);
769  
-    };
770  
-});
771  
-
772  
-// Send any commands that were queued while we were not connected.
773  
-
774  
-Client.prototype.flushQueuedCommands = function () {
775  
-    if (exports.debugMode && this.queuedRequestBuffers.length > 0) 
776  
-        sys.debug("[FLUSH QUEUE] " + this.queuedRequestBuffers.length + 
777  
-                  " queued request buffers.");
778  
-
779  
-    for (var i=0; i<this.queuedRequestBuffers.length && this.stream.writable; ++i) {
780  
-        var buffer = this.queuedRequestBuffers.shift();
781  
-        this.stream.write(buffer, 'binary');
782  
-        this.originalCommands.push(this.queuedOriginalCommands.shift());
783  
-
784  
-        if (exports.debugMode) 
785  
-            sys.debug("[DEQUEUE/SEND] " + debugFilter(buffer) + 
786  
-                      ". queued buffers remaining = " + 
787  
-                      this.queuedRequestBuffers.length);
788  
-    }
789  
-};
790  
-
791  
-Client.prototype.makeErrorForCommand = function (command, errorMessage) {
792  
-    var err = new Error(errorMessage);
793  
-    err.originalCommand = command;
794  
-    return err;
795  
-};
796  
-
797  
-Client.prototype.callbackCommandWithError = function (command, errorMessage) {
798  
-    var callback = command[command.length - 1];
799  
-    if (typeof callback == "function") 
800  
-        callback(this.makeErrorForCommand(command, errorMessage));
801  
-};
802  
-
803  
-Client.prototype.callbackOrphanedCommandsWithError = function () {
804  
-    for (var i=0, n=this.originalCommands.length; i<n; ++i) 
805  
-        this.callbackCommandWithError(this.originalCommands[i], exports.COMMAND_ORPHANED_ERROR);