red and yellow hatchback axa crash tests

Como usar testify para escrever testes

Se você ainda não conhece, Testify é um conjunto de ferramentas para nos auxiliar na hora de escrever testes e mocks.

Por ser uma “casquinha” feita em cima dos próprios testes do Go, a utilização desse package não deve causar grandes problemas.

Na data em que escrevo esse post, o Testify é dividido em 4 packages:

Para começar, considerando o código abaixo, vamos ver como o package assert pode nos ajudar.

Leia mais »
depth of field photography of file arrangement

Como listar arquivos de um diretório

Nesse post vou mostrar três formas diferentes para ler um diretório com Golang.

[SPOILER ALERT] No final desse post vou mostrar como eu apliquei um dos exemplos para poder ler um diretório que continha mais de 3.6 milhões de arquivos.

Vamos iniciar os exemplos utilizando a função ReadDir do package ioutil.

package main

import (
    "fmt"
    "io/ioutil"
    "log"
)

func main() {
    files, err := ioutil.ReadDir("/tmp/")
    if err != nil {
        log.Fatal(err)
    }

    for _, file := range files {
        fmt.Println(file.Name(), file.IsDir())
    }
}
Leia mais »
view of landslide

Como reutilizar o response body de uma request HTTP

Algumas semanas atrás, enquanto eu estava desenvolvendo um web crawler junto com um dos alunos de mentoria aqui do Aprenda Golang, nos deparamos com um problema onde precisávamos fazer o parse do resp.Body duas vezes, sendo a primeira para salvar o body no banco de dados e a segunda para extrair os links do página.

Para ficar mais claro, vamos ver o código abaixo.

// Executa a request
resp, err := http.Get(website)
if err != nil {
	log.Println("ERROR:", err)
	return
}
defer resp.Body.Close()

// Extrai conteúdo para salvar no banco de dados
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
	panic(err)
}

// Retorna uma struct para extrair os links sem regex
body, err := html.Parse(resp.Body)
if err != nil {
	panic(err)
}
Leia mais »
gray concrete road near forest

Mutex ou channels? Como resolver race condition

Existem basicamente duas formas para resolver problemas de race condition. Mutex e Channels.

Se você não está familiarizado com o termo race condition, não se preocupe. Esse termo é utilizado para descrever um pedaço do código que será executado por múltiplas goroutines e que, a cada execução, seu resultado pode variar devido a forma como o Go alterna a execução entre goroutines.

Para ficar um pouco mais claro, vamos dar uma olhada no código abaixo.

package main

import (
	"fmt"
	"sync"
)

var total = 0

func count(wg *sync.WaitGroup) {
	total++
	wg.Done()
}

func main() {
	var wg sync.WaitGroup

	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go count(&wg)
	}
	wg.Wait()
	fmt.Println("total: ", total)
}
Leia mais »
snow top mountain under clear sky

Como diminuir o tamanho da sua aplicação com ldflags

Como você deve ter visto no vídeo que postamos no canal, uma das formas de reduzir o tamanho de uma imagem docker para aplicações Go é utilizando a imagem scratch como base.

Mas e o binário? Como podemos reduzi-lo sem remover código?

É isso que vamos ver nesse post.

Para que tenhamos uma base de programa para testar os comandos que vamos ver nesse post, vamos escrever uma API bem simples.

package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(rw, "Olá Mundo\\n")
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}
Leia mais »
view of elephant in water

Como funciona a gestão de conexões com banco de dados do package database/sql

Uma coisa que sempre tive dúvidas era sobre como o package database/sql geria as conexões com banco de dados. A forma como eu costumava trabalhar era abrir uma nova conexão a cada request recebida, fechando-a ao final.

Mas será que essa é a melhor forma?

A resposta curta é NÃO. Mas como eu sei que você, assim como eu não se satisfaz com uma resposta simples dessa, vamos entender melhor como o package padrão do Go gerencia as conexões.

A primeira coisa que precisamos ter em mente, é que sempre que abrimos uma nova conexão utilizando o sql.Open, ele nos retornará uma struct do tipo *sql.DB.

Essa struct, além de ser segura para ser utilizada em múltiplas goroutines, ou seja, “thread-safe”, ela não lida somente com uma conexão, mas sim com um pool de conexões.

Leia mais »

Como utilizar live reload para melhorar sua produtividade

Se você se sente incomodado em ter que parar sua aplicação e fazer go run novamente toda vez que faz uma alteração, esse post vai melhorar sua vida… e MUITO.

