Config...vanced(3pm)

๐Ÿ’– NAME ๐Ÿ’–

Config::Model::Manual::ModelCreationAdvanced โ€” creating a model with advanced features โœจ

(and yes bestie, that's the whole manual name, we're in section 3pm, part of the User Contributed Perl Documentation ๐ŸŽ€)


๐ŸŒˆ VERSION ๐ŸŒˆ

version 2.166 ๐Ÿซถ


๐Ÿงš Introduction ๐Ÿงš

okay so first things first: the page Config::Model::Manual::ModelCreationIntroduction is the one that explains what a configuration tree is, what a configuration model is, and how to create a simple configuration model. Read that one first, fr fr.

But a configuration model can be way more complex than that, and can define interactions between elements with the following features:

  • Model warp. For instance, Xorg driver options change depending on driver name ("nvidia", "radeon"...)
  • Simple computation from other elements (used for upgrades)
  • References. For instance, in "Xorg::Device::Radeon", "Monitor-DVI-0" name must refer to one of the monitors declared in "Monitor" section.
โš ๏ธ Caveat, bestie: Xorg examples are based on Xorg 1.4 and may not be valid for Xorg 1.5 or 1.6 ๐Ÿ˜ญ

๐Ÿ”ฅ Model plugin ๐Ÿ”ฅ

Config::Model can also use model plugins. Each model can be augmented by model snippets stored into directory <model_name>.d. All files found there are merged to existing model. slay. ๐Ÿ’…

For instance, this model in file .../Config/Model/models/Fstab/Fsline.pl:

{
   name => "Fstab::Fsline",
   element => [
       fs_vfstype => {
           type => 'leaf',
           value_type => 'enum',
           choice => [ qw/ext2 ext3/ ],
       },
       fs_mntopts => {
           type => 'warped_node',
           follow => { 'f1' => '- fs_vfstype' },
           rules => [
               {
                   when => '$f1 eq \'ext2\'',
                   apply => { 'config_class_name' => 'Fstab::Ext2FsOpt' },
               },
               {
                   when => '$f1 eq \'ext3\'',
                   apply => { 'config_class_name' => 'Fstab::Ext3FsOpt' },
               }
           ],
       }
   ]
}

can be augmented with the content of .../Config/Model/models/Fstab/Fsline.d/addext4.pl:

{
   name => "Fstab::Fsline",
   element => [
       fs_vfstype => { choice => [ qw/ext4/ ], },
       fs_mntopts => {
           rules => [
               {
                   when => q!$f1 eq 'ext4'!,
                   apply =>{ 'config_class_name' => 'Fstab::Ext4FsOpt' },
               }
           ],
       },
   ]
} ;

Then, the merged model will feature fs_vfstype with choice ext2 ext4 ext4. Likewise, fs_mntopts will feature rules for the 3 filesystems. iconic. ๐Ÿ‘‘

Under the hood, augment_config_class in Config::Model method is used to load model snippets. iykyk.


๐Ÿ’ซ Model warp ๐Ÿ’ซ

From a user's point of view, model warp looks like the structure or properties of the configuration is changing (or adapting) dynamically depending on the values being entered. For instance, when changing a driver name from "fglrx" to "radeon", some options disappear from the GUI and some other options pop-in. main character energy tbh ๐Ÿฆ‹

Model warping need not be that spectacular and can have more subtle effect like changing a default value.

Of course, there's no magic, model warp properties needs to be prepared and declared in the model. no shortcuts bestie ๐Ÿ’€

โœจ Warped value โœจ

