Custom type
Objectives
In this sections we are going to learn how to create custom types with Rougail. In short, a custom type is nothing more than a kind of a template for a variable or a family.
Prerequisites
We assume that Rougail’s library is installed on your computer.
It is possible to retrieve the current state of the various Rougail files manipulated in this tutorial step by checking out the corresponding tag of the
rougail-tutorialsgit repository. Each tag corresponds to a stage of progress in the tutorial. Of course, you can also decide to copy/paste or download the tutorial files contents while following the tutorial steps.
If you want to follow this tutorial with the help of the corresponding rougail-tutorials git repository, this workshop page corresponds to the tags v1.1_080 to v1.1_085 in the repository.
git clone https://forge.cloud.silique.fr/stove/rougail-tutorials.git
git switch --detach v1.1_080
HTTP Proxy with “proxy” type
Let’s look now at the possibilities for adding types. This feature is essential for having a truly adaptable tool.
Note
It is actually possible to add types, but that involves adding predefined types to the Tiramisu underlying consistency library itself, and that’s a different matter altogether. There is a simpler way to achieve this with the Rougail type definition feature.
It is possible in Rougail to create custom types in a very simple way, it is much like defining variables or families.
Our folder structure will be expanded a bit:
.
├── firefox
│ ├── 00-proxy.yml
│ ├── 10-manual.yml
│ └── 20-manual.yml
└── types
└── proxy
└── 00-type.yml
Notice that we have now a new types folder in our tree structure.
This is where we will place our new proxy type definition.
Note
Choosing the types/proxy/00-type.yml file name is not mandatory.
You can name this folder whatever you want.
It is simply a good practice to separate structure files from type definition files.
We will see later on how to indicate to Rougail that a file contains a type declaration.
In accordance with our naming policy, we have created a file named 00-type.yml
inside the proxy folder.
Let’s examine this more closely this types/proxy/00-type.yml type definition file:
types/proxy/00-type.yml type structure file that defines our new proxy type.%YAML 1.2
---
version: 1.1
proxy:
address:
description: Proxy address
type: domainname
params:
allow_ip: true
port:
description: Proxy port
type: port
default: 8080
...
The new type named proxy is declared the same way we declare a family.
It’s more or less like a family declaration except it will be used as a type definition.
If you remember, it’s mostly the content as the family manual.http_proxy in firefox/10-manual.yml file of this tutorial.
How do we use a new type?
Very simply. In the usual way: we will add a type parameter in our family declaration.
Let’s modify the existing family using this new proxy type:
firefox/10-manual.yml structure file with less code due to the proxy type definition%YAML 1.2
---
version: 1.1
manual:
description: Manual proxy configuration
disabled:
variable: _.proxy_mode
when_not: Manual proxy configuration
http_proxy:
description: HTTP Proxy
type: proxy
...
We can see that the type declared for the http_proxy family is now type: proxy.
It’s perfectly natural in the Rougail style.
Due to the type definition we can “omit” some settings in the structure file
such as the domainname type and the allow_ip parameter.
And the address variable it is not necessary to be specified at all in the structure file
because it’s in the type definition.
In order to use our new type we need to declare it to Rougail otherwise the new type is not available.
The Rougail CLI has the --types type declaration command line option for this purpose.
Let’s launch the Rougail CLI with this command line parameter:
The CLI output is entirely standard and does not specifically mention the call to a new type. The result is the same as usual, as if the type declaration were omitted, which is what we want. We want the same behavior as usual.
╭────────────── Caption ───────────────╮ │ Variable Default value │ │ Modified value │ │ (⏳ Original default value) │ ╰──────────────────────────────────────╯ Variables: ┣━━ 📓 proxy_mode (Configure Proxy Access to the Internet): Manual proxy ┃ configuration ◀ loaded from the YAML file "config/01/config.yml" (⏳ No ┃ proxy) ┗━━ 📂 manual (Manual proxy configuration) ┣━━ 📂 http_proxy (HTTP Proxy) ┃ ┣━━ 📓 address (Proxy address): http.proxy.net ◀ loaded from the YAML ┃ ┃ file "config/01/config.yml" ┃ ┗━━ 📓 port (Proxy port): 3128 ◀ loaded from the YAML file ┃ "config/01/config.yml" (⏳ 8080) ┣━━ 📓 use_for_https (Also use this proxy for HTTPS): false ◀ loaded from ┃ the YAML file "config/01/config.yml" (⏳ true) ┣━━ 📂 https_proxy (HTTPS Proxy) ┃ ┣━━ 📓 address (HTTPS address): https.proxy.net ◀ loaded from the YAML ┃ ┃ file "config/01/config.yml" (⏳ http.proxy.net) ┃ ┗━━ 📓 port (HTTPS port): 3128 ┗━━ 📂 socks_proxy (SOCKS Proxy) ┣━━ 📓 address (SOCKS address): http.proxy.net ┣━━ 📓 port (SOCKS port): 3128 ┗━━ 📓 version (SOCKS host version used by proxy): v5
HTTPS and SOCKS Proxy with “proxy” type
For those who follow the tutorial with the help of the git repository
Now you need to checkout the v1.1_081 version:
git switch –detach v1.1_081
With this use of a type, it is possible to define our HTTPS and SOCKS Proxy in a much more conscious way: It’s very practical. In fact, the type definition adds nothing else to the structural logic.
firefox/20-manual.yml structure file with less code due to the proxy type definition%YAML 1.2
---
version: 1.1
manual:
use_for_https: true # Also use this proxy for HTTPS
https_proxy:
description: HTTPS Proxy
type: proxy
hidden:
variable: _.use_for_https
socks_proxy:
description: SOCKS Proxy
type: proxy
...
Now we have two families with the proxy type: https_proxy and socks_proxy.
But we haven’t recovered everything we had before. Using a type definition is fine, but we still need to find exactly the configuration situation we had before using the type definition.
Add a variable in a family with custom type
For those who follow the tutorial with the help of the git repository
Now you need to checkout the v1.1_082 version:
git switch --detach v1.1_082
In the socks_proxy family definition, we need to added a new variable, the version variable:
version:
description: SOCKS host version used by proxy
choices:
- v4
- v5
default: v5
You can see that what we call a type definition in Rougail is very flexible, because it is perfectly possible to add to what has been defined in the type. The type definition here is therefore not only a template but much more an extensible schema.
Redefine default value in custom type variable
For those who follow the tutorial with the help of the git repository
Now you need to checkout the v1.1_083 version:
git switch --detach v1.1_083
In this section we are going to make an implicit redefinition of variables default values.
- redefine
A redefine is a redefinition of all or part of the attributes of a variable as it was previously defined. There are two types of redefine, an implicit and an explicit. An implicit redefinition is a redefinition which has not been declared with the
redefineattribute. An explicit redefinition is a redefinition which has been declared with theredefine: trueattribute.Only default values can be implicitly redefined (and only in the type definitions), all other attributes need to be explicitely redefined.
In the type definition file, we are now setting default values for the address and port variables:
types/proxy/00-type.yml type definition file with the default values for address and port%YAML 1.2
---
version: 1.1
proxy:
address:
description: Proxy address
type: domainname
params:
allow_ip: true
default:
variable: __.http_proxy.address
port:
description: Proxy port
type: port
default:
variable: __.http_proxy.port
...
But we don’t want theses default values for HTTP definition. We can redefine these type settings in the structure file:
firefox/10-manual.yml structure definition file with the redefined default values for address and port%YAML 1.2
---
version: 1.1
manual:
description: Manual proxy configuration
disabled:
variable: _.proxy_mode
when_not: Manual proxy configuration
http_proxy:
description: HTTP Proxy
type: proxy
address:
default: null
port:
default: 8080
...
This means that the default values for these variables will now be those defined in the structure file if the values are defined (and not in the type definitions file).
Let’s have a closer look at the behavior here.
Let’s launch the Rougail CLI:
We have this output:
╭────────────── Caption ───────────────╮ │ Variable Default value │ │ Modified value │ │ (⏳ Original default value) │ ╰──────────────────────────────────────╯ Variables: ┣━━ 📓 proxy_mode (Configure Proxy Access to the Internet): Manual proxy ┃ configuration ◀ loaded from the YAML file "config/01/config.yml" (⏳ No ┃ proxy) ┗━━ 📂 manual (Manual proxy configuration) ┣━━ 📂 http_proxy (HTTP Proxy) ┃ ┣━━ 📓 address (Proxy address): http.proxy.net ◀ loaded from the YAML ┃ ┃ file "config/01/config.yml" ┃ ┗━━ 📓 port (Proxy port): 3128 ◀ loaded from the YAML file ┃ "config/01/config.yml" (⏳ 8080) ┣━━ 📓 use_for_https (Also use this proxy for HTTPS): false ◀ loaded from ┃ the YAML file "config/01/config.yml" (⏳ true) ┣━━ 📂 https_proxy (HTTPS Proxy) ┃ ┣━━ 📓 address (Proxy address): https.proxy.net ◀ loaded from the YAML ┃ ┃ file "config/01/config.yml" ┃ ┗━━ 📓 port (Proxy port): 8080 ┗━━ 📂 socks_proxy (SOCKS Proxy) ┣━━ 📓 address (Proxy address): socks.proxy.net ◀ loaded from the YAML ┃ file "config/01/config.yml" ┣━━ 📓 port (Proxy port): 8080 ┗━━ 📓 version (SOCKS host version used by proxy): v5
We can see in the output that
the default values of the
portvariable, which is8080in the structure file, comes from the strucure file.the default values of the
addressvariable is not set because it isnullby default in the structure file, so there is not such thing as a default value set for this variable.
What happens if the default value in the type definition is not suitable for us?
If we hadn’t defined a value in the userdata file, the Rougail CLI would have returned the following error:
🛑 Caution
┗━━ Manual proxy configuration
┣━━ HTTP Proxy
┃ ┗━━ Proxy address: 🛑 mandatory variable but has no value
┗━━ SOCKS Proxy
┗━━ Proxy address: 🛑 mandatory variable but has no value
Therefore, the default value that was taken into account is indeed that of the structure file, and not that of the type definition.
Note
We can see that the Rougail behavior is always very simple, there is no such thing as an MRO (Method Resolution Object) that try fallback to the default value defined in the type definition file. The Rougail behavior is much more simple and transparent.
kinematics of setting the address value
Let’s take the time to explain how the value of the variable manual.http_proxy.address is determined using the type definition.
The 10-manual.yml structure file defines the manual.http_proxy family
which is defined with the help of the proxy type:
http_proxy:
description: HTTP Proxy
type: proxy
In the types/proxy/00-type.yml file we have the proxy type definition and its default value setting:
default:
variable: __.http_proxy.address
This default value setting point to the http_proxy.address value in the firefox/10-manual.yml structure file:
address:
default: null
Which is null by default but in the config/01/config.yml user data file we have the http.proxy.net value set:
manual:
http_proxy:
address: http.proxy.net
In the CLI standard output we can see that the http_proxy.address variable value is
finally set by the user data file:
┗━━ 📂 manual (Manual proxy configuration)
┣━━ 📂 http_proxy (HTTP Proxy)
┃ ┣━━ 📓 address (Proxy address): http.proxy.net ◀ loaded from the YAML
┃ ┃ file "config/01/config.yml"
Redefine other parameter in custom type for HTTP
For those who follow the tutorial with the help of the git repository
Now you need to checkout the v1.1_084 version:
git switch --detach v1.1_084
In this sections we are going to make explicit redefinitions.
Note here that, in order to allow for the genericity of a type declaration, we have omitted to specify the variable descriptions in the type declaration. We will therefore need to enter these descriptions in the structure file.
types/proxy/00-type.yml type definition file with the default definitions and no description%YAML 1.2
---
version: 1.1
proxy:
address:
type: domainname
params:
allow_ip: true
default:
variable: __.http_proxy.address
port:
type: port
default:
variable: __.http_proxy.port
...
So, as the address and port variables don’t have any description attribute,
we need to add one. It is something like an extension of the type definitions here
in the structure file:
firefox/10-manual.yml structure definition file with explicit redefinitions%YAML 1.2
---
version: 1.1
manual:
description: Manual proxy configuration
disabled:
variable: _.proxy_mode
when_not: Manual proxy configuration
http_proxy:
description: HTTP Proxy
type: proxy
address:
redefine: true
default: null
description: HTTP proxy address
port:
redefine: true
default: 8080
description: HTTP proxy port
...
Note
Transforming an implicit defintion into an explicit redefinition is very simple; you just need to add the redefine: true attribute:
Let’s launch the Rougail CLI:
We have this output:
╭────────────── Caption ───────────────╮ │ Variable Default value │ │ Modified value │ │ (⏳ Original default value) │ ╰──────────────────────────────────────╯ Variables: ┣━━ 📓 proxy_mode (Configure Proxy Access to the Internet): Manual proxy ┃ configuration ◀ loaded from the YAML file "config/01/config.yml" (⏳ No ┃ proxy) ┗━━ 📂 manual (Manual proxy configuration) ┣━━ 📂 http_proxy (HTTP Proxy) ┃ ┣━━ 📓 address (Proxy address): http.proxy.net ◀ loaded from the YAML ┃ ┃ file "config/01/config.yml" ┃ ┗━━ 📓 port (Proxy port): 3128 ◀ loaded from the YAML file ┃ "config/01/config.yml" (⏳ 8080) ┣━━ 📓 use_for_https (Also use this proxy for HTTPS): false ◀ loaded from ┃ the YAML file "config/01/config.yml" (⏳ true) ┣━━ 📂 https_proxy (HTTPS Proxy) ┃ ┣━━ 📓 address (Proxy address): https.proxy.net ◀ loaded from the YAML ┃ ┃ file "config/01/config.yml" (⏳ http.proxy.net) ┃ ┗━━ 📓 port (Proxy port): 3128 ┗━━ 📂 socks_proxy (SOCKS Proxy) ┣━━ 📓 address (Proxy address): http.proxy.net ┣━━ 📓 port (Proxy port): 3128 ┗━━ 📓 version (SOCKS host version used by proxy): v5
We can see in the output that the address and port variables have default values,
and descriptions (“HTTP proxy address”, “HTTP proxy port”) that weren’t present in the
type definition file.
Redefine other parameter in custom type for HTTPS and SOCKS
For those who follow the tutorial with the help of the git repository
Now you need to checkout the v1.1_085 version:
git switch --detach v1.1_085
We’re going to do exactly the same modification with the socks_proxy family,
that is we will base the socks_proxy family on the type definition.
Following the same reasoning, we add a in the structure file some description attributes to the socks_proxy family
and also add some explicit redefinition attribute (redefine: true).
firefox/20-manual.yml structure file with the redefine and the description attribute%YAML 1.2
---
version: 1.1
manual:
use_for_https: true # Also use this proxy for HTTPS
https_proxy:
description: HTTPS Proxy
type: proxy
hidden:
variable: _.use_for_https
address:
redefine: true
description: HTTPS proxy address
port:
redefine: true
description: HTTPS proxy port
socks_proxy:
description: SOCKS Proxy
type: proxy
address:
redefine: true
description: SOCKS proxy address
port:
redefine: true
description: SOCKS proxy port
version:
description: SOCKS host version used by proxy
choices:
- v4
- v5
default: v5
...
Key points
the very practical aspects of type declaration
type usage in the Rougail CLI
more conscious family definition
using a type for an extended family definition
we can redefine things in the structure file and it overloads the type definition file
implicit or explicit redefinitions
We have seen how the definition of type, used effectively, allows for a new expressiveness.