diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..71849fbf9 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,32 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.12" + # You can also specify other tool versions: + # nodejs: "19" + # rust: "1.64" + # golang: "1.19" + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: docs/conf.py + +# Optionally build your docs in additional formats such as PDF and ePub +# formats: +# - pdf +# - epub + +# Optional but recommended, declare the Python requirements required +# to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: docs/requirements.txt diff --git a/README.md b/README.md index 540cebb24..4ef033f3f 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ The destination file is generated with new values: # Link -* [Documentation](https://silique.fr/rougail/) +* [Documentation](https://rougail.readthedocs.io/en/latest/) * [Licence ](LICENSE) # Related projects diff --git a/doc/README.md b/doc/README.md deleted file mode 100644 index 3260d3ceb..000000000 --- a/doc/README.md +++ /dev/null @@ -1,28 +0,0 @@ -![Logo Rougail](../logo.png "logo rougail") - -# Rougail - -Rougail est une bibliothèque python3 qui permet de charger des dictionnaires (fichiers au format YAML) dans le but de charger les variables dans Tiramisu. - -![Schéma](schema.png "Schéma") - - -[Débutons avec Rougail](getting_started.md). - -## La bibliothèque - - - [La bibliothèque](dev/README.md) - - [La configration de la bibliothèque](dev/config.md) - -## Les dictionnaires - - - [Les dictionnaires](dictionary/rougail.md) - - [Convention d'écriture d'un dictionnaire](dictionary/convention.md) - -### Les variables - - - [Les variables](variable/README.md) - - [Les familles](family/README.md) - - [Les variables à valeur par défaut calculées](fill/README.md) - - [Les vérifications des valeurs](check/README.md) - - [Les propriétés calculées](condition/README.md) diff --git a/doc/check/README.md b/doc/check/README.md deleted file mode 100644 index 2ed4b539b..000000000 --- a/doc/check/README.md +++ /dev/null @@ -1,210 +0,0 @@ -# Fonction de vérification - -## Synopsis - -Une fonction de vérification est une fonction complémentaire au type qui permet de valider plus précisement le contenu d'une variable. - -Un validateur est forcement un calcul de type Jinja. - -## Paramètres - -Suivant les types de calcul les paramètres vont être différents : - -| Type de calcul | Paramètre | Commentaires | Exemple | -|----------------|-----------|--------------|---------| -| | **type**
`string`
`mandatory` | Type du calcul, la seule valeur possible est : jinja | jinja | -| **jinja**
`string`
`mandatory` | Template Jinja. | {% if rougail.variable == 'not\_allowed' %}not allowed!{% endif %} | -| **params**
`list` | Paramètres complémentaire passé au template Jinja | | - -Il existe deux types de paramètre : - -- les paramètres standards (string, boolean, integer, null), dans ce il suffit de faire : "key: value" -- les paramètres avancés : - - - paramètre via une variable - - paramètre via une information - - paramètre via un suffix : dans le cas d'une variable dans une famille dynamique - - paramètre via un index : dans le cas d'une variable suiveuse - -| Type du paramètre | Paramètre | Commentaires | Exemple | -|-------------------|-----------|--------------|---------| -| | **name**
`string`
`mandatory` | Le nom du paramètre | my\_param | -| | **type**
`string`
`mandatory` | Type du paramètre, les valeurs possible sont : variable, information, suffix ou index| suffix | -| Variable | **variable**
`string`
`mandatory` | Nom de la variable | rougail.variable | -| Variable (`mandatory`)
Information | **propertyerror**
`boolean` | Si l'accès à la variable n'est pas possible à cause d'une propriété (par exemple `disabled`) par défaut une erreur est retournée. Si l'attribut est à False, le paramètre n'est pas passé au template Jinja.
**Valeur par défaut :** True | False | -| Variable | **optional**
`boolean` | La variable peut ne pas exister suivant les importations de fichier YAML. Si le paramètre optional est à True, le paramètre sera tout simplement supprimer si la variable n'existe pas.
**Valeur par défaut :** False | True | -| Information | **information**
`string`
`mandatory` | Nom de l'information dont on veut récupérer la valeur. | doc | - -## Exemples - -### Vérification stricte des valeurs - - -Voici un exemple simple de validation des valeurs : - -```yml ---- -version: '1.0' -my_variable: - validators: - - type: jinja - jinja: | - {% if rougail.my_variable and not rougail.my_variable.islower() %} - {{ rougail.my_variable }} is not lowercase string - {% endif %} -``` - -Une fonction de vérification doit prendre en compte 2 aspects important : - -- la valeur peut ne pas être renseigné (même si la variable est obligatoire), la valeur None doit être prise en compte -- si la valeur est invalide, il faut renvoyer une phrase avec un message explicite. - -À partir de maintenant seule None et des valeurs en minuscule seront autorisés. - -### Vérification des valeurs avec avertissement - -Dans la contrainte, il est possible de spécifier le niveau d'erreur et le mettre en avertissement : - -```yml ---- -version: '1.0' -my_variable: - validators: - - type: jinja - jinja: |+ - {% if rougail.my_variable and not rougail.my_variable.islower() %} - {{ rougail.my_variable }} is not lowercase string - {% endif %} - params: - warnings_only: true -``` - -Dans ce cas une valeur avec une majuscule sera accepté, mais un message d'avertissement apparaitra. - - -### Vérification avec paramètres : - -```yml ---- -version: '1.0' -my_hidden_variable: - disabled: true -my_variable: - validators: - - type: jinja - jinja: | - {% if param1 is defined and rougail.my_variable == param1 %} - has same value as rougail.unknown_variable - {% endif %} - {% if param2 is defined and rougail.my_variable == param2 %} - has same value as rougail.my_hidden_variable - {% endif %} - params: - param1: - type: variable - variable: rougail.unknown_variable - optional: true - param2: - type: variable - variable: rougail.my_hidden_variable - propertyerror: false -``` - -Un exemple avec un paramètre de type suffix : - -```yml ---- -version: '1.0' -varname: - multi: true - default: - - val1 - - val2 -my_dyn_family_: - type: dynamic - variable: rougail.varname - description: 'Describe ' - my_dyn_var: - type: string - validators: - - type: jinja - jinja: | - {% if rougail.my_dyn_family_.my_dyn_var == param1 %} - forbidden! - {% endif %} - params: - param1: - type: suffix -``` - -Dans cette exemple, on voit une famille dynamique. Deux familles vont être crées : `rougail.my_dyn_family_val1.my_dyn_var` et `rougail.my_dyn_family_val2.my_dyn_var`. - -La valeur de la variable à l'intérieur de cette famille ne peux pas être égale à la valeur du suffix (respectivement `val1` et `val2`). - -Un exemple avec un paramètre de type index : - -```yml ---- -version: '1.0' -family: - type: leadership - leader: - multi: true - default: - - val1 - - val2 - follower1: - type: number - validators: - - type: jinja - jinja: | - {% if rougail.family.follower1 == param1 %} - forbidden! - {% endif %} - params: - param1: - type: index -``` - -### Rédéfinition - -Dans un premier dictionnaire déclarons notre variable et sa fonction de vérification : - -```yml ---- -version: '1.0' -my_variable: - validators: - - type: jinja - jinja: | - {% if rougail.my_variable and not rougail.my_variable.islower() %} - {{ rougail.my_variable }} is not lowercase string - {% endif %} -``` - -Dans un second dictionnaire il est possible de redéfinir le calcul : - -```yml ---- -version: '1.0' -my_variable: - redefine: true - validators: - - type: jinja - jinja: | - {% if rougail.my_variable and ' ' in rougail.my_variable %} - {{ rougail.my_variable }} has a space - {% endif %} -``` - -Dans ce cas seule ce validateur sera exécuté. - -Voici un troisième dictionnaire dans lequel on supprime la validation : - -```yml ---- -version: '1.0' -my_variable: - redefine: true - validators: -``` diff --git a/doc/condition/README.md b/doc/condition/README.md deleted file mode 100644 index 6102f975a..000000000 --- a/doc/condition/README.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -gitea: none -include_toc: true ---- - -# Les propriétés calculées - -## Synopsis - -Les propriétés calculées permettent d'ajouter ou de supprimer des propriétés à une [variable](../variable/README.md) ou une [famille](../family/README.md) suivant le contexte. - -Voici la liste des propriétés modifiables : - -| Attribut applicable sur | Nom de la propriétée | Commentaire | -|-------------------------|----------------------|-------------| -| Variable
Famille | hidden | Cache une variable ou famille, dans ce cas elle n'est pas accessible en lecture écriture, mais reste accessible dans un calcul ou en lecture seule | -| Variable
Famille | disabled | Désactive une variable ou famille, dans ce cas elle n'est jamais accessible | -| Variable | mandatory | La variable attend une valeur autre que None ou [] pour les variables multiple | - -Une propriété peut être calculée. Dans ce cas on a deux possibilités : - -- calcul via Jinja -- calcul via une variable - -## Paramètres - -Suivant les types de calcul les paramètres vont être différents : - -| Type de calcul | Paramètre | Commentaires | Exemple | -|----------------|-----------|--------------|---------| -| | **type**
`string`
`mandatory` | Type du calcul, les valeurs possible sont : jinja, variable, information, suffix ou index | jinja | -| Jinja | **jinja**
`string`
`mandatory` | Template Jinja. Pour une variable multiple, chaque ligne représente une valeur. | {% if rougail.variable %}{{ rougail.variable }}{% endif %} | -| Jinja | **params**
`list` | Paramètres complémentaire passé au template Jinja | | -| Variable | **variable**
`string`
`mandatory` | Nom de la variable associée. ⚠️ La variable doit être de type `boolean`. | rougail.variable | -| Variable | **propertyerror**
`boolean` | Si l'accès à la variable n'est pas possible à cause d'une propriété (par exemple `disabled`) par défaut une erreur est retournée. Si l'attribut est à False, la valeur calculée est False.
**Valeur par défaut :** True | False | - -Dans le cas d'un calcul de type Jinja, il est possible d'avoir des paramètres. - -Il existe deux types de paramètre : - -- les paramètres standards (string, boolean, integer, null), dans ce il suffit de faire : "key: value" -- les paramètres avancés : - - - paramètre via une variable - - paramètre via une information - - paramètre via un suffix : dans le cas d'une variable dans une famille dynamique - - paramètre via un index : dans le cas d'une variable suiveuse - -| Type du paramètre | Paramètre | Commentaires | Exemple | -|-------------------|-----------|--------------|---------| -| | **name**
`string`
`mandatory` | Le nom du paramètre | my\_param | -| | **type**
`string`
`mandatory` | Type du paramètre, les valeurs possible sont : variable, information, suffix ou index | suffix | -| Variable | **variable**
`string`
`mandatory` | Nom de la variable | rougail.variable | -| Variable (`mandatory`)
Information | **propertyerror**
`boolean` | Si l'accès à la variable n'est pas possible à cause d'une propriété (par exemple `disabled`) par défaut une erreur est retournée. Si l'attribut est à False, le paramètre n'est pas passé au template Jinja.
**Valeur par défaut :** True | False | -| Variable | **optional**
`boolean` | La variable peut ne pas exister suivant les importations de fichier YAML. Si le paramètre optional est à True, le paramètre sera tout simplement supprimer si la variable n'existe pas.
**Valeur par défaut :** False | True | -| Information | **information**
`string`
`mandatory` | Nom de l'information dont on veut récupérer la valeur. | doc | - -## Exemples - -### Une propriété calculée de type Jinja - -Il est possible d'écrire la condition en Jinja : - -```yml ---- -version: '1.0' -condition: - default: 'do not hide!' -my_variable: - hidden: - type: jinja - jinja: | - {% if rougail.condition and rougail.condition == "hide!" %} - this rougail.condition value is 'hide!' - {% endif %} -``` - -Dans ce cas la variable est cachée si la valeur de la variable "rougail.condition" est `hide!` et elle n'a pas caché pour tout autre valeur. -Attention toujours prendre en considération que "rougail.condition" peut être égale à `None`. - -Le message retourner par la fonction est visible dans le message d'erreur en cas de problème d'accès : - -```python ->>> from rougail import Rougail, RougailConfig ->>> RougailConfig['dictionaries_dir'] = ['dict'] ->>> rougail = Rougail() ->>> config = rougail.get_config() ->>> config.property.read_write() -[..] -tiramisu.error.PropertiesOptionError: cannot access to option "my_variable" because has property "hidden" (this rougail.condition value is 'hide!') -``` - -Il est possible d'utiliser des paramètres lors du calcul des propriétés comme pour le calcul de l'attribut [`default`](../fill/README.md). - -### Une propriété calculée de type variable - -Une variable peut donc être calculé via la résultat d'une autre variable. -Attention, cette autre variable doit obligatoirement être de type `boolean` : - -```yml ---- -version: '1.0' -condition: - type: boolean -my_variable: - hidden: - type: variable - variable: rougail.condition -``` - -Si la valeur de la variable "rougail.condition" est `True` alors la variable "rougail.my\_variable" est cachée. - -### Rédéfinition - -Il se peut que dans un dictionnaire on décide de définir une condition. - -Pour supprimer le calcul à partir d'une variable il suffit de faire dans un nouveau dictionnaire : - -```yml ---- -version: '1.0' -my_variable: - redefine: true - hidden: -``` diff --git a/doc/dev/README.md b/doc/dev/README.md deleted file mode 100644 index e47b37f2e..000000000 --- a/doc/dev/README.md +++ /dev/null @@ -1,131 +0,0 @@ -# La bibliothèque Rougail - -Rougail est une bibliothèque de gestion de configuration qui permet de charger simplement des variables. - -Dans les exemples suivants, nous utiliserons une configuration particulière de Rougail. -Vous retrouverez toutes les options pour [personnaliser les répertoires utilisés](config.md). - -Pour charger la configuration il faut importer la variable RougailConfig et changer les valeurs : - -```python -from rougail import RougailConfig - -RougailConfig['dictionaries_dir'] = ['dict'] -``` - -## Convertisons un dictionnaire - -Un dictionnaire est un ensemble d'instruction qui vont permettre de créer des familles et des variables. - -Commençons par créer un [dictionnaire](../dictionary/rougail.md) simple. - -Voici un premier dictionnaire dict/00-base.yml : - -```yml ---- -version: '1.0' -my_variable: - default: my_value -``` - -Puis, créons les objets [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu) via la script script.py suivant : - -```python -from rougail import Rougail, RougailConfig - -RougailConfig['dictionaries_dir'] = ['dict'] -rougail = Rougail() -config = rougail.get_config() -print(config.value.get()) -``` - -Exécution le script : - -```sh -$ python3 script.py -{'rougail.my_variable': 'my_value'} -``` - -## Convertisons un dictionnaire extra - -L'espace de nommage par défaut des variables et familles est "rougail". Il est possible de définir d'autres espaces de nom. -Ces espaces de nom additionnels s'appelle des "[extras](../dictionary/extra.md)". - -Les espaces de nom additionnels se définissent lors de la configuration. - -Par exemple, voici comment ajouter une espace de nom "example" : - -```python -RougailConfig['extra_dictionaries']['example'] = ['extras/'] -``` - -Ensuite créons un dictionnaire extra extras/00-base.yml : - -```yml ---- -version: '1.0' -my_variable_extra: - default: my_value_extra -``` - -Puis, créons les objets [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu) via la script script.py suivant : - -```python -from rougail import Rougail, RougailConfig - -RougailConfig['dictionaries_dir'] = ['dict'] -RougailConfig['extra_dictionaries']['example'] = ['extras/'] -rougail = Rougail() -config = rougail.get_config() -print(config.value.dict()) -``` - -Exécution le script : - -```python -$ python3 script.py -{'rougail.my_variable': 'my_value', 'example.my_variable_extra': 'my_value_extra'} -``` - -## Créons une fonction personnalisé - -Nous créons le dictionnaire complémentaire dict/01-function.yml pour que la variable "my_variable_jinja" soit [calculée](fill/README.md) : - -```yml ---- -version: '1.0' -my_variable_jinja: - type: "string" - default: - type: jinja - jinja: "{{ return_no() }}" -``` - -Puis créons la fonction "return_no" dans functions.py : - -```python -def return_no(): - return 'no' -``` - -Puis, créons les objets [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu) via la script script.py suivant : - -```python -from rougail import Rougail, RougailConfig - -RougailConfig['dictionaries_dir'] = ['dict'] -RougailConfig['extra_dictionaries']['example'] = ['extras/'] -RougailConfig['functions_file'] = 'functions.py' -rougail = Rougail() -config = rougail.get_config() -print(config.value.dict()) -``` - -Exécution le script : - -```python -$ python3 script.py -{'rougail.my_variable': 'my_value', 'rougail.my_variable_jinja': 'no', 'example.my_variable_extra': 'my_value_extra'} -``` - -La valeur de la variable `my_variable_extra` est bien calculé à partir de la fonction `return_no`. diff --git a/doc/dev/config.md b/doc/dev/config.md deleted file mode 100644 index c395dbedc..000000000 --- a/doc/dev/config.md +++ /dev/null @@ -1,94 +0,0 @@ -# Personnalisons la configuration de Rougail - -La configuration de Rougail se trouve dans l'objet `RougailConfig` : - -```python -from rougail import RougailConfig -``` - -C'est un simple dictionnaire python avec différentes clefs. - -Pour modifier il suffit de faire : - -```python -RougailConfig[key] = value -``` - -## Configuration de chargement des dictionnaires - -### Les répertoires des dictionnaires - -Il existe deux types de répertoires de dictionnaires : - -- les dictionnaires principaux avec la clef `dictionaries_dir`. La valeur par défaut est `['/srv/rougail/dictionaries']`. Cette variable doit contenir la liste des répertoires contenants des dictionnaires. - -- les dictionnaires extra avec la clef `extra_dictionaries`. La valeur est un dictionnaire avec l'ensemble des espaces de nom. La clef étant l'espace de nom et la valeur étant une liste de répertoire. - -Par exemple pour ajouter l'extra `example` il faut faire : - -```python -RougailConfig['extra_dictionaries']['example'] = ['/dir1', '/dir2'] -``` - -Les dictionnaires sont chargés dans le même ordre que les dictionnaires principaux. - -### Le fichier de fonction - -Le fichier qui contient les fonctions personnalisés est géré dans la clef "functions_file" et a comme valeur par défaut "/srv/rougail/functions.py". Cette clef peut contenir une liste s'il y a plusieurs fichiers. - -Les fonctions doivent retourner une valeur, même si la variable que l'on calcul est une variable multiple. -Si la fonction peut retourner une valeur multiple (une liste), il faut mettre le nom de la fonction dans la clef "multi_functions". - -### La variable auto_freeze - -La propriété auto_freeze n'est appliqué que une variable spécifique passe à True. Par défaut le nom de la variable est "instancied_module", mais il est possible de changer le nom de cette variable via la clef "auto_freeze_variable". - -### Les modes - -Les modes sont personnalisables dans Rougail. Par défaut les modes sont "basic", "normal" et "expert". -Il est possible de changer cette liste via la clef "modes_level". - -Si vous changer ces valeurs, penser à changer les modes par défaut des familles et des variables. - -### Le mode par défaut pour une famille - -Le mode par défaut d'une famille est "basic". Il est possible de changer le mode par défaut d'une famille via la clef "default_family_mode". - -### Le mode par défaut pour une variable - -Le mode par défaut d'une variable est "normal". Il est possible de changer le mode par défaut d'une variable via la clef "default_variable_mode". - -### Le nom des fonctions internes - -Il est possible d'ajouter des fonctions interne via la clef "internal_functions". - -## Configuration de la templatisation - -### Le répertoire des templates - -Le répertoire des templates est géré dans la clef "templates_dir" et a comme valeur par défaut : "/srv/rougail/templates". Cette clef peut contenir une liste s'il y a plusieurs répertoires. - -### Le répertoire des patchs - -Le répertoire des patches est géré dans la clef "patches_dir" et a comme valeur par défaut : "/srv/rougail/patches". - -### Le répertoire temporaire - -Le répertoire temporaire est utile lors de la génération de template. Il contient une copie des templates avec, éventuellement, les patches appliqués sur les templates. - -Le répertoire de temporaire est géré dans la clef "tmp_dir" et a comme valeur par défaut : "/srv/rougail/tmp". - -### Le répertoire de destination des fichiers générés - -Le répertoire de destination des fichiers générés est géré dans la clef "destinations_dir" et a comme valeur par défaut : "/srv/rougail/destinations". - -### Les informations systemd - -Un certain nombre de variables concerne les templates systemd. - -## Ajout d'une fonction de conversion - -Les fonctions de conversion font partie du moteur de rougail. Il converti les informations des dictionnaires pour créer des variables Tiramisu. - -La clef "extra_annotators" permet d'ajouter des fonctions complémentaires. - diff --git a/doc/dictionary/convention.md b/doc/dictionary/convention.md deleted file mode 100644 index 287b43ba2..000000000 --- a/doc/dictionary/convention.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -gitea: none -include_toc: true ---- - -# Conventions - -## Convention de nom d'un fichier de dictionnaire - -L'ordre des dictionnaires est important pour l'ordre de création des variables et des familles. - -Les fichiers devront donc démarrés par deux numéros suivit d'un tiret. - -Par exemple : `00-base.xml` - -## Convention du nom des familles et variables - -La seule restriction sur le nom des familles et variables est que le nom ne doit pas commencer par le caractère "\_". -Néanmoins il est préférable de n'utiliser que des lettres minuscule ASCII, des chiffres et le caractère "\_". -C'est la convention typographique snake case qui est donc utilisée. diff --git a/doc/dictionary/rougail.md b/doc/dictionary/rougail.md deleted file mode 100644 index cf1399edb..000000000 --- a/doc/dictionary/rougail.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -gitea: none -include_toc: true ---- - -# Les dictionnaires - -## Un dictionnaire ? - -Un dictionnaire est un fichier YAML dont la structure est décrite dans cette documentation. - -Un dictionnaire contient en ensemble de variable chargé dans [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu), utilisable à tout moment, notamment dans des templates. - -Les familles et les variables peuvent être définis dans plusieurs dictionnaires. Ces dictionnaires s'aggrègent alors. - -Les dictionnaires sont chargés dans l'ordre des répertoires [définit avec le paramètre `dictionaries_dir` de la configuration](../dev/config.md). Chaque répertoire est chargé les uns après les autres. A l'intérieur de ces répertoires les fichiers YAML seront classés par ordre alphabétique. - -Il n'y a pas de classement par ordre alphabétique de l'ensemble des fichiers YAML de tous les répertoires. - -Il est également possible de redéfinir des éléments pour changer les comportement d'une famille ou d'une variable. - -## L'espace de nom par défaut - -Les familles et variables de ces dictionnaires sont classées, par défaut, dans l'espace de nom `rougail`. Il est possible de changer le nom de cet espace de nom [avec le paramètre `variable_namespace` de la configuration](../dev/config.md). - -Cet espace de nom est un peu particulier, il peut accéder a des variables dans un autre espace de nom. - -## Les dictionnaires extra - -Un extra est un espace de nom différent. L'idée et de pouvoir classer les variables par thématique. - -Les espaces de nom extra doivent être déclaré au moment [de la configuration de Rougail](../dev/config.md). - -Dans cet espace de nom on ne peut pas accéder à des variables d'un autre espace de nom `extra`. Par contre il est possible d'accéder au variable de l'espace de nom par défaut. diff --git a/doc/family/README.md b/doc/family/README.md deleted file mode 100644 index 0cf421004..000000000 --- a/doc/family/README.md +++ /dev/null @@ -1,146 +0,0 @@ ---- -gitea: none -include_toc: true ---- - -# Une famille - -## Synopsis - -Une famille est un conteneur de variables et de sous-famille. - -⚠️ Une famille sans sous-famille ni sous-variable sera automatiquement supprimée. - -## Paramètres - -| Paramètre | Commentaires | -|-----------|--------------| -| **name**
`string`
`mandatory` | Nom de la famille.
C'est avec ce nom qu'on va pouvoir interagir avec la famille.
Il est préférable de suivre la [convention sur les noms de variable](convention.md). | -| **type**, **\_type**
`string` | Type de la famille.
Le type n'est pas obligatoire même si parfois c'est nécessaire de le faire pour aider le moteur.
**Valeurs possible :**
- `family` ← par defaut
- `leadership`
- `dynamic`
📝 Si une sous-famille ou une sous-variable a déjà le nom "type" il est possible d'utiliser l'attribut "\_type". | -| **description**, **\_description**
`string` | La description de la famille.
Information utilisateur permettant de comprendre l'utilité de la famille.
📝 Si une sous-famille ou une sous-variable a déjà le nom "description" il est possible d'utiliser l'attribut "\_description". | -| **help**, **\_help**
`string` | Aide complémentaire associée à la famille.
📝 Si une sous-famille ou une sous-variable a déjà le nom "help" il est possible d'utiliser l'attribut "\_help". | -| **mode**, **\_mode**
`string` | Mode de la famille
Le mode par défaut d'une famille est le mode le plus petit des familles parentes, les variables enfants ou des familles enfants qui sont contenus dans cette famille.
Ce mode permet aussi de définir le mode par défaut des variables ou des familes inclusent dans cette famille.
📝 Si une sous-famille ou une sous-variable a déjà le nom "mode" il est possible l'attribut "\_mode". | -| **hidden**, **\_hidden**
`boolean` ou [`calcul`](../condition/README.md) | Famille invisible.
Permet de cacher une famille ainsi que les variables ou les familles inclusent dans cette famille.
Cela signifie que la famille ne sera plus visible pour l'utilisateur mais sera visible pour un calcul.
📝 Si une sous-famille ou une sous-variable a déjà le nom "hidden" il est possible l'attribut "\_hidden". | -| **disabled**, **\_disabled**
`boolean` ou [`calcul`](../condition/README.md) | Famille désactivée.
Permet de désactiver une famille ainsi que les variables ou les familles inclusent dans cette famille.
Cela signifie que la famille ne sera plus visible pour l'utilisateur mais également pour un calcul.
📝 Si une sous-famille ou une sous-variable a déjà le nom "disabled" il est possible l'attribut "\_disabled". | - -## Famille crée dynamiquement - -Pour créer une famille dynamiquement, il faut créer une famille fictive liée à une variable. -Le nom et la description de la famille sera en réalité le prefix du nouveau nom/description. Le suffix viendra de la valeur de la variable liée. -Le nom des familles et des variables qu'elle contient sera conservé, par contre, la description sera le prefix de la description réelle. - -Bien évidement si le contenu de variable liée venait a évoluer, de nouvelles familles dynamiques apparaitront ou disparaîtront. - -A noter que : - -- la variable liée à la famille doit être obligatoirement une variable multiple -- il n'est pas possible de mettre une simple famille dans une famille dynamique -- il est possible de mettre une famille meneuse dans une famille dynamique - -## Variable meneuse ou suiveuse - -Une famille meneuse a un attribut type à "leadership". Le type est obligatoire. - -### Un famille meneuse - -Les variables meneuses et suiveuses sont placées dans un famille meneuse. - -Une famille meneuse ne peut pas contenir d'autre famille. - -Le mode par défaut de la famille meneuse est le mode de la variable meneuse. - -### Variable meneuse - -Une variable meneuse est une variable qui va guider la longueur d'autres variables (appelé variables suiveuses). - -Une variable meneuse est une [variable](../variable/README.md) qui est obligatoirement de type multiple. - -Une variable meneuse peut être obligatoire. - -Le mode par défaut correspond au plus petit mode définit pour les variables suiveuses. - -### Variable suiveuse - -Une variable suiveuse est une variable donc la longueur n'est pas déterminé par elle-même, mais est identique à celle de la variable meneuse dont elle dépend. - -Une variable suiveuse est une variable placer juste derrière une variable meneuse ou une autre variable suiveuse. - -L'ordre de définition des variables suiveuses est important. - -Cette variable peut être de type multiple. Dans ce cas, pour un index de la variable meneuse determiné, il est possible de mettre plusieurs valeurs à une même variable. - -Une variable suiveuse peut être obligatoire. Cela signifie que lorsqu'une variable meneuse est renseigné, il faut obligatoirement que la variable suiveuse est également une valeur à l'index considéré. -Si aucune valeur n'est définit pour la variable meneuse, aucune valeur n'est a spécifié pour la variable suiveuse. - -Le mode par défaut d'une variable suiveuse correspond au mode de la variable meneuse. - -Si une variable meneuse est caché ou désactivé, les variables suiveuses le seront également. - -## Exemple - -### Famille simple - -```yml ---- -version: '1.0' -my_family: - type: family - description: This is a great family - help: This is the help of a great family - mode: expert -``` - -### Famille crée dynamiquement - -```yml ---- -version: '1.0' -varname: - multi: true - default: - - val1 - - val2 -my_dyn_family_: - type: dynamic - variable: rougail.varname - description: 'Describe ' - my_dyn_var: - type: string - description: 'Variable description for ' -``` - -Créera dynamiquement deux familles : - -- "rougail.my\_dyn\_family\_val1" avec la description "Describe val1" -- "rougail.my\_dyn\_family\_val2" avec la description "Describe val2" - -Dans la famille dynamique "rougail.my\_dyn\_family\_val1" on retrouvera une variable "my\_dyn\_var" avec la description "Variable description for val1". - -### Variable meneuse ou suiveuse - -#### Définition des variables meneuse et suiveuse - -Voici un exemple de définition d'une variable meneuse et de deux variables meneuses : - -```yml ---- -version: '1.0' -family: - type: leadership - leader: - multi: true - follower1: - follower2: - multi: true -``` - -#### Ajout d'une nouvelle variable suiveuse - -Pour ajouter une nouvelle variable suiveuse, dans un nouveau dictionnaire, il suffit de définir une ou des nouvelles variables dans la famille meneuse : - -```yml ---- -version: '1.0' -family: - follower3: -``` diff --git a/doc/fill/README.md b/doc/fill/README.md deleted file mode 100644 index a2a37d98d..000000000 --- a/doc/fill/README.md +++ /dev/null @@ -1,372 +0,0 @@ ---- -gitea: none -include_toc: true ---- - -# Les valeurs par défault calculées - -## Synopsis - -Une valeur peut être calculée. Dans ce cas on a quatre possibilités : - -- calcul via Jinja -- calcul via une variable -- calcul via une information -- calcul via un suffix : dans le cas d'une variable dans une famille dynamique -- calcul via un index : dans le cas d'une variable suiveuse - -Si l'utilisateur modifie la valeur de la variable, la valeur par défaut n'est plus utilisé, donc le calcul n'est plus réalisé. -C'est le cas également si la variable à l'attribut `auto_save`. - -Par contre si la variable est caché (avec le paramètre `hidden`) c'est la valeur par défaut qui est utilisé et non la valeur personnalisée par l'utilisateur. - -⚠️ Une variable suiveuse ne peut pas être calculé automatiquement. - -## Paramètres - -Suivant les types de calcul les paramètres vont être différents : - -| Type de calcul | Paramètre | Commentaires | Exemple | -|----------------|-----------|--------------|---------| -| | **type**
`string`
`mandatory` | Type du calcul, les valeurs possible sont : jinja, variable, information, suffix ou index | jinja | -| Jinja | **jinja**
`string`
`mandatory` | Template Jinja. Pour une variable multiple, chaque ligne représente une valeur. | {% if rougail.variable %}{{ rougail.variable }}{% endif %} | -| Jinja | **params**
`list` | Paramètres complémentaire passé au template Jinja | | -| Variable (`mandatory`)
Information | **variable**
`string` | Nom de la variable associée | rougail.variable | -| Variable | **propertyerror**
`boolean` | Si l'accès à la variable n'est pas possible à cause d'une propriété (par exemple `disabled`) par défaut une erreur est retournée. Si l'attribut est à False, la valeur calculée est vide.
**Valeur par défaut :** True | False | -| Information | **information**
`string`
`mandatory` | Nom de l'information dont on veut récupérer la valeur. | doc | - -Dans le cas d'un calcul de type Jinja, il est possible d'avoir des paramètres. - -Il existe deux types de paramètre : - -- les paramètres standards (string, boolean, integer, null), dans ce il suffit de faire : "key: value" -- les paramètres avancés : - - - paramètre via une variable - - paramètre via une information - - paramètre via un suffix : dans le cas d'une variable dans une famille dynamique - - paramètre via un index : dans le cas d'une variable suiveuse - -| Type du paramètre | Paramètre | Commentaires | Exemple | -|-------------------|-----------|--------------|---------| -| | **name**
`string`
`mandatory` | Le nom du paramètre | my\_param | -| | **type**
`string`
`mandatory` | Type du paramètre, les valeurs possible sont : variable, information, suffix ou index | suffix | -| Variable | **variable**
`string`
`mandatory` | Nom de la variable | rougail.variable | -| Variable (`mandatory`)
Information | **propertyerror**
`boolean` | Si l'accès à la variable n'est pas possible à cause d'une propriété (par exemple `disabled`) par défaut une erreur est retournée. Si l'attribut est à False, le paramètre n'est pas passé au template Jinja.
**Valeur par défaut :** True | False | -| Variable | **optional**
`boolean` | La variable peut ne pas exister suivant les importations de fichier YAML. Si le paramètre optional est à True, le paramètre sera tout simplement supprimer si la variable n'existe pas.
**Valeur par défaut :** False | True | -| Information | **information**
`string`
`mandatory` | Nom de l'information dont on veut récupérer la valeur. | doc | - -## Exemples - -### Calcul via un template Jinja - -Commençons par exemple à partir d'un simple template Jinja : - -```yml ---- -version: '1.0' -my_calculated_variable: - default: - type: jinja - jinja: 'no' -``` - -Voici un deuxième exemple avec une variable de type booléen : - -```yml ---- -version: '1.0' -my_calculated_variable: - type: boolean - default: - type: jinja - jinja: 'false' -``` - -Et une valeur multiple de type nombre : - -```yml ---- -version: '1.0' -my_calculated_variable: - type: number - multi: true - default: - type: jinja - jinja: | - 1 - 2 - 3 -``` - -Créons une variable dont la valeur est retournée par une fonction Python : - -```yml ---- -version: '1.0' -my_calculated_variable: - default: - type: jinja - jinja: '{{ return_no() }}' -``` - -Puis créons la fonction "return\_no" : - -```python -def return_no(): - return 'no' -``` - -Un exemple avec des paramètres : - -```yml ---- -version: '1.0' -my_calculated_variable: - description: my description - default: - type: jinja - jinja: | - {{ param1 }}{% if param2 is defined %}_{{ param2 }}{% endif %}_{{ param3 }} - params: - param1: value - param2: - type: variable - variable: rougail.unknown_variable - optional: true - param3: - type: information - information: doc - variable: rougail.my_calculated_variable -``` - -Un exemple avec un paramètre de type suffix : - -```yml ---- -version: '1.0' -varname: - multi: true - default: - - val1 - - val2 -my_dyn_family_: - type: dynamic - variable: rougail.varname - description: 'Describe ' - my_dyn_var: - type: string - default: - type: jinja - jinja: 'the suffix is: {{ param1 }}' - params: - param1: - type: suffix -``` - -Dans cette exemple, on voit une famille dynamique. Deux familles vont être crées : `rougail.my_dyn_family_val1.my_dyn_var` et `rougail.my_dyn_family_val2.my_dyn_var`. - -La valeur de la variable à l'intérieur de cette famille 'this suffix is: ' + la valeur du suffix (respectivement `val1` et `val2`). - -Un exemple avec un paramètre de type index : - -```yml ---- -version: '1.0' -family: - type: leadership - leader: - multi: true - default: - - val1 - - val2 - follower1: - default: - type: jinja - jinja: 'the index is: {{ param1 }}' - params: - param1: - type: index -``` - -### Calcul via une variable - -Copier une variable dans une autre : - -```yml ---- -version: '1.0' -my_variable: - multi: true - default: - - val1 - - val2 -my_calculated_variable: - multi: true - default: - type: variable - variable: rougail.my_variable -``` - -Copier une variable dans une autre si la source n'a pas de problème de propriété : - -```yml ---- -version: '1.0' -my_variable: - default: val1 - disabled: true -my_calculated_variable: - multi: true - default: - type: variable - variable: rougail.my_variable - propertyerror: false -``` - -Copier deux variables non multiple dans une variable multiple : - -```yml ---- -version: '1.0' -my_variable_1: - default: val1 -my_variable_2: - default: val2 -my_calculated_variable: - multi: true - default: - - type: variable - variable: rougail.my_variable_1 - - type: variable - variable: rougail.my_variable_2 -``` - -Une variable dans une famille dynamique peut également être utilisé dans un calcul. - -Par exemple en utilisant la variable pour un suffixe particulier : - -```yml ---- -version: '1.0' -varname: - multi: true - default: - - val1 - - val2 -my_dyn_family_: - type: dynamic - variable: rougail.varname - description: 'Describe ' - my_dyn_var: - type: string - default: - type: suffix -all_dyn_var: - default: - type: variable - variable: rougail.my_dyn_family_val1.my_dyn_var -``` - -Dans ce cas, on récupère la valeur `val1`. - -Deuxième exemple en utilisant la variable pour tous les suffixes : - -```yml ---- -version: '1.0' -varname: - multi: true - default: - - val1 - - val2 -my_dyn_family_: - type: dynamic - variable: rougail.varname - description: 'Describe ' - my_dyn_var_: - type: string - default: - type: suffix -all_dyn_var: - multi: true - default: - type: variable - variable: rougail.my_dyn_family_.my_dyn_var_ -``` - -Dans ce cas, on récupère la liste `val1` et `val2`. - - -### Calcul via un suffix - -```yml ---- -version: '1.0' -varname: - multi: true - default: - - val1 - - val2 -my_dyn_family_: - type: dynamic - variable: rougail.varname - description: 'Describe ' - my_dyn_var_: - type: string - default: - type: suffix -``` - -### Calcul via un index - -```yml ---- -version: '1.0' -family: - type: leadership - leader: - multi: true - default: - - val1 - - val2 - follower1: - type: number - default: - type: index -``` - -### Rédéfinition - -Dans un premier dictionnaire déclarons notre variable et notre calcule : - -```yml ---- -version: '1.0' -my_calculated_variable: - default: - type: jinja - jinja: 'the value is calculated' -``` - -Dans un second dictionnaire il est possible de redéfinir le calcul : - -```yml ---- -version: '1.0' -my_calculated_variable: - redefine: true - default: - type: jinja - jinja: 'the value is redefined' -``` - -Dans un troisième on peut même supprimer le calcul : - -```yml ---- -version: '1.0' -my_calculated_variable: - redefine: true - default: null -``` diff --git a/doc/getting_started.md b/doc/getting_started.md deleted file mode 100644 index 94fe5ac28..000000000 --- a/doc/getting_started.md +++ /dev/null @@ -1,698 +0,0 @@ ---- -gitea: none -include_toc: true ---- - -# Construire une liste d'options - -Rougail permet de construite des options [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu) à partir de dictionnaire écrit en YAML. -Le principal avantage étant que l'écrire des options est beaucoup plus simple. - -Une fois chargé, on retrouve toute la puissance de Tiramisu dans la gestion de la configuration. - -Avant de commencer, il faut connaitre les spécificités du format de fichier YAML, des notions de [Jinja](https://jinja.palletsprojects.com/) et de [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu). - -# La configuration du proxy type Mozilla Firefox - -L'objectif de ce première tutorial est de reproduire cette page de paramètres de Mozilla Firefox : - -![Firefox parameters page](firefox.png "Firefox parameters page") - -Les variables vont être créées dans plusieurs fichiers dans un but didactique. Bien évidement toutes les variables pourront être mise dans le même fichier. - -## La famille proxy - -Nous allons classer toutes ces variables dans une famille. -Cette famille s'appelera proxy. Créons le premier fichier dict/00-proxy.yml - -```yml ---- -version: '1.0' -proxy: - description: Configure Proxy Access to the Internet - type: family -``` - -Le type de la famille est ici précisé parce qu'on n'a pas de variable ou de famille à l'intérieur de cette famille. Le moteur pensera alors que c'est une simple variable. - -## Le type de proxy - -Il est possible de définir plusieurs modes de configuration du proxy (de "pas de proxy" à "la configuration d'un fichier de configuration automatique"). - -Nous allons donc créer une première variable dans cette famille dont la description est "Proxy mode". Créons le deuxième fichier dict/01-proxy\_mode.yml - -```yml ---- -version: '1.0' -proxy: - proxy_mode: - description: Proxy mode - type: choice - choices: - - No proxy - - Auto-detect proxy settings for this network - - Use system proxy settings - - Manual proxy configuration - - Automatic proxy configuration URL - default: No proxy - mandatory: true -``` - -Cette variable nécessite une valeur (la valeur `None` n'est pas acceptable), elle est donc obligatoire (`mandatory`). - -Si l'utilisateur ne précise pas de valeurs, la valeur de cette variable sera "No proxy" (`default`). - -La variable est à choix (`type`: choice) donc la liste des valeurs disponible est contrainte (`choices`), seul les valeurs suivantes sont autorisés : - -- No proxy -- Auto-detect proxy settings for this network -- Use system proxy settings -- Manual proxy configuration -- Automatic proxy configuration URL - -Testons nos deux premiers dictionnaires : - -```python ->>> from rougail import Rougail, RougailConfig ->>> from pprint import pprint ->>> RougailConfig['dictionaries_dir'] = ['dict'] ->>> rougail = Rougail() ->>> config = rougail.get_config() ->>> config.property.read_only() ->>> pprint(config.value.get(), sort_dicts=False) -{'rougail.proxy.proxy_mode': 'No proxy'} -``` - -## Le mode manuel - -Toute la configuration manuelle du proxy sera classé dans une famille. Créons le fichier dict/02-proxy\_manual.yml : - -```yml ---- -version: '1.0' -proxy: - manual: - description: Manual proxy configuration - type: family - disabled: - type: jinja - jinja: | - {% if rougail.proxy.proxy_mode != 'Manual proxy configuration' %} - the mode proxy is not manual - {% endif %} -``` - -Si l'utilisateur choisi le mode de proxy "Manual proxy configuration", on veut voir apparaitre (`disabled`) une nouvelle sous-famille appelé manual. -Si le template Jinja renvoi du texte, la famille sera alors désactivé. Sinon elle est accessible. -Désactiver un famille signifie qu'on ne pourra pas y accèder ainsi qu'aux variables ou familles inclusent dans cette famille. - -### La configuration du proxy HTTP - -Dans cette famille ajoutons une sous-famille `http_proxy` contenant les variables `address` et `port`. Créons le fichier dict/03-proxy\_manual\_http\_proxy.yml : - -```yml ---- -version: '1.0' -proxy: - manual: - http_proxy: - description: HTTP Proxy - address: - description: HTTP address - type: domainname - mandatory: true - port: - description: HTTP Port - type: port - default: '8080' -``` - -Les deux variables ont des types particuliers (`domainname` ou `port`) pour valider les valeurs configurer par l'utilisateur. - -Pas la peine de préciser le type de la famille `http_proxy` parce qu'on a déclaré les sous-variables dans ce fichier. - -### Dupliquer la configuration HTTP vers HTTPS - -On veux proposer à l'utilisateur la possiblité de renseigner le même proxy pour les requêtes HTTPS. Créons le fichier dict/04-proxy\_manual\_http\_use\_for\_https.yml : - -```yml -version: '1.0' -proxy: - manual: - use_for_https: - description: Also use this proxy for HTTPS - type: boolean -``` - -Cette variable est de type `boolean`. Sa valeur par défaut est `True`. - -### La configuration du proxy HTTPS - -Ajoutons une nouvelle sous-famille `ssl_proxy` avec les variables `address` et `port`. Créons le fichier dict/05-proxy\_manual\_ssl\_proxy.yml : - -```yml -version: '1.0' -proxy: - manual: - ssl_proxy: - description: HTTPS Proxy - hidden: - type: variable - variable: rougail.proxy.manual.use_for_https - address: - description: HTTPS address - type: domainname - default: - type: jinja - jinja: | - {% if rougail.proxy.manual.use_for_https %} - {{ rougail.proxy.manual.http_proxy.address }} - {% endif %} - mandatory: true - port: - description: HTTPS Port - type: port - default: - type: jinja - jinja: | - {% if rougail.proxy.manual.use_for_https %} - {{ rougail.proxy.manual.http_proxy.port }} - {% endif %} - mandatory: true -``` - -Suivant la valeur de la variable `rougail.proxy.mandatory.use_for_https` cette famille apparaitra ou disparaitra (`hidden`). -Contrairement à tout à l'heure, il n'est pas nécessaire de passer par une fonction Jinja. - -De plus, la famille n'est pas désactivé (`disabled`) parce que les variables devront rester accessible en mode lecture seule. - -Les variables `address` et `port` sont copiées de HTTP vers HTTPS si `rougail.proxy.use_for_https` est à True. - -Testons différents configuration : - -```python ->>> from rougail import Rougail, RougailConfig ->>> from pprint import pprint ->>> RougailConfig['dictionaries_dir'] = ['dict'] ->>> rougail = Rougail() ->>> config = rougail.get_config() ->>> config.property.read_only() ->>> pprint(config.value.get(), sort_dicts=False) -{'rougail.proxy.proxy_mode': 'No proxy'} -``` - -Pour le moment le proxy n'est pas configuré, donc on ne voit aucune variable. - -Regardons ce qui se passe si on accède à l'option description `rougail.proxy.manual` si on n'est pas en mode manuel : - -```python ->>> pprint(config.option('rougail.proxy.manual').value.get(), sort_dicts=False) -``` - -On a bien une erreur (avec le message définit dans le template Jinja) : - -``` -tiramisu.error.PropertiesOptionError: cannot access to optiondescription "Manual proxy configuration" because has property "disabled" (the mode proxy is not manual) -``` - -Configurons le proxy en mode manuel : - -```python ->>> config.property.read_write() ->>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration') ->>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example') ->>> pprint(config.value.get(), sort_dicts=False) -``` - -Les variable apparaisse bien avec les valeurs voulues : - -```json -{'rougail.proxy.proxy_mode': 'Manual proxy configuration', - 'rougail.proxy.manual.http_proxy.address': 'proxy.example', - 'rougail.proxy.manual.http_proxy.port': '8080', - 'rougail.proxy.manual.use_for_https': True} -``` - -Passons en mode `lecture seule` : - -```python ->>> config.property.read_only() ->>> pprint(config.value.get(), sort_dicts=False) -{'rougail.proxy.proxy_mode': 'Manual proxy configuration', - 'rougail.proxy.manual.http_proxy.address': 'proxy.example', - 'rougail.proxy.manual.http_proxy.port': '8080', - 'rougail.proxy.manual.use_for_https': True, - 'rougail.proxy.manual.ssl_proxy.address': 'proxy.example', - 'rougail.proxy.manual.ssl_proxy.port': '8080'} -``` -En mode `lecture seule`, on voit la configuration HTTPS qui apparait. -De plus on peut remarquer également que les valeurs des variables de `rougail.proxy.manual.http_proxy` ont bien été copié dans `rougail.proxy.manual.ssl_proxy`. - -En passant `rougail.proxy.manual.use_for_https` à False, il est possible de modifier la configuration HTTPS : - -```python ->>> config.property.read_write() ->>> config.option('rougail.proxy.manual.use_for_https').value.set(False) ->>> config.option('rougail.proxy.manual.ssl_proxy.address').value.set('other.proxy.example') ->>> pprint(config.value.get(), sort_dicts=False) -{'rougail.proxy.proxy_mode': 'Manual proxy configuration', - 'rougail.proxy.manual.http_proxy.address': 'proxy.example', - 'rougail.proxy.manual.http_proxy.port': '8080', - 'rougail.proxy.manual.use_for_https': False, - 'rougail.proxy.manual.ssl_proxy.address': 'other.proxy.example', - 'rougail.proxy.manual.ssl_proxy.port': '8080'} -``` - -La valeur de la variable `rougail.proxy.manual.ssl_proxy.address` a été modifiée. -Mais si cette variable est à nouveau cachée, la valeur de cette variable reviens à la valeur par défaut : - -```python ->>> config.option('rougail.proxy.manual.use_for_https').value.set(False) ->>> config.property.read_only() ->>> pprint(config.value.get(), sort_dicts=False) -{'rougail.proxy.proxy_mode': 'Manual proxy configuration', - 'rougail.proxy.manual.http_proxy.address': 'proxy.example', - 'rougail.proxy.manual.http_proxy.port': '8080', - 'rougail.proxy.manual.use_for_https': False, - 'rougail.proxy.manual.ssl_proxy.address': 'proxy.example', - 'rougail.proxy.manual.ssl_proxy.port': '8080'} -``` - -### La configuration du proxy SOCKS - -Ajoutons une nouvelle sous-famille `socks_proxy` avec les variables `address`, `port` et `version`. Créons le fichier dict/06-proxy\_manual\_socks\_proxy.yml : - -```yml -version: '1.0' -proxy: - manual: - socks_proxy: - description: SOCKS Proxy - address: - description: SOCKS Address - type: domainname - port: - description: SOCKS Port - type: port - version: - description: SOCKS host version used by proxy - type: choice - choices: - - v4 - - v5 - default: v5 -``` - -Rien a signaler pour cette famille et ces variables. - -## Le mode détection automatique - -Ajoutons une nouvelle sous-variable `auto`. Créons le fichier dict/07-proxy\_auto.yml : - -```yml -version: '1.0' -proxy: - auto: - type: web_address - description: Automatic proxy configuration URL - mandatory: true - disabled: - type: jinja - jinja: | - {% if rougail.proxy.proxy_mode != 'Automatic proxy configuration URL' %} - the proxy mode is not automatic - {% endif %} -``` - -Le type `web_address` impose une valeur qui commence par http:// ou https://. -Cette variable est activée lorsque le proxy est en mode automatique. - -## Les exceptions au proxy - -Enfin ajoutons une variable contenant les exceptions au proxy. Pour cela créons le fichier dict/07-proxy\_no\_proxy.yml : - -```yml -version: '1.0' -proxy: - no_proxy: - description: Address for which proxy will be desactivated - multi: true - type: "domainname" - params: - allow_ip: true - allow_cidr_network: true - allow_without_dot: true - allow_startswith_dot: true - disabled: - type: jinja - jinja: | - {% if rougail.proxy.proxy_mode == 'No proxy' %} - proxy mode is no proxy - {% endif %} -``` - -C'est une variable de type `domainname` mais qu'on personnalise un peu (`params`), en effet on autorisé : - -- les IP -- les réseaux au format CIDR -- les noms de machine (donc sans '.') -- les sous-domaines type .example - -Il peut y avoir plusieurs exceptions au proxy, la variable est donc `multi`. -Cette varible n'est pas accessible uniquement si aucun proxy n'est défini (`disabled`). - -Pour tester : - -```python ->>> from rougail import Rougail, RougailConfig ->>> from pprint import pprint ->>> RougailConfig['dictionaries_dir'] = ['dict'] ->>> rougail = Rougail() ->>> config = rougail.get_config() ->>> config.property.read_write() ->>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration') ->>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example') ->>> config.option('rougail.proxy.no_proxy').value.set(['.example', '192.168.1.1']) ->>> config.property.read_only() ->>> pprint(config.value.get(), sort_dicts=False) -``` - -```json -{'rougail.proxy.proxy_mode': 'Manual proxy configuration', - 'rougail.proxy.manual.http_proxy.address': 'proxy.example', - 'rougail.proxy.manual.http_proxy.port': '8080', - 'rougail.proxy.manual.use_for_https': True, - 'rougail.proxy.manual.ssl_proxy.address': 'proxy.example', - 'rougail.proxy.manual.ssl_proxy.port': '8080', - 'rougail.proxy.manual.socks_proxy.address': None, - 'rougail.proxy.manual.socks_proxy.port': None, - 'rougail.proxy.manual.socks_proxy.version': 'v5', - 'rougail.proxy.no_proxy': ['.example', '192.168.1.1']} -``` - -Mais pas possible de mettre une valeur invalide : - -```python ->>> config.option('rougail.proxy.no_proxy').value.set(['.example', '192.168.1.1', 'not valid']) -[..] -tiramisu.error.ValueOptionError: "not valid" is an invalid domain name for "Address for which proxy will be desactivated", could be a IP, otherwise must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed -``` - -## La demande d'authentification - -Rien de particulier à la création de la demande d'authentification. Pour cela créons le fichier dict/08-proxy\_prompt\_authentication.yml : - -```yml -version: '1.0' -proxy: - prompt_authentication: - description: Prompt for authentication if password is saved - type: boolean - default: true - disabled: - type: jinja - jinja: | - {% if rougail.proxy.proxy_mode == 'No proxy' %} - proxy mode is no proxy - {% endif %} -``` - -## Le DNS du proxy SOCKS v5 - -La variable DNS pour le proxy SOCKS v5 n'apparait que si le proxy est configuré et que la version du proxy SOCKS sélectionné est bien la "v5". - -Créons le fichier dict.09-proxy\_proxy\_dns\_socks5.yml : - -```yml -version: '1.0' -proxy: - proxy_dns_socks5: - description: Use proxy DNS when using SOCKS v5 - type: boolean - default: false - disabled: - type: jinja - params: - socks_version: - type: variable - variable: rougail.proxy.manual.socks_proxy.version - propertyerror: false - jinja: | - {% if rougail.proxy.proxy_mode == 'No proxy' %} - the proxy mode is no proxy - {% elif socks_version is undefined or socks_version == 'v4' %} - socks version is v4 - {% endif %} -``` - -La difficulté ici c'est que la variable `rougail.proxy.manual.socks_proxy.version` peut être désactivé (et donc non utilisable dans un calcul). -Dans ce cas, nous allons ajouter un paramètre (ici appelé `socks_version`) qui contiendra, s'il n'y a pas d'erreur de propriété, la valeur de la variable. -Sinon le paramètre ne sera pas passé au template Jinja. C'est pourquoi il faut tester dans le template Jinja si la variable `socks_version` existe bien. - -## Le DNS à travers HTTPS - -Enfin nous allons configurer le DNS à travers HTTPS dans le fichier 10-proxy\_dns\_over\_https.yml : - -```yml -version: '1.0' -proxy: - dns_over_https: - description: DNS over HTTPS - enable_dns_over_https: - description: Enable DNS over HTTPS - type: boolean - default: false - provider: - description: Use Provider - type: choice - choices: - - Cloudflare - - NextDNS - - Custom - default: Cloudflare - disabled: - type: jinja - jinja: | - {% if not rougail.proxy.dns_over_https.enable_dns_over_https %} - Enable DNS over HTTPS is False - {% endif %} - custom_dns_url: - description: Custom DNS URL - type: web_address - mandatory: true - disabled: - type: jinja - params: - provider: - type: variable - variable: rougail.proxy.dns_over_https.provider - propertyerror: false - jinja: | - {% if provider is not defined or provider != 'Custom' %} - provider is not custom - {% endif %} - validators: - - type: jinja - jinja: | - {% if rougail.proxy.dns_over_https.custom_dns_url.startswith('http://') %} - only https is allowed - {% endif %} -``` - -La seule particularitée ici est qu'on a ajouté une validation (`validators`) supplémentaire à la variable `custom_dns_url`. Seul une adresse commençant par https:// est autorisé (pas http://). - -# La configuration du proxy type FoxyProxy - -Voici maintenant l'intégration d'une partie du plugin Firefox FoxyProxy. - -L'idée est d'avoir un espace de nom spécifique à FoxyProxy et de retrouver dedans une partie du paramétrage qu'on aura fait dans l'espace de nom principal. - -Voici à quoi ressemble la page : - -![FoxyProxy parameters page](foxyproxy.png "FoxyProxy parameters page") - -Il est possible, dans ce plugin, de spécifié un nombre illimité de proxy. -Notre famille "proxy" ne sera plus de type `family` comme tout a l'heure mais du type "leadership". - -Voici le contenu complet de la configuration du proxy type FoxyProxy à mettre dans le fichier foxyproxy/00-base.yml : - -```yml ---- -version: '1.0' -proxy: - _type: leadership - title: - description: Title or Description - multi: true - color: - description: Color - mandatory: true - type: - type: choice - choices: - - HTTP - - HTTPS/SSL - - SOCKS5 - - SOCKS4 - - PAC URL - - WPAD - - System (use system settings) - - Direct (no proxy) - default: Direct (no proxy) - address: - description: IP address, DNS name, server name - multi: true - mandatory: true - disabled: - type: jinja - jinja: | - {% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %} - proxy does not need address - {% endif %} - default: - type: jinja - params: - firefox_address: - type: variable - variable: rougail.proxy.manual.http_proxy.address - propertyerror: false - jinja: | - {% if firefox_address is not undefined %} - {{ firefox_address }} - {% endif %} - port: - description: Port - type: port - mandatory: true - default: - type: jinja - params: - firefox_port: - type: variable - variable: rougail.proxy.manual.http_proxy.port - propertyerror: false - jinja: | - {% if firefox_port is not undefined %} - {{ firefox_port }} - {% endif %} - disabled: - type: jinja - jinja: | - {% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %} - proxy does not need port - {% endif %} - username: - description: Username - type: unix_user - mandatory: - type: jinja - jinja: | - {% if foxyproxy.proxy.password %} - username is mandatory - {% endif %} - disabled: - type: jinja - jinja: | - {% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %} - proxy does not need username - {% endif %} - password: - description: Password - type: secret - disabled: - type: jinja - jinja: | - {% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %} - proxy does not need password - {% endif %} - url: - type: web_address - disabled: - type: jinja - jinja: | - {% if foxyproxy.proxy.type not in ['PAC URL', 'WPAD'] %} - proxy does not need url - {% endif %} -``` - -Quelques remarques : - -- dans la famille meneuse `foxyproxy.proxy` il y a une variable nommée "type", cela peut entrer en conflit avec l'attribute `type`. Dans ce cas, pour spécifier le type on utilise l'attribut `_type` -- une variable suiveuse peut également être multiple (ce qui est le cas de `foxyproxy.proxy.address`) -- `foxyproxy.proxy.username` devient obligatoire si `foxyproxy.proxy.password` est spécifié, en effet un mot de passe sans nom d'utilisateur n'a pas de sens - -Testons : - -```python ->>> from rougail import Rougail, RougailConfig ->>> from pprint import pprint ->>> RougailConfig['dictionaries_dir'] = ['dict'] ->>> RougailConfig['extra_dictionaries']['foxyproxy'] = ['foxyproxy/'] ->>> rougail = Rougail() ->>> config = rougail.get_config() ->>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration') ->>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example') ->>> config.option('foxyproxy.proxy.title').value.set(['MyProxy']) ->>> config.option('foxyproxy.proxy.type', 0).value.set('HTTP') ->>> config.option('foxyproxy.proxy.color', 0).value.set('#00000') ->>> config.property.read_only() ->>> pprint(config.value.get(), sort_dicts=False) -``` - -Ce qui donne : - -```json -{'rougail.proxy.proxy_mode': 'Manual proxy configuration', - 'rougail.proxy.manual.http_proxy.address': 'proxy.example', - 'rougail.proxy.manual.http_proxy.port': '8080', - 'rougail.proxy.manual.use_for_https': True, - 'rougail.proxy.manual.ssl_proxy.address': 'proxy.example', - 'rougail.proxy.manual.ssl_proxy.port': '8080', - 'rougail.proxy.manual.socks_proxy.address': None, - 'rougail.proxy.manual.socks_proxy.port': None, - 'rougail.proxy.manual.socks_proxy.version': 'v5', - 'rougail.proxy.no_proxy': [], - 'rougail.proxy.proxy_dns_socks5': False, - 'rougail.proxy.dns_over_https.enable_dns_over_https': False, - 'foxyproxy.proxy.title': [{'foxyproxy.proxy.title': 'MyProxy', - 'foxyproxy.proxy.color': '#00000', - 'foxyproxy.proxy.type': 'HTTP', - 'foxyproxy.proxy.address': ['proxy.example'], - 'foxyproxy.proxy.port': '8080', - 'foxyproxy.proxy.username': None, - 'foxyproxy.proxy.password': None}]} -``` - -Le parti pris ici est de rendre obligatoire `foxyproxy.proxy.username` si un mot de passe est spécifié dans la variable `foxyproxy.proxy.password`. - -Il est logique d'avoir un nom d'utilisateur sans mot de passe (dans ce cas là le mot de passe sera demander lors de la connexion au proxy). -Mais l'inverse ne l'est pas. - -D'un point de vu utilisateur cela peut paraitre perturbant (si on met le mot de passe, il faut revenir a l'option précédent pour préciser le mot de passe). - -Il est possible d'inverser la logique. -Si la variable `foxyproxy.proxy.username` est renseignée, la variable `foxyproxy.proxy.password` devient modifiable. - -Aucune des deux variables n'a ainsi besoin d'être obligatoire. - -Si vous préférez cette option, voici un second dictionnaire extra "foxyproxy/01-redefine.yml" qui va redéfinir le comportement uniquement des variable `foxyproxy.proxy.username` et `foxyproxy.proxy.password` : - -```yml ---- -version: '1.0' -proxy: - username: - redefine: true - # suppress mandatory constrainte - mandatory: false - password: - redefine: true - hidden: - type: jinja - jinja: | - {% if not foxyproxy.proxy.username %} - no username defined - {% endif %} -``` - -A vous de jouer maintenant ! diff --git a/doc/variable/README.md b/doc/variable/README.md deleted file mode 100644 index 23d9e3521..000000000 --- a/doc/variable/README.md +++ /dev/null @@ -1,247 +0,0 @@ ---- -gitea: none -include_toc: true ---- - -# Variable - -## Synopsis - -Une variable est un conteneur qui contiendra une ou plusieurs valeurs. - -## Paramètres - -| Paramètre | Commentaires | -|-----------|--------------| -| **name**
`string`
`mandatory` | Nom de la variable.
C'est avec ce nom qu'on va pouvoir interagir avec la variable.
Il est préférable de suivre la [convention sur les noms de variable](convention.md). | -| **type**
`string` | Type de la variable.
Ce type définit la validation par défaut de la variable
**Valeur par défaut :** string | -| **params**
`list` | Liste de paramètre permettant d'adapté la validation de type. | -| **description**
`string` | La description de la variable.
Information utilisateur permettant de comprendre l'utilité de la variable.
**Valeur par défaut :** le nom | -| **help**
`string` | Aide complémentaire associée à la variable. | -| **default** | Valeur(s) par défaut de la variable.
Cette valeur est typée, il faut remplir correctement le fichier YAML pour ne pas définir une valeur avec un type incorrect. Par exemple, un "number" doit êre un chiffre, une variable multiple doit être une liste, ...
Pour une variable multiple non [meneuse](../family/leadership.md), la première valeur défini dans la liste sera également la valeur par défaut proposée si on ajoute une nouvelle valeur à cette variable. | -| **validators**
`list` | Validateurs de valeur.
. Liste de template Jinja. La valeur de la variable sera considérée comme invalide si le template retourne quelque chose. | -| **auto_save**
`boolean` | Variable à valeur automatiquement modifiée.
Une variable avec valeur automatiquement modifiée est une variable dont la valeur sera considérée comme modifiée (ce n'est plus une valeur par défaut).
Par exemple, si la valeur de cette variable est issue d'un calcul, la valeur ne sera plus recalculée.
Ces variables sont généralement des variables obligatoires. En effet ces variables ne sont automatiquement modifiées que si elles ont une valeurs.
Une [variable meneuse ou suiveuse](../family/leadership.md) ne peut pas avoir la propriété `auto_save`. | -| **mode**
`string` | Mode de la variable.
**Valeur par défaut :** Le mode par défaut d'une variable est le mode de la famille parente.
Cas particuliers :
- une variable à valeur automatiquement modifiée ou une variable en lecture seule automatique est par défaut en mode "basic"
- si la variable n'est pas dans une famille, la variable aura le mode "normal" par défaut
- une variable obligatoire sans valeur par défaut (calculer ou non) aura le mode "basic" | -| **muli**
`boolean` | La variable a pour valeur une liste. **Valeur par défaut** : false | -| **unique**
`boolean` | La variable multiple accepte plusieurs fois la même valeur. Si unique est à `false` une variable multiple n'accepte qu'une seule fois la même valeur dans la liste. **Valeur par défaut :** false | -| **hidden**
`boolean` ou [`calcul`](../condition/README.md) | Variable invisible.
Permet de cacher une variable.
Cela signifie que la variable ne sera plus visible en lecture écriture, mais uniquement pour des calculs ou en mode lecture seule.
Lorsqu'on rend invisible une variable l'utilisateur ne pourra pas modifier sa valeur, il s'il a déjà réussi à la modifier, cette valeur ne sera pas prise en compte. **Valeur par défaut :** false | -| **disabled**
`boolean` ou [`calcul`](../condition/README.md) | Variable désactivée.
Permet de désactiver une variable.
Cela signifie que la variable ne sera plus visible pour l'utilisateur mais également pour un calcul. **Valeur par défaut :** false | -| **mandatory**
`boolean` ou [`calcul`](../condition/README.md) | Variable obligatoire.
Variable dont une valeur est requise.
Pour une variable multiple, cela signifie que la liste ne doit pas être vide.
📝 Une variable avec une valeur par défaut non calculée ou de type "boolean" sont automatiquement considéré comme obligatoire, si ce n'est pas le comportement voulu, il mettre le préciser comme cela : "mandatory: false". **Valeur par défaut :** false | -| **redefine**
`boolean` | Il est possible de définir une variable dans un dictionnaire et de changer son comportement dans une second dictionnaire. Dans ce cas il faut explicitement redéfinir la variable. **Valeur par défaut :** false| -| **exists**
`boolean` | Cette attribut permet deux choses :
- créer une variable si elle n'existe pas dans un autre dictionnaire (sinon ne rien faire), dans ce cas la valeur de l'attribut doit être `true`
- en conjonction avec l'attribut redefine à la valeur true, ne modifie le comportement si elle est préexistante, dans ce cas la valeur de l'attribut doit être `false`.
**Valeur par défaut :** null | -| **test**
`list` | L'attribut "test" est un attribut spécial qui permet aux concepteurs d'un dictionnaire d'influencer un robot de test en précisant de valeurs utiles à tester.
Concrêtement, le contenu de cet attribut est enregister dans une "information" de l'option Tiramisu correspondante. | - -## Les types des variables - -Une variable a un type. - -Ce type permet de définir les valeurs acceptées par cette variable. - -| valeur | Commentaires | Paramètres | Exemples | -|--------|--------------|------------|----------| -| string | chaine de caractère (type par défaut) | | test
"1"
"true" | -| number | un nombre | `min_number` : nombre minimum autorisé
`max_number` : nombre maximal autorisé | 1 | -| float | un chiffre flottant | | 1.2 | -| boolean | Un booléen, si aucune valeur n'est défini la valeur par défaut de cette variable sera "true", la variable sera également obligatoire par défaut | | true
false | -| secret | un secret (comme un mot de passe, une clef privée, ...) | | hO_'hi | -| mail | une adresse mail | | test@rougail.example | -| filename | nom de fichier au sens Unix | | /etc/passwd | -| date | une date au format "%Y-%m-%d" | | 2021-01-30 -| unix\_user | nom d'utilisateur au sens Unix | | test | -| ip | n'importe quelle adresse IPv4 | `private_only` : seule des IP privée (false par défaut)
`allow_reserved` : autorise les IP réservées (true par défaut) | 1.2.3.4 | -| cidr | n'importe quelle adresse IPv4 au format CIDR | `private_only` : seule des IP privée (false par défaut) `allow_reserved` : autorise les IP réservées (false par défaut) | 1.2.3.4/24 | -| netmask | masque d'une adresse IPv4 | | 255.255.255.0 | -| network | adresse réseau | | 192.168.1.0 | -| network\_cidr | adresse réseau au format CIDR | | 192.168.1.0/24 | -| broadcast | adresse de diffusion | | 255.255.255.255 | -| netbios | nom netbios | | machine | -| domainname | nom de domaine | `allow_ip` : autorise une IP plutôt qu'un nom de domaine (false par défaut)
`allow_cidr_network` : autorise une adresse réseau de type CIDR (false par défaut)
`allow_without_dot` : autorise les noms sans point (false par défault)
`allow_startswith_dot` : autorise de commencer par un point (false par défault) | rougail.example | -| hostname | nom d'hôte | `allow_ip` : autorise une IP plutôt qu'un nom de domaine (false par défaut) | machine | -| web\_address | adresse web | `allow_ip` : autorise une IP plutôt qu'un nom de domaine (false par défaut)
`allow_without_dot` : autorise les noms sans point (true par défault) | http://rougail.example | -| port | port | `allow_range` : autorise un interval de port, par exemple 80:85 (false par défaut)
`allow_zero` : autorise le port 0 (false par défaut)
`allow_wellknown` : autorise les ports de 1 à 1023 (true par défaut)
`allow_registred` : autorise les ports de 1024 à 49151 (true par défaut) `allow_private` : autorise les ports supérieurs à 49152 (true par défaut)
`allow_protocol` : autorise l'ajout du protocole, par exemple tcp:80 (false par défaut) | 8080 | -| mac | adresse MAC | | 11:11:11:11:11:11 | -| unix\_permissions | droit d'accès au fichier, répertoire, ... | | 644 | -| choice | variable à choix | | | - -## Les modes des variables - -Par défault, il existe trois "mode" dans Rougail : - -- basic : variables indispensables à la mise en place d'un service -- normal : variables couramment modifié par l'utilisateur -- expert : variables a manipuler avec précausion et en toutes connaissances de cause - -Il est possible de personnaliser les modes dans la [configuration de rougail](../dev/config.md) - -## Exemples - -### Quelques variables de base - -```yml ---- -version: '1.0' -my_variable: - type: string - description: This is a good variable - help: This is the help of a good variable - default: value -my_variable_multi_1: - multi: true - default: - - value -my_variable_multi_2: - multi: true - default: - - value1 - - value2 -version: '1.0' -my_variable_multi_not_unique: - multi: true - unique: false - default: - - value1 - - value2 - - value2 -``` - -### Redéfinition d'une variable - -Créons notre variable : - -```yml ---- -version: '1.0' -my_variable: - type: string -``` - -Et redéfinisons là : - -```yml ---- -version: '1.0' -my_variable: - redefine: true - description: New description -``` - -### Créer une variable inexistante - - -```yml ---- -version: '1.0' -my_variable: - exists: false -``` - -## Redéfinir une variable si elle existe - -Parfois on veut pouvoir redéfinir une variable mais seulement dans le cas où elle existe déjà : - -```yml ---- -version: '1.0' -my_variable: - exists: true - hidden: true -``` - -Dans ce cas la variable ne sera pas créé mais uniquement cachée si elle existe déjà. - -## Une variable à choix - -Il est possible d'imposer une liste de valeur pour une variable particulière : - -```yml ---- -version: '1.0' -my_variable: - type: choice - choices: - - val1 - - val2 - - val3 -``` - -Dans ce cas, seules les valeurs proposées sont possibles pour cette variable. -Cette variable n'est pas obligatoire donc il est possible de mettre la valeur "None". - -Si la variable est obligatoire ou si une valeur est précisée (la variable passe obligatoire) alors la valeur "None" n'est plus autorisé : - -```yml ---- -version: '1.0' -my_variable: - type: choice - choices: - - val1 - - val2 - - val3 - default: val1 -``` - -Une variable à choix est typée : - -Les choix sont typés : - -```yml ---- -version: '1.0' -my_variable: - type: choice - choices: - - val1 - - 3 - - true - default: val1 -``` - -dans ce cas, le chiffre 3 est autorisé mais pas la chaine de caractère "3". - -## Un variable à choix provenant de variable - -Les choix d'une variable peuvent provenir d'une autre variable multiple : - -```yml ---- -version: '1.0' -source_variable: - multi: true - default: - - val1 - - val2 -my_variable: - type: choice - choices: - type: variable - variable: rougail.source_variable -``` - -Dans ce cas, toutes les valeurs de la variable ou des variables seront des choix utilisables par l'utilisateur. - -## Un variable à choix provenant d'un template Jinja - -Faisons d'abord une fonction `trange` dans le fichier functions.py : - -```python -def trange(min, max): - return range(min, max) -``` - -On va récupérer ici les valeurs de 0 à 9 : - -```yml ---- -version: '1.0' -my_variable: - type: choice - default: 9 - choices: - type: jinja - jinja: | - {% for item in trange(0, 10) %} - {{ item }} - {%- endfor %} - return_type: number -``` diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css new file mode 100644 index 000000000..0d59fcadf --- /dev/null +++ b/docs/_static/css/custom.css @@ -0,0 +1,4 @@ +.wy-table-responsive table td { + white-space: normal; +} + diff --git a/docs/bribes.txt b/docs/bribes.txt new file mode 100644 index 000000000..2f1e0205b --- /dev/null +++ b/docs/bribes.txt @@ -0,0 +1,2 @@ +variable + `_ diff --git a/docs/check.rst b/docs/check.rst new file mode 100644 index 000000000..45801ed8e --- /dev/null +++ b/docs/check.rst @@ -0,0 +1,286 @@ +Verification function +========================== + +Synopsis +------------- + +A verification is a complementary validation to the type which allows the content of a variable to be validated more precisely. + +A :term:`validator` is necessarily a Jinja type calculation. + +Parameters +-------------- + +Depending on the types of calculation, the parameters will be different: + +.. list-table:: + :widths: 15 25 20 15 + :header-rows: 1 + + * - **Calculation type** + - **Parameter** + - **Comments** + - **Sample** + + * - + - **type** + + `string` + + `mandatory` + - Type of calculation, the only possible value is: jinja + - jinja + * - **jinja** + + `string` + + `mandatory` + - Jinja template + - {% if rougail.variable == 'not_allowed' %}not allowed!{% endif %} + - + * - **params** + + `list` + - Additional parameters passed to the Jinja template + - + - + +There are two types of parameter: + +- the standard parameters (string, boolean, integer, null), in this case just do: "key: value" +- advanced settings: + + - parameter via a variable + - parameter via an information + - parameter via a suffix: in the case of a variable in a dynamic family + - parameter via an index: in the case of a :term:`follower` variable + +.. list-table:: + :widths: 15 25 20 15 + :header-rows: 1 + + * - **Parameter's type** + - **Parameter** + - **Comments** + - **Sample** + * - + - **name** + + `string` + + `mandatory` + - Parameter's name + - my_param + * - + - **type** + + `string` + + `mandatory` + - Type of parameter, possible values are: variable, information, suffix or index + - suffix + * - Variable + - **variable** + + `string` + + `mandatory` + - variable's name + - rougail.variable + * - Variable (`mandatory`) information + - **propertyerror** + + `boolean` + - If access to the variable is not possible due to a property + (for example `disabled`) by default an error is returned. + If the attribute is `False`, the parameter is not passed to the Jinja template. + + **Default value**: `True` + - True + * - Information + - **information** + + `string` + + `mandatory` + - Name of the information whose value we want to retrieve. + - doc + +Samples +-------------- + +Strict verification of values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Here is a simple example of validating values: + +.. code-block:: yaml + + --- + version: '1.0' + my_variable: + validators: + - type: jinja + jinja: | + {% if rougail.my_variable and not rougail.my_variable.islower() %} + {{ rougail.my_variable }} is not lowercase string + {% endif %} + + +A verification function must take into account 2 important aspects: + +- the value may not be entered (even if the variable is mandatory), the None value must be taken into account +- if the value is invalid, a sentence must be returned with an explicit message. + +From now on only `None` and lowercase values will be allowed. + +Checking values with warning +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the constraint, it is possible to specify the error level and put it as a warning: + +.. code-block:: yaml + + --- + version: '1.0' + my_variable: + validators: + - type: jinja + jinja: |+ + {% if rougail.my_variable and not rougail.my_variable.islower() %} + {{ rougail.my_variable }} is not lowercase string + {% endif %} + params: + warnings_only: true + +In this case a value with a capital letter will be accepted, but a warning message will appear. + +Verification with parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: yaml + + --- + version: '1.0' + my_hidden_variable: + disabled: true + my_variable: + validators: + - type: jinja + jinja: | + {% if param1 is defined and rougail.my_variable == param1 %} + has same value as rougail.unknown_variable + {% endif %} + {% if param2 is defined and rougail.my_variable == param2 %} + has same value as rougail.my_hidden_variable + {% endif %} + params: + param1: + type: variable + variable: rougail.unknown_variable + optional: true + param2: + type: variable + variable: rougail.my_hidden_variable + propertyerror: false + +An example with a suffix type parameter: + +.. code-block:: yaml + + --- + version: '1.0' + varname: + multi: true + default: + - val1 + - val2 + my_dyn_family_: + type: dynamic + variable: rougail.varname + description: 'Describe ' + my_dyn_var: + type: string + validators: + - type: jinja + jinja: | + {% if rougail.my_dyn_family_.my_dyn_var == param1 %} + forbidden! + {% endif %} + params: + param1: + type: suffix + +In this example, we see a dynamic family. Two families will be created: `rougail.my_dyn_family_val1.my_dyn_var` and `rougail.my_dyn_family_val2.my_dyn_var`. + +The value of the variable within this family cannot be equal to the value +of the suffix (`val1` and `val2` respectively). + +An example with an index type parameter: + +.. code-block:: yaml + + --- + version: '1.0' + family: + type: leadership + leader: + multi: true + default: + - val1 + - val2 + follower1: + type: number + validators: + - type: jinja + jinja: | + {% if rougail.family.follower1 == param1 %} + forbidden! + {% endif %} + params: + param1: + type: index + +Redefinition +--------------- + +In a first dictionary, let's declare our variable and its verification function: + +.. code-block:: yaml + + --- + version: '1.0' + my_variable: + validators: + - type: jinja + jinja: | + {% if rougail.my_variable and not rougail.my_variable.islower() %} + {{ rougail.my_variable }} is not lowercase string + {% endif %} + +In a second dictionary it is possible to redefine the calculation: + +.. code-block:: yaml + + --- + version: '1.0' + my_variable: + redefine: true + validators: + - type: jinja + jinja: | + {% if rougail.my_variable and ' ' in rougail.my_variable %} + {{ rougail.my_variable }} has a space + {% endif %} + +In this case only this validator will be executed. + +Here is a third dictionary in which we remove the validation: + +.. code-block:: yaml + + --- + version: '1.0' + my_variable: + redefine: true + validators: diff --git a/docs/condition.rst b/docs/condition.rst new file mode 100644 index 000000000..4fa7f5b96 --- /dev/null +++ b/docs/condition.rst @@ -0,0 +1,240 @@ +Calculated properties +========================== + +Synopsis +------------ + +Calculated properties allow you to add or remove properties to a :term:`variable` +or a :term:`family` depending on the context. + +Here is the list of editable properties: + +.. list-table:: + :widths: 15 15 25 + :header-rows: 1 + + * - **Attribute applicable on** + - **Property's name** + - Comment + + * - Variable + + Family + - hidden + - Hides a variable or a family, in this case it is not accessible in `read-write` mode, + but remains accessible in a calculation or in `read-only` mode + * - Variable + + Family + - disabled + - Deactivates a variable or family, in this case it is never accessible + * - Variable + - mandatory + - The variable expects a value other than `None` or `[]` for multiple variables + +A property can be calculated. In this case we have two possibilities: + +- calculation via Jinja +- calculation via a variable + +Parameters +--------------- + +.. list-table:: + :widths: 15 25 20 15 + :header-rows: 1 + + * - **Calculation type** + - **Parameter** + - **Comment** + - **Sample** + * - + - **type** + + `string` + + `mandatory` + - Calculation type, possible values are: jinja, variable, information, suffix or index + - jinja + * - Jinja + - **jinja** + + `string` + + `mandatory` + - Jinja template . For a multiple variable, each line represents a value. + - {% if rougail.variable %} + + {{ rougail.variable }} + + {% endif %} + * - Jinja + - **params** + + `list` + - Additional parameters passed to the Jinja template + - + * - Variable + - **variable** + + `string` + + `mandatory` + - Name of the associated variable. + + .. attention:: The variable must be of `boolean` type. + - rougail.variable + * - Variable + - **propertyerror** + + `boolean` + - If access to the variable is not possible due to a property + (for example `disabled`) by default an error is returned. + If the attribute is `False`, the calculated value is False. + + **Default value**: `True` + - False + +In the case of a Jinja type calculation, it is possible to have parameters. + +There are two types of parameter: + +- the standard parameters (string, boolean, integer, null), in this case just do: "key: value" + +- advanced settings: + + - parameter via a variable + - parameter via information + - parameter via a suffix: in the case of a variable in a dynamic family + - parameter via an index: in the case of a follower variable + +.. list-table:: + :widths: 15 25 20 15 + :header-rows: 1 + + * - **Parameter's type** + - **Parameter** + - **Comments** + - **Sample** + * - + - **name** + + `string` + + `mandatory` + - parameter's name + - my_param + * - + - **type** + + `string` + + `mandatory` + - Parameter's type, possible values are: variable, information, suffix or index + - suffix + * - Variable + - **variable** + + `string` + + `mandatory` + - variable's name + - rougail.variable + * - Variable (`mandatory`) Information + - **propertyerror** + + `boolean` + - If access to the variable is not possible due to a property (for example `disabled`) by default an error is returned. If the attribute is False, the parameter is not passed to the Jinja template. + + **Default value**: `True` + - False + * - Variable + - **optional** + + `boolean` + - The variable may not exist depending on YAML file imports. If the optional parameter is True, the parameter will simply be deleted if the variable does not exist. + **Default value**: `False` + - True + + * - information + - **information** + + `string` + + `mandatory` + - Name of the information whose value we want to retrieve. + - doc + +Samples +------------ + +A Jinja-type calculated property +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to write the condition in Jinja: + + +.. code-block:: yaml + + --- + version: '1.0' + condition: + default: 'do not hide!' + my_variable: + hidden: + type: jinja + jinja: | + {% if rougail.condition and rougail.condition == "hide!" %} + this rougail.condition value is 'hide!' + {% endif %} + + +In this case the variable is hidden if the value of the variable "rougail.condition" is `hide!` and it did not hide for any other value. Be careful, always take into consideration that "rougail.condition" can be equal to `None`. + +The message returned by the function is visible in the error message in the event of an access problem: + +.. code-block:: python + + >>> from rougail import Rougail, RougailConfig + >>> RougailConfig['dictionaries_dir'] = ['dict'] + >>> rougail = Rougail() + >>> config = rougail.get_config() + >>> config.property.read_write() + [..] + tiramisu.error.PropertiesOptionError: cannot access to option "my_variable" because has property "hidden" (this rougail.condition value is 'hide!') + +It is possible to use parameters when calculating properties as for calculating the `default` attribute. + +A calculated property of variable type +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A variable can therefore be calculated via the result of another variable. Please note, this other variable must be of `boolean` type: + +.. code-block:: yaml + + --- + version: '1.0' + condition: + type: boolean + my_variable: + hidden: + type: variable + variable: rougail.condition + +If the value of the variable "rougail.condition" is `True` then the variable "rougail.my_variable" is hidden. + +Redefintion +~~~~~~~~~~~~~~~~~ + +It may be that in a dictionary we decide to define a condition. + +To delete the calculation from a variable, simply do in a new dictionary: + +.. code-block:: yaml + + --- + version: '1.0' + my_variable: + redefine: true + hidden: + diff --git a/docs/conf.py b/docs/conf.py new file mode 100755 index 000000000..8b2a7e831 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,149 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = 'Rougail' +copyright = '2019-2023, Silique' +author = 'gwen' + +# The short X.Y version +version = '' +# The full version, including alpha/beta/rc tags +release = '1.0' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. + +extensions = [ + 'sphinx.ext.extlinks', 'sphinx_lesson', + #'myst_parser', 'sphinx.ext.extlinks' +] +# +#myst_enable_extensions = [ +# "amsmath", +# "attrs_inline", +# "colon_fence", +# "deflist", +# "dollarmath", +# "fieldlist", +# "html_admonition", +# "html_image", +## "linkify", +# "replacements", +# "smartquotes", +# "strikethrough", +# "substitution", +# "tasklist", +#] + + +# **extlinks** 'sphinx.ext.extlinks', +# enables syntax like :proxy:`my source ` in the src files +extlinks = {'proxy': ('/proxy/%s.html', + 'external link: ')} + +default_role = "code" + +html_theme = "sphinx_rtd_theme" + +pygments_style = 'sphinx' + +html_short_title = "Rougail" +html_title = "Rougail documenation" + +# If true, links to the reST sources are added to the pages. +html_show_sourcelink = False + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +html_show_sphinx = False + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +html_show_copyright = True + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +#source_suffix = ['.rst', '.md'] +source_suffix = '.rst' +#source_suffix = { +# '.rst': 'restructuredtext', +# '.txt': 'restructuredtext', +# '.md': 'markdown', +#} +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = 'en' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +#html_theme = 'alabaster' +# **themes** +#html_theme = 'bizstyle' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + +def setup(app): + app.add_css_file('css/custom.css') diff --git a/docs/configuration.rst b/docs/configuration.rst new file mode 100644 index 000000000..5f067482f --- /dev/null +++ b/docs/configuration.rst @@ -0,0 +1,76 @@ +Customizing Rougail's configuration +======================================= + +The `Rougail`\ 's configuration is located in the `RougailConfig` object: + +.. code-block:: python + + from rougail import RougailConfig + +It's just a python dictionary with different keys. + +To modify it, just do like with any python dictionary: + +.. code-block:: python + + RougailConfig[key] = value + +Configuring the dictionnaries loading +----------------------------------------- + +Setting the dictionnaries folders +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are two types of dictionary directories: + +- the main dictionaries with the `dictionaries_dir` key. The default setting is `['/srv/rougail/dictionaries']`. This setting shall list the directories folders containing dictionaries files. + +- the extra dictionaries with the `extra_dictionaries` key. The value is a dictionary with all namespaces. The key being the namespace and the value being a directory listing. + +For example, to add the extra example you must do: + +.. code-block:: python + + RougailConfig['extra_dictionaries']['example'] = ['/dir1', '/dir2'] + +Dictionaries are loaded in the same order as the main dictionaries. + +The functions file +~~~~~~~~~~~~~~~~~~~~~~~ + +The file which contains the custom functions is managed in the `functions_file` key and has the default value `/srv/rougail/functions.py`. This key can contain a list if there are several files. + +.. important:: Functions must return a value, even if the variable being calculated is a :term:`multiple` variable. If the function can return a multiple value (a list), you must put the name of the function in the `multi_functions` key. + +The `auto_freeze` variable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `auto_freeze` property is only applied until a specific variable becomes `True`. By default the variable name is `instantiated_module`, but it is possible to change the name of this variable via the `auto_freeze_variable` key. + +Modes +~~~~~~~~ + +.. glossary:: + + mode + + modes are views on variables. + + Modes are customizable in Rougail. By default the modes are `basic`, `standard` and `advanced`. It is possible to change this list via the `modes_level` key. + +If you change these values, consider changing the default modes of families and variables in your dictionaries. + +Default mode for a family +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default mode for a family is `basic`. It is possible to change the default mode of a family via the `default_family_mode` key. + +Default mode for a variable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default mode for a variable is `standard`. It is possible to change the default mode of a variable via the `default_variable_mode` key. + +Internal functions names +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to add internal functions via the `internal_functions` key. diff --git a/docs/developer.rst b/docs/developer.rst new file mode 100644 index 000000000..c5ddac00b --- /dev/null +++ b/docs/developer.rst @@ -0,0 +1,16 @@ +Developer notes +========================== + +.. admonition:: team developer material + + This section is intended to be usefull for team developers only. + + +Quick installation process +--------------------------------------- + +This process describes how to install and run the project locally, e.g. for development purposes. + +*Nota*: command is to be executed through the terminal + +`pip install rougail` diff --git a/docs/dict_convention.rst b/docs/dict_convention.rst new file mode 100644 index 000000000..2cef856a5 --- /dev/null +++ b/docs/dict_convention.rst @@ -0,0 +1,19 @@ +Dictionary conventions +========================= + +Dictionary file naming convention +------------------------------------ + +The order of dictionaries is important for the order in which variables and families are created. + +The files must therefore be started with two numbers followed by a hyphen. + +For example: `00-base.xml` + +Naming convention for families and variables +----------------------------------------------- + +The only restriction on the name of families and variables is that the name must not start with the `"_"` (undescore) character. + +However, it is preferable to only use lowercase ASCII letters, numbers and the `"_"` (undescore) character. +The snake case typographic convention is therefore used. diff --git a/docs/dictionary.rst b/docs/dictionary.rst new file mode 100644 index 000000000..e8f483295 --- /dev/null +++ b/docs/dictionary.rst @@ -0,0 +1,36 @@ +The dictionaries +===================== + +What do you mean by :term:`dictionary`? +------------------------------------------- + +A :term:`dictionary` is a YAML file whose structure is described in this documentation page. + +A dictionary contains a set of variables loaded into :term:`Tiramisu`, usable at any time, especially in a :term:`templates`. + +:term:`Families` and :term:`variables` can be defined in several dictionaries. These dictionaries are then aggregated. + +Dictionaries are loaded in the directory order defined by the `dictionaries_dir` configuration parameter. +Each directory is loaded one after the other. +Inside these directories the YAML files will be classified in alphabetical order. + +There is no alphabetical ordering of all YAML files in all directories. + +It is also possible to :term:`redefine` elements to change the behavior of a family or a variable. + +The default namespace +------------------------- + +The families and variables contained in these dictionaries are ordered, by default, in the `rougail` namespace. It is possible to change the name of this namespace :doc:`with the `variable_namespace` parameter of the configuration `. + +This namespace is a bit special, it can access variables in another namespace. + +The extra dictionaries +--------------------------- + +An extra is a different namespace. The idea is to be able to classify the variables by theme. + +Extra namespaces must be declared :doc:`when configuring Rougail `. + +In this namespace we cannot access variables from another `extra` namespace. +On the other hand, it is possible to access the variable of the default namespace. diff --git a/docs/family.rst b/docs/family.rst new file mode 100644 index 000000000..cc39bdc7a --- /dev/null +++ b/docs/family.rst @@ -0,0 +1,223 @@ +A family +============ + +Synopsis +--------- + +A family is a container of variables and subfamily. + +.. attention:: A family without a subfamily or subvariable will be automatically deleted. + +Name +------------- + +It is with this name that we will be able to interact with the family. + +It's best to follow the :ref:`convention on variable names`. + +Parameters +--------------- + +.. FIXME: faire une page sur la "convention on variable names" + +.. list-table:: + :widths: 15 45 + :header-rows: 1 + + * - Parameter + - Comments + + * - type, _type + + `string` + + - possile values: + + - `family` (**default value**) + - `leadership` + - `dynamic` + + .. note:: If a subfamily or a subvariable already has the name `"type"`, it is possible to use the `"_type"` attribute. + + * - description, _description + + `string` + - Description of the family. + + User information to understand the usefulness of the family. + + ..note:: If a subfamily or subvariable already has the name "description" it is possible to use the "_description" attribute. + + * - help, _help + + `string` + - Additional help associated with the family. + + .. note:: If a subfamily or a subvariable already has the name "help" it is possible to use the "_help" attribute. + + * - mode, _mode + + `string` + - Family mode. + + The default mode of a family is the smallest mode of the parent families, child variables, or child families that are contained in that family. + + This mode also allows you to define the default mode for variables or families included in this family. + + .. note:: If a subfamily or a subvariable already has the name "mode" it is possible to add the "_mode" attribute. + + * - hidden, _hidden + + `string` + - Invisible family. + + Allows you to hide a family as well as the variables or families included in this family. + + This means that the family will no longer be visible in `read-write` mode, but only for calculations or in `read-only` mode. + + .. note:: If a subfamily or a subvariable already has the name "hidden" it is possible to add the "_hidden" attribute. + + * - disabled, _disabled + + `string` + + - Disabled family. + + Allows you to deactivate a family as well as the variables or families included in this family. + + This means that the family will no longer be visible to the user but also to a :term:`calculation`. + + .. note:: If a subfamily or a subvariable already has the name "disabled" it is possible to use the "_disabled" attribute. + +Dynamically created family +----------------------------- + +To create a family dynamically, you must create a fictitious family linked to a variable. +The family name and description will actually be the prefix of the new name / description. +The suffix will come from the value of the bound variable. +The name of the families and variables it contains will be preserved, however, the description will be the prefix of the real description. + +Obviously if the content of the linked variable were to evolve, new dynamic families will appear or disappear. + +To note that: + +- the variable linked to the family must be a multiple variable +- it is not possible to put a simple family into a dynamic family +- it is possible to put a leading family into a dynamic family + +Leader or foller variable +----------------------------- + +A leader family has a typical attribute of “leadership”. The type is required. + +A leader family +---------------- + +The leader and follower variables are placed in a leader family. + +A leader family cannot contain other families. + +The default mode of the leader family is the mode of the leader variable. + +Leader variable +---------------- + +A leader variable is a variable that will guide the length of other variables (called follower variables). + +A leader variable is a :doc:`variable` that must have the `multiple` type. + +A leader variable may be mandatory. + +The default mode corresponds to the smallest mode defined for the follower variables. + +Follower variable +-------------------- + +A follower variable is a variable whose length is not determined by itself, but is identical to that of the leader variable on which it depends. + +A follower variable is a variable placed just behind a leader variable or another follower variable. + +The order in which the tracking variables are defined is important. + +This variable can be of multiple type. In this case, for a determined index of the leading variable, it is possible to put several values to the same variable. + +A follower variable may be required. This means that when a leader variable is entered, the follower variable must also be a value at the index considered. If no value is defined for the leader variable, no value is specified for the follower variable. + +The default mode of a follower variable corresponds to the mode of the leader variable. + +If a leader variable is hidden or disabled, the follower variables will be hidden or disabled as well. + +Examples +---------- + +Simple family + +.. code-block:: yaml + + --- + version: '1.0' + my_family: + type: family + description: This is a great family + help: This is the help of a great family + mode: expert + +Dynamically created family +---------------------------- + +.. code-block:: yaml + + --- + version: '1.0' + varname: + multi: true + default: + - val1 + - val2 + my_dyn_family_: + type: dynamic + variable: rougail.varname + description: 'Describe ' + my_dyn_var: + type: string + description: 'Variable description for ' + +This will dynamically create two families: + +- "rougail.my_dyn_family_val1" with the description "Describe val1" +- "rougail.my_dyn_family_val2" with the description "Describe val2" + +In the dynamic family "rougail.my_dyn_family_val1" we will find a variable "my_dyn_var" with the description "Variable description for val1". + +Leader or follower variable +------------------------------- + +Definition of leader and follower variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Here is an example of defining a leading variable and two following variables: + +.. code-block:: yaml + + --- + version: '1.0' + family: + type: leadership + leader: + multi: true + follower1: + follower2: + multi: true + +Adding a new follower variable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To add a new follower variable, in a new dictionary, simply define one or more new variables in the leader family: + +.. code-block:: yaml + + --- + version: '1.0' + family: + follower3: + diff --git a/docs/fill.rst b/docs/fill.rst new file mode 100644 index 000000000..90abce951 --- /dev/null +++ b/docs/fill.rst @@ -0,0 +1,478 @@ +Calculated default values +============================== + +Synopsis +----------- + +A value can be calculated. In this case we have four possibilities: + +- calculation via Jinja +- calculation via a variable +- calculation via information +- calculation via a suffix: in the case of a variable in a dynamic family +- calculation via an index: in the case of a follower variable + +If the user modifies the value of the variable, the default value is no longer used, so the calculation is no longer carried out. This is also the case if the variable has the `auto_save` attribute. + +On the other hand, if the variable is hidden (with the `hidden` parameter), it is the default value that is used and not the value customized by the user. + +.. note:: A follower variable cannot be calculated automatically. + +Parameters +-------------- + +Depending on the types of calculation, the parameters will be different: + +.. list-table:: + :widths: 15 25 20 15 + :header-rows: 1 + + * - Calculation type + - Parameter + - Comments + - Sample + + * - + - **type** + + `string` + + `mandatory` + + - Type of calculation, possible values are: jinja, variable, information, suffix or index + - jinja + * - Jinja + - **jinja** + + `string` + + `mandatory` + - Template Jinja. For a multiple variable, each line represents a value. + - `{% if rougail.variable %} + + {{ rougail.variable }} + + {% endif %}` + * - Jinja + - **params** + + `list` + - Additional parameters passed to the Jinja template + - + * - Variable (`mandatory`) + + Information + - **variable** + + `string` + - Name of associated variable + - rougail.variable + * - Variable + - **propertyerror** + + `boolean` + - If access to the variable is not possible due to a property (for example `disabled`) by default an error is returned. If the attribute is `false`, the calculated value is empty. + + **Default value:** `true` + - false + + * - Information + - **information** + + `string` + + `mandatory` + - Name of the information whose value we want to retrieve. + - doc + +In the case of a Jinja type calculation, it is possible to have parameters. + +There are two types of parameter: + +- the standard parameters (string, boolean, integer, null), in this case just do: "key: value" + +- the advanced settings: + + - parameter via a variable + - parameter via an information + - parameter via a suffix: in the case of a variable in a dynamic family + - parameter via an index: in the case of a follower variable + +.. list-table:: + :widths: 15 25 20 15 + :header-rows: 1 + + * - Parameter type + - Parameter + - Comments + - Sample + + * - + - **name** + + `string` + + `mandatory` + - parameter's name + - my_param + * - + - **type** + + `string` + + `mandatory` + - parameter's type, possible values are: variable, information, suffix or index + - suffix + * - Variable + - **variable** + + `string` + + `mandatory` + + - Variable's name + - rougail.variable + * - Variable (`mandatory`) information + - **propertyerror** + + `boolean` + - If access to the variable is not possible due to a property (for example `disabled`) by default an error is returned. If the attribute is `False`, the parameter is not passed to the Jinja template. + - **Default value**: `True` + * - Variable + - **optional** + + `boolean` + - The variable may not exist depending on YAML file imports. + If the optional parameter is `True`, the parameter will simply be deleted if the variable does not exist. + + Default value : `False` + - True + * - Information + - **information** + + `string` + + `mandatory` + - Name of the information whose value we want to retrieve. + - doc + +Examples +----------- + +Calculation via a Jinja template +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Let's start with an example from a simple Jinja template: + +.. code-block:: yaml + + --- + version: '1.0' + my_calculated_variable: + default: + type: jinja + jinja: 'no' + +Here is a second example with a boolean variable: + +.. code-block:: yaml + + --- + version: '1.0' + my_calculated_variable: + type: boolean + default: + type: jinja + jinja: 'false' + +And a multiple value of the number type: + +.. code-block:: yaml + + --- + version: '1.0' + my_calculated_variable: + type: number + multi: true + default: + type: jinja + jinja: | + 1 + 2 + 3 + +Let's create a variable whose value is returned by a python function: + +.. code-block:: yaml + + --- + version: '1.0' + my_calculated_variable: + default: + type: jinja + jinja: '{{ return_no() }}' + +Then let's create the `return_no` function: + +.. code-block:: python + + def return_no(): + return 'no' + +An example with parameters: + +.. code-block:: yaml + + --- + version: '1.0' + my_calculated_variable: + description: my description + default: + type: jinja + jinja: | + {{ param1 }}{% if param2 is defined %}_{{ param2 }}{% endif %}_{{ param3 }} + params: + param1: value + param2: + type: variable + variable: rougail.unknown_variable + optional: true + param3: + type: information + information: doc + variable: rougail.my_calculated_variable + +An example with a `suffix` type parameter: + +.. code-block:: yaml + + --- + version: '1.0' + varname: + multi: true + default: + - val1 + - val2 + my_dyn_family_: + type: dynamic + variable: rougail.varname + description: 'Describe ' + my_dyn_var: + type: string + default: + type: jinja + jinja: 'the suffix is: {{ param1 }}' + params: + param1: + type: suffix + +In this example, we see a dynamic family. Two families will be created: `rougail.my_dyn_family_val1.my_dyn_var` and `rougail.my_dyn_family_val2.my_dyn_var`. + +The value of the variable inside this family 'this suffix is: ' + the value of the suffix (`val1` and `val2` respectively). + +An example with an index type parameter: + +.. code-block:: yaml + + --- + version: '1.0' + family: + type: leadership + leader: + multi: true + default: + - val1 + - val2 + follower1: + default: + type: jinja + jinja: 'the index is: {{ param1 }}' + params: + param1: + type: index + +Calculation via a variable +----------------------------- + +Copy a variable in another: + +.. code-block:: yaml + + --- + version: '1.0' + my_variable: + multi: true + default: + - val1 + - val2 + my_calculated_variable: + multi: true + default: + type: variable + variable: rougail.my_variable + +Copy one variable to another if the source has no `property` problem: + +.. code-block:: yaml + + --- + version: '1.0' + my_variable: + default: val1 + disabled: true + my_calculated_variable: + multi: true + default: + type: variable + variable: rougail.my_variable + propertyerror: false + +Copy two non-multiple variables into a multiple variable: + +.. code-block:: yaml + + --- + version: '1.0' + my_variable_1: + default: val1 + my_variable_2: + default: val2 + my_calculated_variable: + multi: true + default: + - type: variable + variable: rougail.my_variable_1 + - type: variable + variable: rougail.my_variable_2 + +A variable in a dynamic family can also be used in a calculation. + +For example using the variable for a particular suffix: + +.. code-block:: yaml + + --- + version: '1.0' + varname: + multi: true + default: + - val1 + - val2 + my_dyn_family_: + type: dynamic + variable: rougail.varname + description: 'Describe ' + my_dyn_var_: + type: string + default: + type: suffix + all_dyn_var: + default: + type: variable + variable: rougail.my_dyn_family_val1.my_dyn_var_val1 + +In this case, we recover the value `val1`. + +Second example using the variable for all suffixes: + +.. code-block:: yaml + + --- + version: '1.0' + varname: + multi: true + default: + - val1 + - val2 + my_dyn_family_: + type: dynamic + variable: rougail.varname + description: 'Describe ' + my_dyn_var_: + type: string + default: + type: suffix + all_dyn_var: + multi: true + default: + type: variable + variable: rougail.my_dyn_family_.my_dyn_var_ + +In this case, we recover the `val1` and `val2` list. + +Calculation via a suffix +--------------------------- + +.. code-block:: yaml + + --- + version: '1.0' + varname: + multi: true + default: + - val1 + - val2 + my_dyn_family_: + type: dynamic + variable: rougail.varname + description: 'Describe ' + my_dyn_var_: + type: string + default: + type: suffix + +Calculation via an index +-------------------------- + +.. code-block:: yaml + + --- + version: '1.0' + family: + type: leadership + leader: + multi: true + default: + - val1 + - val2 + follower1: + type: number + default: + type: index + +Redefinition +---------------- + +In a first dictionary, let's declare our variable and our calculation: + +.. code-block:: yaml + + --- + version: '1.0' + my_calculated_variable: + default: + type: jinja + jinja: 'the value is calculated' + +In a second dictionary, it is possible to redefine the calculation: + +.. code-block:: yaml + + --- + version: '1.0' + my_calculated_variable: + redefine: true + default: + type: jinja + jinja: 'the value is redefined' + +In a third dictionary, we even can delete the calculation if needed: + +.. code-block:: yaml + + --- + version: '1.0' + my_calculated_variable: + redefine: true + default: null + diff --git a/docs/gettingstarted.rst b/docs/gettingstarted.rst new file mode 100644 index 000000000..2853235b0 --- /dev/null +++ b/docs/gettingstarted.rst @@ -0,0 +1,192 @@ +.. |Tiramisu| replace:: Tiramisu +.. _tiramisu: https://forge.cloud.silique.fr/stove/tiramisu + +Getting started +==================== + +What is a consistency handling system ? +------------------------------------------------ + +.. questions:: Question: "OK, I have understood that the Rougail stuff enables me to take advantage of |Tiramisu|. But what is all this for? What is exactly a consistency handling system? And again, what is this |Tiramisu| library used for?" + + *Answer*: Well, let's explain what |Tiramisu| is and how we are using the |Tiramisu| library. + +.. glossary:: + + Tiramisu + + |Tiramisu| is a consistency handling system that was initially designed + in the configuration management scope. To put it more simply, + this library is generally used to handle configuration options. + + It manages variables and group of variables. In the Tiramisu scope we call + it *options* and *option descriptions*. + +In the Rougail scope, we call it :term:`variable`\ s and :term:`families`. +In Rougail, the families and variables are located in the :term:`dictionaries`. + +And this is what we are going to explain in this page. + +The dictionaries +--------------------- + +.. glossary:: + + dictionary + dictionaries + + A dictionary in the Rougail meaning is a YAML file that describes variables + and their dependencies / consistencies. + There can be a lot of dictionary files located in many different folders. + + Rougail reads all the dictionaries and loads them into a single object + that handles the variables consistency. + +.. image:: images/schema.png + +The main advantage is that declaring variables and writing consistency is a simple +as writing YAML. It is not necessary to write :term:`Tiramisu` code. +It simplifies a lot of things. + +And rather than writing :term:`Tiramisu` code, we can declare variables and describe the relationships between variables in a declarative mode. + +Once the dictionaries are loaded by Rougail, we find all the power of the :term:`Tiramisu` configuration management tool. + +The dictionaries YAML format +--------------------------------- + +Before getting started with Rougail we need to learn the specifics of the YAML dictionaries file format (as well as some templating concepts). + +.. FIXME parler de jinja https://jinja.palletsprojects.com + +Here is a :term:`dictionary` example: + +.. code-block:: yaml + :linenos: + + --- + version: '1.0' + proxy: + description: Configure Proxy Access to the Internet + type: family + +Line 3, we declare a **variable** named `proxy` with his `description` line 4 and his `type` line 5. + +The variables +----------------- + +variable + + Here is a second definition of a :term:`variable`: it is a declaration unit that represents a business domain metaphor, + + the most common example is that a variable represents a configuration option + in a application, but a variable represents something more that a configuration option. + It provides a business domain specific representation unit. + +.. note:: dictionaries can just define a list of variables, but we will see that + we can specify a lot more. We can define variables **and** their relations, + and the consistency between them. + +In the next step, we will explain through a tutorial how to construct a list of variables. + +Families of variables +-------------------------- + +.. glossary:: + + family + families + + A family of variables is simply a collection of variables that refer to + the same business model category. It's just a variables container. + Think of it as a container as well as a namespace. + +A "hello world" with Rougail +------------------------------ + +We're gonna make the simplest possible example. + +.. prerequisites:: Prerequisites + +We assume that Rougail's library is installed on your computer (or in a virtual environment). + +.. exercise:: Let's make a Hello world + +Here is the tree structure we want to have:: + + workplace + ├── dict +    │   ├── hello.yml +    └── hello.py + +- Let's make a :file:`workplace` directory, with a :file:`dict` subfolder. +- First, we need a :term:`dictionary`, so let's make the :file:`hello.yml` file + which is located in the :file:`dict` subfolder, with the following content: + +.. code-block:: yaml + :caption: The `hello.yaml` file + + --- + version: '1.0' + hello: + default: world + +- Then we make a :file:`hello.py` in our root :file:`workplace` directory: + +.. code-block:: python + :caption: The :file:`hello.py` file + + from rougail import Rougail, RougailConfig + + RougailConfig['dictionaries_dir'] = ['dict'] + rougail = Rougail() + config = rougail.get_config() + print(config.value.get()) + +.. demo:: Let's run the :file:`hello.py` script + +We launch the script: + +.. code-block:: bash + + python hello.py + +And we obtain the following result: + +.. code-block:: python + + {'rougail.hello': 'world'} + +**Congratulations ! You have successfully completed your first Rougail script.** + +A "Hello, " sample with a family +------------------------------------------ + +Let's continuing on our "Hello world" theme and add a :term:`family` container. + +.. code-block:: yaml + :caption: the :file:`hello.yml` file + :linenos: + + --- + version: '1.0' + world: + description: Hello world family container + name: + description: Somebody to say hello + default: rougail + +Here, we have a family named `world`. +This family contains a variable named `name` + +Again, let's validate this YAML file against Rougail's API: + +.. code-block:: bash + + python hello.py + +We then have the output: + +.. code-block:: python + + {'rougail.world.name': 'rougail'} diff --git a/doc/firefox.png b/docs/images/firefox.png similarity index 100% rename from doc/firefox.png rename to docs/images/firefox.png diff --git a/doc/foxyproxy.png b/docs/images/foxyproxy.png similarity index 100% rename from doc/foxyproxy.png rename to docs/images/foxyproxy.png diff --git a/docs/images/logo.png b/docs/images/logo.png new file mode 100644 index 000000000..b06117058 Binary files /dev/null and b/docs/images/logo.png differ diff --git a/doc/schema.png b/docs/images/schema.png similarity index 100% rename from doc/schema.png rename to docs/images/schema.png diff --git a/doc/schema.svg b/docs/images/schema.svg similarity index 100% rename from doc/schema.svg rename to docs/images/schema.svg diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 000000000..83027ef16 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,64 @@ +.. meta:: + :description: Rougail python library home page + :keywords: python, Rougail, Tiramisu + :http-equiv=Pragma: no-cache + +.. title:: Rougail + +Rougail +=========== + +.. image:: images/logo.png + +- is a `delicious cooked dish `_ from the Mauritius and Reunion Islands, + +- it is also a `Python3 `_ library which enables us to conveniently load application :term:`variable`\ s in a simple `YAML `_ format in such a way that the end user consumer can handle them consistently (that is, against an user-defined consistency). + +In other words, using Rougail in your application or your python libraries can tansform end user consumer defined consistency rules into highly consistent business objects. + +We then have to say that the handling system used to ensure the variables integrity is another python library, called :term:`Tiramisu`. Rougail is currently strongly affiliated with Tiramisu. + +.. note:: Rougail is currently intended to work in coordination with :term:`Tiramisu` and **is not** intended to be connected with any other consistency handling system. + +Explained differently, Rougail allows you to easily implement an integration of the powerful tiramisu consistency handling system. + +.. toctree:: + :titlesonly: + :caption: Getting started + + gettingstarted + tutorial + +.. toctree:: + :titlesonly: + :caption: The library + + library + configuration + +.. toctree:: + :titlesonly: + :caption: The dictionaries + + dictionary + dict_convention + +.. toctree:: + :titlesonly: + :caption: The variables + + variable + family + fill + Value checks + condition + +.. toctree:: + :titlesonly: + :caption: Notes + + developer + +.. rubric:: Index page + +- :ref:`genindex` diff --git a/docs/library.rst b/docs/library.rst new file mode 100644 index 000000000..8f643a7a3 --- /dev/null +++ b/docs/library.rst @@ -0,0 +1,141 @@ +`Rougail`'s library description +================================= + +Rougail is a configuration management library that allows you to load variables in a simple and convenient way. + +In the following examples, we will use a specific configuration of Rougail. You will find all the options to :doc:`customize the directories structure used `. + +To load the configuration you must import the `RougailConfig` class and set the `dictionaries_dir` values: + +.. code-block:: python + + from rougail import RougailConfig + + RougailConfig['dictionaries_dir'] = ['dict'] + +Let's convert a dictionary +----------------------------- + +As a reminder, a :term:`dictionary` is a set of instructions that will allow us to create :term:`families` and :term:`variables`. + +Let's start by creating a simple dictionary. + +Here is a first :file:`dict/00-base.yml` dictionary: + +.. code-block:: yaml + + --- + version: '1.0' + my_variable: + default: my_value + +Then, let's create the :term:`Tiramisu` objects via the following script: + +.. code-block:: python + :caption: the `script.py` file content + + from rougail import Rougail, RougailConfig + + RougailConfig['dictionaries_dir'] = ['dict'] + rougail = Rougail() + config = rougail.get_config() + print(config.value.get()) + +Let's execute `script.py`: + +.. code-block:: bash + + $ python3 script.py + {'rougail.my_variable': 'my_value'} + +Let's convert an extra dictionary +------------------------------------- + +.. index:: extras + +The default namespace for variables and families is `rougail`. It is possible to define other namespaces. These additional namespaces are called `extras`. + +.. FIXME: faire une page pour les extras + +Additional namespaces are defined during configuration. + +For example, here's how to add an `example` namespace: + +.. code-block:: python + + RougailConfig['extra_dictionaries']['example'] = ['extras/'] + +Then let's create an extra :term:`dictionary` :file:`extras/00-base.yml`: + +.. code-block:: yaml + :caption: the :file:`extras/00-base.yml` file content + --- + version: '1.0' + my_variable_extra: + default: my_value_extra + +Then, let's create the :term:`Tiramisu` objects via the following :file:`script.py` script: + +.. code-block:: python + :caption: the :file:`script.py` file content + + from rougail import Rougail, RougailConfig + + RougailConfig['dictionaries_dir'] = ['dict'] + RougailConfig['extra_dictionaries']['example'] = ['extras/'] + rougail = Rougail() + config = rougail.get_config() + print(config.value.dict()) + +Let's execute `script.py`: + +.. code-block:: bash + + $ python3 script.py + {'rougail.my_variable': 'my_value', 'example.my_variable_extra': 'my_value_extra'} + +Let's create a custom function +---------------------------------- + +We create the complementary :term:`dictionary` named :file:`dict/01-function.yml` so that the `my_variable_jinja` variable is :term:`calculated`: + +.. code-block:: yaml + + --- + version: '1.0' + my_variable_jinja: + type: "string" + default: + type: jinja + jinja: "{{ return_no() }}" + +Then let's define the :func:`return_no` function in :file:`functions.py`: + +.. code-block:: python + :caption: the :file:`functions.py` content + + def return_no(): + return 'no' + +Then, let's create the :term:`Tiramisu` objects via the following script: + +.. code-block:: python + :caption: the `script.py` file content + + from rougail import Rougail, RougailConfig + + RougailConfig['dictionaries_dir'] = ['dict'] + RougailConfig['extra_dictionaries']['example'] = ['extras/'] + RougailConfig['functions_file'] = 'functions.py' + rougail = Rougail() + config = rougail.get_config() + print(config.value.dict()) + +Let's execute `script.py`: + +.. code-block:: bash + + $ python3 script.py + {'rougail.my_variable': 'my_value', 'rougail.my_variable_jinja': 'no', 'example.my_variable_extra': 'my_value_extra'} + +The value of the `my_variable_extra` variable is calculated, and it's value comes from the :func:`return_no` function. diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 000000000..87f1fb7c4 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,81 @@ +alabaster==0.7.13 +asttokens==2.4.1 +attrs==23.1.0 +Babel==2.13.1 +certifi==2023.7.22 +charset-normalizer==3.3.2 +click==8.1.7 +colorama==0.4.6 +comm==0.2.0 +debugpy==1.8.0 +decorator==5.1.1 +docutils==0.18.1 +exceptiongroup==1.1.3 +executing==2.0.1 +fastjsonschema==2.18.1 +greenlet==3.0.1 +idna==3.4 +imagesize==1.4.1 +importlib-metadata==6.8.0 +ipykernel==6.26.0 +ipython==8.17.2 +jedi==0.19.1 +Jinja2==3.1.2 +jsonschema==4.19.2 +jsonschema-specifications==2023.7.1 +jupyter-cache==1.0.0 +jupyter_client==8.6.0 +jupyter_core==5.5.0 +livereload==2.6.3 +markdown-it-py==3.0.0 +MarkupSafe==2.1.3 +matplotlib-inline==0.1.6 +mdit-py-plugins==0.4.0 +mdurl==0.1.2 +myst-nb==1.0.0 +myst-parser==2.0.0 +nbclient==0.9.0 +nbformat==5.9.2 +nest-asyncio==1.5.8 +packaging==23.2 +parso==0.8.3 +pexpect==4.8.0 +platformdirs==4.0.0 +prompt-toolkit==3.0.40 +psutil==5.9.6 +ptyprocess==0.7.0 +pure-eval==0.2.2 +Pygments==2.16.1 +python-dateutil==2.8.2 +PyYAML==6.0.1 +pyzmq==25.1.1 +referencing==0.30.2 +requests==2.31.0 +rpds-py==0.12.0 +six==1.16.0 +snowballstemmer==2.2.0 +Sphinx==7.2.6 +sphinx-autobuild==2021.3.14 +sphinx-copybutton==0.5.2 +sphinx-lesson==0.8.15 +sphinx-minipres==0.2.1 +sphinx-rtd-theme==1.3.0 +sphinx-rtd-theme-ext-color-contrast==0.3.1 +sphinx-tabs==3.4.4 +sphinx-togglebutton==0.3.2 +sphinxcontrib-applehelp==1.0.7 +sphinxcontrib-devhelp==1.0.5 +sphinxcontrib-htmlhelp==2.0.4 +sphinxcontrib-jquery==4.1 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.6 +sphinxcontrib-serializinghtml==1.1.9 +SQLAlchemy==2.0.23 +stack-data==0.6.3 +tabulate==0.9.0 +tornado==6.3.3 +traitlets==5.13.0 +typing_extensions==4.8.0 +urllib3==2.0.7 +wcwidth==0.2.9 +zipp==3.17.0 diff --git a/docs/tutorial.rst b/docs/tutorial.rst new file mode 100644 index 000000000..21ac0341a --- /dev/null +++ b/docs/tutorial.rst @@ -0,0 +1,829 @@ +Tutorial: a real world sample +============================== + +.. demo:: Demonstration : configuring (the setting of) your favorite web browser + + This tutorial shows to you an example of Rougail use on + how to set a proxy in the `Mozilla Firefox `_ browser. + +More precisely, this tutorial aims at reproducing this Mozilla Firefox settings page: + +.. image:: images/firefox.png + +.. important:: Here we are in the configuration validation use case, + that is the values entered by the user have to be validated. + It's a common use case, but not the only one. + +Let's explain this use case. + +The Firefox proxy configuration +------------------------------------------- + +The `proxy` family +------------------- + +Let's create our first :term:`dictionary`. + +.. prerequisites:: Let's create a folder named `dict` and a dictionary file inside + +We will put our dictionary files in this folder. + +Then let's put our first dictionary file in this folder, named :file:`00-proxy.yml` + +.. code-block:: yaml + :caption: the :file:`00-proxy.yml` file + :linenos: + + --- + version: '1.0' + proxy: + description: Proxy configuration in order to have access to the internet + type: family + +We can see that we have defined a :term:`family` here, and this family is *empty* +(that is, the family container contains no variable yet). + +.. admonition:: If a family is empty + + We need to specify the :term:`family` type (line 5) here because if we don't, + the Rougail's type engine will infer it by default as a :term:`variable`. + +It's because we don't have set any :term:`variable` inside. + + +.. note:: The variables will be created in several files for educational purposes. + Obviously all the variables can be put in the same file. + + +The proxy's configuration type +---------------------------------- + +In the Firefox configuration, it is possible to define several configuration modes, +from no proxy at all (`no proxy`) to a kind of automatic configuration mode from a file (`set up proxy configuration from a file`). + +We're gonna create a first variable in this family with "Proxy mode" as the description. +Let's create a second :file:`dict/01-proxy_mode.yml` file. + +.. code-block:: yaml + :caption: the :file:`001-proxy_mode.yml` file + :linenos: + + --- + version: '1.0' + proxy: + proxy_mode: + description: Proxy mode + type: choice + choices: + - No proxy + - Auto-detect proxy settings for this network + - Use system proxy settings + - Manual proxy configuration + - Automatic proxy configuration URL + default: No proxy + +The `proxy_mode` variable requires a value (that is, `None` is not an option). +It shall have a value, but what if the user *does not* specify any value? +There is line 13, a possibility of setting a default value, wich is `No proxy` as the default. + +The `proxy_mode` setting is "choice" (`type: choice`) means that +there is a list of available values that can be selected. +We say that the `proxy_mode` variable is *constrained* (by choices). + +Line 8 to 12, we have the list of the possible (authorized) values: + +- No proxy +- Auto-detect proxy settings for this network +- Use system proxy settings +- Manual proxy configuration +- Automatic proxy configuration URL + +Now let's test our first two dictionaries: + + +>>> from rougail import Rougail, RougailConfig +>>> from pprint import pprint +>>> RougailConfig['dictionaries_dir'] = ['dict'] +>>> rougail = Rougail() +>>> config = rougail.get_config() +>>> config.property.read_only() +>>> pprint(config.value.get(), sort_dicts=False) +{'rougail.proxy.proxy_mode': 'No proxy'} + +The manual mode +------------------ + +.. questions:: OK then. What happens when you select the "Manual proxy configuration"? + +A good configuration design is to place all the proxy's manual configuration in a :term:`family`. +Let's create the :file:`dict/02-proxy_manual.yml` dictionary: + +.. code-block:: yaml + :caption: the the :file:`dict/02-proxy_manual.yml` file + + --- + version: '1.0' + proxy: + manual: + description: Manual proxy configuration + type: family + disabled: + type: jinja + jinja: | + {% if rougail.proxy.proxy_mode != 'Manual proxy configuration' %} + the proxy mode is not manual + {% endif %} + +Well, if the user selects the "Manual proxy configuration" proxy mode, we want to see a new subfamily (that is, a new set of configuration variables) called `manual` to appear (which is disabled). + +.. glossary:: + + subfamily + + A subfamily is just a family inside a family, a family that contains a family. + +.. questions:: What about this `Jinja` type? + +If the :term:`Jinja` template returns some text, then the family will be `disabled`. Otherwise it is accessible. +Deactivating a family means that we will not be able to access it as well as the variables or families included in this family. + +.. note:: If the Jinja template does not return any text, the variable will be **enabled**. + Here we are using the Jinja condition statement. + +.. glossary:: + + Jinja + + `Jinja `_ is a template engine. + we are using Jinja in a classical way, that is, Jinja allows us to handle different cases, + for example with the `if` statement. + +The HTTP proxy configuration +------------------------------ + +In this family let's add a *subfamily* named `http_proxy`, containing the address and port configuration variables. + +Let's create the :file:`dict/03-proxy_manual_http_proxy.yml` dictionary: + +.. code-block:: yaml + :caption: the the :file:`dict/02-proxy_manual.yml` file + :linenos: + + --- + version: '1.0' + proxy: + manual: + http_proxy: + description: HTTP Proxy + address: + description: HTTP address + type: domainname + port: + description: HTTP Port + type: port + default: '8080' + +Both variables `address` and `port` have particular types (respectively `domainname` line 9 and `port` line 12) to validate the values configured by the user. + +.. note:: No need to specify the type of the `http_proxy` as a family type, because here we have declared variables inside of it. + +Duplicating the HTTP configuration to HTTPS +--------------------------------------------- + +We then want to offer the user the possibility of providing the same proxy for the HTTPS requests. Let's create the :file:`dict/04-proxy_manual_http_use_for_https.yml` file: + +.. code-block:: yaml + :caption: the :file:`dict/04-proxy_manual_http_use_for_https.yml` file + + version: '1.0' + proxy: + manual: + use_for_https: + description: Also use this proxy for HTTPS + type: boolean + +This variable is a `boolean` type, its default value is `True`. + +HTTPS proxy configuration detail +----------------------------------- + +Let's add a new subfamily named `ssl_proxy`, containing the `address` and `port` variables. + +Let's create the :file:`dict/05-proxy_manual_ssl_proxy.yml` file: + +.. code-block:: yaml + :caption: the :file:`dict/04-proxy_manual_http_use_for_https.yml` file + :linenos: + + --- + version: '1.0' + proxy: + manual: + ssl_proxy: + description: HTTPS Proxy + hidden: + type: variable + variable: rougail.proxy.manual.use_for_https + address: + description: HTTPS address + type: domainname + default: + type: jinja + jinja: | + {% if rougail.proxy.manual.use_for_https %} + {{ rougail.proxy.manual.http_proxy.address }} + {% endif %} + port: + description: HTTPS Port + type: port + default: + type: jinja + jinja: | + {% if rougail.proxy.manual.use_for_https %} + {{ rougail.proxy.manual.http_proxy.port }} + {% endif %} + + +Depending on the value of the `rougail.proxy.mandatory.use_for_https` variable, this family will appear or disappear (the `hidden` setting line 7). Unlike earlier, this time it is not necessary to use a Jinja function. + +Let's notice that the family is not disabled because the variables will need to remain accessible (yet in `read-only` mode). + +The address and port variables are copied from HTTP to HTTPS if `rougail.proxy.use_for_https` is set to `True`. + +Now let's test all of it: + +>>> from rougail import Rougail, RougailConfig +>>> from pprint import pprint +>>> RougailConfig['dictionaries_dir'] = ['dict'] +>>> rougail = Rougail() +>>> config = rougail.get_config() +>>> config.property.read_only() +>>> pprint(config.value.get(), sort_dicts=False) +{'rougail.proxy.proxy_mode': 'No proxy'} + +At this time the proxy is not configured yet, so we do not see any variables. +Let's look at what happens if we try to access the `rougail.proxy.manual` variable if we are not in manual mode: + +.. code-block:: python + + >>> pprint(config.option('rougail.proxy.manual').value.get(), sort_dicts=False) + +We have an error (with the message defined in the Jinja template): + + +.. code-block:: python + + tiramisu.error.PropertiesOptionError: cannot access to optiondescription "Manual proxy configuration" because has property "disabled" (the mode proxy is not manual) + +Let's configure the proxy in manual mode + +>>> config.property.read_write() +>>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration') +>>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example') +>>> pprint(config.value.get(), sort_dicts=False) + +We can see that the returned variables does have the desired values: + +.. code-block:: python + + {'rougail.proxy.proxy_mode': 'Manual proxy configuration', + 'rougail.proxy.manual.http_proxy.address': 'proxy.example', + 'rougail.proxy.manual.http_proxy.port': '8080', + 'rougail.proxy.manual.use_for_https': True} + +Let's set the `read_only` mode: + +.. code-block:: python + + >>> config.property.read_only() + >>> pprint(config.value.get(), sort_dicts=False) + {'rougail.proxy.proxy_mode': 'Manual proxy configuration', + 'rougail.proxy.manual.http_proxy.address': 'proxy.example', + 'rougail.proxy.manual.http_proxy.port': '8080', + 'rougail.proxy.manual.use_for_https': True, + 'rougail.proxy.manual.ssl_proxy.address': 'proxy.example', + 'rougail.proxy.manual.ssl_proxy.port': '8080'} + +In the `read_only` mode, we can see that the HTTPS configuration appears. + +.. note:: We can see that `rougail.proxy.manual.http_proxy` values have been copied + in `rougail.proxy.manual.ssl_proxy` too... + +Changing values programmatically +-------------------------------------- + +We are going to use the :term:`Tiramisu` API to manipulate programmatically the different variables. + +First, let's set `rougail.proxy.manual.use_for_https` to `False`. It is now possible +to configure the HTTPS: + +.. code-block:: python + + >>> config.property.read_write() + >>> config.option('rougail.proxy.manual.use_for_https').value.set(False) + >>> config.option('rougail.proxy.manual.ssl_proxy.address').value.set('other.proxy.example') + >>> pprint(config.value.get(), sort_dicts=False) + {'rougail.proxy.proxy_mode': 'Manual proxy configuration', + 'rougail.proxy.manual.http_proxy.address': 'proxy.example', + 'rougail.proxy.manual.http_proxy.port': '8080', + 'rougail.proxy.manual.use_for_https': False, + 'rougail.proxy.manual.ssl_proxy.address': 'other.proxy.example', + 'rougail.proxy.manual.ssl_proxy.port': '8080'} + +The value of the variable `rougail.proxy.manual.ssl_proxy.address` has actually been modified. +But if this variable is hidden again, then the value comes back to the default value: + +.. code-block:: python + + >>> config.option('rougail.proxy.manual.use_for_https').value.set(False) + >>> config.property.read_only() + >>> pprint(config.value.get(), sort_dicts=False) + {'rougail.proxy.proxy_mode': 'Manual proxy configuration', + 'rougail.proxy.manual.http_proxy.address': 'proxy.example', + 'rougail.proxy.manual.http_proxy.port': '8080', + 'rougail.proxy.manual.use_for_https': False, + 'rougail.proxy.manual.ssl_proxy.address': 'proxy.example', + 'rougail.proxy.manual.ssl_proxy.port': '8080'} + +SOCK's proxy configuration +------------------------------- + +Let's add a new :term:`subfamily` named `socks_proxy` with the `address`, +`port` and `version` variables. + +Let's create the :file:`dict/06-proxy_manual_socks_proxy.yml` file: + +.. code-block:: yaml + :caption: the :file:`dict/06-proxy_manual_socks_proxy.yml` file + + --- + version: '1.0' + proxy: + manual: + socks_proxy: + description: SOCKS Proxy + address: + description: SOCKS Address + type: domainname + port: + description: SOCKS Port + type: port + version: + description: SOCKS host version used by proxy + type: choice + choices: + - v4 + - v5 + default: v5 + +There's nothing new to learn with this file. + +The automatic detection mode +------------------------------ + +Let's add a new variable named `auto`. + +Let's create the :file:`dict/07-proxy_auto.yml` file: + +.. code-block:: yaml + :caption: the :file:`dict/07-proxy_auto.yml` file + + --- + version: '1.0' + proxy: + auto: + type: web_address + description: Automatic proxy configuration URL + disabled: + type: jinja + jinja: | + {% if rougail.proxy.proxy_mode != 'Automatic proxy configuration URL' %} + the proxy mode is not automatic + {% endif %} + +The `web_address` type imposes a value starting with `http://` or `https://`. +This variable is activated when the proxy is in automatic mode. + +The proxy's exceptions +--------------------------- + +Finally, let's add a variable containing proxy exceptions. + +Let's create the :file:`dict/07-proxy_no_proxy.yml` file: + +.. code-block:: yaml + :caption: the :file:`dict/07-proxy_no_proxy.yml` file + :linenos: + + --- + version: '1.0' + proxy: + no_proxy: + description: Address for which proxy will be desactivated + multi: true + type: "domainname" + params: + allow_ip: true + allow_cidr_network: true + allow_without_dot: true + allow_startswith_dot: true + disabled: + type: jinja + jinja: | + {% if rougail.proxy.proxy_mode == 'No proxy' %} + proxy mode is no proxy + {% endif %} + mandatory: false + +This `no_proxy` variable is much like a `domainname` type except that we add +a `params` line 7, we authorize the : + +- IP +- CIDR networks +- machine names (without `'.'`) +- sub-domaines like `.example` + +There can be multiple exceptions to the proxy, so the variable is :term:`multi` (line5). +This variable is only accessible if no proxy is defined (`disabled`). + +.. glossary:: + + multi + + A multi is a multiple variable, that is a variable that can have multiple values. + + +The `no_proxy` variable do not requires a value (that is, `None` is an option), +there is line 19 this statement `mandatory: false` which means that this variable is not mandatory. + + +Let's test it: + + +>>> from rougail import Rougail, RougailConfig +>>> from pprint import pprint +>>> RougailConfig['dictionaries_dir'] = ['dict'] +>>> rougail = Rougail() +>>> config = rougail.get_config() +>>> config.property.read_write() +>>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration') +>>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example') +>>> config.option('rougail.proxy.no_proxy').value.set(['.example', '192.168.1.1']) +>>> config.property.read_only() +>>> pprint(config.value.get(), sort_dicts=False) + +It outputs: + +.. code-block:: python + + {'rougail.proxy.proxy_mode': 'Manual proxy configuration', + 'rougail.proxy.manual.http_proxy.address': 'proxy.example', + 'rougail.proxy.manual.http_proxy.port': '8080', + 'rougail.proxy.manual.use_for_https': True, + 'rougail.proxy.manual.ssl_proxy.address': 'proxy.example', + 'rougail.proxy.manual.ssl_proxy.port': '8080', + 'rougail.proxy.manual.socks_proxy.address': None, + 'rougail.proxy.manual.socks_proxy.port': None, + 'rougail.proxy.manual.socks_proxy.version': 'v5', + 'rougail.proxy.no_proxy': ['.example', '192.168.1.1']} + +But not possible to put an invalid value: + +.. code-block:: python + + >>> config.option('rougail.proxy.no_proxy').value.set(['.example', '192.168.1.1', 'not valid']) + [..] + tiramisu.error.ValueOptionError: "not valid" is an invalid domain name for "Address for which proxy will be desactivated", could be a IP, otherwise must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed + + +The authentification request +-------------------------------- + +Nothing special when creating the authentication request. To do this, let's create a `dict/08-proxy_prompt_authentication.yml` file: + + +.. code-block:: yaml + :caption: the :file:`dict/08-proxy_prompt_authentication.yml` file + :linenos: + + --- + version: '1.0' + proxy: + prompt_authentication: + description: Prompt for authentication if password is saved + type: boolean + default: true + disabled: + type: jinja + jinja: | + {% if rougail.proxy.proxy_mode == 'No proxy' %} + proxy mode is no proxy + {% endif %} + +The proxy SOCKS v5's DNS +------------------------------ + +The DNS variable for the SOCKS v5 proxy only appears if the proxy is configured and the version of the SOCKS proxy selected is `v5`. + +Let's create a `dict/09-proxy_proxy_dns_socks5.yml` file: + +.. code-block:: yaml + :caption: the :file:`dict/09-proxy_proxy_dns_socks5.yml` file + :linenos: + + --- + version: '1.0' + proxy: + proxy_dns_socks5: + description: Use proxy DNS when using SOCKS v5 + type: boolean + default: false + disabled: + type: jinja + params: + socks_version: + type: variable + variable: rougail.proxy.manual.socks_proxy.version + propertyerror: false + jinja: | + {% if rougail.proxy.proxy_mode == 'No proxy' %} + the proxy mode is no proxy + {% elif socks_version is undefined or socks_version == 'v4' %} + socks version is v4 + {% endif %} + +The difficulty here is that the `rougail.proxy.manual.socks_proxy.version` variable +can be deactivated (and therefore not usable in a calculation). + +.. FIXME definir ce qu'est une calculation + +In this case, we will add a parameter (here called `socks_version`) which will contain, +if there is no property error, the value of the variable. +Otherwise the parameter will not be passed to the Jinja template. + +This is why it is necessary to test in the Jinja template whether the `socks_version` variable really exists. + +The DNS over HTTPS +---------------------- + +Finally we will configure DNS over HTTPS in the 10-proxy_dns_over_https.yml file: + +Let's create a `dict/10-proxy_dns_over_https.yml` file: + +.. code-block:: yaml + :caption: the :file:`dict/10-proxy_dns_over_https.yml` file + :linenos: + + --- + version: '1.0' + proxy: + dns_over_https: + description: DNS over HTTPS + enable_dns_over_https: + description: Enable DNS over HTTPS + type: boolean + default: false + provider: + description: Use Provider + type: choice + choices: + - Cloudflare + - NextDNS + - Custom + default: Cloudflare + disabled: + type: jinja + jinja: | + {% if not rougail.proxy.dns_over_https.enable_dns_over_https %} + Enable DNS over HTTPS is False + {% endif %} + custom_dns_url: + description: Custom DNS URL + type: web_address + disabled: + type: jinja + params: + provider: + type: variable + variable: rougail.proxy.dns_over_https.provider + propertyerror: false + jinja: | + {% if provider is not defined or provider != 'Custom' %} + provider is not custom + {% endif %} + validators: + - type: jinja + jinja: | + {% if rougail.proxy.dns_over_https.custom_dns_url.startswith('http://') %} + only https is allowed + {% endif %} + +.. FIXME : define validators + +The only particularity here is that we added additional validation (validators) to the `custom_dns_url` variable. Only an address starting with `https://` is allowed (not `http://`). + +---- + +The FoxyProxy type's proxy configuration +-------------------------------------------- + +Here is now the integration of part of the Firefox FoxyProxy plugin. + +The idea is to have a namespace specific to FoxyProxy and to find in it part of the settings that we will have made in the main namespace. + +This is what the page looks like: + +.. image:: images/foxyproxy.png + +It is possible, in this plugin, to specify an unlimited number of proxies. +Our `proxy` family will no longer be of the `family` type as before but of another type : the :term:`leadership` type. + +.. FIXME: expliquer ce qu'est le type leardership + +Here is the complete content of the FoxyProxy type proxy configuration +(to be put in the `foxyproxy/00-base.yml` file): + +.. code-block:: yaml + :caption: the :file:``foxyproxy/00-base.yml`` file + :linenos: + + --- + version: '1.0' + proxy: + _type: leadership + title: + description: Title or Description + multi: true + color: + description: Color + type: + type: choice + choices: + - HTTP + - HTTPS/SSL + - SOCKS5 + - SOCKS4 + - PAC URL + - WPAD + - System (use system settings) + - Direct (no proxy) + default: Direct (no proxy) + address: + description: IP address, DNS name, server name + multi: true + disabled: + type: jinja + jinja: | + {% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %} + proxy does not need address + {% endif %} + default: + type: jinja + params: + firefox_address: + type: variable + variable: rougail.proxy.manual.http_proxy.address + propertyerror: false + jinja: | + {% if firefox_address is not undefined %} + {{ firefox_address }} + {% endif %} + port: + description: Port + type: port + default: + type: jinja + params: + firefox_port: + type: variable + variable: rougail.proxy.manual.http_proxy.port + propertyerror: false + jinja: | + {% if firefox_port is not undefined %} + {{ firefox_port }} + {% endif %} + disabled: + type: jinja + jinja: | + {% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %} + proxy does not need port + {% endif %} + username: + description: Username + type: unix_user + mandatory: + type: jinja + jinja: | + {% if foxyproxy.proxy.password %} + username is mandatory + {% endif %} + disabled: + type: jinja + jinja: | + {% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %} + proxy does not need username + {% endif %} + password: + description: Password + type: secret + disabled: + type: jinja + jinja: | + {% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %} + proxy does not need password + {% endif %} + url: + type: web_address + disabled: + type: jinja + jinja: | + {% if foxyproxy.proxy.type not in ['PAC URL', 'WPAD'] %} + proxy does not need url + {% endif %} + + +A few comments: + +- in the `foxyproxy.proxy` :term:`leader` family there is a variable named `type` (line 4), this may conflict with the `type` attribute (specified line 10). In this case, to specify the type we use the `_type` attribute +- a :term:`follower` variable can also be multiple + (which is the case for `foxyproxy.proxy.address`) +- `foxyproxy.proxy.username` (line 62) becomes :term:`mandatory` if `foxyproxy.proxy.password` + is specified, in fact a password without a username is meaningless + +Let's test it: + +>>> from rougail import Rougail, RougailConfig +>>> from pprint import pprint +>>> RougailConfig['dictionaries_dir'] = ['dict'] +>>> RougailConfig['extra_dictionaries']['foxyproxy'] = ['foxyproxy/'] +>>> rougail = Rougail() +>>> config = rougail.get_config() +>>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration') +>>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example') +>>> config.option('foxyproxy.proxy.title').value.set(['MyProxy']) +>>> config.option('foxyproxy.proxy.type', 0).value.set('HTTP') +>>> config.option('foxyproxy.proxy.color', 0).value.set('#00000') +>>> config.property.read_only() +>>> pprint(config.value.get(), sort_dicts=False) + +The output is: + +.. code-block:: python + + {'rougail.proxy.proxy_mode': 'Manual proxy configuration', + 'rougail.proxy.manual.http_proxy.address': 'proxy.example', + 'rougail.proxy.manual.http_proxy.port': '8080', + 'rougail.proxy.manual.use_for_https': True, + 'rougail.proxy.manual.ssl_proxy.address': 'proxy.example', + 'rougail.proxy.manual.ssl_proxy.port': '8080', + 'rougail.proxy.manual.socks_proxy.address': None, + 'rougail.proxy.manual.socks_proxy.port': None, + 'rougail.proxy.manual.socks_proxy.version': 'v5', + 'rougail.proxy.no_proxy': [], + 'rougail.proxy.proxy_dns_socks5': False, + 'rougail.proxy.dns_over_https.enable_dns_over_https': False, + 'foxyproxy.proxy.title': [{'foxyproxy.proxy.title': 'MyProxy', + 'foxyproxy.proxy.color': '#00000', + 'foxyproxy.proxy.type': 'HTTP', + 'foxyproxy.proxy.address': ['proxy.example'], + 'foxyproxy.proxy.port': '8080', + 'foxyproxy.proxy.username': None, + 'foxyproxy.proxy.password': None}]} + +The choice we made here is to make `foxyproxy.proxy.username` :term:`mandatory` if a password is specified in the `foxyproxy.proxy.password` variable. + +It makes sense to have a username without a password (in this case the password will be requested when connecting to the proxy). But the opposite does not make sense. + +From a user point of view this may seem disturbing (if you enter the password, you have to return to the previous option to specify the password). + +It is possible to reverse the logic. If the `foxyproxy.proxy.username` variable is set, the `foxyproxy.proxy.password` variable becomes editable. + +None of this two variables needs to be :term:`mandatory`. + +If you prefer this option, here is a second extra dictionary :file:`foxyproxy/01-redefine.yml` which will redefine the behavior only of the `foxyproxy.proxy.username` and `foxyproxy.proxy.password` variables: + + + + +.. code-block:: yaml + :caption: the :file:`foxyproxy/01-redefine.yml` file + :linenos: + + --- + version: '1.0' + proxy: + username: + redefine: true + # suppress mandatory constrainte + mandatory: false + password: + redefine: true + hidden: + type: jinja + jinja: | + {% if not foxyproxy.proxy.username %} + no username defined + {% endif %} + + +**It's up to you to play now !** diff --git a/docs/variable.rst b/docs/variable.rst new file mode 100644 index 000000000..b26e91a69 --- /dev/null +++ b/docs/variable.rst @@ -0,0 +1,284 @@ +The variables +=================== + +Synopsis +------------ + +.. glossary:: + + variable + variables + + A variable is an abstract black box (container) paired with an associated symbolic name, which contains some defined or undefined quantity of data referred to as a `value`. + +.. discussion:: This definition, makes a heavy use of data typing. + Indeed, depending on the type system definition of the constistency handling system used, variables may only be able to store a specified data type. + OK, variables are the containers for storing the values. It has something to do with typing. + But this is not just about typing. + +Name +------------- + +Variable's associated symbolic name. + +It's best to follow the :ref:`convention on variable names`. + +Parameters +------------- + +.. list-table:: + :widths: 15 45 + :header-rows: 1 + + * - Parameter + - Comments + + * - **help** + + `string` + - Additional help associated with the variable. + + * - **default** + - Default value(s) of the variable. + + This value is typed, you must correctly fill out the YAML file to avoid defining a value with an incorrect type. For example, a `number` must be a digit type, a multiple variable must be a `list` type, ... + + For a non :term:`leading` multiple variable, the first value defined in the list will also be the default value proposed if a new value is added to this variable. + + * - **validators** + + `list` + - Value validators. + + Jinja template list. The value of the variable will be considered invalid if the template has a return value. + * - **auto_save** + + `boolean` + - Variable with automatically modified value. + + A variable with automatically modified value is a variable whose value will be considered as *modified* (that is, it is no longer the variable's default value). + + For example, if the value of this variable comes from a calculation, the value will no longer be recalculated. + + These variables are usually :term:`required` variables. In fact, these variables are only automatically modified if they have a value. + + A :term:`leader` or :term:`follower` variable cannot have the `auto_save` property. + + **Default value**: `false` + * - **mode** + + `string` + - Variable's mode. + + **Default value**: The `default` mode of a variable is the mode of the parent family. + + Special cases : + + - a variable with an automatically modified value or an automatic read-only variable is by default in `basic` mode + - if the variable is not in a family, the variable will have a `standard` mode by default + - a :term:`mandatory` variable without default value (calculate or not) will have a `basic` mode + * - **multi** + + `boolean` + - The value of the variable is a list. + + **Default value**: `false` + * - **unique** + + `boolean` + - The :term:`multiple` type variable accepts the same value several times. If unique is set to `false`, a :term:`multiple` variable only accepts the same value once in the list. + + **Default value**: `false` + * - **hidden** + + `boolean` or :term:`calculation` + - Invisible variable. + + Enables us to *hide* a variable. + + This means that the variable will no longer be visible in `read-write` mode, but only for calculations or in `read-only` mode. + + When a variable is made invisible, the user will not be able to modify its value; if he has already succeeded in modifying it, this value will not be taken into account. + + **Default value**: `false` + * - **disabled** + + `boolean` or :term:`calculation` + - Disabled variable. + + Allows us to deactivate a variable. + + This means that the variable will no longer be visible to the user but also to a :term:`calculation`. + + **Default value**: `false`. + * - **mandatory** + + `boolean` or :term:`calculation` + - Mandatory variable. + + Variable whose value is `required`. + + For a multiple variable, this means that the list shall not be empty. + + **Default value**: `true` + * - **redefine** + + `boolean` + - It is possible to define a variable in one :term:`dictionary` and change its behavior in a second :term:`dictionary`. In this case you must explicitly redefine the variable. + + **Default value**: `false` + * - **exists** + + `boolean` + - This attribute does two things: + + - creates a variable if it does not exist in another :term:`dictionary` (otherwise do nothing), in this case the value of the attribute must be `true` + - in conjunction with the `redefine` attribute set to `true`, only modifies the behavior if it is pre-existing, in which case the attribute's value must be `false`. + + **Default value**: `null` + * - **test** + + `list` + - The `test` attribute is a special attribute that allows :term:`dictionary` designers to influence a test robot by specifying useful values to test. + + Concretely, the content of this attribute is recorded in the `information` attribute of the corresponding `Tiramisu` option object. + +Variables types +---------------- + +A variable **has a type**. + +This type enables the variable to define the values that are accepted by this variable. + +.. list-table:: + :widths: 15 25 20 15 + :header-rows: 1 + + * - Value + - Comments + - Parameters + - Samples + + * - string + - character string (default type) + - + - test + + "1" + + "true" + * - number + - a number + - `min_number`: minimum number allowed + + `max_number`: maximum number allowed + - 1 + * - float + - a floating number + - + - 1.2 + * - boolean + - A boolean, if no value is defined the default value of this variable will be `true`, the variable will also be :term:`mandatory` by default + - + - `true` + + `false` + * - secret + - a secret (like a password, a private key, etc.) + - + - `hO_'hi` + * - mail + - a mail address + - + - test@rougail.example + * - unix_filename + - a file name in the Unix meaning + - + - :file:`/etc/passwd` + * - date + - a date in the format `%Y-%m-%d` + - + - `2021-01-30` + * - unix_user + - a user in the Unix meaning + - + - test + * - ip + - any kind of IPv4 address + - `private_only`: only private IPs (`false` by default) + + `allow_reserved`: allows reserved IPs (`true` by default) + - `1.2.3.4` + * - cidr + - any IPv4 address in the CIDR format + - `private_only`: only private IPs (`false` by default) + + `allow_reserved`: allows reserved IPs (`false` by default) + - `1.2.3.4/24` + * - netmask + - mask of an IPv4 address + - + - `255.255.255.0` + * - network + - network address + - + - `192.168.1.0` + * - network_cidr + - network address in CIDR format + - + - `192.168.1.0/24` + * - broadcast + - broadcast address + - + - `255.255.255.255` + * - netbios + - netbios name + - + - machine + * - domainname + - domain name + - `allow_ip`: allows an IP rather than a domain name (`false` by default) + + `allow_cidr_network`: allows a CIDR type network address (`false` by default) + + `allow_without_dot`: allows names without a dot (`false` by default) + + `allow_startswith_dot`: allows starting with a point (`false` by default) + - `rougail.example` + * - hostname + - host name + - `allow_ip`: allows an IP rather than a domain name (`false` by default) + - machine + * - web_address + - web address + - `allow_ip`: allows an IP rather than a domain name (`false` by default) + + `allow_without_dot`: allows names without a dot (`true` by default) + - http://rougail.example + * - port + - port + - `allow_range`: allows a port range, for example 80:85 (`false` by default) + + `allow_zero`: allows port 0 (false by default) + + `allow_wellknown`: allows ports from 1 to 1023 (`true` by default) + + `allow_registred`: allows ports from 1024 to 49151 (`true` by default) + + `allow_private`: allows ports greater than 49152 (`true` by default) + + `allow_protocol`: allows the addition of the protocol, for example tcp:80 (`false` by default) + + - 8080 + * - mac + - MAC address + - + - 11:11:11:11:11:11 + * - unix_permissions + - access rights to the file, directory, etc. + - + - 644 + * - choice + - choice variable + - + -