Let it Groovy!

Este blog deixou de ser mantido, mas o autor continua escrevendo aqui. Não deixe de assinar o novo feed!

GroovyDepois de mostrar aqui os procedimentos de instalação do Grails, eu aproveitei este fim de semana para aprender um pouco mais dos recursos que este framework oferece a nós, desenvolvedores.

Se você não conhece o Grails, faça um favor para si mesmo e reserve um tempo para fazer uns testes. Trata-se de um framework excelente, capaz de simplificar muito as coisas e tornar o desenvolvimento de aplicações web bem mais… hmm… divertido. Sim, divertido. No entanto, este não é mais um post com o objetivo de puxar a sardinha para o lado do Grails, mas sim para o lado do Groovy (tá bom, no fim das contas dá na mesma). :D

A surpresa…

Apesar de parecer óbvio, eu estava a pensar que o Grails por era, por si só, o grande merecedor de todos esses elogios. Entretanto, a verdade não é bem essa. O que ocorre é que o grande responsável pelo sucesso (ou até viabilização, eu diria) do Grails é o Groovy. O legal de estar gastando algum tempo estudando o Grails é ter uma boa oportunidade para aprender a programar em Groovy por osmose. Algo semelhante acontece com o pessoal do Rails em relação ao Ruby.

Voltando ao Groovy, para quem não conhece, esta é uma linguagem de script para a plataforma Java. De acordo com o site do projeto, podemos encarar sua sintaxe como sendo um pouco de Python, Ruby e Smalltalk. Então, para ter uma idéia do gosto dessa gororoba toda, misture tudo isso no liqüidificador, adicione Java a gosto e voilá! :P

Para mostrar na prática um pouco sobre o Groovy, implementaremos duas classes que serão responsáveis por gerar e codificar senhas. Mesmo sendo algo trivial (quase todo mundo já implementou algo parecido), são exemplos ótimos e que mostram muito bem a integração entre scripts Groovy e classes Java, além de sintetizar algumas construções importantes da linguagem. Veremos também como implementar testes unitários para validar o funcionamento dos nossos scripts Groovy.

Introdução ao Groovy!

Vamos aos códigos! Se você ainda não possui o Groovy instalado, veja aqui como fazer a instalação e configuração em um sistema Linux.

Com o editor de sua preferência, crie um arquivo chamado PasswordUtils.groovy, em um diretório qualquer. Coloque o seguinte conteúdo:

  1.  
  2. class PasswordUtils {
  3.  
  4. }
  5.  

Esta é a estrutura básica de uma classe Groovy. Apesar de dividirmos o código em classes, no Groovy não somos obrigados a criar classes para todo e qualquer código, assim como acontece em Java.

Nesta classe iremos definir dois métodos: (1) um para gerar senhas e (2) outro para codificar (criptografar) senhas.

Vamos implementar a funcionalidade (1). Para definirmos os caracteres que poderão ser utilizados em uma senha, iremos definir um vetor constante de caracteres e o método que sorteia caracteres deste vetor para compor a senha:

  1.  
  2. class PasswordUtils {
  3.  
  4.     private static def charmap = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’,
  5.                                   ‘k’, ‘l’, ‘m’, ‘n’, ‘o’, ‘p’, ‘q’, ‘r’, ’s’, ‘t’,
  6.                                   ‘u’, ‘v’, ‘w’, ‘x’, ‘y’, ‘z’, ‘1′, ‘2′, ‘3′, ‘4′,
  7.                                   ‘5′, ‘6′, ‘7′, ‘8′, ‘9′, ‘0′]
  8.  
  9.     private static def charmapSize = charmap.size()
  10.  
  11.     def generate(passwordSize = 7) {
  12.  
  13.         def result =
  14.         passwordSize.times {
  15.             result += charmap[(int) (Math.random() * charmapSize)]
  16.         }
  17.         result
  18.     }
  19. }
  20.  

Vamos devagar. Primeiramente note a ausência dos ‘;’ no final de cada instrução. O uso do ponto-e-vírgula é opcional.

Nas linhas 3, 4, 5 e 6 definimos um vetor com os caracteres permitidos para a senha gerada automaticamente. O private static já é familiar para nós, Javeiros, e significa que trata-se de uma variável privada de classe. O def serve para definir que a variável pode assumir valores de diversos tipos… então, se você precisa de uma variável para guardar diferentes tipos de dados, especifique def no lugar do tipo. Neste caso, como o tipo da variável não muda, poderíamos declarar o tipo da variável como String[] em vez de def. Mas, como sou preguiçoso, preferi o def :D

