domingo, 24 de abril de 2016

EventEmitters em Node.js

Em Node.js a classe EventEmitter é responsável por emitir eventos, e também, por adicionar listeners para esses eventos. Os listeners são callbacks que são chamados toda vez que o evento em questão ocorre. Para cada evento podemos ter um ou mais listeners.

Exemplo:

var http = require('http');

var server = http.createServer( (req, res) => {

 // req e res são streams e em Node.js todas as streams são EventEmitters

 // se não setarmos o encoding para utf-8 vamos receber objetos da classe Buffer ao invés de strings
  req.setEncoding('utf8');
  
  // adicionamos um listener para o evento data que recebe como argumento 
  // os dados enviados na requisição
  req.on('data', (data) => {
    // escrevemos os dados recebidos de volta como resposta
    res.write(data+'\n');
  });
  
  // adicionamos um listener para o evento end
  req.on('end', () => {    
    // chamamos o método end() de respose
    res.end();
  });

});

// Iniciamos o servido escutando na porta 3000
server.listen(3000);

Para testar, abra um terminal e digite:
curl http://localhost:3000 -d {} // {}
curl http://localhost:3000 -d '"string'" // string
curl http://localhost:3000 -d 10 // 10

No exemplo acima utilizamos o módulo http para criar um servidor escutando na porta 3000. O método createServer de http recebe como calback uma função com os argumentos req (request) e res (response). Utilizando o EventEmitter req nós registramos event listeners para os eventos data e end.

Todos os eventos tem um tipo que é definido por uma string, como no exemplo acima, "data" e "end". A única forma de saber os tipos de eventos disponíveis em determinada API é através de sua documentação, não é possível descobrir estes tipos programaticamente. No entanto, grande parte dos event emitters do Node.js emitem o evento "error". Fica a cargo do programador registrar um listener para este evento ou não. Se não houver um listener para tratar o evento error, e este ocorrer, o event emitter lançará uma exceção não tratada.

É possível também criarmos nossos próprios EventEmitters como no exemplo a seguir:

// criamos um objeto do tipo EventEmitter chamado meuEmitter
// a classe EventEmitter encontra-se no módulo 'events'
var meuEmitter = new (require('events')).EventEmitter();

// Todo EventEmitter emite o evento 'newListener' quando um listener é adicionado
// utilizamos once para que seja chamado somente uma vez, para não ocorrer um loop infinito
meuEmitter.once('newListener', (evento, listener) => {
if(evento === 'evento') {
// o evento newListener será acionado antes que um listener seja adicionado ao array interno de listeners
// desta forma o listener adicionado dentro de 'newListener' será adicionado antes de qualquer outro
meuEmitter.on('evento', () => {
console.log("chamado primeiro");
});
}
});

function listener() {
console.log("chamado depois");
}

// registramos um listener para o evento "evento" que não recebe nenhum argumento
meuEmitter.on('evento', listener);

// registramos um listener para o evento "evento2" que recebe um argumento
// "on" e "addListener" fazem exatamente a mesma coisa
meuEmitter.addListener('evento2', (num) => {
console.log(num*num);
});

// o método emit emite o evento
meuEmitter.emit('evento');
// chamado primeiro
// chamado depois

meuEmitter.emit('evento2', 5);
// 25

// o método removeListener remove um listener de determinado evento
meuEmitter.removeListener('evento', listener);
meuEmitter.emit('evento');
// chamado primeiro

Espero que este post tenha sido útil.
Abraço.


quinta-feira, 21 de abril de 2016

Generators no JavaScript ES6 (ECMAScript 6)

Generators são um tipo especial de função, também conhecidos como iteradores. Estas funções são responsáveis pela geração de uma sequencia de valores, como um array, porém, elas não produzem um array contendo todos os elementos da sequencia e retornam o mesmo, ao invés disto, é produzido um elemento da sequencia a cada vez que estas funções são chamadas. Os elementos podem ser gerados indefinidamente sem que haja necessidade de alocação de memória para esta sequencia, já que a mesma não é um array.

