Introdução ao Revel Framework

Walisson de Deus Casonatto
Quick Mobile Labs
Published in
6 min readMay 16, 2016

Quando comecei a testar GO, eu imaginei o quanto seria massa ter uma aplicação web baseada nessa linguagem. Então comecei a estudar como fazer essa aplicação, porém achei extremamente complicado. Passei então a procurar um framework e assim cheguei ao Revel. Pretendo deixar aqui uma introdução ao Revel, que é um framework simples e solucionou meu problema de como fazer um aplicação web em GO.

Já que montar um ambiente GO é um pouco problemático, fiz uma imagem do Docker. Mas, se você quiser, pode instalar o GO e o Revel em sua máquina.

Para baixar imagem do Docker acesse: https://hub.docker.com/r/walissoncasonatto/revel-docker/

Ou para instalar diretamente acesse: https://revel.github.io/tutorial/gettingstarted.html

Caso você tenha escolhido usar o docker:

$ docker run --name docker-revel -e APP_NAME=myappname  -v <caminho-onde-a-aplicacão-sera-armazenada>:/root/go/src/myapps walissoncasonatto/revel-docker

Caso tenha escolhido instalar o Revel:

$ Revel new <nome_da_aplicacao>
$ Revel run $GOPATH/src/<nome_da_aplicacao>

Neste caso a aplicação sera armazenada em $GOPATH/src/<nome_da_aplicacao>.

Agora que temos a aplicação é hora de fazer algo nela. Iremos desenvolver uma calculadora que recebe dois valores e efetua a soma.

Caso tenha escolhido o Docker, há um probleminha. Ao criar a aplicação, o Docker a cria como root então é necessário executá-la como root ou utilizar os seguintes comandos para deixá-la editável:

find /path/to/base/dir -type d -exec chmod 777 {} +

find /path/to/base/dir -type f -exec chmod 666 {} +

Não é necessário parar o Revel para fazer alterações no projeto, pois ele compila em tempo de execução. Isso acontece apenas quando se está no modo desenvolvedor, quando está em modo de produção ele não compila dessa forma.

Routes

Para criar uma rota devemos apenas adicioná-la em <diretorio_da_aplicação>/conf/routes.