Na linha 8, definimos uma outra variável estática que irá guardar o número de elementos do vetor explicado anteriormente. Nada de diferente aqui.

Na linha 10, definimos um método chamado generate(). Duas coisas importantes aqui: o uso do def como tipo de retorno e o uso de um parâmetro opcional. A explicação para o def é a mesma dada anteriormente e, em relação ao parâmetro opcional, é bem provável que você já tenha visto isso em alguma linguagem. Esta estrutura basicamente indica que, se chamarmos o método generate() sem passar nenhum parâmetro, o inteiro 7 será utilizado como um valor-padrão, resultando na geração de uma senha com 7 caracteres. Caso o parâmetro seja fornecido, ele será utilizado no lugar do valor-padrão. Sempre senti falta disso no Java, mesmo sendo possível simular um comportamento semelhante (através de sobrecarga de métodos… credo!).

Na linha 12 definimos e inicializamos uma variável local de método.

Na linha 13 utilizamos uma estrutura muito interessante do Groovy (e do Ruby): o método times, presente na classe Number. Este método aceita uma closure como parâmetro e a executa pelo número de vezes correspondente ao valor do Number em questão. Por exemplo, o código abaixo imprime os números de 0 a 9, um em cada linha:

  1.  
  2. 10.times {println it}
  3.  

O 10 é uma instância de Number. Os caracteres { e } delimitam uma closure (código a ser executado). O it dentro da closure é uma variável que armazena o elemento sendo iterado no momento da execução da closure: na primeira vez em que a closure é executada, o it vale 0, na segunda vale 1 e assim sucessivamente.

Continuando a explicar a classe PasswordUtils, na linha 14 concatenamos o caractere sorteado através do método Math.random() (que está limitado a retornar o índice correspondente a um dos caracteres do vetor charmap).

Finalizando este método, na linha 16 retornamos a senha pronta. Neste caso, a palavra-chave return é totalmente dispensável, fazendo com que o Groovy considere esse último valor como sendo o retorno do método (parecido com Ruby).

Quer rodar o script? Abra o terminal e vá até o mesmo diretório onde a classe PasswordUtils está localizada. Digite os comandos:

  1.  
  2. import java.security.*
  3.  
  4. class PasswordUtils {
  5.  
  6.     private static def charmap = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’,
  7.                                   ‘k’, ‘l’, ‘m’, ‘n’, ‘o’, ‘p’, ‘q’, ‘r’, ’s’, ‘t’,
  8.                                   ‘u’, ‘v’, ‘w’, ‘x’, ‘y’, ‘z’, ‘1′, ‘2′, ‘3′, ‘4′,
  9.                                   ‘5′, ‘6′, ‘7′, ‘8′, ‘9′, ‘0′]
  10.  
  11.     private static def charmapSize = charmap.size()
  12.  
  13.     def generate(passwordSize = 7) {
  14.  
  15.         def result =
  16.         passwordSize.times {
  17.             result += charmap[(int) (Math.random() * charmapSize)]
  18.         }
  19.         result
  20.     }
  21.  
  22.     def digest(password) {
  23.  
  24.         def digest = []
  25.         if (password != null) {
  26.             def digester = MessageDigest.getInstance(‘SHA-1′)
  27.             digester.update(password.getBytes())
  28.             digest = digester.digest()
  29.         }
  30.  
  31.         // StringUtils.getHexString(digest)
  32.     }
  33. }
  34.  

Note a importação do pacote java.security na linha 1. Fazemos a importação deste pacote para que possamos utilizar as classes Java pertencentes a este pacote sem que seja necessário utilizar as classes através de seus nomes “totalmente qualificados”. Em outras palavras, é igual a Java! :P

Na linha 21 definimos o método digest() conforme já estamos habituados.

Na linha 23 definimos uma variável inicializada com um vetor vazio.

A novidade está mesmo nas linhas 25, 26, 27 e 30. Nas três primeiras linhas obtemos um MessageDigest “SHA-1″ para digerir (deu até ânsia de vômito agora :D ) a senha em um código hash. O interessante aqui é que a classe MessageDigest é uma classe Java! Veja a simplicidade em se utilizar classes Java dentro de um script Groovy!

Então, o código hash gerado (ou digerido, argh) vêm em forma de um vetor de bytes que será utilizado pelo método StringUtils.getHexString(), na linha 30. Ainda não implementamos este método, por isso a linha está comentada.