Em JavaScript os generators retornam um objeto do tipo Generator. Este objeto possui um método chamado next() que retorna um objeto contendo as propriedades done e value.

  • done = true indica que o iterador já chegou ao fim da sequencia a ser iterada, done = false indica que ha mais valores a serem produzidos na sequencia.
  • value é o valor retornado pelo iterador. 

O generator possui uma expressão yield, que quando atingida, retorna o novo valor do generator e pausa a execução da função. Então, da próxima vez que next() é chamado o fluxo continua a partir da chamada de yield até que não haja mais um valor para retornar. O estado da função é mantido a cada chamada.

Um generator é caracterizado pelo asterisco após a palavra chave function e pelo uso da expressão yield.

Neste exemplo criaremos um generator que gera a sequencia de fibonacci.


function* fibonacci() {
   var a = 0;
   var b = 1;
   yield a;
   yield b;
   while(true) {
     var f = a + b;
     a = b;
     b = f;
     yield f;
  }
}

var generator = fibonacci();
generator.next(); // {value: 0, done: false}
generator.next(); // {value: 1, done: false}
generator.next(); // {value: 1, done: false}
generator.next(); // {value: 2, done: false}
generator.next(); // {value: 3, done: false}
generator.next(); // {value: 5, done: false}
generator.next(); // {value: 8, done: false}
generator.next(); // {value: 13, done: false}
.......
.......

Espero que este post tenha sido útil.
Abraço

segunda-feira, 18 de abril de 2016

Programação dirigida a eventos e Node.js: I/O Assíncrono

No modelo tradicional de programação as funções são executadas de forma linear e síncrona, ou seja, elas são executadas em uma ordem definida onde as chamadas subsequentes devem esperar que a chamada anterior termine. Por exemplo, se temos a chamada a uma função que faz o acesso ao banco de dados, a instrução subsequente só será chamada após o término deste acesso, fazendo com que o andamento do programa seja bloqueado naquele ponto, tornando a interação com o programa bastante pobre.

recurso = busca_recurso(argumento);
// A linha abaixo só vai executar quando a função busca_recurso retornar
utiliza_recurso(recurso);


Uma alternativa a este modelo é a utilização de threads. Um conjunto de threads pode ser executado em paralelo possibilitando que uma tarefa seja executada por uma thread sem a necessidade de que uma thread chamada anteriormente termine.  Porém, estas threads dividem os mesmo recursos, como memória por exemplo, fazendo com que seja necessário bastante cuidado para que uma thread não acesse indevidamente os recursos que estão sendo utilizados por outras threads, levando a comportamentos inesperados e indesejáveis. Alguns destes cuidados incluem o uso de locks e semáforos para sincronizar o acesso aos dados entre as diferentes threads.


O Node.js/JavaScript utiliza um paradigma diferente, conhecido como "Programação dirigida a eventos" através de manipuladores de eventos e callbacks. Um evento pode ser um clique em um botão, o pressionamento de uma tecla, etc.
Para isto, utilizasse um loop de eventos. O loop de eventos roda continuamente em uma thread única e fica "escutando" os eventos que ocorrem afim de chamar o callback registrado para este evento.
A tradução de callback seria algo como "chamar de volta". Ou seja, quando chamamos uma função assíncrona passando para ela um callback é como se o callback disse-se: "Continue com o processamento daqui pra diante e me chame de volta quando for necessário, ai então, eu farei o meu trabalho".

//Função de callback
var utiliza_recurso = function(recurso) {
     //faz algo aqui
}

busca_recurso(argumento, utiliza_recurso)
// A execução continua mesmo que o callback utiliza_recurso ainda não tenha sido chamado
// em algum momento no futuro ele é chamado e faz seu trabalho sem bloquear o programa

