2
\$\begingroup\$

This is a silly Redis implementation that handles only three commands: INFO, ECHO, PING.

The implementation is based on streams:

var _ = require('lodash')
var expect = require('chai').expect;
var assert = require('chai').assert;
var redis = require('redis')
var split = require('split')
var net = require('net');

var util = require('util');
var Transform = require('stream').Transform;



function makeCommand(parts) {
  var parts = _.filter(parts, (_, i) => i % 2 === 1)
  return {
    command: _.first(parts),
    args: _.drop(parts)
  }
}

function CommandParser(options) {
  if (!(this instanceof CommandParser)) {
    return new CommandParser(options)
  }

  Transform.call(this, {readableObjectMode: true})
  this.__counter = 0
  this.__parts = []
}

util.inherits(CommandParser, Transform);

CommandParser.prototype._transform = function(chunk, encoding, done) {
  var str = chunk.toString('utf-8')
  if(this.__counter == 0) {
    assert(str[0] == '*')
    this.__counter = Number(_.drop(str)) * 2
  } else {
    console.log(this.__counter, str)
    this.__parts.push(str)
    this.__counter--
  }
  if(this.__counter === 0 && !_.isEmpty(this.__parts)) {
    console.log('here')
    var command = makeCommand(this.__parts)
    console.log(command)
    this.push(command)
    this.__parts = []
  }
  done()
};

function CommandProcessor(options) {
  if (!(this instanceof CommandProcessor)) {
    return new CommandProcessor(options)
  }

  Transform.call(this, {writableObjectMode: true})
}

util.inherits(CommandProcessor, Transform);

CommandProcessor.prototype._transform = function(command, encoding, done) {
  switch (command.command) {
    case 'ping':
      done(null, '+pong\r\n')
      break;
    case 'info':
      done(null, '$9\r\nsome info\r\n')
      break;
    case 'echo':
      done(null, `\$${command.args[0].length}\r\n${command.args[0]}\r\n`)
      break;
    default:
      console.log(command)
      assert.fail()
  }
};


var TEST_PORT = 6380

var server = net.createServer(function(socket) {
  var commandParser = new CommandParser()
  var commandProcessor = new CommandProcessor()
  socket
    .pipe(split())
    .pipe(commandParser)
    .pipe(commandProcessor)
    .pipe(socket)
});

server.listen(6380, '127.0.0.1')

You can use the standard redis client to work with it. I'm mostly interested in any advice about node streams, but any other ideas are welcome too.

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

var parts = _.filter(parts, (_, i) => i % 2 === 1) this looks like a habit from python. I do not recommend shadowing lodash in any part of the code, it is harder to read.

\$\endgroup\$
2
  • \$\begingroup\$ how do you recommend to rewrite the code? \$\endgroup\$ Commented Apr 20, 2016 at 9:18
  • \$\begingroup\$ anything that does not shadow _ the lodash variable, try:var parts = _.filter(parts, (e, i) => i % 2 === 1) \$\endgroup\$
    – Jan Grz
    Commented Apr 20, 2016 at 9:40
1
\$\begingroup\$

You can simplify the whole thing by using a library such as this.

Your stream, I believe, should be a duplex, one onto which you write command into redis, and read output from redis. For example, sockets are duplex (net.Socket).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.