Les outils d’inclusion de ressources comme embed
permettent d’embarquer dans
l’exécutable aussi bien les assets statiques que des fichiers non publiés, comme
les templates.
Voyons comment.
Pourquoi utiliser un “bundler” comme embed
?
Pour préserver la facilité de déploiement des applications Go sous la forme d’un unique fichier exécutable, sans avoir à copier également ses ressources statiques comme les fichiers CSS, JS, ou les templates, de multiples mécanismes d’inclusion des ressources ont été élaborés par la communauté des développeurs Go au fil des ans, dont rakyll/statik, markbates/pkger, et bien d’autres.
Face à la prolifération de ces solutions et leurs degrés variables d’efficacité et de simplicité d’utilisation, le projet Go a fini par inclure ce mécanisme directement au niveau du langage et de sa bibliothèque standard, au moyen du paquet embed.
Dans un article précédent, nous avons vu comment
embarquer des templates avec pkger. Le nouveau mécanisme standard avec embed
est l’aboutissement du
ticket
35950
évoqué dans cet article, toujours pertinent quant
aux motivations des mécanismes d’inclusion.
Nous allons donc voir comment convertir à l’utilisation d’embed
l’exemple
d’inclusion de templates présenté dans l’article précédent.
Comment utiliser embed
dans son projet
À la différence de pkger
et d’autres paquets d’inclusion de ressources:
- le nouveau mécanisme ne nécessite plus aucune commande spécifique, mais uniquement un SDK Go 1.16 ou plus récent
- il ne distingue plus entre un mode développement et un mode exécution
- l’API n’est plus nécessaire pour charger une ressource simple comme un fichier de template
- l’API se limite à trois méthodes pour charger une arborescence de ressources.
Les projets compilés n’ont aucune dépendance à l’exécution.
Pour permettre à embed
d’inclure les ressources dans le bundle (exécutable, bibliothèque, greffon),
les sources du module doivent être rédigés en conséquence. Voyons comment.
Les exemples illustrés dans cet article sont extraites du mini-projet github.com/fgm/pkger_demo V21 qui est prêt à compiler: vous pouvez le récupérer pour suivre plus facilement dans votre IDE:
$ GO111MODULE=off go get github.com/fgm/pkger_demo
$ cd $GOPATH/src/github.com/fgm/pkger_demo/v2
Charger une ressource isolée
Avec embed
, charger une ressource isolée se limite à déclarer une variable
dans la portée paquet, étiquetée d’une directive //go:embed
et de type string
ou []byte
.
|
|
L’API de embed
: embed.FS
Pour manipuler une arborescence embarquée, et charger sélectivement ses divers
contenus, la directive d’inclusion reste la même, par exemple: //go:embed templates
,
mais la variable déclarée sera cette fois du type embed.FS
.
Ce type, introduit en Go 1.16 implémente 3 nouvelles interfaces
du nouveau paquet io/fs
de la bibliothèque standard,
issues de la rationalisation des entrées/sorties fichier dans cette version.
Méthode | embed.FS | fs.FS | fs.ReadDirFS | fs.ReadFileFS |
---|---|---|---|---|
Open(name string) (fs.File, error) | X | X | via fs.FS | via fs.FS |
ReadDir(name string) ([]fs.DirEntry, error) | X | X | ||
ReadFile(name string) ([]byte, error) | X | X |
Cette API est donc considérablement plus simple que celle des paquets contribués
antérieures, permettant l’emploi des fonctions de la bibliothèque standard sur
les ressources énumérées dans un //go:embed
.
Les inclusions peuvent comporter un astérisque pour accepter les fichiers par motif, et peuvent être multiples dans une même directive, soit sur la même ligne, séparées par des espaces, soit sur des lignes distinctes adjacentes
- ce qui est plutôt plus lisible et maintenable dans le temps.
Tous les fichiers ainsi décrits dans un même bloc de //go:embed
sont alors
placés dans la variable embed.FS
sous leur chemin d’accès relatif.
Chargement de templates dans une arborescence
Le programme de démo pkger_demo V2 est une application Web basique, renvoyant une page mise en forme par deux templates, reflétant une disposition courante.
templates/page.gohtml
:
|
|
templates/layout/footer.gohtml
|
|
Examinons le source du fichier main.go
point par point. Les lignes 1 à 13
contiennent le préambule du programme (package
, import
).
Le paquet embed
y est importé pour pouvoir être référencé plus bas.
Rien de particulier sur ce point.
Aux lignes 14-16 se trouve l’exemple de déclaration d’une inclusion de fichier
isolé, automatiquement chargé dans une variable string
:
|
|
Tandis qu’aux lignes 18-20 se trouve l’exemple de déclaration d’une inclusion d’arborescence,
automatiquement chargée dans une variable embed.FS
|
|
Le programme utilise les templates présents dans le répertoire templates
et
tous ses sous-répertoires.
Il va donc parcourir l’arborescence située sous /templates
, et ouvrir les
fichiers templates qu’il y trouvera:
|
|
- ligne 33: le fonctionnement est similaire à celui de
filepath.Walk
: la fonctionWalkDir
parcourt l’arborescence et invoque une fonction de rappelwalkFn
pour chaque fichier et sous-répertoire rencontré. Dans notre exemple, c’est la fonction anonyme des lignes 33-46. - lignes 34-37: la fonction ignore les fichiers ne correspondant pas au format des noms de templates attendus.
- ligne 40, elle ouvre le fichier avec
templates.Open
, obtenant unfs.File
- ligne 42, elle en lit le contenu avec la fonction
io.ReadAll
du runtime. Ces lignes 40/42 pourraient aussi être remplacées par un seul appel à la méthodetemplates.ReadFile()
- ligne 44, elle compile la chaîne ainsi obtenu en tant que template Go.
En fin d’exécution, le résultat est un jeu de templates compilés valide, ou une erreur.
Une fois le jeu de templates compilé obtenu à la ligne 57, les handlers de requête HTTP peuvent l’utiliser comme si de rien n’était:
|
|
Ressources additionnelles
- Documentation officielle: https://pkg.go.dev/embed
- Article introductif de Carl Johnson, recommandé par le blog Go lors de l’annonce de Go 1.16.
- Vidéo d’introduction de la logique de conception du paquet
embed
par Russ Cox (07/2020)
la V1 du module est celle de l’article précédent, qui utilise
markbates/pkger
. ↩︎