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\$
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\$
  • \$\begingroup\$ how do you recommend to rewrite the code? \$\endgroup\$ – kharandziuk Apr 20 '16 at 9:18
  • \$\begingroup\$ anything that does not shadow _ the lodash variable, try:var parts = _.filter(parts, (e, i) => i % 2 === 1) \$\endgroup\$ – Jan Osch Apr 20 '16 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, privacy policy and cookie policy

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