Nesse post vou te mostrar como fazer live reload de aplicações Go utilizando uma ferramenta open source chamada Air (https://github.com/cosmtrek/air).

Para testar as funcionalidades da ferramenta, vamos escrever um pequeno programa que retorna um “Olá mundo”.

Vamos inicializar um novo módulo com o comando go mod init github.com/aprendagolang/live-reload e criar um arquivo main.go com o seguinte conteúdo.

package main

import (
    "net/http"

    "github.com/go-chi/chi/v5"
)

func main() {
    r := chi.NewRouter()

    r.Get("/", func(rw http.ResponseWriter, r *http.Request) {
        rw.Write([]byte("Olá Mundo"))
    })

     http.ListenAndServe(":8080", r)
}

Como estamos utilizando uma dependência externa, vamos executar o go mod tidy para atualizar nosso go.mod, que deve ficar parecido com isso:

module github.com/aprendagolang/live-reload

go 1.18

require github.com/go-chi/chi/v5 v5.0.7

Com nosso pequeno programa escrito, vamos instalar o Air.

Por ser escrito em Go, podemos fazer sua instalação utilizando o próprio install do Go.

go install github.com/cosmtrek/[email protected]

Para testar se deu tudo certo com a instalação, execute um air -v no seu terminal. Se a instação ocorreu sem problemas, você deverá ver o seguinte output:

Agora vamos criar um arquivo de configuração para o Air com o comando air init. Esse comando irá criar um arquivo chamado .air.toml com o seguinte conteúdo.

root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"

[build]
  args_bin = []
  bin = "./tmp/main"
  cmd = "go build -o ./tmp/main ."
  delay = 1000
  exclude_dir = ["assets", "tmp", "vendor", "testdata"]
  exclude_file = []
  exclude_regex = ["_test.go"]
  exclude_unchanged = false
  follow_symlink = false
  full_bin = ""
  include_dir = []
  include_ext = ["go", "tpl", "tmpl", "html"]
  kill_delay = "0s"
  log = "build-errors.log"
  send_interrupt = false
  stop_on_error = true

[color]
  app = ""
  build = "yellow"
  main = "magenta"
  runner = "green"
  watcher = "cyan"

[log]
  time = false

[misc]
  clean_on_exit = false

[screen]
  clear_on_rebuild = false

Uma pequena mudança que eu gosto de fazer é mudar o time para true. Porém se você não quiser, não precisa alterar nada nesse arquivo.

Agora tudo que precisamos fazer é executar o comando air no terminal e nosso live reload já estará funcionando.

Para testar o live reload, vamos modificar o nosso r.Get("/", ... para o seguinte:

r.Get("/{name}", func(rw http.ResponseWriter, r *http.Request) {
    name := chi.URLParam(r, "name")

    rw.Header().Add("Content-Type", "application/json")
    json.NewEncoder(rw).Encode(map[string]string{
        "message": fmt.Sprintf("Olá %s!!!", name),
    })
})

Como podemos ver no terminal, a aplicação foi recompilada automaticamente.

É isso, espero que essa dica ajude a melhorar sua produtividade.

Deixem suas dúvidas nos comentários.

Até a próxima!


Subscreva

Fique por dentro de tudo o que acontece no mundo Go.

Como executar migrations de forma automatizada

Se você não é muito fã de ORMs como eu, um dos problemas que você mais enfrenta é o de como realizar alterações em seu banco de dados de forma segura e automática.

Nesse post, vou mostrar como fazer isso utilizando o Migrate (https://github.com/golang-migrate/migrate), um projeto open source escrito em Go para realizar migrations em bancos de dados.

Antes de começar, embora os exemplos contidos nesse tutorial serem utilizando postgres, o Migrate suporta os seguintes bancos de dados:

mongodb+srv, firebirdsql, clickhouse, cockroachdb, mongodb, mysql, sqlserver, cassandra, crdb-postgres, postgres, postgresql, spanner, stub, cockroach, neo4j, pgx, redshift, firebird.

Tendo esclarecido esse ponto, vamos iniciar um projeto com nome github.com/aprendagolang/migrate (go mod init github.com/aprendagolang/migrate) e depois escrever uma pequena API.

Leia mais »

Entenda o que são os arquivos go.mod e go.sum

Se você já trabalhou ou conhece um pouco de outras linguagens, podemos comprar o arquivo go.mod ao package.json do JS, composer.json do PHP ou requirements.txt do Python.

Ou seja, o arquivo go.mod, nada mais é do que o arquivo onde o Go vai gerenciar a lista de pacotes que sua aplicação precisa para funcionar.

Além das dependências, é nesse arquivo onde o Go adiciona o nome do seu package e a versão do Go que estava sendo utilizada no momento da criação do projeto.

Essa segunda informação é muito valiosa, pois com base nela o Go vai saber quais versões das dependências externas ele pode ou não utilizar.

Leia mais »

Como trabalhar com herança em Go

Uma dúvida que tenho visto com certa frequência é sobre as classes e herança em Go.

Sempre lembrando que GO não tem orientação a objetos na sua forma original de ser. Porém, quando o assunto é classe e herança, é possível alcançar algo similar utilizando structs e a técnica de embedding.

Tento esclarecido isso, vamos ver como podemos utilizar “herança” em Go imaginando um banco de dados com 4 tabelas.

imagem meramente ilustrativa 😉
Leia mais »