Diego Rubin

Fullstack Developer

Entre em contato

Meu pequeno e funcional experimento com Nodejs e Websocket.

Ereader icon

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*/});
  }
}

Novo Comentário

Comentários

Grande diegão! Todo código com a palavra "prototype" me dá calafrios. Mas como a única coisa que sei de NodeJS até agora é que o npm e o Grunt têm alguma coisa a ver com ele hahaha. Um dia eu chego lá mestre.

BTW, dei 1 build no seu Gnomato aqui outro dia e funcionou beleza. Não consegui colocá-lo perto do relógio do Ubuntu, mas funcionou :-)

Siga em frente \o/

Capi, tudo blz mano?

Cara, eu normalmente escrevo meus javascripts no formato como no artigo:

http://diegorubin.com/2012/03/15/html5-file-upload-utilizando-drag-and-drop

mas dessa vez escrevi utilizando "prototype" porque estava testando uns esquemas de "herança".

Quanto ao Gnomato, você está com o Unity ai? Faz tempo que não vejo como o Unity está, mas acho que ele não implementa o systray como outros ambientes fazem. Mas de qualquer forma, o Gnomato roda em uma única instância.

[]'s

Diegão, me expressei mal, nem me referi a nada específico no seu código, e nem a propriedade nativa "prototype" do JS. O que me dá calafrios é o framework Prototype, não porque não seja bom, mas porque tem coisa melhor (tipo jQuery, só para começar), mas em pleno 2015 muita gente usa Prototype ainda, e vez ou outra me vejo preso ao Prototype em algum projeto, quando pego o "bonde andando". Mas a piada ficou vaga demais mesmo rsrs.

Uso sim o Unity! Ainda estou me acostumando à vida 100% Linux. Só fiz a migração completa no fim do ano passado... Tentei o elementary OS primeiro, mas era tudo muuuuito desatualizado. Tinha que dar build no software, na dependência, na dependência da dependência, etc... Instalei Ubuntu 14.10 e estou feliz desde então.

Capi, entendi mano. E eu tentei me "defender" porque eu não curto muito esse jeito de escrever javascript, acho que fica muito feio e repetitivo. Só fiz essa parte assim porque estava testando outras coisas.

Atualmente eu estou usando bastante o Ubuntu também por causa do notebook do trampo, pra manter uma certa compatilibidade com o pessoal. Mas sou muito fã do Fedora, distro que uso no meu note pessoal. Mas mesmo usando o Ubuntu, preferi trocar o Unity por outra coisa. Não sei se você já viu o Cinnamon, é o que estou usando atualmente, e estou gostando bastante.

[]'s