Aller au contenu

Ajustement dynamique des proportions des recettes

Gram est conçu pour que les recettes se recalculent (scale) avec une précision mathématique parfaite dans tout l’écosystème. Que vous utilisiez le Playground interactif, génériez des documents HTML/Markdown ou lanciez des commandes CLI, Gram intègre nativement deux fonctionnalités taillées pour la cuisine basée sur les ratios (comme la boulangerie ou la pâtisserie) : les Quantités Relatives et le Pourcentage du Boulanger (Baker’s Math).

Ce guide vous montre comment exploiter les Quantités Relatives pour architecturer des recettes dynamiques et auto-documentées, et comment utiliser le Pourcentage du Boulanger pour formater des recettes traditionnelles en pourcentages purs.

Prenons une recette de pain classique :

[Mélanger] La @farine{500 g}, l'@eau{350 ml}, le @sel{10 g} et la @levure{5 g}.

Si vous ajustez les proportions de cette recette avec un facteur 2 (via gram build --scale 2), l’ensemble des quantités est multiplié par deux. Et si vos placards sont vides et qu’il ne vous reste que 400 g de farine, vous pouvez utiliser l’ajustement par ingrédient repère : gram build --scale farine=400g. Gram calculera le multiplicateur (0.8) et ajustera tout automatiquement.

Alors, pourquoi aurions-nous besoin d’autre chose ?

Le hic survient lorsque vous voulez modifier la logique de la recette elle-même. Et si vous vouliez passer l’hydratation de 70 % à 75 % ? Vous devriez calculer de tête ce que représentent 75 % de 500 g, puis remplacer 350 ml par 375 ml. La logique métier de la recette (ses ratios) est masquée par des valeurs statiques (hardcodées), la rendant laborieuse à lire et à maintenir.

La solution pour les recettes par ratios : les quantités relatives

Section intitulée « La solution pour les recettes par ratios : les quantités relatives »

Certaines préparations reposent fondamentalement sur des relations mathématiques entre leurs composants, plutôt que sur des quantités absolues (ex: une pâte à pain entièrement définie par son hydratation à 70 %).

Gram permet d’exprimer ces relations directement dans le code source via des Quantités Relatives (% @&cible pour un ingrédient, ou % &cible pour une variable intermédiaire). La recette devient alors capable de se recalculer dynamiquement à la volée dès que vous tweakez un seul pourcentage.

  1. Commencez par définir la farine comme un ingrédient standard dans votre recette.

    [Ajouter] La @farine{500 g}.
  2. Maintenant, remplacez les quantités statiques de l’eau, du sel et de la levure par des pourcentages pointant vers la farine. Puisque la farine a déjà été définie dans une étape précédente, vous la référencez en utilisant @&farine.

    [Ajouter] La @farine{500 g}.
    
    [Verser] L'@eau{70% @&farine}, le @sel{2% @&farine}, et la @levure{1% @&farine}.

Puisque ces relations sont encodées à la racine de la recette, Gram assure toute la gymnastique mathématique dynamiquement. Tout développeur ou boulanger lisant le .gram comprend instantanément qu’il s’agit d’une pâte hydratée à 70 %.

Si vous voulez ajuster la recette pour une pâte plus humide à 75 % d’hydratation, vous ne changez qu’un seul nombre :

[Verser] L'@eau{75% @&farine}

Et Gram s’occupe du reste. Fini le calcul mental fastidieux des poids absolus pour ajuster vos recettes.

Vous pouvez ajuster les proportions des recettes visuellement dans le Playground, ou depuis le CLI en utilisant le paramètre --scale (disponible sur view, build, print, et export) :

gram view pain.gram --scale farine=400g

Gram va résoudre le facteur global (400/500 = 0.8), ajuster l’ancre farine à 400 g, et tous les ingrédients relatifs s’évalueront en cascade sur cette nouvelle base (Eau : 70 % de 400 g = 280 g).

Les limites de l’ajustement par ingrédient repère

Section intitulée « Les limites de l’ajustement par ingrédient repère »

Puisque l’ajustement par ingrédient repère dérive le facteur depuis la quantité d’un ingrédient, tout n’est pas éligible comme cible — seul un ingrédient doté d’une quantité absolue explicite fait l’affaire. Si vous tentez de passer l’un des cas suivants, Gram jettera une erreur (throw) plutôt que de calculer silencieusement un facteur aberrant :

