Dans ce tutoriel nous allons créer deux modules, Le premier sera une librairie qui sera importée par d’autres librairies ou applications.
Le second module est une application qui va appeler ce premier module.
Ce tutoriel est composé de septs parties
Il faut dans un premier temps créer un module dans lequel on va regrouper dans un ou plusieurs packages des fonctions.
Par exemple créer un module avec des packages qui possède des fonctions qui vont faire de l’analyse financière afin que les autres applications puissent l’utiliser. Pour développer un module vous pouvez regarder Developper et publier des modules.
mkdir salutations
cd salutations
go mod init tokenizers/salutations
Comme vu dans un autre article la commande go mod init
va créer un fichier go.mod
qui va garder les dépendances de notre projet.
Si jamais je publie ce module, alors son path sera tokenizers/salutations
Maintenant nous allons créer un fichier salutations.go et créer une fonction Hello qui va à partir d’un prénom nous renvoyer une salutation sur ce même prénom.
package salutations
import "fmt"
func Bonjour(name string) string {
var message string = fmt.Sprintf("Salutation noble, %v. Bienvenue!", name)
return message
}
Dans ce code nous on déclare un package salutations dans lequel on va écrire une ou plusieurs fonctions. Nous implémentons une fonction bonjour qui à partir d’un nom va dire bonjour.
Remarque : Nous utilisons la fonction Sprintf pour créer un message de salutations, on peut voir cela comme une concaténation.
Nous allons créer un module bonjour qui va pouvoir aller chercher les fonctions du module salutations
<home>/
|-- salutations/
|-- bonjour/
Pour cela même chose on va créer le repertoire bonjour
, initialiser le module
et créer un fichier main
qui pourra être lancé avec la commande go run
package main
import (
"fmt"
"tokenizers/salutations"
)
func main() {
// Get a greeting message and print it.
message := salutations.Bonjour("Gladys")
fmt.Println(message)
}
Ce code ne fonctionne pas car le programme ne trouve pas salutations dans la librairie standard. Et c’est tout à fait normal car il n’est pas publié. pour adapter la résolution de ce module avec un module en local.
go mod edit -replace tokenizers/salutations=../salutations
Une fois cela fait il faut télécharger la librairie, pour cela on va synchroniser avec la command go mod tidy
go mod tidy
go run .
Résultat:
Et voilà nous venons de créer deux modules dont un qui utilise l'autre. Maintenant nous allons voir comment gérer les erreurs
Gérer les erreurs est une fonctionnalité essentielle pour du code robuste.
Nous allons rajouter un bout de code dans le module “salutations” pour gérer ça dans l’appelant.
Pour cela on va tout d’abord importer le package errors
qui va nous permettre de créer des erreurs.
Ensuite il faudra mettre à jour la fonction “Bonjour”
afin qu’elle puisse retourner également une erreur en plus d’une chaine de caractères.
package salutations
import (
"errors"
"fmt"
)
func Bonjour(name string) (string, error) {
// If no name was given, return an error with a message.
if name == "" {
return "", errors.New("Le nom est vide")
}
// If a name was received, return a value that embeds the name
// in a greeting message.
var message string = fmt.Sprintf("Salutation noble, %v. Bienvenue!", name)
return message, nil
}
Dans le cas où nous il n’y a pas d’erreurs, nous allons retourner nil
ce qui signifie pas d’erreurs.
Ensuite pour gérer l’erreur nous allons utiliser le logguer contenu dans le packagelog
En utilisant ce logger on peut afficher l’erreur et utiliser la fonction “Fatal”
pour afficher l’erreur et terminer le programme.
package main
import (
"fmt"
"log"
"tokenizers/salutations"
)
func main() {
// Set properties of the predefined Logger, including
// the log entry prefix and a flag to disable printing
// the time, source file, and line number.
log.SetPrefix("salutations: ")
log.SetFlags(0)
// Get a greeting message and print it.
message, err := salutations.Bonjour("")
// If an error was returned, print it to the console and
// exit the program.
if err != nil {
log.Fatal(err)
}
fmt.Println(message)
}
Ce qui donne bien le résultat suivant:
Nous allons proposer une liste de salutations et en prendre une au hasard à chaque fois que la fonction sera appelée. Pour faire cela on va utiliser un slice
.
Un slice est comme un array, sauf que sa taille change dynamiquement quand on ajoute et supprime des éléments. Le slice est un des types de Go le plus utilisé.
On va déclarer un slice de quelques élements. Ensuire nous selectionnerons aléatoirement un élément à partir de ce même slice.
package salutations
import (
"errors"
"fmt"
"math/rand"
)
func Bonjour(name string) (string, error) {
// If no name was given, return an error with a message.
if name == "" {
return "", errors.New("Le nom est vide")
}
// If a name was received, return a value that embeds the name
// in a greeting message.
var message string = fmt.Sprintf(obtenirSalutations(), name)
return message, nil
}
func obtenirSalutations() string {
var salutations = []string{
"Salutation noble, %v. Bienvenue!",
"Ravis de te rencontrer, %v. Bienvenue!",
"Plop, %v!",
}
// on recupère un nombre aléatoire entre 0 et la taille max
choix := rand.Intn(len(salutations))
return salutations[choix]
}
Si on rejoue maintenant le code appelant.
L’objectif est de pouvoir saluer plusieurs personnes en entrée, je tente sans le tutoriel
Je vais créer une fonction BonjourATous qui prendra en entrée un slice de noms, et qui retournera un slice de messages à afficher. Si jamais un des noms en entrée est vide alors il erreur surviendra.
Dans le fichier main.go
package main
import (
"fmt"
"log"
"tokenizers/salutations"
)
func main() {
// Set properties of the predefined Logger, including
// the log entry prefix and a flag to disable printing
// the time, source file, and line number.
log.SetPrefix("salutations: ")
log.SetFlags(0)
var noms = []string{
"Romain",
"David",
"Meryem",
}
// Get a greeting message and print it.
messages, err := salutations.BonjourATous(noms)
// If an error was returned, print it to the console and
// exit the program.
if err != nil {
log.Fatal(err)
}
for i := 0; i < len(messages); i++ {
fmt.Println(messages[i])
}
}
Dans le module salutations.go
func BonjourATous(names []string) ([]string, error) {
var messages []string
var emptyError error
for i := 0; i < len(names); i++ {
message, err := Bonjour(names[i])
if err != nil {
return nil, errors.New("Un des noms en entrée est vide")
}
messages = append(messages, message)
}
return messages, emptyError
}
_Résultat : _
Et si je met un prénom à vide
var noms = []string{
"Romain",
"",
"Meryem",
}
Résultat:
// Hellos returns a map that associates each of the named people
// with a greeting message.
func BonjourATous(names []string) (map[string]string, error) {
// A map to associate names with messages.
messages := make(map[string]string)
// Loop through the received slice of names, calling
// the Hello function to get a message for each name.
for _, name := range names {
message, err := Hello(name)
if err != nil {
// breaking loop
return nil, err
}
// In the map, associate the retrieved message with
// the name.
messages[name] = message
}
return messages, nil
}
Ils ont géré le retour dans un tableau associatif, afin de pouvoir avoir une combinaison
avec le nom comme clé et le message comme valeur.
On voit grosso modo 4 choses nouvelles :
On va ajouter un test pour assurer et garantir la robustesse de notre code afin d’éviter les bugs et le regressions futures.
Dans le répertoire du module “salutations”
on va créer un fichier salutations_test.go
Voici ce que nous allons écrire dans le test :
package salutations
import (
"regexp"
"testing"
)
// TestBonjourWithName appelle salutations.Bonjour avec un nom, on
// vérifie qu'il nous renvoie bien une valeur valide
func TestBonjourWithName(t *testing.T) {
name := "Gladys"
// Doit contenir 'Gladys'
want := regexp.MustCompile(`\b` + name + `\b`)
msg, err := Bonjour(name)
// Si ne contient pas Glady et n'a pas d'erreur alors on signale que le test ne passe pas.
if !want.MatchString(msg) || err != nil {
t.Fatalf(`Bonjour("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
}
}
func TestBonjourWithoutName(t *testing.T) {
name := ""
msg, err := Bonjour(name)
// Si il contient quelquechose ou n'a pas d'erreur alors on signale que le test ne passe pas.
if msg != "" || err == nil {
t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
}
}
Voici le package testing qui va permettre d’initier nos tests et d’attendre un objet testing qui sera certainement alimenté par la commande test
et également un package de regex pour définir si le résultat attendu correspond à ce qu’on attendait. Classique.
go test
Par définition chaque fonction correspondra à un test, donc son nom doit être clair et compréhensible concernant l’attendu.
Pour avoir plus de détails il est possible ( et préférable) de mettre en verbose
go test -v
Résultat:
Nous avons vu dans cet article quelques commandes Go, la commande run
est un raccourci pratique pour compiler et lancer le programme quand vous faites des changements fréquents.
Cela ne génère pas d’executable.
Nous allons parler de deux commandes additionnelles, pour construire notre code.
build
install
Dans le répertoire bonjour, nous allons lancer la commande build
pour compiler le code en executable.
go build
Nous voyons qu’il a crée un executable bonjour, nous allons donc l’executer
Maintenant nous allons rendre ce programme executable n’importe où et pour cela il faut rajouter le path de l’executable dans le path d’installation de Go. Mais où est il ?
Nous pouvons savoir cela grâce à la commande list
go list -f '{{.Target}}'
Cela doit vous afficher quelque-chose du genre : home/gopher/bin/bonjour
On va ajouter cette ligne dans le path afin que le programme soit accessible et surtout il faut l’installer
export PATH=$PATH:/path/to/your/install/directory
go install
Vous devriez avoir le résultat suivant :
Et voilà c’était très complet et long et on n’a pas pu aller en détail dans chacun des articles. J’attaquerai demain la suite.
sources :