Já existem algumas rotas padrões. Vamos adicionar a nossa logo abaixo a principal.

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
module:testrunnerGET / App.Index
GET /calc/ Calc.Index
# Ignore favicon requests
GET /favicon.ico 404
# Map static resources from the /app/public folder to the /public path
GET /public/*filepath Static.Serve(“public”)
# Catch all
* /:controller/:action :controller.:action

A sintaxe é simples:

TIPO_REQUISICAO         CAMINHO        CONTROLLER
GET / App.Index

Controller

Agora que temos nossa rota devemos criar uma controller. Nossa controller deverá ficar localizada no diretório: <diretorio_da_aplicação>/app/controllers.

Criaremos com o nome calc.go.

Vamos adicionar o “package controller”. Como padrão toda controller do Revel estende a classe revel.Controller. O nome da Controller deve ser o mesmo declarado no arquivo routes.

package controllersimport (
“github.com/revel/revel”
)
type Calc struct {
*revel.Controller
}

Agora criaremos um método para renderizar a pagina Index. Esse método será chamado na Rota que adicionamos ao arquivo routes.

[...]
func (c Calc) Index() revel.Result {
title := "calculadora"
return c.Render(title)
}

As páginas do Revel recebem quantos atributos você desejar. O title, por exemplo, é um atributo que pode ser recebido. Iremos definir ele no template passando a variável e resgatando ela na view.

Nosso código ficará assim:

package controllersimport (
"github.com/revel/revel"
)
type Calc struct {
*revel.Controller
}
func (c Calc) Index() revel.Result {
title := "calculadora"
return c.Render(title)
}

View

Vamos criar uma view básica.

Por padrão as views de uma controller ficam em: /<nome_da_controller>/<ação>

Então criaremos o diretorio Calc em: <diretorio_da_aplicação>/app/views

Dentro do diretório Calc criaremos a página index.html.

GO é case sensitive, então cuidado com os nomes!


{{template "header.html" .}}
<p> funciona </p>{{template "footer.html" .}}

Testes

O Revel tem um lugar especifico para testes. Está localizado em <diretório_da_aplicação>/testes

Vamos adicionar um teste básico para nossa aplicação. Iremos verificar com esse teste se a página Index está sendo renderizada corretamente.

package testsimport (
“github.com/revel/revel/testing”
)
type CalcTest struct {
testing.TestSuite
}
func (t *CalcTest) Before() {
println("Set up")
}
func (t *CalcTest) TestCalcPageWork() {
t.Get("/calc/")
t.AssertOk()
t.AssertContentType("text/html; charset=utf-8")
}
func (t *CalcTest) After() {
println("Tear down")
}

Classes de teste do revel devem extender testing.TestSuite.

Se seu Revel ainda está em execução basta apenas testar sua aplicação. Entretanto, caso você tenha parado o Revel execute o projeto novamente ou execute docker start (se estiver usando Docker).

O Docker é configurado para não sobreescrever o projeto caso ele já exista. É sua oportunidade de sair dessa vida de instalar aplicações e usar Docker!

Para testar basta acessar

A calculadora que começamos a criar está em http://localhost:9000/calc e os testes estão em http://localhost:9000/@tests.

Bom, agora que temos nossa página Calculadora vamos transformar ela em uma calculadora de verdade!

Primeiro devemos adicionar uma rota POST para nossas ações.

POST    /calc/:action                           Calc.:action

Usando :action estaremos atribuindo qualquer ação passada depois de calc à um método da nossa controller.

ex: /calc/Somar chamará o método Somar.

Nosso arquivo final de rotas deve estar assim:

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
module:testrunnerGET / App.Index
GET /calc/ Calc.Index
POST /calc/:action Calc.:action
# Ignore favicon requests
GET /favicon.ico 404
# Map static resources from the /app/public folder to the /public path
GET /public/*filepath Static.Serve("public")
# Catch all
* /:controller/:action :controller.:action

Agora devemos criar ométodo Somar em nossa controller que receberá dois valores e redirecionará para index o valor total da soma.

[...]
func (c Calc) Somar(valor1 int, valor2 int) revel.Result {
total := valor1 + valor2
return c.Redirect(routes.Calc.Index(total))
}

Precisamos alterar nosso método Index para receber este total.

[...]
func (c Calc) Index(total int) revel.Result {
title := "calculadora"
return c.Render(title,total)
}
[...]

Veja que usamos routes.Calc.Index e que não o importamos. Bom, não tem como isso funcionar, então importe o routes da sua aplicação. O Revel solucionará até o $GOPATH:

package controllersimport (
"github.com/revel/revel"
"myapps/webapp2/app/routes"
)

Se você esta usando Docker, saiba que ele cria um diretório myapps antes do diretório da sua aplicação. Por exemplo, minha aplicação chama webapp2.

Nossa Controller deverá estar assim:

package controllersimport (
"github.com/revel/revel"
"myapps/webapp2/app/routes"
)
type Calc struct {
*revel.Controller
}
func (c Calc) Index(total int) revel.Result {
title := "calculadora"
return c.Render(title,total)
}
func (c Calc) Somar(valor1 int, valor2 int) revel.Result {
total := valor1 + valor2
return c.Redirect(routes.Calc.Index(total))
}

Por último mas não menos importante: a view.

Antes de tudo, criaremos um formulário para fazer o POST dos nossos valores:

<form method="POST">  <input type="submit" value="Soma" formaction="{{url "Calc.Somar"}}">
</form>

Logo adicionaremos uma tabela para colocar nossos campos:

<table>
<th>
<td colspan="6">CALCULADORA</td>
</th>
<tr>
<td colspan="2">
<input type="text" name="valor1">
</td>
<td colspan="2">
<input type="text" name ="valor2">
</td>
<td colspan="2">
<input name="total">
</td>
</tr>
<input type="submit" value="Soma" formaction="{{url "Calc.Somar"}}">
</table>

E adicionaremos a integração com o Revel. Para mais detalhes sobre a integração acesse a documentação do Revel.

Nossa view terminará assim:

{{template "header.html" .}}
<form method="POST">
<table>
<th>
<td colspan="6">CALCULADORA</td>
</th>
<tr>
{{with $valor1 := field "valor1" .}}
<td colspan="2"><input type="text" name="{{$valor1.Name}}" value = "{{$valor1.Value}}" style="width:50px;"></td>
{{end}}
{{with $valor2 := field "valor2" .}}
<td colspan="2"><input type="text" name ="{{$valor2.Name}}" value = "{{$valor2.Value}}" style="width:50px;"></td>
{{end}}
{{with $total := field "total" .}}
<td colspan="2"><input name="{{$total.Name}}" value = "{{$total.Value}}" readonly="readonly" style="width:50px;"></td>
{{end}}
</tr>
</table>
<input type="submit" value="Soma" formaction="{{url "Calc.Somar"}}">
</form>
{{template "footer.html" .}}

Agora ao acessar /calc você terá uma calculadora que soma.

UnitTests

Revel ainda não tem suporte para testes unitários, porém é possível testar normalmente usando o padrão do GO.

Também é possível implementar testes usando a interface do Revel, só não será possível obter dados como coverage, ou automatizar os testes.

Vamos então fazer mais um teste no Revel:

Primeiro devemos extraír a lógica da nossa controller para um método testável:

[...]
func (c Calc) Somar(valor1 int, valor2 int) revel.Result {
total := c.Soma(valor1,valor2)
return c.Redirect(routes.Calc.Index(total))
}
func (c Calc) Soma(valor1 int, valor2 int) int{
return valor1+valor2
}

Importaremos a controller em nossa classe de teste da calculadora:

package testsimport (
"github.com/revel/revel/testing"
"myapps/webapp2/app/controllers"
)

Importaremos o atributo calc que será um objeto que representa a controller:

type CalcTest struct {
testing.TestSuite
calc controllers.Calc
}

E criaremos uma função que testa nosso método Soma:

func (t *CalcTest) TestaSoma(){
expected := 10
current := t.calc.Soma(3,7)
t.Assert(expected == current)
}

Nossa classe de testes terminará assim:

package tests
import (
"github.com/revel/revel/testing"
"myapps/webapp2/app/controllers"
)
type CalcTest struct {
testing.TestSuite
calc controllers.Calc
}
func (t *CalcTest) Before() {
println("Set up")
}
func (t *CalcTest) TestCalcPageWork() {
t.Get("/calc/")
t.AssertOk()
t.AssertContentType("text/html; charset=utf-8")
}
func (t *CalcTest) TestaSoma(){
expected := 10
current := t.calc.Soma(3,7)
t.Assert(expected == current)
}
func (t *CalcTest) After() {
println("Tear down")
}

Com isso chegamos ao fim. Espero que estejam familiarizados a ponto de fazer agora um método de subtração com testes.

--

--