O modelo de programação assíncrona utilizando um loop de eventos faz com que o Node.js possa trabalhar com várias requisições ao mesmo tempo,  o que constitui uma das maiores vantagens em relação à outras tecnologias.


Espero que este post tenha sido útil.
Abraço.


sábado, 16 de abril de 2016

Arrow Functions no JavaScript ES6 (ECMAScript 6)

Arrow Functions são uma das novidades do ECMAScript 6 (ES6) e compartilham dos mesmos conceitos das Expressões Lambda, presentes em linguagens como C#, Python, Java, etc, amplamente utilizadas em Programação Funcional.

Uma das principais vantagens em relação as funções tradicionais é o fato das Arrow Functions terem uma sintaxe mais "enxuta", podendo-se omitir as palavras chave function e return, e também, o nome da função, Arrow Functions são sempre anônimas, e portanto, um bom uso para elas e passá-las como callback anônimo para outras funções.

Vejamos um exemplo:

a = [1,2,3,4,5,6];

var result = a.filter(function(num) { return num % 2 == 0; });

var result = a.filter((num) => num % 2 == 0);

Notem que no segundo caso o código ficou mais conciso.

Aquilo que vem antes da flecha (=>) são os parâmetros da função, e o que vêm depois são as declarações ou uma expressão. No caso de ser apenas uma expressão, como no exemplo acima, não é necessário utilizarmos chaves, porém, se utilizarmos as chaves devemos declarar o return explicitamente. Ex: (num) => { return num % 2 == 0; }.

Se temos apenas um parâmetro, o uso dos parenteses não é obrigatório.
Ex: num => num % 2 == 0;

No entanto, podemos utilizar mais de um parâmetro, neste caso os parenteses são obrigatórios:
Ex: (x, y) => x * y;

Se não houver nenhum parâmetro, obrigatoriamente devemos utilizar os parenteses:
Ex: () => 2 * 2;

As Arrow Functions podem ser atribuídas a variáveis.Ex:
var f = (x) => x * x;
f(2); //4

Para retornarmos um objeto devemos utilizar parenteses no corpo da função.
Ex: x => ({a: 5})

Espero que este post tenha sido útil.
Abraço.



sexta-feira, 15 de abril de 2016

Módulos em Node.js

Por questões de manutenibilidade, separação de responsabilidades, reaproveitamento de código, etc, é importante que nossas aplicações, principalmente aquelas de grande porte, sejam separadas em módulos.
Em Node.js, cada módulo é criado em seu respectivo arquivo JavaScript.

Para utilizarmos um módulo em nossa aplicação, utilizamos a função require, passando como parâmetro o nome ou caminho do módulo (não é necessário informar a extensão js).

O Node.js já vem com vários módulos para que possamos utilizar, os Core Modules, como o módulo http por exemplo.
Para importação deste módulos, informamos para a função require apenas o nome do módulo e a mesma saberá onde encontrá-lo, por exemplo: require('http').

Além dos Core Modules, existem também os módulos criados por terceiros, que podemos instalar através do NPM (Node Package Manager) utilizando o comando npm install nome_modulo.
Estes módulos são instalados em uma pasta chamada node_modules que é criada na pasta de nosso projeto no momento da instalação destes módulos. Para carregá-los, informamos apenas seu nome para a função require (sem o caminho) que a mesma saberá onde encontrá-los.

No entanto, também temos a possibilidade de criar nossos próprios módulos de acordo com nossas necessidades. Suponhamos que temos um arquivo chamado app.js com o seguinte código:
var meuModulo = require('./meuModulo'); 
meuModulo.foo(5, 5);

 O "./" indica para a função require buscar pelo modulo, que está no arquivo meuModulo.js, no mesmo diretório onde o arquivo app.js se encontra.

O arquivo meuModulo.js, contém o seguinte código:

var x = 10; 

var o = { 

        foo: function(a, b) {
             console.log((a + b) * x); 
       } 
}; 

 module.exports = o;


