Já falamos sobre várias formas de utilizar módulos no JavaScript aqui no blog, usando AMD, CommonJS e UMD.
Mas agora o JavaScript tem suporte a módulos nativos! Quer aprender como utlizar? Vem comigo =)
EcmaScript é o que podemos chamar de “a documentação da linguagem JavaScript”.
É o lugar onde é definido como as coisas devem funcionar na linguagem.
Você já deve ter lido alguns posts aqui do blog, onde eu falo sobre meus workflows para trabalhar como módulos no JavaScript. Se ainda não leu, pode ler aqui, aqui e aqui.
Se você perceber, não existia uma forma padrão de criar módulos em JS sem passar a informação pelo escopo global.
Usávamos técnicas como IIFE, AMD, CommonJS (Node), UMD… tudo para tentar modularizar nossas aplicações, e não deixar o código virar um espaguete em arquivos com 4k linhas de código.
Então surgiu essa nova especificação para salvar nossas vidas: EcmaScript Modules (ou ES Modules, para os mais chegados).
E a ideia dessa especificação é exatamente o que o seu nome diz: permitir a modularização dos nossos códigos JS, sem precisar passar pelo escopo global :D
Da hora, não!?
Vamos ver como ela funciona? Vem comigo!
Já vou começar falando que não precisamos mais usar a diretiva 'use strict'
: ES Modules são strict por padrão!
Vamos ver na prática como é o funcionamento dos módulos! Eles ainda não estão implementados totalmente no Node.js, mas na versão mais recente do Chrome você já consegue usá-los!
Obs.: Não testei em outros navegadores, mas aqui você consegue ver o suporte atual dessa feature.
A primeira coisa a fazer é criar um arquivo index.html
, onde iremos testar nosso código. Vamos criar uma estrutura básica:
1 |
|
Essa é uma estrutura padrão de uma página em HTML5, com um pequeno detalhe: perceba que a tag script
tem um type="module"
. Isso faz o browser “ativar” a feature de módulo, e todo o código escrito ali dentro dessa tag script é reconhecido como um módulo.
Lembrando que ES Modules são strict por padrão: veja que, ainda que não façamos uso da diretiva 'use strict'
, o resultado de this
será undefined
no exemplo acima, e não o objeto global window
=)
E como boa prática, para separar o código JS do documento HTML, usamos a mesma ideia de sempre: uma tag script
com o atributo src
, mas com a adição do type="module"
:
1 |
|
E só colocamos no arquivo main.js
o console.log que estava na index.html:
1 | console.log('this:', this) |
Só tem um pequeno problema: ao tentar abrir esse arquivo apenas clicando duas vezes nele, ele será aberto no navegador usando o protocolo file://
. E por se tratar de um módulo, o navegador não permite acessá-lo dentro de uma página web sem ser via o protocolo HTTP, dentro da mesma origem (mesmo domínio), devolvendo no console um erro de CORS:
1 | index.html:1 Access to script at 'file:///media/storage/code/test-es-modules-browser/02/main.js' from origin 'null' has been blocked by CORS policy: The response is invalid. |
Pra resolver isso, é preciso executar o index.html à partir de um servidor HTTP.
Vamos usar o http-server
.
E pra facilitar nossa vida, se você tiver a última versão do Node.js instalado na sua máquina, você deve ter também, além do NPM, uma ferramenta chamada npx
, que permite usar módulos globais sem instalação. Para usar o http-server
à partir do npx
, acesse o diretório onde você criou os arquivos index.html
e main.js
, e execute o comando:
1 | npx http-server -c-1 |
Esse comando vai servir o index.html
no endereço http://localhost:8080
. Acesse esse endereço no seu navegador, e a mensagem que estava aparecendo no console deve estar sendeo exibida corretamente =)
Detalhe: o -c-1
ao final do comando é apenas para não deixar o http-server
fazer cache, e evitar problemas conhecidos haha :D
Agora podemos voltar ao assunto :P
Ok, mas qual a utilidade de ter módulos?
Basicamente duas:
Vamos começar com um exemplo simples: uma função de soma. Crie um arquivo chamado sum.js
:
1 | function sum (a, b) { |
Agora, para que possamos utilizar esse módulo, ele precisa estar disponível para ser importado, lembrando que módulos têm um escopo próprio, e não são compartilhados no escopo global.
Para fazer isso, temos algumas formas. Vamos aprender e analisar cada uma delas:
A maneira mais simples de exportar a função é usando a seguinte expressão no final do arquivo sum.js
:
1 | export default sum |
Isso irá exportar a função de forma “default”. Vamos falar sobre o que isso significa em breve.
Agora, para que possamos importar e usar essa função, podemos adicionar, no início do nosso arquivo main.js
, a seguinte expressão:
1 | import sum from './sum.js' |
Dessa forma, estamos importando a função que foi exportada como “default” do arquivo sum.js
, atribuindo o valor exportado à uma variável nomeada sum
.
Podemos usar qualquer nome para importar um valor que foi exportado como “default”:
1 | import arroz from './sum.js' |
Isso faz com que com que a variável arroz
seja criada e receba a referência da função sum
, que foi exportada do arquivo sum.js
:)
Voltando ao exemplo, vamos tentar agora usar a função sum
, importada no arquivo main.js
. Logo após a chamada de “import” da função sum
, adicione o código:
1 | console.log(sum(1, 2)) |
Ao atualizar a página, você verá no console o valor 3
:)
Como você pode perceber, quando usamos export default
não precisamos, necessariamente, dar um nome ao valor exportado, pois ele pode ser importado com qualquer nome.
Então temos a opção de exportar diretamente uma função (nomeada ou não) do arquivo sum.js
, dessa forma:
1 | export default function sum (a, b) { |
Veja que agora, no arquivo sum.js
, apenas tiramos o export default sum
do final do arquivo, e exportamos diretamente a função, dessa forma em forma de expressão, como função nomeada, não como uma função literal.
Se podemos usar a função como expressão, o nome dela é opcional. O código abaixo também funciona corretamente:
1 | export default function (a, b) { |
Veja que a expressão da função foi exportada sem um nome =)
Pra reduzir ainda mais esse código, é possível exportar uma arrow function:
1 | export default (a, b) => a + b |
Se você não entende a sintaxe de uma arrow function, eu escrevi sobre o assunto aqui
E isso vale para qualquer tipo de dado (funções, strings, objetos, arrays, promises, etc).
Podemos também exportar de forma nomeada, forçando assim a obrigatoriedade de um nome específico na hora de importar o módulo.
Vamos alterar o tipo de exportação do módulo sum.js
. Primeiro, vamos separar novamente a criação da função, e a linha que faz a exportação:
1 | const sum = (a, b) => a + b |
No código acima, só criamos uma variável sum
, e atribuímos a ela a função, e então exportamos essa função.
Para usarmos o export nomeado, nosso código deve ficar assim:
1 | const sum = (a, b) => a + b |
Perceba que simplesmente trocamos a palavra default
por chaves, e colocamos o nome que será exportado entre as chaves. Só como adendo: essas chaves não são chaves de criação de um novo objeto, ok? É outra sintaxe =)
Se você executar o código como está, você deve ver o seguinte erro no console:
1 | Uncaught SyntaxError: The requested module './sum.js' does not provide an export named 'default' |
Isso porque não fizemos nenhuma exportação “default” no nosso módulo, temos apenas um export nomeado.
Beleza, mas então como importamos um módulo que foi exportado de forma nomeada?
Bem simples! No arquivo main.js
, apenas envolva a palavra sum
, que foi usada como variável que recebe o módulo, entre chaves:
1 | import { sum } from './sum.js' |
Veja que o negócio segue um padrão: quando você exporta um módulo com default
, você pode dar qualquer nome na hora de importar. Já se exportar de forma nomeada, na hora da importação é sempre necessário usar o mesmo nome que foi usado para exportar.
Podemos ainda exportar a função sum
de forma nomeada na mesma linha, apenas fazendo assim no arquivo sum.js
:
1 | export const sum = (a, b) => a + b |
O interpretador da linguagem vai entender o nome da variável - ou o nome da função, quando usado uma função nomeada - como o nome a ser exportado. Bem legal né? =)
Agora, vamos ver o que acontece se, na hora de importar o módulo, usarmos um nome diferente do que foi exportado. No arquivo main.js
, vamos trocar sum
por arroz
:
1 | import { arroz } from './sum' |
Obviamente recebemos um erro:
1 | Uncaught SyntaxError: The requested module './sum.js' does not provide an export named 'arroz' |
Mas e se eu quisesse usar mesmo arroz
ao invés de sum
, é possível?
Sim! E não só é possível renomear um módulo que foi exportado com um nome, como é bem simples, veja só. Ainda no arquivo main.js
, vamos fazer nosso módulo funcionar com o nome arroz
:
1 | import { sum as arroz } from './sum.js' |
Veja que simples: só precisamos usar a palavra as
(como), ou seja: “importe sum
como arroz
lá do arquivo ./sum.js
“ :D
Simples não? =)
Podemos usar a mesma ideia para renomear um export nomeado! Vamos voltar ao arquivo sum.js
. Ele estava assim:
1 | export const sum = (a, b) => a + b |
Agora vamos exportar sum
com o nome plus
:
1 | const sum = (a, b) => a + b |
Lembra que eu falei que as chaves no export
não eram de um objeto? Então.. essa é a sintaxe para renomear uma exportação. É como se disséssemos: “Exporte sum
como plus
“ =)
E agora só temos que fazer a importação correta do nome plus
no arquivo main.js
para tudo voltar a funcionar:
1 | import { plus } from './sum.js' |
Pronto! =)
Uma coisa interessante de notar: lembra do erro quando nós exportamos a função sum
de forma nomeada, mas tentamos importar como default
(sem usar as chaves)? O erro foi o seguinte:
1 | Uncaught SyntaxError: The requested module './sum.js' does not provide an export named 'default' |
Veja a dica: o erro diz que o módulo sum.js
não proveu um export nomeado como “default”.
Isso significa que podemos fazer algumas mandingas, do tipo:
No arquivo sum.js
, exportamos a função como “default”:
1 | const sum = (a, b) => a + b |
E no arquivo main.js
, fazemos a mandinga:
1 | import { default as plus } from './sum.js' |
Veja que default
pode ser usado como um nome para renomear o módulo! Claro que seria muito mais simples fazer apenas:
1 | import plus from './sum.js' |
Mas o exemplo foi só pra você entender que isso também é possível, e pode ajudar em alguns casos que veremos mais adiante =)
Como você já deve imaginar, podemos fazer também o processo inverso: renomear um módulo para ser exportado como default
, dessa forma (no arquivo sum.js
):
1 | const sum = (a, b) => a + b |
Para exportar mais de um valor por módulo, vamos criar um novo arquivo chamado calculator.js
, e nesse arquivo vamos criar funções para as operações matemáticas mais comuns:
1 | const sum = (a, b) => a + b |
Para exportar facilmente cada uma dessas funções, só precisamos usar a palavra chave export
antes de cada declaração:
1 | export const sum = (a, b) => a + b |
Pronto!
Certo, mas como importar várias funções de uma única vez?
Simples: lá no nosso arquivo main.js
, vamos importar essas funções de calculator.js
:
1 | import { sum, sub, mult, div } from './calculator' |
Show! Ainda tem a segunda forma de exportar lá do calculator.js
. Podemos fazer assim também:
1 | const sum = (a, b) => a + b |
Que funciona do mesmo jeito. Escolha o que fica melhor para o seu caso e use :D
No último exemplo, importamos vários módulos em uma só chamada. Mas é possível também importar todos os módulos que foram exportados de forma nomeada, usando o operador especial *
, olha só (no main.js
):
1 | import * as calculator from './calculator.js' |
Dessa forma nós importamos todos os valores que foram exportados com um nome em calculator.js
, e atribuímos esses valores como propriedades de um objeto que chamamos de calculator
.
E se tivéssemos algum valor exportado de forma default
no calculator.js
?
Vamos ver como ficaria esse caso. Agora o arquivo calculator.js
deve ficar assim:
1 | const sum = (a, b) => a + b |
Veja que criamos uma variável obj
e exportamos essa variável com o default
. Para ter acesso a esse objeto no main.js
, onde foi usado o *
para importar todos os módulos, podemos usar calculator.default
, já que esse objeto foi exportado como default
:
1 | console.log(calculator.default) // {} |
Lembre-se que o export default
pode ser exportado de duas formas:
1 | export default obj |
Ou ainda, usando a forma nomeada:
1 | export { obj as default } |
Por isso conseguimos pegar esse valor quando importado usando o *
:)
Nesse último exemplo do arquivo calculator.js
, é importante frisar que só podemos ter um único módulo exportado como default
, por motivos óbvios =)
Mas se não estiver tão claro, é só pensar no seguinte: o nome default
pode ser usado como um “nome” na hora de importar. Se você tiver dois export default
, qual deles deveria ser usado com o nome default
, na hora de importar?
Deu pra entender a ideia? Sempre que você precisar exportar um só valor, pode usar tanto o default
como o formato nomeado. Mas se for exportar mais de um valor, use o formato nomeado.
O default
não é obrigatório, e muitas vezes desencorajado por algumas pessoas da comunidade, com a alegação de que exports nomeados podem ser melhor “padronizados” já que eles precisam ter o nome exato na hora de importar.
Exemplo: você vai importar o jQuery na sua aplicação. Se ele fosse exportado de forma default
, você poderia usar em um arquivo:
1 | import jQuery from './jquery.min.js' |
Em outro arquivo, poderia usar:
1 | import jQ from './jquery.min.js' |
E assim por diante. Agora, se o export for nomeado, você é forçado a usar o nome que foi usado para exportar - tudo bem que dá pra renomear, mas ainda assim fica muito mais explícito =)
Acho que deu pra entender :)
Também é possível fazer o import do valor default de forma separada dos nomeados, mas ainda na mesma chamada, veja o que vamos fazer no arquivo main.js
:
1 | import calc, { sum, sub, mult, div } from './calculator.js' |
Veja que calc
fica fora dos parênteses, pois é o nome dado ao valor que foi exportado com default
:)
Ainda tem uma outra feature interessante, onde você pode exportar um módulo, fazendo a importação desse na mesma sentença. Vamos fazer o seguinte: vamos criar um arquivo para cada operação matemática: sum.js
, sub.js
, mult.js
e div.js
.
Ao fazer isso, vamos mover cada função que está em calculator.js
para o seu respectivo arquivo, e exportar de forma default
em cada um.
Depois, só precisamos importar tudo no arquivo calculator.js
, e então exportar. Mas podemos fazer ainda melhor, saca só:
1 | export { default as sum } from './sum.js' |
Veja que usamos a sintaxe export from
, que, basicamente, importa o módulo que foi exportado como default
do seu arquivo setado após o from
, nomeia esse módulo e faz o export
dele à partir do arquivo calculator.js
.
Assim, podemos fazer a importação nomeada de qualquer um desses módulos direto do calculator.js
no main.js
:
1 | import { sum, sub, mult, div } from './calculator.js' |
Da hora não? Isso é bem legal pra quando precisamos separar os nossos módulos, mas ainda assim queremos uma melhor organização, fazendo o import à partir de um único módulo central =)
É importante dizer também que esse sistema de módulos é estático. Significa que não podemos, por exemplo, importar arquivos à partir de um nome dinâmico.
Vou exemplificar: imagine que você tem os seguintes arquivos: mod1.js
, mod2.js
, mod3.js
.
Você poderia pensar: eu tenho um padrão no nome dos meus arquivos. Talvez eu possa fazer um loop e importá-los todos de uma só vez, não?
NÃO!
Não dá pra fazer algo assim, por exemplo:
1 | const arrayDe1A3 = Array.from({ length: 3 }, (_, i) => i + 1) |
Uma porque eu acabei de falar que o import é estático :P
Outra que item
seria o nome da variável atribuída para cada import.
E essa sintaxe não aceita nomes dinâmicos, como usado em ./mod${item}.js
. Ainda que usasse concatenação - algo assim: import mod1 from 'mod' + item
- não funciona.
Depois do from
, você só consegue usar aspas simples ou duplas, não dá pra usar template literals (as crases).
Por isso é importante que os seus import
estejam sempre no início do seu arquivo. Nunca faça algo do tipo:
1 | import sum from './sum' |
Apesar de funcionar, sempre faça todos os imports no início do arquivo, e só depois de todos os imports é que você pode usar os valores, ok? =)
A sintaxe que eu acabei de apresentar é estática, mas com certeza tem alguns momentos em que precisamos importar um ou outro módulo sob demanda, e de forma dinâmica, afinal, precisamos de liberdade e performance =)
Para isso, existe uma especificação que está para entrar também nos ES Modules, para conseguirmos importar módulos de forma dinâmica: é a função import()
.
Isso mesmo, uma função! Quando usamos import()
como função, passando via parâmetro o caminho do módulo que queremos importar, ela nos retornará uma Promise.
E dessa vez, o nome do módulo pode ser passado de forma dinâmica, sem problemas. E esse import você pode usar onde quiser, inclusive de forma condicional (dentro de um if
, por exemplo). :D
Vamos ver alguns exemplos.
Começaremos importando no main.js
o sum.js
:
1 | import('./sum.js').then((sum) => { |
Lembre-se que o import()
função sempre retorna uma Promise. Por isso precisamos usar o .then
para saber quando essa Promise resolveu com o valor do nosso módulo.
Outro ponto interessante a levar em consideração: após resolver o módulo, sempre será retornado um objeto com todos os valores de forma nomeada. Como lá no módulo sum.js
nós exportamos com default
a função, aqui nós tivemos que usar sum.default
para ter acesso à essa função =)
Como eu tinha dito anteriormente, com o import()
função é possível usar nomes dinâmicos. Vamos fazer um teste no main.js
:
1 | const module = 'sum' |
Veja que passamos o nome do módulo em uma variável module
, e concatenamos com o ./
no início e o .js
no final da chamada do nome do arquivo =)
Acho que com isso já dá pra se divertir bastante :D
Em algumas aplicações atuais, nós vemos import sem o .js
no final, e até import de outros tipos de arquivos diferentes de .js
, ou ainda a importação de módulos sem passar o ./
na frente, pra representar o caminho do arquivo.
É importante entender que os ES Modules servem apenas para arquivos JS, e não funcionam para importar imagens, markdown, svg, CSS, ou qualquer outro tipo de arquivo.
O que acontece nas aplicações usando libs e frameworks como React, Vue, Angular, etc., é que existe uma ferramenta por trás interceptando esses imports, e transformando esse código antes de ele, de fato, chegar no interpretador do navegador.
Por causa dessas configurações nessas ferramentas - como webpack, parcel, etc - é que conseguimos importar outros tipos de arquivos, mas isso não é o padrão dos ES Modules, ok? É importante estar ciente disso =)
Bom, o artigo ficou bastante extenso, mas acho que eu consegui cobrir, se não tudo, ao menos as partes mais importantes quando se trata de módulos nativos no JS.
Gostou do artigo? Esqueci de algo? Me deixe saber nos comentários, e compartilhe esse post para que mais pessoas aprendam a usar essa feature maravilhosa da nossa amada liguagem <3
Até o próximo artigo :D
]]>Apesar de ser uma feature bastante usada hoje em dia, algumas pessoas ainda não conhecem alguns segredos dessa nova sintaxe de funções em JavaScript.
Quer saber tudo o que as arrow functions são capazes de fazer? Vem comigo que eu te mostro =)
Pra quem ainda não teve contato com as arrow functions, Vamos começar entendendo a nova sintaxe.
1 | const sum = (a, b) => { |
À primeira vista pode parecer estranho, mas usando function
tradicional, podemos obter o mesmo resultado assim:
1 | const sum = function (a, b) { |
Acho que assim fica mais fácil de entender =)
A criação de uma arrow function consistem em 3 “passos”:
function
tradicional);=>
- responsável pelo nome “arrow” function;Perceba que a ideia é bastante similar à forma padrão de usar funções em JS, com a diferença que não usamos a palavra chave function
, e sim uma seta (=>
) entre os parênteses (argumentos) e as chaves (bloco que gera o corpo da função).
Perceba também que nós usamos a arrow function como uma expressão - no caso, atribuindo o resultado da criação da arrow function à uma variável.
Diferente das function
tradicionais, não existe uma forma literal de escrever uma arrow- function. Ela sempre deve ser usada como uma expressão.
Também não é possível nomear uma arrow function. Elas são sempre anônimas.
E para executar a função, nada muda:
1 | console.log(sum(1, 2)) // 3 |
Arrow functions permitem algumas variações, para deixar o código ainda mais enxuto. Vamos ver alguns exemplos:
1) Função que não tem nada no seu corpo além do retorno
Se a função vai apenas retornar algum valor, nós podemos omitir as chaves (corpo da função) e a palavra chave return
.
Ao invés de escrever:
1 | const sum = (a, b) => { |
Podemos apenas escrever:
1 | const sum = (a, b) => |
O resultado é o mesmo! E não há problema algum em quebrar linha após a “seta”, pois diferente do return
, aqui não se aplica o ASI (Automatic Semicolon Insertion, ou Inserção Automática do Ponto-e-Vírgula).
Pra deixa ainda mais simples, podemos colocar tudo em uma linha só:
1 | const sum = (a, b) => a + b |
Para esse exemplo, o retorno da função fica implícito. Tudo o que está sendo colocado logo após a “seta” será considerado o retorno da função =)
ATENÇÃO
No exemplo acima, nós estamos retornando apenas a soma de dois números. Essa sintaxe é válida para qualquer tipo de dado sendo retornado exceto para objetos.
Por que objetos são uma exceção?
Para criarmos objetos literais, nós usamos as chaves. Logo, ao retornar um objeto, a criação da função teria um sentido ambíguo: as chaves são para abrir o bloco do corpo da função, ou é um retorno de objeto literal?
Para esse caso específico, sempre que quisermos retornar um objeto de forma implítica usando uma arrow function, nós precisamos envolver esse objeto em parênteses.
Vamos ver alguns exemplos.
Para retornar um objeto de forma explícita (com o corpo da função, e o return
), nós faríamos assim:
1 | const person = () => { |
Agora, se retornar de forma implícita, teríamos que fazer da seguinte forma:
1 | const person = () => ({ |
Veja que a diferença foi apenas envolver o objeto em parênteses.
Em JS, os parênteses servem apenas para dar prioridade na execução de alguma expressão, então basicamente o que acontece é que toda a expressão dentro dos parênteses é que será retornada pela função person
, assim que ela for executada. Fique muito atenta(o) a isso =)
2) Funções com apenas um argumento
Quando a função recebe apenas um argumento, podemos omitir os parênteses.
Então essa função:
1 | const plusOne = (value) => { |
Poderia ser escrita com retorno implícito:
1 | const plusOne = (value) => value + 1 |
E como só um argumento é esperado pela função, podemos omitir os parênteses:
1 | const plusOne = value => value + 1 |
Importante: Não é obrigatório omitir os parênteses. Eu, particularmente, prefiro deixar, pois acho que torna a leitura mais fácil. Mas é uma possibilidade. Com o tempo pode ser que você se acostume, então use da forma que achar melhor. Apenas siga um padrão do início ao fim do seu projeto =)
Essa é uma ótima pergunta, e muitas pessoas não a fazem, apenas substituem e vida que segue.
Mas não é bem assim!
As arrow functions não foram criadas apenas para ser uma sintaxe mais exuta e substituir as function
tradicionais. Elas têm alguns detalhes importantes que é preciso levar em consideração antes de saber se devemos ou não só sair substituindo tudo.
Funções, ao serem criadas, geram um escopo próprio. E esse escopo só existe enquanto a função existe.
No JavaScript, temos a palavra chave this
, que é bastante polêmica, pois na maioria das linguagens, essa palavra chave faz referência à um objeto criado à partir de um construtor, quando usada dentro de uma classe; ou quando usada dentro de um método de objeto, ela faz referência à esse objeto.
Mas isso nem sempre é verdade: é possível “injetar” o this
, dependendo da forma que você executa uma função.
Se você não está por dentro desse assunto, recomendo esse post que escrevi a um tempo atrás. Leia o artigo, e depois volte aqui que tudo vai fazer sentido. Pode ir. Eu espero =)
…
Já leu? Ótimo, vamos continuar! :D
O importante aqui é saber que, diferente das function
tradicionais, você não consegue “injetar” o this
em arrow functions. Isso mesmo!
Se o this
for usado dentro de uma arrow function, esse this
vai fazer referência ao objeto que ele já era referência no momento da criação da arrow function.
Ficou complicado de entender? Vamos ver alguns exemplos!
1 | function getThis () { |
Se executarmos a função getThis
, criada no escopo global, em algum navegador, vamos ter como retorno o objeto window
, pois quando uma função que têm o this
dentro não for uma função construtora, nem um método de objeto, o this
vai ser referência ao objeto global.
Mas se usarmos 'use strict'
, como você já deve saber, essa diretiva faz com que o this
global seja undefined
. Logo, esse é o resultado quando estamos executando nossa função em strict mode:
1 |
|
E a questão interessante ao usar arrow function é exatamente essa: nos dois casos, a arrow function vai retornar o objeto window
, por causa do escopo léxico. Lembre-se: o this
dentro de uma arrow function sempre vai ser referência ao objeto que ele já era referência no momento da criação da arrow function. (nesse caso, o objeto window
):
1 | const getThis = () => { |
E usando 'use strict'
:
1 |
|
Uma outra situação bastante comum é ao usar um método de objeto:
1 | const counter = { |
Agora, ao tentar usar uma arrow function como método do objeto, olhe o que acontece:
1 | const counter = { |
Veja que o valor retornado pelo método increment
agora é NaN
. E isso acontece porque o objeto está sendo criado no escopo global, logo, o método increment
também está sendo criado nesse escopo, já que o objeto counter
não está dentro de nenhuma função.
E outra coisa importante a notar é o valor de value
:
1 | console.log(counter.value) // 0 |
Veja que o valor de value
não foi alterado! Logo, o value
que está sendo exibido é um valor que foi criado no objeto global window
, e para esse value
é que foi atribuído o valor NaN
:
1 | console.log(window.value) // NaN |
Com isso, fica claro que o this
dentro de uma arrow function vai ser sempre referência ao objeto ao qual ele já era, no momento em que a função foi criada.
Essa regra vale para qualquer chamada de função que precise usar o this
dentro, então preste atenção, ok? =)
Só mais um exemplo, apenas para complementar: eventos!
O método addEventListener
sempre injeta o this
dentro da função usada como listener desse evento.
Veja esse exemplo:
1 | const $input = document.querySelector('input[type="text"]') |
No exemplo acima, estou assumindo que o código vai ser executado em um ambiente onde existe um campo input
do tipo text
.
Ao começar a digitar algo dentro desse campo, podemos ver o valor do campo sendo exibido no console
, pois o this
dentro da função passada como listener do evento é injetado pelo addEventListener
, fazendo referência ao objeto do DOM ao qual o evento foi atrelado.
Agora tente usar uma arrow function no lugar da function
tradicional:
1 | const $input = document.querySelector('input[type="text"]') |
Nesse caso, veja que o valor exibido no cosole é sempre undefined
(a menos que exista um objeto no escopo em que a função foi criada, e esse objeto tenha uma propriedade value
).
Com tudo isso em mente, podemos considerar alguns pontos:
this
, você pode substituí-la por arrow function sem problemas;this
. No caso do exemplo do evento, toda função listener de um evento recebe um objeto de evento, com uma propriedade target
, que faz referência ao elemento que recebeu o evento. Use esse objeto se precisar manipular ou fazer qulquer coisa com o elemento que disparou o evento, ao invés de usar this
. Dessa forma você evita os problemas vistos acima.Parabéns! Agora você já sabe como usar corretamente as arrow functions sem problemas!
Já conhecia essa nova sintaxe de funções? Ficou com alguma dúvida? Faltou falar alguma coisa no post? Gostaria de sugerir um assunto para os próximos posts?
Deixe nos comentários! Ficarei muito feliz em ler e te responder :D
Até a próxima :D
]]>Quer saber como foi meu ano? Vem comigo =)
Acabei não escrevendo uma retrospectiva de 2017 para 2018, mas posso dizer que 2017 foi um ano sensacional pra mim. Muitas coisas boas aconteceram, e cada ano tem sido melhor. Mas o que posso dizer que marcou meu 2017 foi que comecei a cuidar melhor da minha saúde.
Desde sempre que eu tento começar a fazer exercícios físicos em casa ou na rua: caminhar, correr, etc., mas nunca conseguia seguir em frente. Depois de alguns dias caminhando / correndo, começava com um dia chuvoso ou muito frio e eu já parava. Não conseguia colocar foco.
Cheguei ao auge do meu peso com 95kg, de 2015 para 2016.
Em 2016 eu procurei alternativas para me alimentar melhor. Consegui perder 10kg, mas depois eu encontrei novamente 5kg desses 10, e travei nos 90kg.
Eu não entendia de fato como funcionava se alimentar de forma saudável, e principalmente não entendia como o corpo funcionava para que eu pudesse perder peso. Não estava feliz com meu corpo e com meu peso atual, e não somente por conta de estética, por causa da saúde mesmo: sentia muita preguiça, mal conseguia andar por algumas horas que já estava todo dolorido, postura toda errada - lombar virada num “U”, pescoço de dinossauro, sabe? haha :P
É engraçado lembrar hoje, mas é ruim viver assim. Eu tinha muitas dores na coluna - principalmente na região do pescoço e lombar.
Então em 12 de Julho de 2017 eu resolvi me matricular na academia e começar a malhar. Algumas pessoas me falaram que fazer musculação era chato, que eu não iria gostar, que iria desistir, mas mesmo assim resolvi tentar.
E não é que tomei gosto pela coisa! Gostei tanto que comecei a estudar sobre como executar corretamente os exercícios - obrigado YouTube 💙 -, sobre alimentação saudável, e acabei aprendendo o segredo para emagrecer: MATEMÁTICA! - Se estiver interessada(o) no assunto, deixe nos comentários que eu faço um post especificamente falando sobre isso, com detalhes! Mas você pode pesquisar no YouTube por “déficit calórico”, “dieta flexível”, que são assuntos que vão ajudar bastante a entender a ideia!
Resultado: nesses 6 meses finais de 2017 consegui perder de 4kg a 5kg, e cheguei nos 80kg, fechando o ano com 17% de BF*! Foi uma grande vitória =)
- BF = Body Fat, ou Porcentagem de Gordura Corporal.
Agora começando com 2018: ainda falando em saúde, só pra finalizar, comecei o ano com 17% de BF e estou finalizando com 12% ~ 13%!
A meta é chegar a 5% ~ 7% até final de 2019 :D
Comecei também a fazer Muay Thai no final de Novembro, pois quase não estava fazendo exercícios cardiovasculares. De vez em quando eu jogo futebol, mas ultimamente tem sido bem de vez em quando mesmo haha!
A única arte marcial que eu tinha praticado na vida foi Capoeira, quando eu era moleque (devia ter entre 9 a 11 anos), e nunca mais fiz nada.
Por enquanto o Muay Thai tem me surpreendido, estou gostando bastante!
Falando do lado profissional: participei de muitos eventos esse ano, palestrei em alguns, mas o mais sensacional de tudo é sempre conhecer pessoas novas!
Conhecer pessoas que eu admiro, conhecer meus alunos pessoalmente, ouvir relatos de que o conteúdo que eu tenho compartilhado tem sido útil para eles conseguirem emprego - alguns até fora do país - é o que me deixa mais feliz e animado para os novos projetos que virão em 2019!
E o objetivo para 2019 é: palestrar menos e participar mais!
Dar palestras tem sim o seu valor, mas o networking e o engajamento das pessoas é o que fortalece nossa comunidade =)
Tentei uma nova experiência em 2018, que foram os cursos presenciais!
Fizemos duas edições em Joinville / SC do curso React Ninja. Curti bastante esse formato de curso. Em 2019 teremos novidades com relação a isso :D
Enfim, rolou muita coisa legal em 2018:
Viajei, conheci novas cidades, visitei parques nas cidades, andei de kart, cuidei da minha saúde, mudei de casa, assei carne (muito importante haha), participei de meetups e eventos, postei memes no Facebook e conteúdo sério e relevante no Twitter, aprendi receitas fitness, revi amigas e amigos, conheci novas(os), tive tempo para a família, para a minha esposa, fizemos trilha, fomos à praia - conheci a praia de Daniela em Floripa, que lugar sensacional! Recomendo se você ainda não conhece :D - ufa!
Foi muita coisa! Mas ficou muita coisa ainda por fazer.
Alguns planos / projetos eu não consegui terminar (alguns nem começar).
Mas isso vai ficar de lição para 2019: planejar muito bem os próximos passos, medir, testar, me organizar, estudar muito, pois ainda tenho muito a aprender, e dar meu melhor ajudar a comunidade, principalmente àqueles(as) mais necessitados e com menos condições financeiras!
Tenho certeza que 2019 vai ser um ano excelente!
Obrigado por ler até aqui, e espero que o seu 2019 seja lindo! Se 2018 não foi tudo aquilo, 2019 com certeza será! :D
Cuide de você. Cuide da sua saúde, cuide da sua mente. Estude, mas também descanse. Se organize, e seja feliz =)
PS.: E vou começar a tirar as teias desse blog também, pois tem muito conteúdo legal que precisa ser compartilhado!
Que venha 2019! :D
]]>Vamos conversar um pouco sobre objetos em JavaScript? Vem comigo então =)
Em todas as linguagens (ao menos todas que eu conheço :P), existem duas formas de você passar valores:
Quando eu digo “passar valores”, eu estou me referindo à:
Por exemplo:
1 | var string = 'uma string qualquer' |
No exemplo acima, eu atribuí o valor 'uma string qualquer'
à variável string
,
e também atribuí um novo objeto à variável objeto
.
Até aqui, nada de anormal. Mas existem alguns conceitos por detrás dessas atribuições:
O valor 'uma string qualquer'
foi passado para a variável string
por valor.
Já o objeto passado para a variável objeto
, foi passado por referência.
E para explicar a diferença entre essas duas formas de passagem passagem de valor, precisamos entender algumas coisas da linguagem JavaScript.
JavaScript tem alguns tipos de dados que podemos separar em:
Os tipos primitivos da linguagem são: number
, string
, boolean
, null
e undefined
.
(Symbol
também é um tipo primitivo, que veio no novo pacotinho do ES6/2015).
Todos os outros tipos de dados em JavaScript são do tipo object
:
Tá, e daí? O que isso tem a ver com o post?
Aí que está: tem tudo a ver! E agora que você conhece quem são os tipos primitivos e quem são os objetos,
eu já posso te dizer que:
Guarde bem essa informação, ela vai ser útil logo, logo =)
Quando é feita uma passagem de tipos por valor, significa que o valor foi copiado.
Vamos aos exemplos:
1 | var a = 10 |
No exemplo, eu atribuí o valor 10
para as variáveis a
e b
. O 10
- tipo primitivo number
- foi passado por valor, ou seja, ele foi copiado
para as variáveis a
e b
. Ao tentar comparar seus valores, vemos que o resultado é true
, ou seja, é o mesmo valor.
Agora vamos ver o que acontece se tentarmos fazer o mesmo com um objeto:
1 | var a = {} |
O resultado da comparação deu false
? Por quê?
Lembra que eu falei ali em cima que objetos são passados por referência? Então, vou explicar o que está acontecendo.
A cada vez que você cria um novo objeto, o JavaScript reserva um espaço para esse objeto na memória.
Sim, alguns bytes da memória do seu computador são reservados para guardar as informações desse novo objeto criado.
Usar as chaves {}
é o mesmo que new Object()
, só que mais rápido (performance não vem ao caso agora).
O que eu quero dizer é que, sempre que você faz isso no seu código: {}
; você está criando um novo objeto.
E a cada novo objeto criado, um novo espaço na memória é ocupado.
Huummmm, acho que estou sacando xD
É isso mesmo! O resultado do exemplo acima deu false
, porque são dois objetos diferentes! Cada um foi criado em um espaço
diferente da memória!
O que acontece quando você cria um novo objeto e o atribui à uma variável, é que, a partir desse momento, essa variável
passa a apontar para o objeto criado. É o que chamamos de ponteiro.
Logo, se fizermos:
1 | var a = {} |
Ou seja: a operação acima não copiou o valor do objeto que foi atribuido à a
para a variável b
;
o que aconteceu foi que, agora as duas variáveis apontam para o mesmo objeto.
a
;b
, e atribuímos o valor de a
para b
;a
é um objeto, o valor não foi copiado, mas sim passado por referência;Deu pra entender?
Tá, entendi, mas por que isso é tão importante assim?
Pelo seguinte motivo: lembra que eu falei ali em cima que objetos são mutáveis e tipos primitivos não?
Eis a treta:
1 | var a = {} |
Olha só o que aconteceu: como objetos são mutáveis, significa que, mesmo após ter criado o objeto,
você pode modificá-lo, adicionando ou removendo propriedades e métodos.
No exemplo acima, eu fiz o seguinte:
a
;b
, e atribuí o objeto passado para a
, por referência;a
, eu criei uma nova propriedade chamada prop
, com a string 'any value'
;prop
da variável b
, vemos que lá está o valor que foi atribuído à variável a
.E isso acontece simplesmente porque o objeto atribuido para a
e b
é o mesmo!
Esse comportamento acontece com qualquer tipo de dado que seja um objeto. Olhe, por exemplo, com uma função:
1 | var a = function () { |
Veja que, como funções também são objetos, você também pode adicionar novas propriedades e métodos.
No exemplo acima, eu criei uma função, e adicionei uma propriedade prop
. Perceba que a função é mutável,
ou seja, eu consigo criar novas propriedades mesmo depois que a função já foi criada, e veja como ela é
passada por referência para b
, pois eu fiz as modificações na variável a
, e elas foram refletidas
também na variável b
.
Acho que ficou claro até aqui, certo? Se não ficou, só comenta aí no post que a gente discute sobre o assunto =)
Por agora, vamos prosseguir =)
Você pode passar referências de objetos não somente usando atribuição, mas também como argumentos para uma função.
Veja o exemplo:
1 | function updateObject (object) { |
Olha que loucura o que aconteceu aqui em cima:
updateObject
, que recebe via argumento um objeto qualquer, e adiciona uma nova popriedade newProp
nesse objeto, com o valor 'vixx, mudou mesmo!'
;object
;updateObject
, passando por parâmetro o valor da variável object
. Lembre-se que objetos são passados por referência;object
, e olhe o que aconteceu: uma nova propriedade newProp
foi criada e o valor 'vixx, mudou mesmo!'
atribuído!Nossa que legal! Então quer dizer que eu posso passar qualquer objeto para uma função, e modificá-lo lá dentro que, quando eu pegar meu objeto novamente, ele vai estar atualizado?
Sim, você pode. Mas NÃO DEVE!. Isso se chama efeito colateral (ou side effect), e isso é assunto para um outro post (que vai sair em breve xD).
Por enquanto, você só precisa saber que NÃO DEVE fazer isso, de forma alguma.
Entender essa forma de passagem de valores em JavaScript é muito importante se você quer aprender a linguagem de verdade.
Entendendo isso, você consegue, no mínimo, duas coisas:
Imutabilidade é o assunto do nosso próximo post, aguarde!
Enquanto isso: deu pra entender o assunto do post? Ficou alguma dúvida? Gostaria de acrescentar algo? Comenta aí embaixo e até o próximo! :D
]]>2016 foi um ano conturbado para o nosso país (e para boa parte do mundo), mas gostaria de compartilhar nesse post as coisas boas que aconteceram comigo nesse ano =)
Bom, não lembro exatamente a ordem que muitas coisas aconteceram, mas posso dizer que foi um ano sensacional! Aprendi, criei, participei. Só tenho a agradecer por tudo o que aconteceu <3
Logo no início do ano, comecei a estudar sobre Redux. Já estava na pegada do React a algum tempo, e estava ouvindo algumas pessoas falarem sobre Redux, até que eu fui ver qual era a dele, até me apaixonar pela simplicidade de como ele faz as coisas acontecerem =)
No Zimp, comecei a escrever uma lib em Node que consumiria o webservice da Magazine Luiza - que responde em XML - para utilizar com Node, com a resposta em JSON. Finalizei a primeira versão no dia 27/01.
Para treinar minha escrita em inglês, criei o Daily JS Tips no Medium. Escrevi apenas alguns poucos posts, mas já foi válido pra começar a fazer algo em inglês.
Resolvi também sair da vida sedentária, e começar a caminhar diariamente, ao menos 1h por dia. Obviamente não consegui fazer todos os dias, até porque Joinville é conhecida carinhosamente como “Chuville”, e deixando de caminhar alguns dias por conta da chuva, já começa a bater um desânimo. Mas em vista dos anos anteriores em que eu não fazia praticamente nada de exercícios, esse foi um ano, digamos, interessante. Comecei o ano com 89kg e terminei com 84kg. Já foi um bom começo xD
Diminuí drasticamente também o consumo de carboidratos e açúcar, sem contar no corte total de café (que eu quase não tomava mesmo) e leite.
Em fevereiro, dei minha primeira (e única) palestra do ano no Femug Joinville, sobre Redux.
Em março, lancei o HMH. Até escrevi um post sobre ele - o único post de 2016 aqui no blog :| - prometo melhorar isso em 2017 =)
Antes desse projeto, eu tinha alguns padrões próprios para lint de código, e você deve saber o quanto isso é um processo chato de se manter e configurar. Por isso eu passei a utilizar o standard, que é um padrão pré-definido para escrita de código em JS usado em várias libs open source. Gostei e adotei para todos os meus projetos a partir de então.
Em algum momento do ano, o Fe teve uma ideia de criar um fórum de discussão no GitHub, para que as discussões sobre desenvolvimento saíssem do ambiente fechado do Facebook, e fossem para um ambiente que fizesse mais sentido. Foi - e está sendo - um sucesso. Muitos devs engajaram na ideia, e muitas discussões de alto nível têm surgido por lá! Se você ainda não conhece o fórum, não perde tempo e vai lá dar uma olhada (tá, termina de ler o post antes :P)
Em maio, tive a oportunidade de ajudar a organizar o SC Dev Summit. Foi um evento sensacional, de dois dias, sobre PHP (no primeiro dia) e Frontend (no segundo dia).
Criei uma lib bastante simples, chamada iscpf, que apenas testa se um número (ou string) entrado é um CPF válido.
Também tive a oportunidade de começar a desenvolver o curso React Ninja, que ainda está em andamento, mas já tenho recebido feedbacks sensacionais sobre o curso!
Comecei um canal no Youtube para compartilhar conhecimento. Inicialmente, liberei uma parte do primeiro módulo do curso React Ninja, mas a ideia é que várias dicas sejam compartilhadas nesse canal. Aproveita e já se inscreve lá =)
E com o trabalho pesado de quase um ano inteiro, lançamos o Zimp, empresa da qual sou sócio e tenho muito orgulho de todos que trabalharam nesse projeto e fizeram - e ainda estão fazendo - acontecer!
Tivemos até uma festa de lançamento do projeto na iFly em São Paulo! Foi animal! :D
Para sair um pouco mais da zona de conforto, comecei a estudar Haskell. Tenho curtido muito estudar uma linguagem puramente funcional! Criei uma organização no GitHub onde eu fico fazendo testes de várias coisas, e não poderia faltar o playground para o Haskell =)
Por conta do curso React Ninja, eu comecei a escrever alguns componentes para usar como exemplo no curso, mostrando desde a parte de testes unitários, testes visuais, que garantem que cada estado do componente funciona corretamente, até o componente em si. E um que eu acabei lançando open source foi o react-trianglify, um wrapper component React para usar com o trianglify.
Já tenho usado o Vim como meu editor de código padrão desde o ano passado, e esse ano eu conheci o tmux - apresentado pelo Dan Jesus -, e comecei a utilizá-lo a partir do projeto Byobu, que tem alguns atalhos mais “fáceis” de usar, para que você possa começar a se acostumar com o Tmux. Achei o projeto bem interessante, e só tenho elogios até agora. Em breve pretendo gravar um vídeo mostrando como ele funciona =)
Um outro projetinho simples que eu fiz esse ano também foi o @fdaciuk/is, que é basicamente uma lib que facilita checar tipos em JavaScript.
E finalmente, no final de 2016, eu saí de Joinville e me mudei para Curitiba. Agora estou morando na Rússia brasileira! Pretendo colaborar e aprender muito com as comunidades de desenvolvimento por aqui. 2017 promete! :D
Claro que muito mais coisas aconteceram, mas esses são os acontecimentos que eu lembro. Deixei um lembrete diário no meu Google Keep para que eu anote tudo o que acontecerá comigo. Assim, quando for fazer a retrospectiva de 2017, terei informações mais palpáveis.
Um ponto negativo desse ano foi que eu não pude participar de muitos eventos. Apenas marquei presença na BrazilJS, pois o ingresso já estava comprado desde o ano passado (e inclusive já garanti o ingresso da próxima edição, recomendo que você faça o mesmo xD), e no Front in Floripa, que foi sensacional!
Enfim, a cada dia das nossas vidas nós precisamos fazer escolhas, escolher o que fazer, o que priorizar. Já vi muitos amigos falando - e eu também já falei isso por muitas vezes - que gostaria que o dia tivesse mais de 24h para poder colaborar mais com projetos open source, escrever mais artigos, fazer mais…
Mas a realidade é que todos temos 24h por dia, não tem como ter mais do que isso. O que faz a diferença não é o que você precisar de mais tempo para fazer mais coisas, mas sim o que você prioriza em 24h do seu dia.
Em 2016 precisei dar mais prioridade para projetos internos (o Zimp, meus cursos, etc), por isso não escrevi tanto aqui no blog, mas eu sabia que isso poderia acontecer.
Já em 2017, vou separar um tempo da minha semana para escrever ao menos 1 post por semana. E tentar escrever ao menos 1 em português e 1 em inglês. E eu sei que para isso acontecer, só vai depender de mim mesmo, organizando meu tempo, e priorizando as tarefas corretas para que a procrastinação não tome conta do meu tempo, e eu chegue ao final de 2017 sem ter feito nada do que eu queria ter feito.
Sim, é difícil. É preciso foco, determinação e organização. Mas a cada ano nós podemos aprender mais, e sermos melhores que o ano que passou.
Se eu pudesse deixar uma dica para esse ano, seria: repense suas prioridades. Estude JS puro. Estude inglês.
E que você tenha um ótimo 2017! Que nós possamos focar nas nossas prioridades, e realizar o que precisamos realizar esse ano, para chegar em dezembro e sermos surpreendidos positivamente com o que foi feito! :D
Ah, se você também escreveu sua retrospectiva, comenta ae que eu vou ficar muito feliz em ler :D
]]>hmh - How many hours?
foi uma ferramenta que eu resolvi criar, pois precisava fazer alguns cálculos com horas, mas não achei nada que eu pudesse usar, assim, “a toque de caixa”.
O projeto está no GitHub, nesse link. Quer saber como foi desenvolvido, desde a ideia, os testes até a publicação? Então vem comigo que eu te mostro :D
O que me motivou a criar a ferramenta foi: eu precisava calcular, de forma fácil, as horas que eu estava trabalhando em algum projeto. Eu deixava o tempo anotado em algum lugar - pois nem sempre a tarefa era finalizada de uma vez, às vezes era necessário começar outra sem terminar a primeira - e depois eu precisava calcular o tempo total que eu gastei nessa tarefa.
Isso ajuda pra que você saiba exatamente quanto tempo gasta para fazer cada coisa, ficando mais fácil “orçar” quando tempo você irá precisar para fazer algo parecido futuramente.
O que eu precisava, naquele momento, era de:
Com essas informações, ficaria mais simples para que eu pudesse planejar as próximas sprints com uma precisão maior.
Antes de fazer a lib, pesquisei para ver se encontraria algo já pronto que eu pudesse utilizar, e talvez até colaborar no desenvolvimento. Enfim, não achei nada que resolvesse o que eu precisava de forma fácil (inclusive, se você souber de alguma ferramenta que faça isso, deixe nos comentários que eu preciso saber :D).
Assim surgiu o hmh
! :grin:
O nome é uma abreviação para How many hours?
, pois eu precisava saber quantas horas eu gastei, e quantas horas eu tinha disponível. Inicialmente eu pensei em fazer uma [CLI], pois como eu fico o dia todo com o terminal aberto, seria bem simples simplesmente digitar as horas ali e ver o resultado, passando alguns parâmetros para a CLI.
Mas depois eu percebi que isso também poderia ser usado para outras coisas, então faria mais sentido criar uma lib com toda a lógica que eu precisava, e então criar a CLI, consumindo essa lib.
Você pode ver o código da lib aqui e da CLI aqui.
O uso da lib é bastante simples: para instalar, execute:
1 | npm i --save hmh |
Então é só usar! A lib tem 4 métodos: sum
, sub
, diff
e div
, que servem para os propósitos que citei acima.
O retorno desses métodos vai ser sempre um horário, um valor em horas
e minutos
.
Exemplo:
Imagine que você trabalhou um tempo em um projeto, e quer somar todo esse tempo. Só precisa passar o tempo por parâmetro para o método sum
:
1 | console.log(hmh.sum('1h 20m 15m 2h 40m')) // '4h 15m' |
E o resultado é a string '4h 15m'
. Se você quiser ver o resultado somente em minutos, passe minutes
como segundo parâmetro:
1 | console.log(hmh.sum('1h 20m 15m 2h 40m', 'minutes')) // '255m' |
O método sub
funciona da mesma forma, mas ele subtrai
valores.
O método diff
mostra a diferença entre duas horas. Imagine que você começou a trabalhar em uma tarefa às 8h15m da manhã e parou às 11h30m. Quanto tempo se passou?
1 | console.log(hmh.diff('8h 15m', '11h 30m')) // '3h 15m' |
E ele mostra o resultado! Para ver em minutos, mesmo esquema dos outros métodos: como último parâmetro, passe minutes
:
1 | console.log(hmh.diff('8h 15m', '11h 30m', 'minutes')) // '195m' |
E por último, o método div
. Digamos que você trabalha em um projeto, e vende horas de manutenção. Você ainda tem 7h
para mexer nesse projeto nos próximos 4 dias. Quantas horas por dia você pode trabalhar para completar as 7h
? Simples: é só dividir 7h
em 4 dias:
1 | console.log(hmh.div('7h', 4)) // '1h 45m' |
Você pode trabalhar 1h 45m
por dia =)
Mas isso seria mais útil se eu pudesse usar isso direto no terminal. Por isso eu fiz o hmh-cli
, que também é bastante simples de usar!
Primeiro de tudo, instale globalmente:
1 | [sudo] npm i -g hmh-cli |
Os métodos são os mesmos da lib, que você pode passar por parâmetro, como --sum
, --sub
, --diff
e --div
.
Espaços em branco a mais ou a menos são ignorados, tanto na lib quanto na CLI. Mas, no caso dos métodos --diff
e --div
da CLI, que precisam ter 2 parâmetros, você pode fazer de duas formas:
1 | $ hmh --diff 8h15m 11h30m |
Juntando as horas e minutos, e separando os parâmetros por espaço. Ou ainda, usando aspas:1
$ hmh --diff "8h 15m" "11h 30m"
O resultado é o mesmo =)
Para o método de divisão, mesmo esquema:
1 | $ hmh --div 7h20m 4 |
ou
1 | $ hmh --div "7h 20m" 4 |
E para ter a saída em minutos, use o parâmetro -o minutes
ou --output minutes
:
1 | $ hmh --div "7h 20m" 4 -o minutes |
Nos próximos posts, vou abordar toda a etapa de desenvolvimento da lib, testes, integração com CI e a criação da CLI.
Fique ligado! :D
Gostou da ferramenta? Vai ser útil pra você? Já existe algo que faça isso? Comente! :D
]]>Vi muitas pessoas comemorando, outras reclamando de como foi esse ano de 2015 para elas. Quer saber como foi o meu? Vem comigo que eu te conto! :D
Em resumo: 2015 pra mim foi sensacional! Em praticamente todos os sentidos!
Você deve lembrar do desafio que eu lancei publicamente a mim mesmo - e a quem quisesse acompanhar - no último dia do ano de 2014, não?
Foi o de escrever um post por dia, e compartilhar usando a hashtag #1postperday. Isso mesmo! E foi um dsafio e tanto :D
O que mais me deixou feliz com isso, foi ver a quantidade de devs que também se animaram a criar seus blogs e escrever mais conteúdo de qualidade em pt-br, uma vez por dia, uma vez por semana, uma vez por mês, ou só um post no ano. Não importa. O importante é que a meta principal de alcançar muitas pessoas foi cumprida :D
Recebi muitos feedbacks positivos por causa do #1postperday. O Willian Justen compartilhou conteúdos sensacionais no seu blog sobre vários assuntos; o Rômulo Zoch deu dicas animais de NodeJS, Ionic, entre várias outras coisas; Fernando Moreira, Raphael Fabeni, e vários outros caras feras - que eu não conseguiria citar todos aqui - compartilhando o seu conhecimento com a comunidade, mostrando que temos ótimos profissionais aqui no Brasil \o/
O Felipe Fialho ainda criou um repositório para que qualquer pessoa pudesse colaborar com assuntos para os próximos posts, que inclusive você pode continuar abrindo issues por lá, que os posts continuarão a ser escritos :D
Enfim, obviamente que eu não consegui escrever o ano todo. Consegui fazer, em média 70 posts, depois começou a ficar complicado :P
Mas fiquei muito feliz mesmo com o resultado positivo que isso trouxe para toda a comunidade de desenvolvimento web brasileira :D
E esse pequeno desafio trouxe frutos muito legais:
A partir dos posts, para ter ideias de conteúdo, acabei criando alguns projetos open source bastante simples, para exemplificar nos posts, mas que foram tomando corpo e, pelo que parece, foi e está sendo útil para várias pessoas :)
Um deles é o ajax.js, que é uma abstração do objeto XMLHttpRequest do Javascript, usando promises em uma implementação em JS puro, sem o uso de bibliotecas. Foi um aprendizado gigante pra mim poder fazer isso, com certeza isso melhorou muito minhas skills em JS :)
E tudo começou com esse post, que é um primeiro de toda uma série, mostrando como a lib foi montada, inclusive mostrando a aplicação de testes :)
Um outro projeto bastante simples foi o hoost, uma ferramenta de linha de comando que adiciona, edita, remove e lista os virtual hosts no arquivo /etc/hosts
. Apesar de simples, várias pessoas tem usado, e algumas colaborado com o projeto. Inclusive, a implementação para que o hoost
funcionasse no Windows veio de um Pull Request enviado por um gringo :)
E ainda tem coisas a fazer nesse projeto! Já tenho algumas ideias legais de implementação para adicionar as entradas automaticamente nos arquivos de configuração do Apache e nginx xD
Enfim, foi um ano de muito aprendizado, pois, para escrever sobre algum assunto, eu precisava pesquisar para não escrever besteiras, e com isso eu pude aprender muito. Recomendo a todos que estejam dispostos a aprender mais sobre qualquer coisa, e que ainda não tem blog, que crie um, e comece a escrever! Existem algumas ferramentas que facilitam muito a criação de um blog, como o Hexo por exemplo. Escrevi sobre ele aqui. Veja como é fácil ter um blog rodando em apenas 3 minutos :D
Ainda por causa do #1postperday, eu consegui colocar em prática algo que eu já queria fazer há muito tempo: criar um curso de Javascript que ensinasse desde a pessoa que nunca tinha programado na vida, como aqueles que já estavam estudando JS a algum tempo, mas não conseguiam evoluir no aprendizado.
Assim nasceu o curso Javascript Ninja!
E eu tenho recebido ótimos feedbacks com relação ao curso. Pelo visto tem funcionado xD
Percebi também a necessidade que algumas pessoas tinham em aprender a utilizar Git e GitHub para uso no dia a dia do trabalho, ainda que para coisas simples. Pensando nisso, criei também o curso Git e GitHub Ninja, para mostrar o básico de como utilizar o Git, e como aproveitar o melhor do GitHub para trabalhar em equipe :D
Ministrar cursos era algo que eu já queria fazer a um tempo, mas nunca tinha tirado a ideia do papel. Nesse ano de 2015 eu resolvi me mexer e fazer acontecer! E tem dado ótimos resultados :D
Outro ponto forte de 2015 foram os eventos que eu tive o prazer de participar. Tivemos alguns meetups locais aqui em Joinville, sobre WordPress, frontend, e desenvolvimento de software em geral.
Pude participar pela primeira vez da BrazilJS, e foi uma das melhores experiências que eu tive em eventos! Com certeza, em 2016 estarei lá novamente :D
Não só o evento foi sensacional, como também conhecer pessoalmente pessoas que eu só conversava pelos grupos do Facebook, chats e fóruns.
Posso dizer que eventos foram o ponto alto do meu ano: WordCamps, Frontins; o contato e networking com pessoas apaixonadas pelo que fazem me fizeram sentir em casa em cada um deles.
Tive oportunidade de palestrar em alguns também :D
Foi muito bom conhecer pessoalmente caras como Willian Justen, Caio Ribeiro, Jaidson Gomes, Ju Gonçalves, Eduardo Matos, Rafael Amorim, Fabeni, Henrique Silvério, Felquis, Pedro Polisenso, entre tantos outros; sem contar as pessoas que eu já conhecia e pude rever, como: Filipe Moura, Jean Emer, Talita Pagani, Diogo Moretti, Lucas e a galera do WP: Rafael Funchal, Claudio Sanches, Deblyn, Valério Souza, Guga Alves; conheci também pessoalmente alguns dos alunos que fazem meus cursos, como o Anderson Nascimento, o Matheus Martins, o Willian Santos; entre tantos outros que eu poderia ficar dois dias escrevendo, e ainda assim não conseguiria lembrar de todos aqui para citar, mas que foi um prazer enorme conhecê-los e revê-los :D
Comecei o ano trabalhando como Frontend Engineer na ContaAzul, onde pude aprender muito mesmo, e crescer tanto pessoalmente, como profissionalmente. Lá eu vi realmente que valores são levados a sério, vi pessoas comprometidas trabalhando juntas por um único ideal de fazer o cliente feliz! Lá dentro eu tive aprendizado gigantesco!
Só saí de lá quando recebi uma oportunidade de trabalhar com algo que estava alinhado profissionalmente com os meus objetivos pessoais. Foi quando o Luan Muniz me apresentou o Zimp, onde estou trabalhando hoje como Full Stack Developer.
E como o trabalho é remoto, isso acaba meio que “obrigando” a todos do time a terem uma comunicação frequente, para manter o alinhamento das entregas e tudo sair o mais próximo possível do esperado, o que faz com que todos saibam exatamente o que está acontecendo em todas as áreas da empresa, unindo forças para que o produto seja o melhor possível em todos os sentidos.
Fiquem ligados que, para 2016, estamos preparando algo animal para o Zimp :D
Enfim, 2015 foi animal! E com certeza 2016 será 1000x melhor! Obrigado a todos que fizeram parte desse ano! Ano que vem espero conhecer muito mais pessoas, participar de muito mais eventos, e ver a comunidade crescer e evoluir muito mais!
E 2016 teremos muitas novidades! Não vou fazer #1postperday, mas vou procurar escrever mais, e de forma mais uniforme xD
Feliz ano novo! \o/
]]>Operador “+”? O que tem de “mais” nisso - além do próprio operador? Vou te mostrar que esse operador pode te surpreender, dependendo da forma que você o usa. Quer ver? Vem comigo que eu te mostro! :)
Todo mundo que conhece um pouquinho de Javascript, sabe que o operador + serve para ao menos duas coisas: somar números e concatenar strings!
Mas você pode fazer muito mais com ele! Vou te mostrar todos os usos possíveis :)
Quando você usa o operador + com dois operandos números, o JS efetua uma soma:
1 | 10 + 10; // 20 |
Executando esse código no console, você tem 20
como resultado.
Tá, e daí? Isso eu já sabia!
Calma que tem mais!
Quando você usa o operador + com dois operandos strings, ou um número e uma string, é feita uma concatenação. O interpretador do JS irá converter o valor que não é string para String
, e fazer a concatenação:
1 | '10' + 10; // "1010" |
Experimente executar os códigos acima no seu console.
Nenhuma novidade ainda :(
Ainda não acabou ;)
Usando o + como um operador unário, você pode fazer pós ou pré-incremento em valores de variáveis. Vou exemplificar:
1 | var number = 10; |
O que aconteceu acima foi o seguinte: declaramos uma variável number
com o valor 10
. Ao usar o operador de pós-incremento ++
, a variável é avalidada, devolvendo o seu valor atual, e logo após, e feita a atribuição de todo o valor já existente na variável + 1.
Por isso que, chamando number++
, o retorno é 10
, mas na próxima chamada de number
, o valor é 11
.
Podemos fazer també pré-incremento:
1 | var number = 10; |
Usando o operador de pré-incremento ++
, a atribuição é feita primeiro, e então a variável é avaliada. Por isso, na chamada ++number
, o valor retornado já não é o valor inicial de 10
, mas sim 11
.
Lembrando que esses operadores devem ser usados com cautela, pois causam efeito colateral no valor das varáveis, fazendo uma atribuição implícita.
Além dos operadores de pré e pós incremento, você ainda pode usar o operador de soma com atribuição abreviada, para somar valores diferentes de 1:
1 | var number = 10; |
Faz basicamente o que os exemplos anteriores faziam, com a diferença que você pode escolher o valor que será atribuído, e ainda deixar explícito que uma atribuição está sendo feita ;)
Mas ainda tem mais :D
Você ainda pode usar o operador + para converter de strings numéricas para números:
1 | typeof '10'; // "string" |
Perceba que, usando o operador +
como unário antes de qualquer string numérica, essa string é convertida para número, e assim você consegue efetuar operações com números normalmente ;)
Usando o operador + dessa forma, tem o mesmo efeito de usar o objeto Number
:
1 | Number('10') + 10; // 20 |
Bom, esses são alguns usos do operador +. Você já conhecia todos? Ficou alguma dúvida? Comente!
]]>Seguindo com nossa série, vamos agora fazer funcionar o método POST do nosso módulo Ajax!
No último artigo, criamos um describe()
para testar o método get()
. Faremos o mesmo agora para o método post()
. No arquivo tests/test.ajax.js
:
1 | describe( 'Test `post` method', function() { |
O método post()
na nossa API de testes retorna um objeto com os dados do usuário passado. Estamos chamando os dados do usuário joao
, então, nosso primeiro teste, é verificar se a requisição nos retorna um objeto.
Obviamente, o teste não passa, pois ainda não implementamos a funcionalidade para o método post()
no nosso módulo. Sem mais delongas, vamos fazer isso agora mesmo. No arquivo src/ajax.js
:
1 | $public.post = function post( url ) { |
O método post na nossa API recebe via URL o parâmetro slug
, onde iremos fazer algo com os dados desse usuário. No exemplo, nós simplesmente retornamos os dados, mas em uma API real, provavelmente iremos alterar esses dados, ou tratar de alguma forma.
Por ser simples, a implementação no nosso módulo é basicamente a mesma do método get()
, com a diferença do método open()
, onde passamos o atributo POST
, ao invés de GET
.
Agora nosso teste passa! Estamos com todos os testes verdes, mas temos código repetido. Já sabe o que temos que fazer né? Isso mesmo: REFACTORY!
Esse é o momento em que deixamos nosso código um pouco melhor, mais legível, muitas vezes removendo código - pois menos é mais :D - mas sempre mantendo os testes passando. Não podemos implementar nada novo nesse momento, apenas melhorar o que já temos :)
Vamos então fazer o refactory. Criaremos um novo método para envolver o código repetido, e passaremos por parâmetro somente o tipo e a URL. No arquivo src/ajax.js
:
1 | $private.XHRConnection = function XHRConnection( type, url ) { |
E os nossos métodos get()
e post()
agora ficarão assim:
1 | $public.get = function get( url ) { |
Se executarmos nossos testes, veremos que tudo continua passando lindamente! Refactory executado com sucesso \o/
Como disse acima, na maioria dos casos, os métodos post
, put
e delete
podem receber os parâmetros implicitamente, ao invés de enviar via URL. Vamos então preparar nossa API para receber um POST através da URL http://localhost:3000/user
, e passar o parâmetro implicitamente.
Primeiro vamos precisar instalar o body-parser
, que vai parsear os dados que recebermos via POST em req.body
. Instale com o comando abaixo no seu terminal:
1 | npm i --save-dev body-parser |
Agora, vamos adicionar o body-parser
à nossa API. No início do nosso arquivo api/app.js
, vamos adicionar o body-parser
:
1 | var bodyParser = require( 'body-parser' ); |
Agora, antes da chamada onde setamos o header res.setHeader( 'Access-Control-Allow-Origin', '*' );
, vamos dizer ao connect
que ele deve usar o middleware do bodyParser
:
1 | app.use( bodyParser.urlencoded({ extended: false }) ); |
Vamos também fazer uma pequena alteração na função postResponse()
:
1 | function postRequest( req, res, next ) { |
Essa alteração é para que possamos obter dados vindo tanto da URL, com req.params.slug
, como os dados enviados no corpo da requisição, com req.body.slug
. Como vamos sempre usar um ou outro, podemos tratar dessa forma, com ||
.
E por fim, vamos criar mais um endpoint para nossa nova requisição à http://localhost:3000/user
:
1 | router.post( '/api/user', postRequest ); |
A linha 2
, já existe na nossa API, somente adicionamos a linha 1
. Vamos testar nossa API com o Postman, e verificar se o request retorna a requisição que desejamos:
Pronto! Tudo funciona corretamente! Só precisamos enviar o header x-www-form-urlencoded
na nossa requisição :D
Se ficou em dúvida de como o arquivo deve ficar, consulte o repositório onde estamos desenvolvendo nosso módulo.
Mas para não ficarmos só no teste manual, vamos adicionar mais um teste no nosso arquivo tests/test.ajax.js
, para essa URL:
1 | it( 'Should return data about `joao`', function( done ) { |
A novidade aqui é que passamos mais um parâmetro no método post()
: os dados que serão enviados ao servidor, em formato de query string. Passamos o parâmetro slug
, que é o que nossa API espera, com o slug do usuário que queremos os dados de volta. Para passar mais dados, você pode usar o formato: dado1=valor1&dado2=valor2&dado3=valor3
.
Vamos agora implementar essa funcionalidade em src/ajax.js
. Primeiro, passamos o parâmetro para o método post()
:
1 | $public.post = function post( url, data ) { |
Agora vamos alterar o método da conexão, para receber os dados e enviar ao servidor, com o header correto:
1 | $private.XHRConnection = function XHRConnection( type, url, data ) { |
Passamos o parâmetro data
no método, que será passado para xhr.send()
, e adicionamos o header application/x-www-form-urlencoded
.
Agora nosso método faz requisições GET e POST, e todos os testes estão passando! o/
Os métodos put()
e delete()
são praticamente o post()
, somente mudando para o verbo correto - PUT
e DELETE
.
Podemos ainda melhorar o tipo de envio de dados: ao invés de passar uma query string, poderíamos passar um objeto e, no módulo, tratar esses dados, convertendo para query string antes de fazer o envio.
Como você pode ver, tem muitas coisas que podemos fazer para melhorar: o envio dos dados, headers dinâmicos, credenciais para validação no servidor (usuário e senha), até que todos os métodos do objeto XMLHttpRequest estejam cobertos. A série acaba por aqui, mas vou fazendo essas implementaçôes aos poucos! Se quiser acompanhar, dê um watch no repositório: https://github.com/fdaciuk/ajax
Dúvidas? Comente!
Até o próximo o/
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
]]>Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1
Ferramentas de linha de comando ajudam quando você precisa executar tarefas mais rapidamente. Que tal aprender a criar você mesmo a sua? E em NodeJS! E ainda disponibilizar no NPMJS! Ficou curioso? Vem que eu te mostro como faz ;)
CLI significa Command Line Interface, ou seja, é uma ferramenta que disponibiliza uma interface de linha de comando para que você possa executar alguns comandos específicos no terminal. Normalmente essas ferramentas são criadas utilizando shell script, mas nós vamos criar a nossa com Javascript :D
Antes de começar, que tal mostrar alguns exemplos de CLI?
Gulp, Grunt, Express, Mocha, e inclusive o próprio NodeJS e NPM são ótimos exemplos de CLI. Essas ferramentas de CLI criadas em NodeJS normalmente estão disponíveis para baixar no https://www.npmjs.com/, onde você instala com o comando npm i -g nome-da-ferramenta
.
Quando você instala um módulo do Node globalmente, usando o -g
, é porque essa ferramenta normalmente disponibiliza essa interface de linha de comando, onde você não precisa depender do comando node nome-da-ferramenta
para executá-la, e sim poder usar o próprio nome da ferramenta como comando.
Por exemplo: como você faz para executar uma tarefa do Gulp? Você executa gulp nome-da-task
, sem precisar do comando node
! Isso é uma CLI :D
Vamos criar uma ferramenta bastante simples: uma calculadora que faz a soma entre dois números passados como parâmetro. A primeira coisa é escolher um nome para a ferramenta, - vixxxxxx - pois esse nome precisa estar disponível no NPMJS :)
Acho que essa é a parte mais difícil, haha :D
Não conheço nenhuma ferramenta que verifique se um nome está disponível no NPMJS - inclusive, isso seria uma ideia bem útil de CLI ;) - então vamos verificar manualmente se tem algum nome disponível, chutando mesmo:
Acesse https://www.npmjs.com/package/, e coloque um nome após a barra. Se ele estiver disponível, você verá uma página de not found
:)
Nossa CLI se chamará calculatr
, e será usada da seguinte forma: usaremos no terminal o comando para somar dois números:
1 | calculatr sum 2 3 |
Que nos dará o resultado 5
! Bem inútil, mas vai servir para você ver como funciona uma CLI :D
Observação: existem muitos pacotes no NPMJS, alguns indispensáveis, outros bastante inúteis. Então lembre-se que você sempre terá a opção de despublicar um pacote, se você estiver fazendo somente testes para ver como tudo funciona por lá. Assim nós ajudamos ao próximo e a nós mesmos, não deixando no NPMJS coisas desatualizadas e que não sirvam para nada ;)
Agora que sabemos como será a interface do nosso projeto, vamos ver como criar a ferramenta.
O passo inicial é criar um diretório separado para o projeto. A estrutura será bastante simples: só precisaremos de um diretório chamado calculatr/
e, dentro dele, um arquivo com qualquer nome. Vamos chamar de calculatr.js
:)
Precisaremos também de um package.json
com algumas informações para o nosso módulo.
Em artigos anteriores, mostrei que você poderia sempre criar o seu package.json
com o comando echo "{}" > package.json
, que imprimiria dentro do arquivo somente um “abre e fecha chaves”, que é o mínimo que precisamos quando estamos usando o Node com um task runner, por exemplo, onde o package.json
será usado somente para gerenciamento de pacotes.
Agora, nós vamos criar nosso próprio módulo NodeJS, então o package.json
precisa de uma atenção especial :)
package.json
Para criar o package.json
, você pode usar o comando npm init
, e então responder algumas perguntas para criar seu módulo. A primeira pergunta é sobre o nome do módulo. Por default, NPM vai te sugerir o nome do diretório atual, que normalmente é o nome do projeto. Vamos manter (se estive como calculatr
).
A próxima pergunta é sobre a versão. Estamos começando agora, então vamos deixar com a versão 0.0.1
. Daqui a pouco vou explicar melhor sobre como funcionam as versões ;)
Por enquanto, digite 0.0.1
e dê enter.
Agora é pedido uma descrição sobre o projeto. Essa descrição aparecerá no site do NPMJS, então capriche ;)
Vamos deixar algo como:
1 | CLI Calculator |
Depois, ele pede o entrypoint
. Esse parâmetro referencia o arquivo principal, ou o ponto de entrada da nossa aplicação. No nosso caso, esse arquivo é o calculatr.js
.
O próximo é test command
. Esse será o comando que você irá utilizar para testar seu código. Vamos utilizar o comando:
1 | istanbul cover _mocha -- -R spec |
Utilizaremos o istanbul
para coverage e o mocha
para os testes. Lembre-se de ter esses dois pacotes instalados globalmente. Se não tiver, execute no seu terminal:
1 | npm i -g istanbul mocha |
A próxima pergunta é sobre o repositório no Github. Se você ainda não criou o repositório para esse projeto, faça-o e entre com a URL. Se quiser fazer depois, não há problemas. Quando você configurar o git
para esse projeto e rodar npm init
novamente, o seu package.json
será atualizado com essas informações :)
A licença, você pode escolher uma que esteja de acordo com o formato que você irá disponibilizar sua ferramenta. Normalmente usa-se MIT
, ou GPLv2
, v3
. Se tiver dúvidas quanto à licença, pode escolher uma [por aqui] (http://choosealicense.com/).
Se tudo der certo, você terá um package.json
preenchido!
Mas, como estamos criando uma ferramenta de CLI, precisamos adicionar mais algumas entradas. Edite seu package.json
, e adicione:
1 | "preferGlobal": true, |
No site do NPMJS, é mostrado o comando de instalação do módulo. O preferGlobal
diz ao NPM que esse módulo deve ser instalado globalmente. Logo, no site do NPMJS irá aparecer o seguinte comando para instalação do móulo:
1 | npm install -g calculatr |
E o parâmetro bin
, é para que possamos dizer ao NPM que, quando executarmos calculatr
no terminal, o arquivo que será chamado é o calculatr.js
.
Adicione também mais uma entrada em scripts
, no seu package.json
. Ele deve ficar assim:
1 | "scripts": { |
Para que possamos executar nossos testes sem precisar nos preocupar em ficar executando o comando npm test
toda hora. Só precisaremos executar npm run watch
e voilà!
E agora nosso package.json
está pronto!
As versões para os módulos funcionam mais ou menos assim: o terceiro número se refere somente a ajustes, solução de bugs, mas sem adicionar novas funcionalidades. Quando temos um módulo com versão 0.0.x
, onde x
você incrementa a cada alteração que você fizer no módulo, chamamos de versão alpha
. O segundo número, é quando você tem uma versão de algo já funcional. Então, a versão 0.1.0
tem a primeira versão beta
, ou seja, ainda em testes, mas com alguma funcionalidade pronta. O incremento do segundo número deve ser a cada nova funcionalidade. O terceiro número continua sendo para resolver bugs, ou pequenos problemas. Então, se você tiver uma versão 0.1.3
, significa que, na versão beta, que tem 1
funcionalidade que funciona (?), você já fez 3
correções de bugs, ou ajustes.
E o último número, é quando você tem uma versão estável do módulo, com o mínimo de funcionalidades que você considera ideal para que ele seja um módulo “completo”. Esse completo é muito relativo, mas, dando o exemplo do nosso módulo, quando ele estiver somando dois números, ele já estará estável, e poderemos usar a versão 1.0.0
.
Nem todos os módulos seguem essa ideia. Alguns tem sua própria forma de gerenciar suas versões, mas a base é mais ou menos essa.
Agora, vamos começar a criar nosso módulo!
Como boa prática, vamos primeiro aos testes :D
Crie um diretório test
, e, dentro dele, um arquivo chamado test.calculatr.js
. Antes de começar, vamos instalar as dependências que usaremos. Já temos o istanbul
e o mocha
, como citado anteriormente. Se não os tiver instalados globalmente ainda, instale.
Após isso, vamos às dependências para os testes:
1 | npm i --save-dev should |
Instalamos com –save-dev, pois queremos o should
somente para desenvolvimento, ele não fará parte da aplicação.
E então vamos escrever nosso primeiro teste. No arquivo test/test.calculatr.js
:
1 | ; |
Opa! Tem bastante coisa nova ae!
Sim, vou explicar tudo :D
O require( 'child_process' ).exec
faz parte do NodeJS. Ele vai nos ajudar a executar comandos no terminal, permitindo-nos usar uma função de callback para fazer nosso teste quando o comando for executado :)
Na linha 4
, adicionamos o nosso package.json
, e já veremos o porquê!
Na linha 5
, vamos simular o comando para executar o calculatr
, pois ainda não temos o módulo instalado globalmente. Mas quando ele for instalado, usando calculatr
irá chamar o arquivo calculatr.js
:)
Adicionamos o should
para fazer os testes.
No primeiro teste, vamos verificar se nossa CLI retorna a versão correta. Se usarmos o comando calculatr --version
, deveria retornar a versão. Na linha 10
você pode ver o teste sendo feito, usando o exec
para dar o comando.
O que ele faz é bem simples: como primeiro parâmetro da função, passamos o comando calculatr + ' --version'
. A função de callback nos retorna, como primeiro parâmetro, um erro (se houver), depois a saída no terminal (stdout
), ou algum erro para a saída (stderr
).
O que iremos usar para testar é a saída (stdout
). Fazemos o teste com o comando da linha 12
. Testamos se a saída - que retorna sempre uma quebra de linha no final, por isso o replace
- é igual à versão do nosso projeto. Pegamos a versão diretamente do package.json
, pois ela será atualizada constantemente. Se colocarmos manualmente, teremos que lembrar de alterar a cada versão lançada, para não quebrar nosso teste.
Usamos a função done()
, que já vimos no artigo sobre como criar um módulo Ajax com Promises, que serve para dizer à função it()
, que é o nosso teste, que nossa asserção já foi feita, e ele pode verificar se passou ou não.
Obviamente, nosso teste quebra, pois ainda não temos nada no arquivo principal :)
Ainda antes de criar nosso módulo, você percebeu que no nosso teste não executamos o comando node ./calculatr.js --version
, e sim ./calculatr.js --version
, sem o node
?
Como essa é uma ferramenta de CLI, ela precisa ser executada sem o comando node
, mas para isso, ela precisa de permissão de execução. Para dar essa permissão, execute no seu terminal, na raiz do projeto:
1 | chmod +x calculatr.js |
Isso fará com que seja possível executar esse arquivo diretamente com o comando ./calculatr
! Mas ainda tem outro segredo que só vou contar quando escrevermos nosso módulo :P
Vamos fazê-lo agora! No arquivo calculatr.js
, adicione:
1 | #!/usr/bin/env node |
A primeira linha é essencial para nossa CLI, pois é ela que faz a mágica para que nosso módulo funcione sem precisar escrever em shell script: dizemos que, ao chamar esse arquivo - executando ./calculatr
no terminal - , o programa que irá executá-lo será o node
!
Então, por baixo dos panos, ao executarmos ./calculatr
, na verdade, o comando executado será node ./calculatr
;)
Safadeeeenho! Mas pera, tem um módulo novo ali! O que é esse commander?
O commander
nos ajuda a montar uma interface para usarmos na linha de comando. Você deve instalá-lo com o comando:
1 | npm i --save commander |
Agora usamos o --save
, pois esse módulo será uma dependência do nosso projeto. Toda vez que o baixarmos, também será baixado o commander
:)
Na linha 7
, passamos para o commander
a versão do nosso projeto. Pegamos direto do package.json
, como fizemos no arquivo de teste. E na linha 8
, fazemos o parse com os argumentos que forem passados no terminal. O process.argv
recebe uma coleção com os argumentos passados. Ao fazer o parse, podemos executar o comando:
1 | ./calculatr.js --version |
Que retornará a versão da nossa ferramenta.
Agora, salvando o arquivo e olhando para o nosso teste, ele passou!! \o/
sum
)Vamos fazer o primeiro método da nossa calculadora: a soma. Vamos receber, inicialmente, dois valores e somá-los.
E vamos usar o comando abaixo para efetuar a soma:
1 | calculatr sum 1 2 |
Que deverá imprimir o resultado 3
. Vamos fazer o teste? No arquivo test/test.calculatr.js
, adicine mais um teste:
1 | it( 'Command "calculatr sum 1 2" Should return 3', function( done ) { |
O formato desse teste é bem parecido com o anterior. Só mudaremos o comando, que agora é calculatr + ' sum 1 2 '
, que deveria retornar 3
. O retorno da CLI é sempre uma string, então precisamos converter para número antes de fazer o teste, por isso usamos o objeto Number
aqui, e então verificamos se o resultado é 3
.
E adivinha: o teste NÃO PASSA! A-ha! ¬¬
Agora vamos fazer a implementação do código. Vamos modificar um pouco o arquivo calculatr.js
:
1 | program.version( pkg.version ); |
Temos três métodos novos do commander
: command
, description
e action
. Nós poderíamos ter encadeado todos os métodos, ficando algo como:
1 | program |
Se você achar que assim fica mais legível, fique à vontade :)
Separei pois acho que fica melhor deixar os comandos separados, já que poderemos adicionar mais funcionalidades à nossa ferramenta xD
O command
recebe como parâmetro uma string, com o comando, e os argumentos que ele suporta. Quando você usa o parâmetro envolto por <
e >
, quer dizer que o parâmetro é obrigatório. Para parâmetros opcionais, use [
e ]
.
Depois, o description
é uma descrição sobre o comando. Essa descrição é útil quando você executa no terminal calculatr --help
, onde será mostrado o help
da sua ferramenta. O commander
já deixa toda a interface de retorno do help
bonitinha, no padrão da maioria dos CLIs em sistemas *nix :D
E a action
é a ação para quando o comando for executado no terminal. Essa ação fica em uma função de callback, que recebe como parâmetro os argumentos passados no terminal. O primeiro é o primeiro número, o segundo, o próximo número.
No retorno, executamos um console.log()
, para mostrar na saída do terminal, convertendo os dois parâmetros para número com Number()
e retornando a soma dos mesmos.
E nosso teste passou \o/
O commander
tem vários outros métodos legais. Para saber mais sobre ele, o TJ escreveu um post bem completo, que você pode ler aqui.
E agora, qual o próximo passo?
O próximo passo é subir nossa ferramenta no https://www.npmjs.com/!
Para subir o módulo é bastante simples: você só precisa executar o comando:
1 | npm publish |
Mas antes de fazer isso, você precisa ter um cadastro no site do NPM, e configurar sua máquina para poder publicar seus módulos. Vamos ver como fazer isso!
O cadastro é simples: entre em https://www.npmjs.com/, e cadastre-se, como em qualquer outro site xD
Você ainda pode se cadastrar via terminal, com o comando:
1 | npm adduser |
Será pedido um nome de usuário, uma senha e um e-mail (que ficará público).
Se você já tinha cadastro no site, só precisa então logar no seu ambiente, com o comando:
1 | npm login |
Após isso, você precisa verificar se os seus dados estão corretamente configurados. Use o comando:
1 | npm config ls |
Isso irá listar os seus dados. Confira se tudo está correto.
Mas temos alguns arquivos que não queremos enviar para o NPMJS, como o diretório test
, o node_modules
, o diretório coverage
, criado pelo istanbul
.
Para isso, podemos criar um arquivo .npmignore
na raiz do nosso projeto, e adicionar esses diretórios, como faríamos no .gitignore
.
Feito isso, só executar o comando:
1 | npm publish |
E aguardar! Quando o comando terminar de rodar, acesse o https://www.npmjs.com/package/nome-do-seu-pacote e veja se o seu CLI está lá! Pronto, agora você tem uma ferramenta de CLI que funciona e está publicada no NPMJS!
Vamos testar se ela funciona? Instale-a globalmente, com o comando:
1 | [sudo] npm i -g calculatr |
Coloquei o sudo
, pois provavelmente você irá precisar dele. Tente primeiro instalar sem o sudo
. Se não der certo, então use ele.
Agora com nosso módulo instalado, vamos executá-lo para testar, com o comando:
1 | calculatr sum 3 4 |
E o resultado é: 7
!
Agora tudo pronto :D
Para ver o código completo desse projeto, você pode acessá-lo no Github ou no NPMJS.
Até a próxima! Dúvidas? Comente :D
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
]]>Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1
Após uma série de 4 posts (aqui, aqui, aqui e aqui), vamos começar a ver nosso módulo finalmente funcionando! Vem comigo :D
Vamos testar as funcionalidades do nosso módulo. Começaremos sempre pelo mais simples: o método get
.
Ao requisitar uma URL da nossa API, via GET, esse método deve retornar um objeto.
Vamos ver então como ficaria o teste. Nosso arquivo de testes tem um bloco describe
que está testando a interface do nosso módulo. Vamos criar então outro bloco describe
, para testar a resposta de cada método, referente aos verbos HTTP, separadamente. Adicione ao arquivo tests/test.ajax.js
, logo após o primeiro bloco describe
:
1 | describe( 'Test `get` method', function() { |
Temos várias coisas acontecendo aqui, vamos por partes:
Primeiro criamos nosso novo bloco de testes, na linha 1
, onde testaremos o método get
do nosso módulo. Criamos o primeiro teste, na linha 2
, onde este deveria retornar, como resposta da requisição bem sucedida, um objeto.
Para testar se realmente funciona, precisamos fazer a asserção com a resposta da requisição. Então instanciamos o nosso módulo Ajax
, na linha 3
, e logo após, na linha 4
, fazemos a requisição para uma URL utilizando o método get
. Sabemos que esse método retorna outros dois métodos done
e error
- usados como Promises - que já testamos acima, garantindo que esses métodos existem.
Então passamos para o método done
uma função de callback, que será chamada assim que nossa requisição retornar com sucesso. Essa função de callback recebe um parâmetro response
, com os dados da nossa requisição.
Enfim, na linha 5
, fazemos a asserção, para verificar se o retorno é realmente um objeto. Se você não entendeu como esses métodos encadeados funcionam, formando uma frase, sugiro consultar a documentação do Chai.
Mas o que é essa função done()
, sendo invocada na linha 6
?
Quando precisamos testar métodos assíncronos, ou seja, quando não sabemos exatamente o momento da resposta, pois dependemos do retorno de um callback, o Mocha nos dá uma função que passamos como parâmetro na função it()
, que pode ser invocada logo após nossa asserção, para dizer ao Mocha quando ele deve realmente verificar se nosso teste passa.
Como as requisições do nosso módulo serão sempre assíncronas (ainda iremos definir isso), precisamos usar a função done()
para que o teste não execute antes que resposta da requisição esteja realmente pronta.
Se abrirmos a nossa index.html
, que contém os testes, veremos que nosso teste NÃO PASSA. Era o que esperávamos, visto que não implementamos nada ainda!
Mas agora sabemos exatamente o que precisa ser feito: criaremos o processo que faz a requisição e responde ao método done()
do nosso módulo :D
Ao acessar a index.html
, onde tem os testes, podemos ver no console do nosso navegador que temos um problema com CORS. Vamos resolver isso de forma prática, mas que não deve ser feita para toda aplicação: adicionaremos um header na nossa API, que permite que requisições de domínios diferentes consumam essa API. Vamos liberar para todos os domínios, mas fica o alerta: quando você criar uma API Rest, libere somente requisições para domínios específicos, ou então use um acess token para validar as requisições. Mas foi só um alerta, isso é assunto para um post somente sobre APIs Rest.
Voltando ao que interessa: vamos então adicionar o header à nossa API. No arquivo api/app.js
, adicione logo após o objeto users
:
1 | app.use(function( req, res, next ) { |
Isso irá garantir que, antes de toda requisição, o header Access-Control-Allow-Origin
seja passado, liberando acesso de qualquer domínio à nossa API :)
Agora vamos ao nosso módulo. Em src/ajax.js
, vamos mudar um pouco nosso método get
:
1 | $public.get = function get( url ) { |
Quando falamos em TDD, e desenvolver usando baby steps, sabemos que devemos escrever o mínimo de código possível para que nosso teste passe, e então ir refatorando até que o código fique aceitável. Nesse caso, escrevemos código até demais, mas tem um bom motivo: não estamos criando o XMLHttpRequest. Ele já existe, e precisa de uma quantidade mínima de código para funcionar corretamente. E as linhas adicionadas acima é o que precisamos para que uma requisição seja feita corretamente.
Primeiro passamos para o método get
o parâmetro url
, pois é à partir dele que iremos requisitar os dados do servidor. Na linha 2
, instanciamos o objeto XMLHttpRequest
, que será responsável por fazer a requisição. Na linha 3
, abrimos uma nova conexão, passando o verbo GET
(já que estamos usando o método get()
do nosso módulo), depois a URL, e o terceiro parâmetro diz se a requisição será assíncrona ou não. Vamos deixá-lo como true
, pois teremos callbacks para nos orientar quando a requisição tiver um retorno.
Na linha 4
, atrelamos ao nosso objeto um evento chamado readystatechange
, onde poderemos tratar todos os passos da requisição. Na linha 5
, invocamos o método send()
, que irá enviar a requisição ao servidor, para que comece a brincadeira! :D
No final, retornamos nesse método as Promises, pois queremos utilizar os métodos done()
e error()
, dependendo da resposta da requisição.
Como você sabe, cada listener de evento recebe como segundo parâmetro uma função de callback, onde será feito o tratamento dos dados quando aquele evento for disparado. Vamos ver como ficará o método $private.handleReadyStateChange()
, no nosso módulo src/ajax.js
:
1 | $private.handleReadyStateChange = function handleReadyStateChange() { |
Antes de qualquer coisa, vamos verificar se os dados são retornados corretamente. Para isso, o objeto XMLHttpRequest
tem uma propriedade somente leitura chamada readyState
que, quando está em 4
, significa que a requisição está completa. A propriedade status
retorna o status HTTP da requisição. 200 significa _OK_, ou seja, os dados foram retornados corretamente.
Em responseText
, recebemos uma DOMString, com o resultado da requisição. Como estamos retornando um JSON do servidor, usamos o método JSON.parse()
para parsear a string, transformando-a em um objeto do Javascript.
A resposta que temos é essa:
Antes de retornar o objeto, podemos ver dois requests acusando 404
. Analisando o erro, vemos que é por causa do assert que testa a interface do método get
, se ele retorna os métodos done
e error
. O erro acontece porque não estamos passando uma URL para o método, e o XMLHttpRequest tenta requisitar uma URL que não existe. Precisamos ajustar isso. Temos dois caminhos: ou simplesmente passamos uma string vazia na chamada get()
do nosso objeto, ou validamos dentro do nosso módulo que, se não for passada nenhuma URL, ele passa uma string vazia, considerando a URL atual.
Vamos alterar nosso módulo, para manter retrocompatibilidade. Imagine que outras pessoas já estão usando esse módulo, então não podemos fazer com que a atualização do mesmo quebre os projetos que usam versões mais antigas. No método get()
do nosso módulo, em src/ajax.js
, vamos adicionar:
1 | xhr.open( 'GET', url || '', true ); |
Dessa forma, se não for passada URL alguma, consideramos a URL atual, passando uma string vazia, e não precisamos mudar as implementações anteriores :)
Hora de fazer nossas promises funcionarem! Nosso método get()
retorna os métodos done()
e error()
, mas não é no exato momento do retorno desse método que as informações estarão disponíveis, mas sim no retorno do callback do evento readystatechange
. Então nós precisaremos de um objeto auxiliar, que será usado para fazer com que as nossas Promises conversem com o retorno da requisição.
Parece complicado? Vamos ver na prática como fazer isso! Primeiro, no nosso módulo, criaremos o objeto auxiliar. No início do módulo, em src/ajax.js
, logo após as declarações dos objetos $public
e $private
, adicione:
1 | $private.methods = { |
Esse objeto conterá os métodos que nos auxiliarão a criar nossas Promises.
Agora, na função de callback do evento readystatechange
, vamos adicionar os métodos done()
e error()
ao $private.methods
, e invocá-los, passando a resposta da requisição para eles. Mudando o método $private.handleReadyStateChange()
, vamos agora ter o seguinte:
1 | $private.handleReadyStateChange = function handleReadyStateChange() { |
Só vamos retornar algo quando a requisição estiver completa, por isso fazemos a verificação do xhr.readyState
na linha 4
. Na linha 5
, verificamos se o status HTTP da requisição está OK, ou seja, se for um status entre 200
e 300
, a requisição retornou com sucesso.
Na linha 6
é onde acontece a primeira parte da magia das Promises: usamos o objeto $private.methods
, invocando o método done()
com o call()
, e setamos o próprio objeto para ser o this
dentro do método done()
. Passamos também como parâmetro o JSON de resposta da requisição, já parseado como objeto Javascript.
Mas tem um porém: esse método $private.methods.done( response )
, que recebe esse parâmetro response
, deve ser a função de callback que passaremos como parâmetro do método done()
da Promise.
Para que isso seja possível, vamos mexer um pouco no método $private.promises()
do nosso módulo, fazendo com que, quando os métodos done()
e error()
do módulo Ajax, quando forem chamados, recebam essa função como callback:
1 | $private.promises = function promises() { |
Complicou? Pois é, eu disse que seria uma aventura :D
Vou explicar o que acontece: quando invocamos o método get()
do nosso módulo, através de:
1 | var ajax = new Ajax(); |
Nós temos disponíveis outros dois métodos: done()
e error()
, que são chamados através do return $private.promises()
dentro do método get()
no nosso módulo.
No método $private.promises()
- atualizado acima - nós retornamos esses dois métodos done()
e error()
, que recebem como parâmetro uma função de callback.
Então, nós atribuímos ao objeto $private.methods
o callback passado como parâmetro.
Lá na função de callback do evento readystatechange
, você viu que invocamos o $private.methods.done
e o $private.methods.error
com o call
, passando o xhr.responseText
como parâmetro desses métodos.
É aproveitando-se da natureza funcional do Javascript que conseguimos fazer as Promises conversarem com o retorno dos métodos no momento certo, retornando a interface que temos agora!
E se você perceber, ainda no método $private.promises()
, dentro dos métodos done()
e error()
, nós retornamos o this
, que é o próprio objeto retornado nesse método, para que possamos encadear as respostas, exatamente como vimos no primeiro artigo dessa série, mas com jQuery :D
Por isso, nós podemos utilizar nosso módulo dessa forma:
1 | var ajax = new Ajax(); |
Vamos executar nosso teste para ver se agora passa:
1 | Error: Uncaught SyntaxError: Unexpected token < (http://localhost/00-opensource/ajax/:1) |
Opa! Alguma coisa deu errado! O que aconteceu?
Vamos tentar debugar, para ver se a resposta está vindo corretamente. Em nosso teste, em tests/test.ajax.js
, vamos adicionar um console.log( response )
:
1 | it( 'Should return an object', function( done ) { |
E vemos que, como esperado, o nosso objeto é retornado com sucesso! Mas está dando um erro de sintaxe no teste!
O que acontece é que, em algum momento, o resultado que é retornado por nossa API como JSON, é convertido para string, com toString()
(não sei porque o Mocha faz isso), e depois, quando tentamos fazer o parse com JSON.parse
, acontece o erro de sintaxe.
Como o formato da resposta pode variar, temos que garantir que nosso módulo traga a resposta corretamente. Então vamos fazer uma pequena modificação no método $private.handleReadyStateChange
do nosso método, em src/ajax.js
, para ficar assim:
1 | if( xhr.readyState === DONE ) { |
Somente vamos chamar um método $private.parseResponse
, passando como parâmetro a resposta da requisição. Nesse método, vamos tratar o parse do JSON:
1 | $private.parseResponse = function parseResponse( response ) { |
E pronto! Simplesmente colocamos o retorno em um bloco try/catch
. Se der erro ao tentar parsear, retornamos somente a resposta, que já deve estar em JSON :D
Agora o nosso teste passou! Ufa!
Agora você está vendo como realmente as Promises funcionam. Não precisamos de nenhuma library ou framework para fazê-las funcionar. Claro que não é um trabalho tão simples, mas conhecendo um pouco de programação funcional, nós conseguimos fazer uma funcionalidade onde antes precisaríamos carregar um lib inteira como a Q
ou async
só para fazer isso!
Mas ainda não acabou: já temos nosso módulo funcional, mas ainda temos testes a fazer! Precisamos testar agora o próximo método: o post
.
Mas como esse artigo já está grande demais, o método post
ficará para o próximo artigo! Não perca :D
Ficou com dúvida de algo? Não fique com vergonha! Comente :D
Até o próximo! o/
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
]]>Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1
Já vimos o quanto callback hell é ruim, e como as Promises resolvem o problema. Agora estamos criando nosso próprio módulo Ajax, que retorna Promises done
e error
para que saibamos exatamente o momento em que a requisição retornou algum resultado.
Como vimos no artigo anterior, após todos os nossos testes estarem passando, é hora de verificarmos se nosso código precisa de um refactory. Como tínhamos algumas duplicidades, criamos um novo método e centralizamos o código para remover a duplicidade.
Mas código limpo não vale apenas para o nosso código final, e sim também para os testes! Isso mesmo! Se você der uma olhada no nosso arquivo de testes tests/test.ajax.js
, vai perceber que, em todos os testes, estamos instanciando o objeto Ajax
.
Ainda não precisamos de instâncias diferentes, então podemos centralizar tudo em uma única chamada, no início da função describe()
, que centraliza os testes de interface, e remover as chamadas dentro de cada teste (funções it()
).
Nosso arquivo de testes, tests/test.ajax.js
, refatorado, agora está assim:
1 | describe( 'Test module interface', function() { |
Agora que temos nosso código refatorado, e nossos testes também refatorados, podemos dar continuidade ao módulo o/
Precisamos fazer nosso módulo funcionar de verdade. Como a funcionalidade dele depende de requisições get
, post
, put
e delete
, antes de escrever a funcionalidade, e até mesmo antes de testar, precisamos ter um ambiente básico de backend que responda à esses métodos.
Vamos então criar uma API Rest básica, com NodeJS, para que possamos testar a resposta à esses 4 métodos.
Quando falamos sobre como utilizar o Gulp com o connect e o modrewrite foi mostrado um exemplo básico de API Rest. Vamos usar a mesma ideia, só incrementando com os métodos put
e delete
.
Crie um diretório api
na raiz do projeto, e um arquivo chamado app.js
dentro desse diretório. E então, vamos para o código da nossa API:
1 | ; |
A única diferença para o código que fizemos no outro arquivo são os novos métodos e também exportamos a variável app
, para que possamos importar em outro arquivo, e subir nosso servidor com o Gulp.
Agora, na raiz do projeto, você precisa instalar os módulos necessários:
1 | npm i --save-dev connect connect-route |
E já temos nossa API respondendo à todos os verbos que precisamos! :D
Para subir o servidor, vamos adicionar a nossa API à task default
do Gulp. No gulpfile.js
, altere a task default
para ficar assim:
1 | gulp.task( 'default', [ 'assets' ], function() { |
Lembra que exportamos o app
na API, usando exports = module.exports = app
? Esse comando vai servir para que possamos usar o require
para adicionar o app.js
onde quisermos! No nosso caso, ao executar a task default
do Gulp, também subiremos nossa API, na porta 3000.
Agora, executando o comando gulp
no terminal, você terá os testes rodando, o coverage e também a nossa API Rest, - que pode ser acessado via http://localhost:3000
- para testar os métodos do nosso módulo de Ajax! :D
Para testar a API, você pode utilizar o Postman, por exemplo :)
Agora já temos tudo o que precisamos para ver nosso módulo funcionando! No próximo artigo vamos finalmente fechar com chave de ouro! :D
Se ficou alguma dúvida, comente!
Até lá! :D
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
]]>Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1
E então, fez sua lição de casa proposta no post anterior? xD
Vamos então dar continuidade no nosso módulo, para que possamos trabalhar com Ajax de forma organizada, e sem depender de módulos de terceiros o/
Nosso próximo passo é testar se nosso módulo tem os métodos post
, put
e delete
.
No arquivo tests/test.ajax.js
, vamos criar um novo teste, logo após o teste do get
:
1 | it( 'Should have `post` method', function() { |
Testamos agora pelo método post
. É óbvio que ele não existe, mas é por isso que esse teste chama-se TDD, não? Primeiro testamos se algo existe, para que o teste falhe, e logo após, fazemos sua implementação, com o mínimo de código possível.
Vamos então adicionar no arquivo src/ajax.js
, logo após o método get
:
1 | $public.post = function post() {}; |
E o nosso teste passa! Agora ficou fácil, não? Vou deixar então pra você criar os métodos put
e delete
;)
Lembrando que você pode acompanhar o desenvolvimento desse módulo aqui.
Só um detalhe sobre o método delete
: como delete
é um operador válido em Javascript, você não pode utilizá-lo como nome de variável ou função, mas pode usá-lo como método ou propriedade de um objeto. Então, vamos nomear nossa função somente como del
, no arquivo src/ajax.js
:
1 | $public.delete = function del() {}; |
Assim não teremos problemas ;)
Certo, e qual é o próximo passo?
Ainda não testamos o retorno dos nossos métodos. Ainda dentro dos testes de interface do nosso módulo, precisamos garantir que eles retornem os métodos done
e error
. Esses métodos ainda não precisamo funcionar, só precisamos ter certeza que eles serão retornados, já que estamos falando de Promises :)
Criaremos um novo teste então, verificando se o nosso método get
retorna esses dois outros métodos. No arquivo tests/test.ajax.js
:
1 | it( 'Should `get` method return `done` method', function() { |
Testamos primeiro pelo método done
. Ele ainda não existe, logo nosso teste falha. Vamos então criá-lo:
1 | $public.get = function get() { |
É só isso? Retornar um método done
vazio?
Sim! Não é isso que o nosso teste pede: um método done
? Então é isso que damos a ele :)
Precisamos sempre pensar simples, para dar somente o código necessário que o nosso teste precisa, e fazê-lo passar. A funcionalidade do método será feita depois, logo, ela será testada depois, então não precisamos nos preocupar com ela agora.
Dessa forma você mantém seus códigos com o mínimo de código possível, fazendo com que tudo fique tão limpo quanto possível :D
Agora precisamos testar o método error
. No arquivo de testes test/test.ajax.js
, vamos fazer a asserção para esse método:
1 | it( 'Should `get` method return `error` method', function() { |
Então, escrevemos o código para que nosso teste passe. Em src/ajax.js
:
1 | $public.get = function get() { |
Adicionamos o método error
, para que seja retornado por get
e nosso teste volta a passar!
Mas e os outros métodos?
Então, todos os nossos métodos precisam retornar os mesmos métodos done
e error
. Vamos agora testar próximo método, post
, para que ele também retorne as Promises done
e error
. Começamos por done
. No arquivo tests/test.ajax.js
:
1 | it( 'Should `post` method return `done` method', function() { |
Nosso método post
também deveria retornar done
- assim diz o teste, que por sinal, falhou. Vamos então fazê-lo retornar o método done
. No arquivo src/ajax.js
:
1 | $public.post = function post() { |
E nosso teste volta a passar! Mas perceba uma coisa: o retorno do método post
é exatamente igual - ou está se encaminhando para ser igual - ao retorno do método get
. Como todos os nossos testes até aqui passaram, vamos parar de criar funcionalidades e aplicar o BLUE, fazendo um refactory. No arquivo src/ajax.js
:
1 | function Ajax() { |
A função Ajax
do nosso módulo agora ficou um pouco mais limpa: removemos a duplicidade do código, criando um método $private.promises
, que retorna os métodos da Promise - done
e error
.
Deixamos o método promises()
no objeto $private
, pois não precisamos que ele seja acessado através da interface do nosso módulo. Ou seja, ninguém conseguirá chamar assim:
1 | var ajax = new Ajax(); |
Isso irá retornar um erro dizendo que undefined is not a function
, pois o método promises()
não é público :D
Agora só precisamos criar os outros testes de Promises para os outros métodos put
e delete
, para manter tudo documentado.
Como você pode perceber, o resultado dos nossos testes, além de garantir que tudo está funcionando, acabam servindo como documentação do nosso código. Não precisamos usar comentários dentro do módulo para dizer o que cada coisa faz, já que os testes já o fazem por nós :D
Quando alguém ler o resultado dos testes, ele saberá exatamente quais são os métodos disponíveis para uso!
Já temos os testes para toda a nossa interface pronta, mas a continuação ficará para o próximo artigo. Até lá!
Dúvidas até aqui? Comente :D
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
]]>Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1
No artigo anterior, você viu como as Promises resolvem o problema de callback hell. Agora vamos ver na prática como criar nossas próprias Promises!
Nosso desafio é criar um módulo para usar Ajax, sem qualquer library ou framework, e que possamos obter os resultados das requisições via Promises.
O nosso módulo será feito usando a técnica de TDD, ou seja, primeiro criamos um teste com o resultado esperado, e então partimos para o código em si :)
E o nosso primeiro passo é criar a estrutura do nosso projeto!
Você pode acompanhar o andamento desse módulo através desse repositório no Github :D
Acho que o exemplo mais comum é o carrinho de compras com Ajax.
Execute o comando abaixo no seu terminal, em um diretório separado para o projeto:
1 | mkdir src tests && touch .editorconfig .jshintrc .gitignore gulpfile.js index.html src/ajax.js tests/test.ajax.js && echo "{}" > package.json |
Isso irá gerar a seguinte estrutura:
1 | . |
Com nosso projeto criado, vamos montar nosso ambiente de testes :D
Se você ainda não está habituado com testes, ou nunca escreveu um, ou ainda não sabe para o que realmente eles servem, é muito importante que você leia esse artigo antes de continuar :D
Vamos instalar os módulos do Gulp necessários para que possamos fazer os testes no nosso módulo:
1 | npm i --save-dev gulp gulp-mocha gulp-istanbul gulp-load-plugins chai |
Nosso arquivo .jshintrc
ficará assim:
1 | { |
Já o .editorconfig
terá a seguinte configuração:
1 | root = true |
E o .gitignore
:
1 | node_modules/ |
E agora vamos configurar nosso gulpfile.js
:
1 | ; |
E Enfim, nosso ‘index.html’:
1 |
|
Acessando essa index.html
no seu navegador, você terá acesso à todos os testes, podendo acompanhá-los à medida em que forem sendo implementados :)
Antes de darmos início, nós precisamos definir o que precisa ser implementado no nosso módulo. Nessa etapa, não vamos definir código, mas sim como ele deve funcionar.
Basicamente, nós queremos que nosso módulo utilize Ajax (XMLHttpRequest), e que ele responda aos métodos get
, post
, put
e delete
.
A resposta deve vir no formato de Promises, com os métodos done
e error
.
Esse será nosso MVP.
Já pode colocar o Gulp pra rodar e ficar assistindo nossos arquivos, com o comando:
1 | gulp |
Para que nosso módulo funcione corretamente com CommonJS, AMD ou sem nenhum padrão de módulos específico, precisamos usar o formato UMD.
Nosso módulo src/ajax.js
iniciará com a seguinte estrutura:
1 | ;(function ( root, factory ) { |
Das linhas 1
a 13
, aplicamos o UMD. Agora nosso módulo é Universal :D
O comentário na linha 3
, é para que o Istanbul ignore esse if
, pois não teremos os 3
módulos juntos (AMD, CommonJS e sem nenhum), será somente um desses, então não temos como fazer com que toda essa parte seja coberta por testes. Logo, podemos ignorá-la.
Observação importante:
Se você tentar incluir esse módulo em qualquer um dos 3 formatos citados acima, vai ver que ele vai funcionar. Quando você não consegue dar cobertura a um código, como é o caso do UMD, você precisa, ao menos, garantir manualmente que tudo funciona. Não adianta comentar todo o código para que o Istanbul ignore, pois você estará enganando a si mesmo. Use essa feature com cautela.
Agora sim estamos prontos para começar a codar! Nossa primeira asserção irá testar a interface do nosso módulo. Precisamos garantir que nosso módulo tenha os métodos get
, post
, put
e delete
.
Vamos então criar o teste para isso. A base do nosso arquivo tests/test.ajax.js
ficará assim:
1 | ;(function ( root, factory ) { |
Vamos adicionar o UMD também para o nosso teste, pois ele ficará rodando no terminal, com Node. Assim nós poderemos acompanhar os resultados tanto no browser, como no terminal :)
Incluímos a biblioteca Chai, para fazer as asserções dos nossos testes.
Na linha 18
, damos início ao teste:
O describe
vai criar um wrapper
com vários testes de uma parte específica do módulo. Nesse primeiro describe
, vamos testar a interface do módulo.
Nosso primeiro teste, na linha 19
, diz que nosso módulo “Deveria ter um método chamado get“.
Instanciamos o objeto do nosso módulo - new Ajax()
- e fazemos a asserção, verificando se a propriedade get
existe.
E o nosso teste quebra, pois esse método não existe:
E esse é exatamente o comportamento esperado, pois ainda não temos código no nosso módulo que o faça funcionar! Esse é o primeiro passo do TDD: O RED.
Agora, você lembra qual o próximo passo? Após nosso teste quebrar, pois adicionamos um teste para verificar algo que ainda não existe, vamos tentar fazer o teste passar, com o mínimo de código possível (baby steps):
Adicionamos então ao nosso arquivo src/ajax.js
:
1 | ... |
Pronto, agora temos um método get
! Vamos ver se nosso teste passou?
Agora sim! Temos o GREEN, pois nosso teste passou.
Podemos ainda acompanhar no terminal como está a cobertura do nosso código:
Como estamos usando TDD, a tendência é que tenhamos sempre 100% do nosso código coberto por testes! :D
O próximo passo é o BLUE, ou Refactory. Nesse caso, não precisamos refatorar nosso código, pois não tem nada a ser refatorado.
Com o passar dos testes, dependendo do baby step que nós utilizarmos, precisaremos refatorar o código para que ele se mantenha em ordem. O passo do refactory serve somente para limpar o código já existente. Você nunca deve incluir nenhuma funcionalidade a mais no momento do refactory. Os testes que passam devem continuar passando, mas a leitura do código deve ser melhor do que você deixou da última vez :)
Curioso para ver como isso continua? Então aguarde o próximo post! :D
Fica como lição de casa pra você fazer os outros 3 testes com os métodos post
, put
e delete
!
Nos próximos artigos, vamos continuar criando nosso módulo, até que ele resolva nosso primeiro objetivo: ter os 4 métodos e responder com as Promises.
Ficou com dúvidas? Comentae!
Até lá!
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
]]>Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1
Você já deve ter ouvido falar sobre Promises, e provavelmente sabe do problema que elas resolvem. Mas você sabe como implementar suas próprias Promises? Vamos ver como fazer isso na prática!
Sempre que você precisa executar uma ação após uma requisição assíncrona, normalmente o código vai se parecer com isso:
1 | obj.asyncFunction(function( response ) { |
Esse é o famoso callback hell, onde você depende do retorno de cada requisição para iniciar a próxima! Parece que o Ryu está dando uma hadouken ali!
Como resolver?
Você pode resolver o callback hell usando métodos acoplados, ou seja, um método que vai depender de outro como resposta:
1 | obj.asyncFunction( responseFunc ); |
Isso já ajuda a resolver o problema do callback hell. Mas tem uma forma ainda mais elegante: Promises!
As Promises, como o próprio nome diz, são promessas de que você terá um resultado ao final da requisição. Um exemplo é o método $.get
do jQuery. Você passa a URL onde você vai buscar seus dados para esse método, e, ao finalizar, você pode utilizar os métodos done()
, para saber se o retorno foi bem sucedido, fail()
para erro e always()
para o final da requisição, independente de sucesso ou erro.
Dessa forma, você consegue utilizar algo como:
1 | $.get( 'http://myapp.com/api/data' ) |
Assim você remove o acoplamento de várias funções, e usa métodos encadeados. :)
Essa é a ideia inicial da utilização de Promises. Nos próximos artigos, vamos construir um módulo que faz requisições Ajax, parecido com esse do jQuery, mas implementando nossas próprias Promises de success e error para os verbos get, post, put e delete. Esse nosso módulo será todo coberto por testes, então se prepare para uma grande aventura :D
Até lá!
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
]]>Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1
Já vimos como é fácil criar mixins e functions no Stylus. Mas como os parâmetros podem ser passados para obtermos os resultados esperados? Vem comigo que eu te mostro ;)
O Stylus suporta keyword arguments, ou “kwargs”. Isso significa que você não precisa manter a mesma ordem dos parâmetros quando invocar uma function ou um mixin, e inclusive pode fazer algumas variações um bastante diferentes. Vamos ver nos exemplos para ficar mais claro:
1 | body { |
Que irá compilar:
1 | body { |
Se você ainda não conhece o Stylus, recomendo que leia o primeiro artigo da série, para que você possa montar seu ambiente e colocar o Stylus pra rodar :)
O Stylus tem uma função embutida (_Built-in Function), chamada rgba
, que retorna o valor CSS
para rgba
.
Essa função aceita 4 parâmetros: red
, green
, blue
e alpha
. Como você pode ver no exemplo acima, na primeira chamada de color
, fazemos no padrão do CSS, chamando os parâmetros na ordem em que eles devem ser chamados.
Na segunda chamada de color
, nós nomeamos os parâmetros, ainda mantendo a ordem. E, como você pode ver, a compilação é a mesma.
Agora olhe que loucura: na terceira chamada, os parâmetros não estão em ordem, mas ainda assim as coisas compilam como deveriam, pois o Stylus sabe exatamente onde deve colocar cada valor, pois nomeamos os argumentos!
E mais interessante: perceba que o último argumento não está nomeado!
Então como o Stylus sabe onde deve ir esse valor?
Como já tinhamos nomeado alpha
, blue
e red
, o único argumento que sobrou foi o green
. Logo, esse valor só pode ser dele ;)
Agora veja a quarta chamada: temos somente dois valores nomeados. Quando isso acontece, o Stylus atribui os valores dos argumentos nomeados, e os que não estão nomeados, ele coloca na ordem em que deveriam ser chamados na function ou mixin, excluindo apenas os que já foram passados com nome.
Olhe novamente para a última chamada:
1 | color: rgba(alpha: 0.5, blue: 100, 255, 200); |
Os valores para alpha
e blue
o Stylus já tem. A ordem para passar os parâmetros é:
1 | rgba(red, green, blue, alpha) |
Então ainda faltam os valores para red
e green
, que receberão, respectivamente, os próximos valores :D
Mas como eu faço para saber qual a ordem que eu devo passar os parâmetros?
O Stylus tem uma função chamada p()
, que retorna como o mixin ou a function foram criados.
Se você colocar no seu arquivo Stylus:
1 | p(rgba) |
Você terá como retorno no seu terminal:
1 | inspect: rgba(red, green, blue, alpha) |
E isso funciona também para os mixins e functions criados por você! :D
Da hora não?
Aproveite essa incrível ferramenta e até o próximo artigo :)
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
]]>Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1
Quando eu instalo um módulo do NodeJS, devo usar –save ou –save-dev? E porque eu tenho as duas opções?
Vem descobrir ;)
Post curto, mas pra sanar a dúvida de uma vez por todas:
–save ou –save-dev?
Em NodeJS, quando você está utilizando algum módulo que não faz parte do core, você precisa instalá-lo com o comando npm install
, ou npm i
. E para que você possa facilmente reinstalar esses módulos em vários ambientes diferentes, sem precisar ficar lembrando quais você utilizou, ou fazendo isso manualmente, você cria um arquivo na raiz do seu projeto chamado package.json
- que pode ser criado a partir do comando npm init
, e respondendo a algumas perguntas - que irá gerenciar todas as dependências do seu projeto.
Quando você instala um módulo utilizando a flag --save
, o módulo é salvo em dependencies
, dentro do package.json
.
Por exemplo: no nosso projeto, nós iremos utilizar o express e o mongoose. O Express é um web framework que irá nos ajudar a facilitar nosso trabalho ao trabalhar com aplicações web no NodeJS. Já o mongoose é um módulo que vai nos ajudar a modelar nossa base de dados, criada em Mongo DB. Para instalá-lo, você vai utilizar o comando:
1 | npm i --save express mongoose |
E ele fica salvo no package.json
, assim:
1 | "dependencies" : { |
Aqui você usa o --save
, pois esses dois módulos são uma dependência do seu projeto. Você precisa deles em produção, senão a sua aplicação não funciona.
Agora imagine a seguinte situação: você precisa fazer testes unitários na sua aplicação, e vai utilizar o Mocha para criar a base dos testes, e o Chai para fazer as asserções. Você usa --save
ou --save-dev
?
A pergunta que deve ser feita antes de instalar é a seguinte: Preciso desse módulo em produção? A minha aplicação funciona sem ele?
Se a resposta for “não” para a primeira pergunta, e “sim” para a segunda, você só irá precisar do módulo em ambiente de desenvolvimento. Logo, você pode utilizar o --save-dev
:
1 | npm i --save-dev mocha chai |
E isso irá criar uma entrada devDependencies
no seu package.json:
1 | "devDependencies": { |
Essas entradas servem basicamente para facilitar o seu trabalho. Sempre que você precisar reinstalar qualquer um desses módulos, você não precisa utilizar novamente o comando de instalação, deixando explícito o nome dos módulos. Pode usar somente npm i
, que o NPM irá procurar o seu arquivo package.json
e, ao encontrá-lo, irá baixar e instalar todas as dependências em dependencies
e devDependencies
pra você.
Agora você sabe que deve utilizar o --save
somente para instalar módulos que serão usados em produção, e o --save-dev
somente para módulos que serão usados no momento do desenvolvimento.
Mas executando o npm i
, todos os módulos serão instalados. Imagine que você só quer que, quando estiver no seu servidor de produção, somente os módulos em dependencies
sejam instalados, e os módulos em devDependencies
sejam ignorados.
Para fazer isso, vocề pode utilizar o comando:1
npm i --production
Isso é bastante interessante se você estiver utilizando uma ferramenta de CI, por exemplo, onde o seu build compila toda a sua aplicação e disponibiliza em produção. Usando o comando acima, somente os módulos em dependencies
serão instalados :D
UPDATE
Como bem lembrado nos comentários pelo Osmar, se você setar a sua variável de ambiente NODE_ENV
para production
, e executar o npm i
, somente as dependências de produção serão instaladas, sem a necessidade do --production
;)
Espero que tenha ficado claro a diferença entre o --save
e o --save-dev
. Se ainda ficou alguma dúvida, poste nos comentários :D
Até o próximo!
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
]]>Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1
A situação é basicamente essa: Você está trabalhando em uma SPA, e usando Gulp para subir seu webserver. Você tem uma API Rest que foi feita em outra linguagem, e responde em outra porta. E o pior: essa aplicação não tem um header que previna o problema com CORS. Como resolver isso?
Vou te mostrar nesse post! Vem comigo :D
Na situação acima, você teria algumas URLs locais da API Rest para consultar, que na verdade, deveriam responder em outra porta. Vamos exemplificar.
Primeiro precisamos criar nossa estrutura de arquivos. Crie um novo diretório e use o comando abaixo para criá-la:
1 | mkdir api app && touch app/gulpfile.js app/index.html app/main.js api/app.js && echo "{}" > api/package.json && echo "{}" > app/package.json |
Que vai gerar a seguinte estrutura:
1 | . |
Agora, acesse o diretório api
, e instale os módulos abaixo:
1 | npm i --save-dev connect connect-route |
Feito isso, vamos criar a API Rest. No arquivo api/app.js
:
1 | ; |
Prometo que em breve eu escrevo um post só explicando como criar uma API Rest :)
Esse arquivo está bastante simples: incluímos nossos módulos connect
e connect-route
, criamos uma lista de usuários em users
que irá responder às URLs: /api/users
via GET, onde todos os usuários do objeto serão retornados; e URL:
/api/user/:slug`, via POST.
Vamos ver se isso funciona? Execute dentro do diretório api
:
1 | node app.js |
E acesse no seu navegador:
1 | http://localhost:3000 |
Se tudo deu certo, você deve ter um retorno como esse:
1 | {"joao":{"name":"João da Silva","age":30},"maria":{"name":"Maria Firmina","age":26},"paulo":{"name":"Paulo Torres","age":25}} |
Ótimo, nosso método GET funciona! Agora vamos testar o POST! Para o Chrome, existe uma extensão chamada Postman. Instale-a para testarmos outros verbos HTTP.
Após fazer isso, vamos testar o POST da nossa API:
Fazemos a requisição, via POST, para a URL http://localhost:3000/api/user/joao
, e o resultado retornado é o objeto joao
!
Pronto! Deixe a API executando e abra uma nova aba no seu terminal. Vamos agora subir o frontend da nossa aplicação em uma porta diferente, para testar um cenário real.
Vamos começar instalando os módulos do Gulp necessários:
1 | npm i --save-dev gulp gulp-connect |
Vamos somente subir nosso servidor, por enquanto. No arquivo app/gulpfile.js
:
1 | ; |
E no arquivo app/index.html
:
1 |
|
Agora, precisamos requisitar os dados à nossa API, para que eles sejam mostrados na nossa aplicação. No arquivo app/main.js
:
1 | ;(function( win, doc, undefined ) { |
Basicamente fazemos uma requisição Ajax à http://localhost:3000/api/users
, via GET, tentando inserir o resultado no elemento div[data-js="container"]
, que colocamos na tela. E a resposta que nós temos é:
É isso que a gente merece mesmo! :(
Mas podemos contornar essa situação, usando um módulo chamado connect-modrewrite
, (valeu Becker xD) que faz reescritas de URL, semelhante ao que você faz com o .htaccess
em PHP, mas em NodeJS :D
Como faz?
Instale o módulo em app/
:
1 | npm i --save-dev connect-modrewrite |
Agora, vamos configurá-lo no app/gulpfile.js
:
1 | ; |
Incluímos o módulo e adicionamos, no retorno do parâmetro middleware
, dentro de connect.server
, o código que vai fazer o proxy
da nossa URL. Sempre que a nossa URL apontar para /api/users
, na verdade será buscado o endereço http://localhost:3000/api/users
.
Agora só precisamos alterar a URL no arquivo app/main.js
de http://localhost:3000/api/users
para /api/users
e testar:
E olha que loucura! Agora temos o resultado correto, e sem erros!
Esse módulo ajuda muito, principalmente nesses casos. Mas ele serve ainda para qualquer tipo de redirect que você precisar fazer :D
Era isso que eu queria mostrar hoje! Até o próximo post! :D
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
]]>Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1
Hoje vamos ver como é simples subir um servidor web usando NodeJS, e ainda aproveitar as facilidades do Gulp para rodar nossas tarefas diárias, inclusive fazendo livereload! Quer ver como é fácil? Vem comigo :D
Crie um diretório e execute dentro dele, no terminal:
1 | touch gulpfile.js index.html style.css && echo "{}" > package.json |
Isso irá criar a estrutura do nosso projeto:
1 | . |
Como de praxe, você precisa ter o NodeJS instalado, e o Gulp, instalado de modo global.
O quê? Ainda não sabe como usar o Gulp? Esse artigo pode te ajudar a começar! Se quiser saber um pouco mais sobre essa incrível ferramenta, aqui você encontra várias coisas legais sobre ele ;)
Vamos agora instalar os módulos necessários para os nossos testes. Execute no terminal, no diretório do seu projeto:
1 | npm i --save-dev gulp gulp-connect |
Agora vamos configurar nosso gulpfile.js
:
1 | ; |
As tarefas são bastante simples. Nas linhas 3
e 4
, incluímos as dependências: gulp
e gulp-connect
. Na linha 5
, criamos um array com os arquivos que serão assistidos e, ao serem alterados, executarão o livereload
.
Na linha 7
, criamos a task files
, passamos como source o array já configurado na varável files
, e chamamos o connect.reload()
. Isso diz ao connect
quais arquivos devem ser alterados para que ele faça o reload :)
Na linha 11
, criamos a task watch
, que vai assistir os arquivos, para saber quando eles foram alterados.
Na linha 15
, criamos a task connect
, que vai subir nosso servidor web. Por padrão, ao chamar connect.server()
, ele vai usar a porta 8080
. Para subir em outra porta, passe um parâmetro port
no objeto passado para esse método. No exemplo, estamos passando somente o parâmetro livereload: true
, para que o livereload
seja ativado.
Se quiser ver todas as opções do gulp-connect
, você pode visitar a documentação completa aqui.
E, para finalizar, na linha 19
, criamos a task default
, que executa as tasks connect
, para subir o servidor, e a task watch
, para assistir as mudanças nos arquivos index.html
e style.css
.
Agora execute no seu terminal o comando:
1 | gulp |
Abra no seu navegador, o endereço http://localhost:8080
e você verá uma página em branco. Deixe o navegador e o seu editor lado a lado, e edite o arquivo index.html
, colocando um conteúdo básico:
1 |
|
Ao salvar o arquivo, você já vai ver que, sem precisar recarregar a tela, o título Gulp Connect & Livereload já irá aparecer!
Agora, brinque um pouco, editando o arquivo style.css
, e veja a mágica acontecendo xD
Você pode usar qualquer estrutura de diretórios, e fazer o livereload
funcionar para qualquer tipo de arquivo, desde que passe corretamente o caminho dos mesmos.
Bastante simples não? E não precisa adicionar nenhum script a mais na sua index.html
! O gulp-connect
já faz todo o trabalho sujo xD
Curtiu a dica? Já usa livereload? Tem algo a complementar? Compartilhe conosco nos comentários!
Até a próxima :D
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
]]>Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1
Depois de aprendermos como criar a view para o formulário, de forma que ela converse com o WP, e inputar o resultado na tela, agora é hora de tratar os dados e enviar o e-mail! Follow-me! :D
A primeira coisa a se fazer, é verificar se essas informações estão vindo através de um verbo POST, pois é assim que o nosso formulário foi configurado para enviar a mensagem. O usuário não deve visualizar nenhuma mensagem antes de executar a ação de enviar o formulário.
No arquivo services/service-contact-form.php
, faça uma adição no método send_contact_form()
:
1 | public function send_contact_form() { |
Pronto! Agora a mensagem só será mostrada se o campo field_name
for enviado via POST. Poderíamos testar por qualquer campo do nosso formulário. Só não testamos por !isset( $_POST )
, pois $_POST
é uma superglobal do PHP, e está sempre disponível, ou seja, isso retornaria sempre true
.
O próximo passo é verificar se todos os campos obrigatórios foram preenchidos:
1 | public function send_contact_form() { |
Passamos para a variável $all_fields
o retorno do método get_fields()
. Ainda precisamos criá-lo. Depois, passamos para $are_all_fields_ok
o retorno do método are_all_fields_ok( $all_fields )
. Com isso, podemos verificar se todos os campos que deveriam estar preenchidos, foram feitos corretamente.
Vamos criar então o método `get_fields(), que retorna um array dos campos, com o valor dele, e se ele é obrigatório. Assim fica mais fácil e organizado para tratar os dados:
1 | private function get_fields() { |
Se não estivéssemos obtendo o resultado de um POST, já teríamos retornado false
na primeira verificação. Logo, podemos usar esse superglobal aqui com segurança.
Criamos o nosso método como private
, pois não precisamos que ele seja acessado externamente. Basicamente o que esse método faz é criar um array com cada campo, onde o valor do campo é passado para value
e se o campo é obrigatório ou não, é passado para is_required
. O WordPress tem uma função chamada sanitize_email()
(usada acima), que faz a limpeza dos caracteres do campo. Se houver qualquer tentativa de inserção de tags HTML, script, ou qualquer código malicioso, o valor não será enviado. Só serão aceitos caracteres válidos para um campo de e-mail. O mesmo para sanitize_text_field()
.
Já a função wp_kses()
- KSES Strips Evil Scripts - remove tags HTML. No segundo parâmetro você pode passar as tags permitidas. No nosso caso, vamos permitir somente quebras de linha (br
).
Em posts futuros, falaremos com mais detalhes sobre essas funções. Por enquanto é isso que você precisa saber :)
Já criamos nosso método que retorna todos os campos, agora precisamos verificar se eles são válidos. Crie o método are_all_fields_ok()
:
1 | private function are_all_fields_ok( $fields ) { |
Fazemos um foreach
para passar por todos os campos passados como parâmetro e, se algum campo obrigatório não estiver preenchido, retornamos false
. Se todos os campos obrigatórios estiverem preenchidos, então retornamos true
.
Perceba que aqui, para verificar se algum campo obrigatório está em branco, nós usamos o método is_field_required_empty()
, passando como parâmetro o campo que está sendo iterado no foreach
.
Vamos criar esse método?
1 | private function is_field_required_empty( $field ) { |
Pronto! Essa função verifica se nosso capo é obrigatório e se ele está vazio! Se estiver, então não devemos enviar o formulário! Vamos mostrar uma mensagem de erro ao usuário. Edite o método send_contact_form()
:
1 | public function send_contact_form() { |
Se você tentar fazer o envio do formulário agora, sem preencher todos os campos obrigatórios, você irá ver a mensagem de erro :D
Legal! E agora, qual o próximo passo?
O próximo passo é verificar se não estamos recebendo algum SPAM, testando aquele campo que deixamos oculto.
Vamos fazer primeiro a validação no nosso método send_contact_form()
:
1 | public function send_contact_form() { |
É hora de criar um outro método para verificação. Vamos chamá-lo de is_spam()
, para verificar se tem algum robô engraçadinho tentando nos sacanear:
1 | private function is_spam() { |
Criamos 3 novos métodos para verificação de SPAM: O método has_malicious_content()
, irá verificar se foi inserido algo com bcc:
, cc:
, multipart
, [url
, [link
e Content-Type
, ignorando case sensitive (/i
), em todos os campos.
O has_more_than_three_links()
, verifica se existem mais de 3 links sendo enviados. Se você achar que 3 é muito, pode diminuir para o seu caso. Aqui usamos preg_match_all
ao invés de preg_match
, pois ele retorna a quantidade de vezes que um valor da regex foi encontrado;
E por último, mas não menos importante, temos o spam_field_is_filled()
, onde verificamos o nosso campo de e-mail fake, pra saber se ele foi preenchido.
Se qualquer uma dessas condições for verdadeira, entendemos que estão tentando nos enviar SPAM, e ignoramos o envio, retornando true
para is_spam()
. Caso contrário, retornamos false
.
Não precisamos do else
pois, sempre que um valor é retornado (usando return
), o PHP nem continua a leitura do que vem depois desse código. Assim deixamos também nosso método mais legível.
Bom, se olharmos para o nosso método principal, dá pra perceber que ele está um pouco verboso demais. Estamos retornando dois arrays (convertidos para objeto), com a mesma estrutura, mas que muda somente a mensagem interna. Vamos deixar isso mais organizado, e colocar essas mensagens também em um método. Até porque, ainda vamos precisar de mais uma mensagem: a de sucesso.
Editando então nosso método send_contact_form()
:
1 | public function send_contact_form() { |
Vamos obter, através de um método chamado get_status_message()
, as mensagens de status, conforme o parâmetro passado para esse método.
Nosso método get_status_message()
vai ficar assim:
1 | private function get_status_message( $status = 'success' ) { |
Não adianta nada tirar uma tripa de arrays do método principal e simplesmente jogar em outro método, concorda? Por isso, vamos criar um método para cada mensagem. Passamos como padrão o status success
, ou seja, se esse método for chamado sem nenhum parâmetro, retornamos a mensagem de sucesso :)
No final, o array retornado é o objeto que precisamos na nossa view :D
Vamos ver como irão ficar esses 3 novos métodos com os status:
1 | private function get_status_message_success() { |
Ah, agora sim! Bem melhor! Cada método fica pequeno, legível, e com uma responsabilidade única :)
Agora, se você tentar enviar o e-mail, preenchendo o campo de e-mail fake, vai receber uma mensagem de erro :D
Estamos quase lá! Vamos então fazer a última verificação: tentar enviar o e-mail, e verificar se este foi enviado com sucesso. No nosso método send_contact_form()
, adicione:
1 | public function send_contact_form() { |
Nessa última verificação, vamos ver se a mensagem foi enviada corretamente. Invocamos o método message_sent()
, passando os campos como parâmetro já dentro de um if
, pois o retorno dele vai nos dizer se a mensagem foi enviada com sucesso ou não.
Se não foi, retornamos a mesma mensagem que é enviada quando o usuário tenta enviar um SPAM. Aqui você pode criar sua própria mensagem se preferir. Como é a mesma mensagem, poderíamos ter juntado, no mesmo if
do is_spam()
. Mas assim facilita a leitura, e você consegue ver exatamente quais passos estão sendo feitos antes de tentar enviar a mensagem.
E o último return
, envia a mensagem com status de sucesso. Se não deu nenhum erro, quer dizer que foi tudo bem :)
O parâmetro success
é opcional, pois deixamos setado como padrão no método get_status_message()
. Vamos deixar assim só para ficar mais legível ;)
Vamos então criar nosso método que envia o e-mail:
1 | private function message_sent( $fields ) { |
Aqui executamos o retorno da função wp_mail()
, que retorna true
se conseguiu enviar o e-mail, e false, caso contrário. Mas antes, nós configuramos algumas variáveis, até para saber a ordem correta dos parâmetros passados para a função wp_mail()
.
O primeiro é o $to
, que é o e-mail que vai receber a mensagem. O segundo, $subject
, é o assunto do e-mail. No nosso formulário já tem esse campo, mas ele não é obrigatório. Vamos então deixar uma mensagem padrão antes de concatenar o valor desse campo, para que a gente saiba que o e-mail foi enviado pelo formulário de contato do site.
Como nós vamos configurar nossa mensagem para ser enviada em HTML, vamos colocá-la em um método separado, para não deixar esse muito grande.
Ainda podemos configurar alguns $headers
, como Reply-To:
, que é o e-mail para quem nós devemos responder, ao clicar no botão Responder do seu e-mail. Podemos também configurar para recebermos uma cópia (Cc:
), ou cópia oculta (Bcc:
) em outro e-mail. Se esse formulário envia e-mail para o seu cliente, é importante saber se os e-mails estão chegando corretamente, então você pode configurar esses campos de cópia. Ou ainda simplesmente para enviar para mais de uma pessoa de uma só vez.
Não vamos enviar nenhum anexo, então deixamos o $attachments
em branco.
Agora só falta nosso método que gera a mensagem:
1 | private function message_to_sent( $fields ) { |
Um HTML simples, que retorna uma string com algumas tags, para deixar o e-mail mais organizado. Utilizamos sprintf()
aqui para substituir os %s
, passados na string. A partir do segundo parâmetro dessa função, o PHP começa a substituir cada %s
por um desses parâmetros, nessa ordem.
No final, retornamos a string da mensagem :)
E assim nós terminamos nosso formulário de contato, com várias validações importantes, e sem precisar de plugin algum! Claro que poderíamos fazer muitas outras melhorias, como por exemplo, colocar um metabox na página de contato para que o cliente adicione os e-mails que ele quer usar para receber o contato, mas isso é assunto para outro post!
Até o próximo! :D
Link para o índice dessa série:
https://blog.da2k.com.br/2015/01/11/indice-da-serie-como-criar-temas-para-wordpress/Post anterior:
https://blog.da2k.com.br/2015/02/24/wordpress-criando-um-formulario-de-contato-sem-plugin-parte-2/Próximo post:
EM BREVE!
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
]]>Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1