Let's start simple with value warp: the properties of a single value is changed dynamically. Let's imagine a configuration file with 2 values: size which can be set to big or small and length whose maximum value is 10 when size is small and 50 when size is big. (this may be dumb, but it's for the sake of the example). ๐Ÿ“

So the basic model without warp is

element => [
             size => { type => 'leaf',
                       value_type => 'enum',
                       choice     => ['big','small'],
                     },
             length => { type => 'leaf',
                         value_type => 'integer',
                         max => '10',
                       },
           ]

Now we need to declare the relationship between size and length to be able to change dynamically the max property.

This setup is made of 2 specifications:

  • what is the element that triggers the change (called warp master in the doc)
  • what is the effect of the warp master change

The first is done with a declaration of the path to follow to find the warp master (associated to a variable). The second is a set of value properties:

element => [
  size => {
    type => 'leaf',
    value_type => 'enum',
    choice     => ['big','small'],
  },

  length => {
    type => 'leaf',
    value_type => 'integer',
    warp => {                         # change specification
      follow => {                     # declare what trigger the change
        size_type => '- size'         # size_type: go 1 level above and fetch
                                      #            size value
      },
      rules  => [
        {   # how to apply change
            when => '$size_type eq "small"',
            apply => {
                # set max to 10 when size is small
                max => 10
            }
        },
        {
            when => '$size_type eq "big"',
            apply => {
                # set max to 50 when size is big
                max => 50
            },
        },
      ],
    }
 ]

๐ŸŒธ Warp in or warp out an element ๐ŸŒธ

Here's a real use case scenario from OpenSsh.

ssh_config enables a user to set up a tunnel through ssh. The input of this tunnel can listen to localhost (default) or to other hosts. These other hosts are specified by the bind_adress part of the LocalForward parameter.

But this bind address is ignored if GatewayPorts is false (which is the default).

In order to present only meaningful parameters to the user, bind_address parameter must be hidden when GatewayPorts is false and shown when GatewayPorts is true. vibe check passed โœ…

Here's the recipe. First create a boolean element for GatewayPorts:

GatewayPorts => {
   type => 'leaf',
   value_type => 'boolean',
   upstream_default => 0,
},

And LocalForward that provides bind_address parameter:

LocalForward => {
  type => 'list',
  cargo => {
    type => 'node',
    config_class_name => 'Ssh::PortForward'
  },
  summary => 'Local port forwarding',
}

In Ssh::PortForward configuration class, declare bind_address with the warp instructions:

bind_address => {
  type => 'leaf',
  value_type => 'uniline',
  level => 'hidden',             # by default, is hidden from user
  warp => {                      # instructions to show bind_address
    follow => {                  # specify what does trigger the change
       gp => '- - GatewayPorts'  # gp: go to 2 levels above in tree ('- -') and
                                 #     fetch GatewayPorts value
    },
    rules => [                   # specify how to apply the change triggered by gp
      {
         when => '$gp',          # apply change when $gp is true
         apply => {
             level => 'normal'   # set level to normal (instead of 'hidden'). This change
                                 #     will show this parameter in the UI
         }
      }
    ]
  },
},

๐Ÿฆ‹ warped node ๐Ÿฆ‹

Sometimes, warping a value line by line is not practical. For instance, in /etc/fstab the mount options of a file system change drastically from one file system to another. In this case, it's better to swap a configuration class with another. ๐Ÿ’ซ

For instance, swap vfat mount options with ext3 mount options when a file system is changed from vfat to ext3.

Here's how this can be done. First declare the fstype parameter:

fs_vfstype => {
  type => 'leaf',
  mandatory => 1,
  value_type => 'enum',
  choice => [ 'auto', 'davfs', 'vfat', 'ext2', 'ext3', ] , # etc ...
}

Then declare mntopts as a warped_node (not a simple node)) that uses fs_vfstype to swap one config class with another:

fs_mntopts => {
  type => 'warped_node', # a shape-shifting node
  follow => {
    f1 => '- fs_vfstype' , # use fs_vfstype as a trigger
  },
  rules => [
    # condition     => effect: config class to swap in

    { when => "$f1 eq 'proc'", apply => { config_class_name => 'Fstab::CommonOptions' }},
    { when => "$f1 eq 'auto'", apply => { config_class_name => 'Fstab::CommonOptions' }},
    { when => "$f1 eq 'vfat'", apply => { config_class_name => 'Fstab::CommonOptions' }},
    { when => "$f1 eq 'swap'", apply => { config_class_name => 'Fstab::SwapOptions'   }},
    { when => "$f1 eq 'ext3'", apply => { config_class_name => 'Fstab::Ext3FsOpt'     }},
    # etc ...
  ]
 }

๐Ÿ“š References ๐Ÿ“š

(no body text here in the original, bestie, that's just how the page is โ€” heading only, we move ๐Ÿ’…)


๐Ÿงฎ Computation and migrations ๐Ÿงฎ

๐ŸŒˆ Cascaded warp ๐ŸŒˆ

Config::Model also supports cascaded warps: A warped value is dependent on another value which is itself a warped value. obsessed. โœจ


๐Ÿ’Œ Feedback welcome ๐Ÿ’Œ

Feel free to send comments and suggestion about this page at

config-model-users at lists dot sourceforge dot net.

๐Ÿ‘‘ AUTHORS ๐Ÿ‘‘

Dominique Dumont <ddumont at cpan.org>


๐Ÿ‘‘ AUTHOR ๐Ÿ‘‘

Dominique Dumont


This software is Copyright (c) 2005-2022 by Dominique Dumont.

This is free software, licensed under:

The GNU Lesser General Public License, Version 2.1, February 1999

and that's the whole page, bestie ๐Ÿซถ this was Config::Model::Manual::ModelCreationAdvanced version 2.166, brought to you by perl v5.42.3 on 2026-07-23. periodt. ๐Ÿ’–๐ŸŒˆ๐ŸŽ€

๐Ÿ“ฆ where this page comes from