Neste caso, estamos exportando o objeto o, a variável x não será acessível fora do nosso módulo, portanto ela é privada a ele.

O objeto module.exports é local ao nosso módulo. E é este objeto que é retornado pela função require.

Espero que este post tenha sido útil.
Abraço.

terça-feira, 12 de abril de 2016

Automação de tarefas com Gulp

Gulp é um automatizador de tarefas que roda sobre Node.js. Dentre as tarefas que podem ser automatizadas utilizando o Gulp estão: compilação de CSS pré-processado, minificação e concatenação de Javascript, reinicialização do servidor, entre outras. Estas tarefas podem ser executadas automaticamente pelo Gulp à medida que certos arquivos são alterados (gulp.watch).

Para auxiliar, o Gulp possui uma vasta gama de plugins (mais de 2000!) para as mais diversas tarefas.

Neste post pretendo mostrar algumas tarefas que podem ser automatizadas com o Gulp utilizando como base o tutorial sobre MEAN que escrevi aqui no blog.

Primeiramente devemos instalar o Gulp de forma global:
npm install gulp -g

Além do Gulp, vamos utilizar os seguintes plugins: gulp-concat (para juntar os arquivos javascript em um único arquivo) e gulp-uglify (para minificação do javascript).

Vamos instalá-los como dependências de desenvolvimento. Abra o terminal, e na pasta raiz do projeto, digite:
npm install gulp gulp-uglify gulp-concat --save-dev

Agora vamos refatorar nosso arquivo /public/javascripts/app.js, da aplicação Angular, separando as funcionalidades deste arquivo nos seguintes arquivos: module.js, config,js, services.js, controllers.js, filters.js e directives.js. Apesar de estarmos fazendo isto para demonstração da concatenação e minificação dos arquivos em um único arquivo, a modularização da aplicação em diversos arquivos separados por responsabilidade é uma boa prática que deve ser seguida, principalmente em aplicações grandes, para uma melhor manutenção do código.

O próximo passo é a criação do arquivo gulpfile.js que é onde o Gulp vai buscar as tarefas a serem executadas. Crie este arquivo na raiz do projeto com o seguinte conteúdo:

var gulp         = require('gulp');
var spawn = require('child_process').spawn;
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var node, mongo;


var scripts = function() {
        /* pegamos os arquivos passados em gulp.src, concatenamos todos em um único arquivo chamado app.js, minificamos este arquivo e salvamos o mesmo na pasta /public/javascripts/build */
return gulp.src(['./public/javascripts/module.js',
            './public/javascripts/config.js',
            './public/javascripts/services.js',
            './public/javascripts/controllers.js',
            './public/javascripts/filters.js',
            './public/javascripts/directives.js'])
  .pipe(concat('app.js'))
  .pipe(uglify())
  .pipe(gulp.dest('./public/javascripts/build'))
}

var server = function() {

console.log("reiniciando servidor");
// inicializamos o processo mongod se o mesmo não foi inicializado ainda
if(!mongo)
mongo = spawn('mongod');

        // Reinicializamos o servidor
if(node)
node.kill();

node = spawn('node', ['./bin/www'], {stdio: 'inherit'});

};

// a tarefa start é responsável por concatenar e minificar os scripts e reinicializar o servidor
gulp.task('start', function(callback) {
       
scripts();
server();

        /* sempre que o arquivo app.js (nosso arquivo principal do servidor) ou algum arquivo com extensão js na pasta /routes for modificado refazemos o processo de concatenação e minificação dos scripts e reinicialização do servidor */
gulp.watch(['./app.js','./routes/*.js'], function() {
scripts();
server();
});
});

Altere o arquivo index.html na pasta /public para que o atributo src da tag script de app.js aponte para /javascripts/build/app.js


No terminal, no diretório raiz do projeto, digite: gulp start

Espero que este post tenha sido útil.
Abraço.

