Cavando o Fonte #02 – O package Path

Nesse segundo vídeo da série “Cavando o Fonte”, vamos explorar o package path e ver como algumas das suas funções mais utilizadas são implementadas.

Deixem suas dúvidas nos comentários.

Até a próxima!


Se inscreva na nossa newsletter

* indicates required

Formatando strings para logs e mensagens

Se você já trabalha com Go a algum tempo, muito provavelmente você já conhece e utiliza o %s, %d e o \n. Pois bem, nesse post vamos abordar os principais os verbos disponíveis para te ajudar na hora de formatar uma string em Go.

Se você é novo em Go, saiba que os verbos que vamos abordar nesse post podem ser utilizados com as funções Printf, Sprintf e Errorf do package fmt, assim como as funções Fatalf, Panicf e Printf do package log.

Para começar, vamos a um exemplo bem simples utilizando somente os dois verbos que já comentamos. Digamos que nosso programa, sempre que alguém pede um novo café, exibe o nome da pessoa e a quantidade de café que ela consumiu no dia.

func main() {
    name := "Tiago"
    coffee := 5

    fmt.Printf("Olá %s, você já bebeu %d cafés hoje", name, coffee)
}

Ao ser executado, o %s será substituido pelo conteúdo da váriavel name, e o %d pela vafiável coffee.

Leia mais »

Carregando configurações com Viper

Na semana passada, falamos sobre como carregar configurações de um arquivo TOML.

Hoje vamos ver como carregar configurações de arquivos JSON, TOML, YAML, HCL, envfile e Java properties usando uma única dependência, Viper.

Viper é um package utilizado por grande projetos como Hugo, Docker Notary e doctl.

Além de ler todos os tipos de arquivo mencionado acima, ele também consegue ler variáveis de ambiente, etcd, Consul, flags de linhas de comando e buffers.

Sem mais delongas, vamos iniciar nosso tutorial instalando essa dependência em nosso projeto.

$ go get github.com/spf13/viper

Agora que já temos a dependência em nossa máquina, vamos criar um arquivo JSON com nome configs.json para armazenar nossas configurações.

{
  "databases": {
    "mysql": {
      "host": "localhost",
      "port": 3306,
      "user": "admin",
      "pass": "1234abc?"
    },
    "mongodb": {
      "host": "localhost",
      "port": 27017,
      "user": "logs",
      "pass": "log123"
    }
  },
  "environment": "production"
}

Feito isso, vamos criar um arquivo main.go para carregar as configurações. Como queremos carregar as configurações assim que o programa iniciar, vamos adicionar uma função init com as funções do Viper dentro dela.

func init() {
    viper.SetConfigName("configs") // nome do arquivo que queremos carregar
    viper.SetConfigType("json") // extensão do arquivo
    viper.AddConfigPath(".") // caminho onde está o arquivo
    viper.AddConfigPath("/etc/") // caminho alternativo onde está o arquivo
    err := viper.ReadInConfig() // lê o arquivo e carrega seu conteúdo
    if err != nil {
	panic(fmt.Errorf("Erro fatal no arquivo de configuração: %w \n", err))
    }
}

Essa função init é uma função reservado do Go que é chamada durante a inicialização do programa, ou seja, antes da função main.

E por falar em main, vamos criar nossa função main e printar o valor que temos na chave environment.

func main() {
    fmt.Println(viper.GetString("environment"))
}

Para recuperar valores que estão encadeados, tudo que precisamos fazer é chamar todas as chaves usando um . entre elas, como podemos ver no exemplo abaixo, onde vamos printar o host do mysql.

func main() {
    fmt.Println(viper.GetString("environment"))
    fmt.Println(viper.GetString("databases.mysql.host"))
}

No entanto, ficar acessando item por item de uma configuração de banco de dados ou algo mais complexo pode ser bem chato e repetitivo. Para mitigar esse problema, podemos criar uma struct e carregar toda a configuração direto para ela.

type MysqlCfg struct {
    Host string `json:"host"`
    Port int `json:"port"`
    User string `json:"user"`
    Pass string `json:"pass"`
}

func main() {
    fmt.Println(viper.GetString("environment"))
    fmt.Println(viper.GetString("databases.mysql.host"))

    b, err := json.Marshal(viper.Get("databases.mysql"))
    if err != nil {
	panic(err)
    }

    var mysql MysqlCfg
    err = json.Unmarshal(b, &mysql)
    if err != nil {
	panic(err)
    }

    fmt.Println(mysql)
}

Simples não?!

Nos dois exemplos, usamos funções do Viper para retornar valores carregados do arquivo de configuração, sendo o primeiro o GetString que retorna o dado como string e o segundo o Get, que retorna o dado como uma interface{}.

Mas é claro que não existem somente essas duas funções, por isso, abaixo vou deixar a lista de todas as funções que o Viper diponibiliza para recuperar valores do arquivo carregado.

  • Get(key string) : interface{}
  • GetBool(key string) : bool
  • GetFloat64(key string) : float64
  • GetInt(key string) : int
  • GetIntSlice(key string) : []int
  • GetString(key string) : string
  • GetStringMap(key string) : map[string]interface{}
  • GetStringMapString(key string) : map[string]string
  • GetStringSlice(key string) : []string
  • GetTime(key string) : time.Time
  • GetDuration(key string) : time.Duration
  • IsSet(key string) : bool
  • AllSettings() : map[string]interface{}

Para encerrar, vou mostrar uma função que nos ajuda a deixar valores padrão já configurado na aplicação, para caso a configuração não seja colocada no arquivo.

Vamos adicionar as seguintes linhas no final da nossa função init.

viper.SetDefault("environment", "development")
viper.SetDefault("databases.mysql.host", "localhost")
viper.SetDefault("databases.mongodb.host", "localhost")

Feito isso, remova as entradas do arquivo de configurações e execute o go run main.go.

Como você pode notar, ao executar o go run main.go os dois primeiros prints funcionaram normal, já o print da struct não exibiu o host.

Isso acontece pois só definimos um valor padrão para a chave databases.mysql.host e não para databases.mysql.

Espero que tenham gostado.

Deixem suas dúvidas nos comentários.

Até a próxima


Se inscreva na nossa newsletter

* indicates required

Diretórios e packages especiais

No post de hoje vamos falar um pouco sobre nomes reservados para packages e pastas.

Main

Package main é o principal package de um programa Go. É nesse package que vamos colocar nossa função main, que é a primeira função a ser executada.

package main

import "fmt"

func main() {
    fmt.Println("Olá Mundo!")
}

O package main só deve existir em programas executáveis, ou seja, caso você esteja fazendo uma lib, esse package não deve existir.

Leia mais »