Vous avez tenté d’utiliser…Pourquoi c’est rejetéQue faire à la place
Une quantité relative (ex : eau, définie comme 70% @&farine)Sa valeur est dérivée d’un autre ingrédient — elle ne peut pas non plus servir de référenceAjustez les proportions de l’ingrédient ancre (--scale farine=400g)
Un ingrédient fixe (@=) ou une quantité textuelle comme une pincéeIl ne s’ajuste jamais par définition, il ne peut donc pas non plus décrire un facteur d’ajustementChoisissez un autre ingrédient qui évolue effectivement avec la recette
Un ingrédient utilisé uniquement à l’intérieur d’une sous-recette (ex : zeste-de-citron, partie d’un composite <@citron)Il n’est pas directement déclaré avec sa propre quantité — seul le parent composite en possède uneAjustez les proportions du parent composite (ex : --scale citron=4) — son propre total est une cible valide
Une option au sein d’un groupe d’ingrédients alternatifs (@beurre{100 g}|@margarine{100 g})Choisir une option de manière isolée ne représente pas le choix “soit l’un soit l’autre” qu’exprime la recetteAjustez les proportions d’un ingrédient différent et non ambigu, ailleurs dans la recette
Un ingrédient fragmenté sur deux unités incompatibles dans la recette (ex : 300 g dans une étape, 2 tasses dans une autre)Le total de la liste de courses ne peut pas être réduit à un seul chiffre par lequel diviserRéécrivez la recette en utilisant une seule unité pour cet ingrédient, ou ajustez les proportions par rapport à un autre ingrédient
Une unité appartenant à une famille physique différente (ex : eau=1L contre une recette écrite en g) sans aucune densité disponibleConvertir de la masse au volume requiert une densité spécifique à l’ingrédientAjoutez une densité via gram db enrich, ou déclarez-en une directement dans le bloc densities: du frontmatter de la recette

Les unités d’une même famille se convertissent automatiquement — --scale farine=1kg pour une recette écrite en 500 g fonctionne directement (facteur 2), sans recourir à la base de données. Croiser les familles (masse ↔ volume) fonctionne également, tant qu’une densité est disponible : soit via votre ingredients.yaml (gram db enrich), soit via une surcharge ponctuelle au sein de la recette elle-même :

---
densities: ["eau:1.0"]
---

Avec cela, --scale eau=150g sur une recette écrite en ml se résout correctement — pas besoin de base de données pour cet ingrédient précis.

Si les Quantités Relatives sont idéales pour concevoir des recettes dynamiques, les professionnels se reposent souvent sur un concept appelé Pourcentage du Boulanger (Baker’s Math) pour déchiffrer et analyser des recettes statiques. Dans ce paradigme, l’ingrédient phare (généralement la farine) fait office de base de référence (100 %), et tous les autres ingrédients s’expriment en pourcentage de ce poids.

Si vous avez une recette classique avec des poids absolus, vous pouvez utiliser le Modificateur de Pourcentage du Boulanger (*) pour indiquer explicitement à Gram : “Cet ingrédient est le point de référence de 100 %”.

[Ajouter] La @*farine{500 g}, l'@eau{350 g} et le @sel{10 g}.

La recette compilée calculera automatiquement ces pourcentages afin que n’importe quelle interface (frontend) puisse les afficher.

Lorsque vous utilisez le CLI, vous pouvez afficher ces pourcentages en ajoutant l’argument --bakers-math :

gram view pain.gram --bakers-math

La sortie affichera automatiquement le pourcentage de chaque ingrédient par rapport à la farine :

  farine                 100% (500 g)
  eau                    70% (350 g)
  sel                    2% (10 g)

Si votre recette n’inclut pas le modificateur @*, vous pouvez tout de même forcer le calcul des pourcentages du boulanger en spécifiant explicitement l’ingrédient de référence à la volée :

gram view pain.gram --bakers-reference=farine

(Vous pouvez également utiliser l’argument --bakers-math-only si vous souhaitez masquer complètement les poids absolus.)

Parfois, une recette contient des ingrédients dont les proportions ne doivent jamais varier, peu importe le nombre de portions générées. Pensez à l’huile de friture au fond d’une poêle, ou à une pincée de sel pour l’assaisonnement.

Vous pouvez utiliser le Modificateur Fixe (=) pour protéger un ingrédient de l’ajustement des proportions :

Faire chauffer @=huile de friture{1 L} dans une sauteuse.

Si vous exécutez gram build --scale 2, l’huile de friture restera à 1 L.

En combinant les Quantités Relatives pour les ratios et les Quantités Fixes pour les constantes immuables, vous pouvez écrire des recettes qui s’adaptent parfaitement à n’importe quelle contrainte de votre garde-manger ou taille de portion.