segunda-feira, 11 de abril de 2016

Testando uma API Node.js/Express com Mocha

Os testes de unidade durante o desenvolvimento de uma aplicação são de extrema importância, garantindo que à medida que a aplicação é desenvolvida estejamos garantindo o funcionamento correto de cada modulo. Isto pode tornar o processo de desenvolvimento um pouco mais trabalhoso, mas, no entanto, evita muita dor de cabeça posteriormente.

Neste post vamos observar como é interessante irmos testando cada rota de nossa API REST à medida que a mesma é desenvolvida, através do módulo Mocha do Node.js que nos permite realizar testes assíncronos de maneira fácil.
Para tanto, vamos utilizar os seguintes módulos, além do Mocha:

  • chai, para asseveração (assertion) e
  • request, para fazermos as requisições para as rotas

Primeiramente é importante que tenhamos o Mocha instalado de forma global (indicado pelo flag -g do comando npm install):
npm install -g mocha

Agora vamos criar o projeto:
express teste-unidade-mocha
cd teste-unidade-mocha

Instale os módulos definidos em package.json
npm install

Vamos instalar os outros módulos necessários:
npm install mocha chai request --save-dev

Modifique o arquivo app.js para que o mesmo fique desta forma:

var express = require("express");
var bodyParser = require("body-parser");

var app = express();

app.use(bodyParser.urlencoded());
app.use(bodyParser.json());

var router = express.Router();

router.get('/produtos', function(req, res) {
  var produtos = [
    {
      id: 1,
      descricao: "Produto 1",
      categoria: 10
    },
    {
      id: 2,
      descricao: "Produto 2",
      categoria: 11
    },
    {
      id: 3,
      descricao: "Produto 3",
      categoria: 12
    }
  ];

  res.json(produtos);
});

router.post('/produtos', function(req, res, next) {

  /* A forma como salvaríamos o produto no banco
   var produto = new Produto(req.body);
   produto.save(function(err, produto) {
    if(err)
      next(err); */
   
      res.json({mensagem: "salvo com sucesso", erro: false});

   //});


});

app.use('/', router);


module.exports = app;

Foram criadas duas rotas, uma que retorna uma lista de produtos e outra que "insere" um produto no banco. São estas rotas que vamos testar.

Crie um diretório na raiz da aplicação chamado test (por padrão é neste diretório que o Mocha procura os testes a serem executados), e dentro dele um arquivo chamado teste.js com o seguinte conteúdo:

var assert = require('chai').assert;
var request = require('request');

var url = "http://localhost:3000/produtos";

describe("Testando a API", function() {

    it("deve retornar 3 produtos", function(done) {
    request.get(url, function(err, res, body) {    
                // Foi retornada a quantidade correta de produtos?
    assert.equal(JSON.parse(res.body).length, 3);
                // Ja que estamos fazendo uma chamada assíncrona devemos nos assegurar que o Mocha vai aguardar o término da mesma chamando o callback "done".
    done();
    });
    });

    it("deve salvar um produto no banco", function(done) {
    request.post(url, {id:4, descricao:"Produto 4", categoria:100}, function(err, res, body) {    
                // Houve algum erro ao tentar inserir o produto?  
assert.equal(JSON.parse(res.body).erro, false);
    // Ja que estamos fazendo uma chamada assíncrona devemos nos assegurar que o Mocha vai aguardar o término da mesma chamando o callback "done".
                done();
    });
    });

});

Na pasta raiz do projeto digite:
npm start
Abra outro terminal, navegue até o diretório do projeto e digite:
mocha



Vamos simular um caso de erro.
No arquivo app.js, na rota post, altere o objeto passado para res.json() para que a propriedade erro seja true:
res.json({mensagem: "salvo com sucesso", erro: true});
Reinicie o servidor (npm start) e novamente digite:
mocha

E desta vez o teste falha.

Espero que este post tenha sido útil.
Abraço.