Meu pequeno e funcional experimento com Nodejs e Websocket.

31 Mar 2015 . category: article . Comments
#nodejs #javascript #websocket #independent #experimento

Introdução

Um dos projetos pessoais que mais tenho me dedicado nos últimos tempos Independent, o CMS que criei e que uso como site pessoal. Tento escrever meus textos diretamente nele e por isso tenho gasto um tempo tentando deixar essa tarefa o mais agradável possível. Com essa linha de pensamento, o que mais sentia faltar era de um bom mecanismo de preview. Eu tinha feito um sistema de preview bem simples, onde o texto era renderizado utilizando o layout do site atual e não o do admin, porém era necessário que o artigo já estivesse persistido no banco. Mas pensando em um mecanismo melhor para poder ver a edição em tempo real, pensei em fazer algo utilizando WebSocket.

Como esse serviço é algo que pode ser separado do código do CMS resolvi utilizar uma tecnologia que nunca tinha feito nada de útil, mas que sempre escuto falar, o NodeJS.

Meu objetivo aqui não é fazer nenhum tipo de introdução à nenhuma tecnologia utilizada neste projeto. O objetivo é somente demonstrar algo que fiz com essas tecnologias.

Arquitetura da Solução

Criei duas “classes” em Javascript para escutarem o servidor. Uma delas fica no admin e monitora as mudanças realizadas nos campos do artigo e a outra fica na página de preview e é responsável por receber as informações do servidor e colocar o resultado nos lugares corretos.

Para intermediar a conversa entre duas classes há o servidor que cria as sessões. Por enquanto ele está bem simples, mas com o tempo eu vou melhorando sua implementação.

Código dos Clientes

Abaixo eu listo os códigos dos clientes. Tentei fazer algo bem simples. Como primeiro teste está funcionando muito bem.

Cliente que escuta o formulário de edição de conteúdo

var PreviewForm = function() {

}

PreviewForm.prototype.connect = function(session) {
  var _this = this;

  this.session = session;
  this.connection = new WebSocket($('meta[name="preview_host"]').attr('content'));
  this.connection.onopen = function() {
    var message = JSON.stringify({'event': 'start', 'session': session});
    _this.connection.send(message);  
  }

}

PreviewForm.prototype.send = function(content) {
  var _this = this;
  var object = {'event': 'update', 'session': _this.session};
  object.content = content;
  _this.connection.send(JSON.stringify(object));  
}


Cliente que recebe as alterações e exibi o recurso de forma correta

var PreviewView = function() {
}

PreviewView.prototype.connect = function(session) {
  var _this = this;

  this.session = session;
  this.connection = new WebSocket($('meta[name="preview_host"]').attr('content'));
  this.connection.onopen = function() {
    var message = JSON.stringify({'event': 'view', 'session': session});
    _this.connection.send(message);  
  }

  this.connection.onmessage = function(message) {
    var content = JSON.parse(message.data);

    //render
    $.each(content, function(attr, value) {
      $("[data-preview-attribute='" + attr + "']").html(value);
    });
  }

}

Código do Servidor

Utilizei para construir esse pequeno servidor o pacote ws, que pode ser instalado através do comando npm install ws.

var WebSocketServer = require('ws').Server;

var server = new WebSocketServer({ port: 8080 });

var sessions = {};
var nextId = 0;
 
server.on('connection', function connection(client) {

  client.on('message', function incoming(message) {
    console.log(message);
    var message = JSON.parse(message);
    if(message.event === 'start') newSession(message);
    if(message.event === 'view') newClient(client, message);
    if(message.event === 'update') updateClients(message);
  });

});

function newSession(message) {
  if(!sessions[message.session]) sessions[message.session] = {'clients': []};
}

function newClient(client, message) {
  if(!sessions[message.session]) sessions[message.session] = {'clients': []};
  sessions[message.session].clients.push(client);
}

function updateClients(message) {
  for(var i = 0; i < sessions[message.session].clients.length; i++) {
    var client = sessions[message.session].clients[i];
    client.send(JSON.stringify(message.content), function(){/*not send*/});
  }
}


Me

Tenho estudado esse mundo mágico da programação desde 2005. Já consegui sustentar minha família usando Ruby, Java, Python, C++ e Javascript. O resto tenho usado para diversão ou aprendizado.