Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

First commit.

  • Loading branch information...
commit 962dd7c1420bb5b8b66e7a091218ac8720b0ac0f 0 parents
Jérémy Faivre authored
2  .gitignore
@@ -0,0 +1,2 @@
+node_modules/
+.DS_Store
42 Cakefile
@@ -0,0 +1,42 @@
+
+fs = require 'fs'
+findit = require 'findit'
+exec = require('child_process').exec
+coffee = require 'coffee-script'
+path = require 'path'
+
+escapeShell = (arg) -> "'" + arg.replace(/[^\\]'/g, (m, i, s) -> m.slice(0, 1) + "\\'") + "'"
+src = __dirname + '/src'
+bin = __dirname + '/bin'
+
+task 'build', 'build project', (options) ->
+
+ # Remove bin contents
+ exec "rm -rf #{escapeShell bin}", (err, stdout, stderr) ->
+ if err? then throw err
+
+ # Copy src content into bin
+ exec "cp -R #{escapeShell src} #{escapeShell bin}", (err, stdout, stderr) ->
+ if err? then throw err
+
+ # Compile coffee files
+ for filename in findit.sync bin
+ if filename.substring(filename.length-7) is '.coffee'
+ # Get script from coffee file
+ script = fs.readFileSync filename, 'utf8'
+ # Compile into js code
+ js = coffee.compile script
+ # Write js file with compiled code
+ file = fs.openSync filename.substring(0, filename.length-7)+'.js', 'w+'
+ fs.writeSync file, js
+ fs.closeSync file
+ # Delete coffee file
+ fs.unlinkSync filename
+
+ # Ensure redis-dump is executable
+ exec "chmod +x #{escapeShell bin+'/cli/redis-dump'}", (err, stdout, stderr) ->
+ if err? then throw err
+
+ # Finish
+ console.log 'built redis-dump.'
+
30 bin/cli.js
@@ -0,0 +1,30 @@
+(function() {
+ var argv, dump, fs, package, params, path, _ref, _ref2, _ref3;
+
+ fs = require('fs');
+
+ path = require('path');
+
+ argv = require('optimist').argv;
+
+ dump = require('./dump');
+
+ package = JSON.parse(fs.readFileSync(path.normalize(__dirname + '/../package.json'), 'utf8'));
+
+ if (argv.help) {
+ console.log("" + package.name + " " + package.version + "\n\nUsage: " + package.name + " [OPTIONS]\n -h <hostname> Server hostname (default: 127.0.0.1)\n -p <port> Server port (default: 6379)\n -f <filter> Query filter (default: *)\n --help Output this help and exit\n\nExamples:\n redis-dump\n redis-dump -p 6500\n redis-dump -f 'mydb:*' > mydb.dump.txt\n\nThe output is a valid list of redis commands.\nThat means the following will work:\n redis-dump > dump.txt # Dump redis database\n cat dump.txt | redis-cli # Import redis database from generated file\n");
+ } else {
+ params = {
+ filter: (_ref = argv.f) != null ? _ref : '*',
+ port: (_ref2 = argv.p) != null ? _ref2 : 6379,
+ host: (_ref3 = argv.h) != null ? _ref3 : '127.0.0.1'
+ };
+ dump(params, function(err, result) {
+ if (err != null) return process.stderr.write("" + err + "\n");
+ if ((result != null) && ("" + result).replace(/^\s+/, '').replace(/\s+$/, '') !== '') {
+ return console.log(result);
+ }
+ });
+ }
+
+}).call(this);
3  bin/cli/redis-dump
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+
+require('../cli');
158 bin/dump.js
@@ -0,0 +1,158 @@
+(function() {
+ var RedisDumper, redis, run,
+ __slice = Array.prototype.slice;
+
+ run = require('async').waterfall;
+
+ redis = require('redis');
+
+ module.exports = function(params, callback) {
+ var dumper;
+ dumper = new RedisDumper(params);
+ return dumper.dump(params, function() {
+ var params;
+ params = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ dumper.close();
+ return callback.apply(null, params);
+ });
+ };
+
+ RedisDumper = (function() {
+
+ function RedisDumper(_arg) {
+ var host, port;
+ port = _arg.port, host = _arg.host;
+ this.db = redis.createClient(port, host);
+ }
+
+ RedisDumper.prototype.close = function() {
+ return this.db.end();
+ };
+
+ RedisDumper.prototype.escape = function(value) {
+ if (/^([a-zA-Z0-9_\:\-]+)$/.test("" + value)) {
+ return "" + value;
+ } else {
+ return "'" + ("" + value).split('\\').join('\\\\').split('\'').join('\\\'') + "'";
+ }
+ };
+
+ RedisDumper.prototype.dump = function(_arg, callback) {
+ var filter, keys, types, values,
+ _this = this;
+ filter = _arg.filter;
+ keys = [];
+ types = [];
+ values = [];
+ return run([
+ function(next) {
+ return _this.db.keys(filter, next);
+ }, function(reply, next) {
+ var key, multi, _i, _len;
+ multi = _this.db.multi();
+ for (_i = 0, _len = reply.length; _i < _len; _i++) {
+ key = reply[_i];
+ keys.push(key);
+ multi.type(key);
+ }
+ return multi.exec(next);
+ }, function(replies, next) {
+ var i, multi, type, _len;
+ multi = _this.db.multi();
+ for (i = 0, _len = replies.length; i < _len; i++) {
+ type = replies[i];
+ types.push(type);
+ switch (type) {
+ case 'string':
+ multi.get(keys[i]);
+ break;
+ case 'list':
+ multi.lrange(keys[i], 0, -1);
+ break;
+ case 'set':
+ multi.smembers(keys[i]);
+ break;
+ case 'zset':
+ multi.zrange(keys[i], 0, -1, 'withscores');
+ break;
+ case 'hash':
+ multi.hgetall(keys[i]);
+ }
+ }
+ return multi.exec(next);
+ }, function(replies, next) {
+ var commands, i, item, j, k, key, type, v, value, _i, _len, _len2;
+ for (_i = 0, _len = replies.length; _i < _len; _i++) {
+ value = replies[_i];
+ values.push(value);
+ }
+ commands = [];
+ for (i = 0, _len2 = types.length; i < _len2; i++) {
+ type = types[i];
+ key = keys[i];
+ value = values[i];
+ switch (type) {
+ case 'string':
+ commands.push("SET " + (_this.escape(key)) + " " + (_this.escape(value)));
+ break;
+ case 'list':
+ commands.push("DEL " + (_this.escape(key)));
+ commands.push("RPUSH " + (_this.escape(key)) + " " + (((function() {
+ var _j, _len3, _results;
+ _results = [];
+ for (_j = 0, _len3 = value.length; _j < _len3; _j++) {
+ item = value[_j];
+ _results.push(this.escape(item));
+ }
+ return _results;
+ }).call(_this)).join(' ')));
+ break;
+ case 'set':
+ commands.push("DEL " + (_this.escape(key)));
+ commands.push("SADD " + (_this.escape(key)) + " " + (((function() {
+ var _j, _len3, _results;
+ _results = [];
+ for (_j = 0, _len3 = value.length; _j < _len3; _j++) {
+ item = value[_j];
+ _results.push(this.escape(item));
+ }
+ return _results;
+ }).call(_this)).join(' ')));
+ break;
+ case 'zset':
+ commands.push("DEL " + (_this.escape(key)));
+ commands.push("ZADD " + (_this.escape(key)) + " " + (((function() {
+ var _len3, _results, _step;
+ _results = [];
+ for (j = 0, _len3 = value.length, _step = 2; j < _len3; j += _step) {
+ item = value[j];
+ _results.push(this.escape(value[j + 1]) + ' ' + this.escape(value[j]));
+ }
+ return _results;
+ }).call(_this)).join(' ')));
+ break;
+ case 'hash':
+ commands.push("DEL " + (_this.escape(key)));
+ commands.push("HMSET " + (_this.escape(key)) + " " + (((function() {
+ var _results;
+ _results = [];
+ for (k in value) {
+ v = value[k];
+ _results.push(this.escape(k) + ' ' + this.escape(v));
+ }
+ return _results;
+ }).call(_this)).join(' ')));
+ }
+ }
+ return callback(null, commands.join("\n"));
+ }
+ ], function(err) {
+ return callback(err);
+ });
+ };
+
+ return RedisDumper;
+
+ })();
+
+}).call(this);
5 bin/index.js
@@ -0,0 +1,5 @@
+(function() {
+
+ module.exports = require('./dump');
+
+}).call(this);
13 bin/test.js
@@ -0,0 +1,13 @@
+(function() {
+ var dump;
+
+ dump = require('./dump');
+
+ dump({
+ filter: '*'
+ }, function(err, result) {
+ if (err != null) return process.stderr.write("" + err + "\n");
+ return console.log(result);
+ });
+
+}).call(this);
24 package.json
@@ -0,0 +1,24 @@
+{
+ "name": "redis-dump",
+ "version": "0.1.0",
+ "description": "Redis dump library",
+ "author": "Jeremy Faivre <contact@jeremyfa.com>",
+ "main": "./bin/index.js",
+ "scripts": {
+ "test": "node ./bin/test.js"
+ },
+ "bin": {
+ "redis-dump": "./bin/cli/redis-dump"
+ },
+ "dependencies": {
+ "async": ">=0.1.15",
+ "optimist": ">=0.3.1"
+ },
+ "devDependencies": {
+ "coffee-script": ">=1.2.0"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/jeremyfa/redis-dump.git"
+ }
+}
38 src/cli.coffee
@@ -0,0 +1,38 @@
+
+fs = require 'fs'
+path = require 'path'
+argv = require('optimist').argv
+dump = require './dump'
+package = JSON.parse fs.readFileSync path.normalize(__dirname+'/../package.json'), 'utf8'
+
+if argv.help
+ console.log """
+ #{package.name} #{package.version}
+
+ Usage: #{package.name} [OPTIONS]
+ -h <hostname> Server hostname (default: 127.0.0.1)
+ -p <port> Server port (default: 6379)
+ -f <filter> Query filter (default: *)
+ --help Output this help and exit
+
+ Examples:
+ redis-dump
+ redis-dump -p 6500
+ redis-dump -f 'mydb:*' > mydb.dump.txt
+
+ The output is a valid list of redis commands.
+ That means the following will work:
+ redis-dump > dump.txt # Dump redis database
+ cat dump.txt | redis-cli # Import redis database from generated file
+
+ """
+else
+ params =
+ filter: argv.f ? '*'
+ port: argv.p ? 6379
+ host: argv.h ? '127.0.0.1'
+
+ dump params, (err, result) ->
+ if err? then return process.stderr.write "#{err}\n"
+ if result? and "#{result}".replace(/^\s+/, '').replace(/\s+$/, '') isnt ''
+ console.log result
3  src/cli/redis-dump
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+
+require('../cli');
94 src/dump.coffee
@@ -0,0 +1,94 @@
+
+run = require('async').waterfall
+redis = require 'redis'
+
+module.exports = (params, callback) ->
+ dumper = new RedisDumper(params)
+ dumper.dump params, (params...) ->
+ dumper.close()
+ callback params...
+
+class RedisDumper
+
+ constructor: ({port, host}) ->
+ # Connect to redis database
+ @db = redis.createClient(port, host)
+
+ close: ->
+ # Close redis connection
+ @db.end()
+
+ escape: (value) ->
+ if /^([a-zA-Z0-9_\:\-]+)$/.test "#{value}"
+ return "#{value}"
+ else
+ return "'"+"#{value}".split('\\').join('\\\\').split('\'').join('\\\'')+"'"
+
+ dump: ({filter}, callback) ->
+ keys = []
+ types = []
+ values = []
+
+ run [
+ # Get keys matching filter
+ (next) =>
+ @db.keys filter, next
+
+ # For each key, get its type
+ (reply, next) =>
+ multi = @db.multi()
+ for key in reply
+ keys.push key
+ multi.type key
+ multi.exec next
+
+ # Get data of each key according to its type
+ (replies, next) =>
+ multi = @db.multi()
+ for type, i in replies
+ types.push type
+ switch type
+ when 'string'
+ multi.get keys[i]
+ when 'list'
+ multi.lrange keys[i], 0, -1
+ when 'set'
+ multi.smembers keys[i]
+ when 'zset'
+ multi.zrange keys[i], 0, -1, 'withscores'
+ when 'hash'
+ multi.hgetall keys[i]
+ multi.exec next
+
+ # Render result as the requested format
+ (replies, next) =>
+ for value in replies
+ values.push value
+
+ # Create redis-cli compliant commands from key's type and data (default)
+ commands = []
+ for type, i in types
+ key = keys[i]
+ value = values[i]
+ switch type
+ when 'string'
+ commands.push "SET #{@escape key} #{@escape value}"
+ when 'list'
+ commands.push "DEL #{@escape key}"
+ commands.push "RPUSH #{@escape key} #{(@escape(item) for item in value).join(' ')}"
+ when 'set'
+ commands.push "DEL #{@escape key}"
+ commands.push "SADD #{@escape key} #{(@escape(item) for item in value).join(' ')}"
+ when 'zset'
+ commands.push "DEL #{@escape key}"
+ commands.push "ZADD #{@escape key} #{((@escape(value[j+1])+' '+@escape(value[j])) for item, j in value by 2).join(' ')}"
+ when 'hash'
+ commands.push "DEL #{@escape key}"
+ commands.push "HMSET #{@escape key} #{((@escape(k)+' '+@escape(v)) for k, v of value).join(' ')}"
+
+ # Return result
+ callback null, commands.join("\n")
+
+
+ ], (err) ->
+ callback err
2  src/index.coffee
@@ -0,0 +1,2 @@
+
+module.exports = require './dump'
6 src/test.coffee
@@ -0,0 +1,6 @@
+
+dump = require './dump'
+
+dump filter: '*', (err, result) ->
+ if err? then return process.stderr.write "#{err}\n"
+ console.log result
Please sign in to comment.
Something went wrong with that request. Please try again.