Então, vamos criar essa classe que falta. Crie o arquivo StringUtils.groovy com o seguinte conteúdo:

  1.  
  2. class StringUtils {
  3.  
  4.     static def getHexString(digest) {
  5.  
  6.         def result =
  7.         digest.each {
  8.             def hex = Integer.toHexString(it)
  9.             if (hex.length() == 1) hex = ‘0′ + hex
  10.             hex = hex.substring(hex.length() - 2)
  11.             result += hex
  12.         }
  13.  
  14.         result
  15.     }
  16. }
  17.  

… aproveite e descomente a linha 30 da classe PasswordUtils.

Na linha 3 definimos o método estático getHexString().

Na linha 6 temos novamente um trecho interessante. Como o parâmetro recebido é um vetor, todo vetor possui um método each, que recebe uma closure. Esta closure é executada uma vez para cada elemento contido no vetor.

Por exemplo, o script abaixo imprime os caracteres ‘a’, ‘b’ e ‘c’ (que são os elementos do vetor):

  1.  
  2. [‘a’, ‘b’, ‘c’].each{println it}
  3.  

Voltando ao código da classe StringUtils, dentro da closure, obtemos o valor hexadecimal correspondente ao byte armazenado na variável it e concatenamos no resultado, gerando assim uma String que representa o vetor de bytes.

Na linha 13 retornamos o resultado do método.

Vamos rodar o script:

  1.  
  2. class PasswordUtilsTests extends GroovyTestCase {
  3.  
  4.     def passwordUtils = new PasswordUtils()
  5.  
  6.     void testGenerate() {
  7.  
  8.         def result = passwordUtils.generate() // 7 é o comprimento padrão
  9.         assert result != null
  10.         assert result.length() == 7
  11.  
  12.         result = passwordUtils.generate(10) // outro comprimento
  13.         assert result != null
  14.         assert result.length() == 10
  15.     }
  16.  
  17.     void testDigest() {
  18.  
  19.         def result
  20.         def password = ‘admin_password’
  21.         def password_hash = ‘407c6798fe20fd5d75de4a233c156cc0fce510e3′
  22.  
  23.         result = passwordUtils.digest(null) // testa com valor nulo
  24.         assert result ==
  25.  
  26.         result = passwordUtils.digest(password) // teste válido
  27.         assert result == password_hash
  28.     }
  29. }
  30.  

Na linha 1 declaramos a classe PasswordUtilsTests, que estende da classe GroovyTestCase. Esta (e outras) classes servem para implementar um mini-framework de testes unitários, parecido com o JUnit.

Na linha 3 declaramos uma variável de instância com o objeto a ser testado.

Nas linhas seguintes declaramos métodos onde o objeto é efetivamente testado. Para verificar se as saídas correspondem ao esperado, utilizamos a palavra-chave assert.

Para rodar os testes, digite no terminal:

  1.  
  2. class StringUtilsTests extends GroovyTestCase {
  3.  
  4.     void testGetHexString() {
  5.  
  6.         def array = [0×7a, 0×75, 0×18, 0xAF] // bytes em hexadecimal para teste
  7.  
  8.         def result = StringUtils.getHexString(null) // testa com variável nula
  9.         assert result ==
  10.  
  11.         result = StringUtils.getHexString([]) // testa com vetor vazio
  12.         assert result ==
  13.  
  14.         result = StringUtils.getHexString(array) // teste válido
  15.         assert result == ‘7a7518af’
  16.     }
  17. }
  18.  

Nenhuma novidade aqui. Para testar, utilize o executável groovy igual fizemos com a classe PasswordUtilsTests:

groovy StringUtilsTests.groovy

Conclusão

34660631_ddcd89b91c_m.jpgVocê é um daqueles programadores Java rabugentos que vivem reclamando da sintaxe do Ruby, dizendo que é muito diferente do que você está acostumado, e todo aquele blá-blá-blá? Então, Groovy é a sua linguagem! Com ela, temos uma sintaxe enriquecida de facilidades e novos recursos, sem que precisemos abrir mão do “jeitão Java de ser”. Isso nos permite estudar as nuances da linguagem aos poucos, sem aquela sensação “I Suck!” que costumamos sentir ao aprender algo completamente novo.

Quanto à aplicabilidade do Groovy no mercado, bom, o Grails é a prova viva (?) de que o Groovy será cada vez mais empregado pelos frameworks Java disponíveis hoje no mercado e saber trabalhar com ele será um grande diferencial a um médio-prazo. Acha que é mentira? Entre esses frameworks podemos citar o Tapestry e o Mentawai. Além do uso em frameworks, o Groovy já é suportado por grande parte das IDEs consideradas respeitáveis, como o NetBeans, IntellijIDEA, Eclipse, JDeveloper, entre outras.

Então, aumente o som no último volume e let it groovy!

Foto por: “SIR: Poseyal

Tags: , , , , , , , ,