Config::Model(3pm)

πŸ’– NAME πŸ’–

okay so bestie, meet Config::Model(3pm) β€” from the User Contributed Perl Documentation β€” and she is literally a framework to validate, migrate and edit configuration files. πŸ‘‘


✨ VERSION ✨

version 2.166 β€” periodt. πŸ’…


πŸ¦‹ SYNOPSIS πŸ¦‹

πŸ“ Perl program to use an existing model

Bestie, if you already have a model and just wanna use it, it's giving one-liner energy:

use Config::Model qw(cme);
# load, modify and save popcon configuration file
cme('popcon')->modify("PARTICIPATE=yes");

🌈 Command line to use an existing model

Same vibe, but from your shell, with App::Cme:

# with App::Cme
cme modify popcon 'PARTICIPATE=yes'

πŸŽ€ Perl program with a custom model

Now we're in main character mode and rolling our own model:

use Config::Model;

# create new Model object
my $model = Config::Model->new() ; # Config::Model object

# create config model. A more complex model should be stored in a
# file in lib/Config/Model/models. Then, run cme as explained below
$model ->create_config_class (
  name => "MiniModel",
  element => [
    foo => { type => 'leaf', value_type => 'uniline' },
    bar => '*foo',
    baz => '*foo',
  ],
  rw_config => {
    backend => 'IniFile',
    auto_create => 1,
    config_dir => '.',
    file => 'mini.ini',
  }
) ;

# create instance (Config::Model::Instance object)
my $instance = $model->instance (root_class_name => 'MiniModel');

# get configuration tree root
my $cfg_root = $instance -> config_root ; # C::M:Node object

# load some dummy data
$cfg_root -> load("bar=BARV foo=FOOV baz=BAZV") ;

# write new ini file
$instance -> write_back;

# now look for new mini.ini file un current directory

πŸ¦„ Create a new model file and use it

Okay, so you can also make a new model file and then use it, like:

$ mkdir -p lib/Config/Model/models/
$ echo "[ { name => 'MiniModel', \
            element => [ foo => { type => 'leaf', value_type => 'uniline' }, qw/bar *foo baz *foo/], \
            rw_config => { backend => 'IniFile', auto_create => 1, \
                           config_dir => '.', file => 'mini.ini', \
                         } \
          } \
        ] ; " > lib/Config/Model/models/MiniModel.pl
# require App::Cme
$ cme modify -try MiniModel -dev bar=BARV foo=FOOV baz=BAZV
$ cat mini.ini

Note that model creation is easier running "cme meta edit" with App::Cme and Config::Model::Itself. ✨


🌸 DESCRIPTION 🌸

Okay so here's the whole point, bestie: Config::Model enables a project developer to provide an interactive configuration editor β€” graphical, curses based or plain terminal β€” to users. That's the vibe. πŸ’…

To provide these tools, Config::Model needs:

  • A description of the structure and constraints of the project's configuration. Fear not: a GUI is available with App::Cme to create this structure. πŸŽ€
  • A module to read and write configuration data (aka a backend class). πŸ’Ύ

With the elements above, Config::Model generates interactive configuration editors (with integrated help and data validation). These editors can be graphical (with Config::Model::TkUI), curses based (with Config::Model::CursesUI) or based on ReadLine. ✨

Smaller models targeted for configuration upgrades can also be created:

  • only upgrade and migration specifications are required
  • unknown parameters can be accepted

A command line is provided to perform configuration upgrade with a single command. πŸ”₯

🧚 How does this work ?

Using this project, a typical configuration editor/validator/upgrader is made of 3 parts:

 GUI <--------> |---------------|
 CursesUI <---> | |---------|   |
                | | Model   |   |
 ShellUI <----> | |---------|   |<-----read-backend------- |-------------|
                |               |----write-backend-------> | config file |
 FuseUI <-----> | Config::Model |                          |-------------|
                |---------------|
  1. A reader and writer that parse the configuration file and transform its data into a tree representation within Config::Model. The values contained in this configuration tree can be written back in the configuration file(s). πŸ“–
  2. A validation engine which is in charge of validating the content and structure of configuration stored in the configuration tree. This validation engine follows the structure and constraint declared in a configuration model. This model is a kind of schema for the configuration tree. βœ…
  3. A user interface to modify the content of the configuration tree. A modification is validated immediately by the validation engine. πŸ–₯️

The important part is the configuration model used by the validation engine. This model can be created or modified with a graphical editor (Config::Model::Iself). πŸ‘‘

πŸ’œ Question you may ask yourself πŸ’œ

πŸ’€ Don't we already have some configuration validation tools ?

Bestie, you're probably thinking of tools like webmin. Yes, these tools exist and work fine, but they have their set of drawbacks. 😭

Usually, the validation of configuration data is done with a script which performs semantic validation and often ends up being quite complex (e.g. 2500 lines for Debian's xserver-xorg.config script which handles "xorg.conf" file). πŸ’€

In most cases, the configuration model is expressed in instructions (whatever programming language is used) and interspersed with a lot of processing to handle the actual configuration data. 🫠

🌟 What's the advantage of this project ?

Config::Model projects provide a way to get a validation engine where the configuration model is completely separated from the actual processing instructions. ✨

A configuration model can be created and modified with the graphical interface provide by Config::Model::Itself. The model is saved in a declarative form (currently, a Perl data structure). Such a model is easier to maintain than a lot of code. πŸ’…

The model specifies:

  • The structure of the configuration data (which can be queried by generic user interfaces)
  • The properties of each element (boundaries check, integer or string, enum like type, default value ...)
  • The on-line help

So, in the end:

  • Maintenance and evolution of the configuration content is easier
  • User sees a common interface for all programs using this project.
  • Upgrade of configuration data is easier and sanity check is performed during the upgrade.
  • Audit of configuration is possible to check what was modified by the user compared to default values

πŸŽ€ What about the user interface ?

Config::Model interface can be:

  • a shell-like interface (plain or based on Term::ReadLine).
  • Graphical with Config::Model::TkUI (Perl/Tk interface).
  • based on curses with Config::Model::CursesUI. This interface can be handy if your X server is down.
  • Through a virtual file system where every configuration parameter is mapped to a file. (Linux only)

All these interfaces are generated from the configuration model. ✨

And configuration model can be created or modified with a graphical user interface (with "cme meta edit" once Config::Model::Itself is installed) πŸ‘‘

πŸ“ What about configuration data storage ?

Since the syntax of configuration files vary wildly form one application to another, people who want to use this framework may have to provide a dedicated parser/writer. 😭

To help with this task, this project provides writer/parsers for common format: INI style file and perl file. With the additional Config::Model::Backend::Augeas, Augeas library can be used to read and write some configuration files. See http://augeas.net for more details. 🌈

πŸ¦‹ Is there an example of a configuration model ?

The "example" directory contains a configuration model example for "/etc/fstab" file. This example includes a small program that use this model to show some ways to extract configuration information. πŸŽ€


πŸ“§ Mailing lists πŸ“§

For more question, please send a mail to:

config-model-users at lists.sourceforge.net

πŸ“š Suggested reads to start πŸ“š

🌱 Beginners

  • Config::Model::Manual::ModelCreationIntroduction
  • Config::Model::Cookbook::CreateModelFromDoc

πŸ”₯ Advanced

  • Config::Model::models::Itself::Class: This doc and its siblings describes all parameters available to create a model. These are the parameters available in the GUI launched by "cme meta edit" command.
  • Config::Model::Manual::ModelCreationAdvanced

πŸ‘‘ Masters

use the source, Luke


πŸ›‘ STOP πŸ›‘

The documentation below is quite detailed and is more a reference doc regarding "Config::Model" class. πŸ’…

For an introduction to model creation, please check: Config::Model::Manual::ModelCreationIntroduction 🫢


πŸ’Ύ Storage backend, configuration reader and writer πŸ’Ύ

See Config::Model::BackendMgr for details


βœ… Validation engine βœ…

"Config::Model" provides a way to get a validation engine from a set of rules. This set of rules is called the configuration model. πŸ‘‘


πŸ–₯️ User interface πŸ–₯️

The user interface uses some parts of the API to set and get configuration values. More importantly, a generic user interface needs to analyze the configuration model to be able to generate at run-time relevant configuration screens. ✨

A command line interface is provided in this module. Curses and Tk interfaces are provided by Config::Model::CursesUI and Config::Model::TkUI. πŸŽ€


πŸ› οΈ Constructor πŸ› οΈ

my $model = Config::Model -> new ;

creates an object to host your model. πŸ’…

🌟 Constructor parameters 🌟

log_level

Specify minimal log level. Default is "WARN". Can be "INFO", "DEBUG" or "TRACE" to get more logs. Can also be "ERROR" to get less traces. 😭

This parameter is used to override the log level specified in log configuration file. πŸŽ€

πŸ’– Configuration Model structure πŸ’–

Okay bestie, real talk: to validate a configuration tree, you gotta create a configuration model that defines all the properties of the validation engine you want to create. That's the whole assignment. ✨

And the model itself is expressed in a declarative form β€” basically a Perl data structure, which (ngl) should be easier to maintain than a whole lotta code. πŸ’…

Each configuration class may contain a set of:

  • node elements that refer to another configuration class πŸ¦‹
  • value elements that contain actual configuration data πŸ“
  • list or hash elements that also contain several node or value elements 🌸

The structure of your configuration tree is shaped by the set of configuration classes that are used in node elements. Periodt. πŸ‘‘

And the structure of the configuration data must be based on a tree structure. Which, honestly, is giving major benefits:

  • Unique path to get to a node or a leaf. πŸŽ€
  • Simpler exploration and query πŸ”
  • Simple hierarchy. Deletion of configuration items is simpler to grasp: when you cut a branch, all the leaves attached to that branch go down. πŸ’€πŸŒΏ

But bestie, using a tree has also some drawbacks, and we do not gatekeep the downsides:

  • A complex configuration cannot be mapped on a tree. Some more relation between nodes and leaves must be added. 😭
  • A configuration may actually be structured as a graph instead as a tree (for instance, any configuration that maps a service to a resource). The graph relation must be decomposed in a tree with special reference relations that complete the tree to form a graph. See "Value Reference" in Config::Model::Value. 🌈

Note: a configuration tree is a tree of objects. The model is declared with classes. The classes themselves have relations that closely match the relation of the object of the configuration tree. But the class need not to be declared in a tree structure (always better to reuse classes). But they must be declared as a DAG (directed acyclic graph). See also Directed acyclic graph on Wikipedia at http://en.wikipedia.org/wiki/Directed_acyclic_graph β€” "More on DAGs" πŸ”₯


🌸 Configuration Model declaration 🌸

Each configuration class declaration specifies:

  • The "name" of the class (mandatory, no skipping this one) πŸ‘‘
  • A "class_description" used in user interfaces (optional)
  • Optional include specification to avoid duplicated declaration of elements.
  • The class elements (usually matching configuration parameters)

Each element specifies:

  • First and foremost, the type of the element (mostly "leaf", or "node") πŸ’…
  • The properties of each element (boundaries, check, integer or string, enum like type ...)
  • The default values of parameters (if any)
  • Whether the parameter is mandatory
  • On-line help (for each parameter or value of parameter)

"Configuration class declaration" in Config::Model::Node provides more details on how to declare a configuration class. 🫢

Example:

$ cat lib/Config/Model/models/Popcon.pl
use strict;
use warnings;

return [
  {
    'name' => 'PopCon',
    'author' => ['Dominique Dumont'],
    'copyright' => ['2010,2011 Dominique Dumont'],
    'license' => 'LGPL2',
    'element' => [
      'PARTICIPATE',
      {
        'description' => 'If you don\'t want to participate [...]',
        'type' => 'leaf',
        'upstream_default' => '0',
        'value_type' => 'boolean',
        'write_as' => ['no', 'yes']
      },
      'ENCRYPT',
      {
        'choice' => ['no', 'maybe', 'yes'],
        'description' => 'encrypt popcon submission.',
        'help' => {
          'maybe' => 'encrypt if gpg is available',
          'yes' => 'try to encrypt and fail if gpg is not available'
        },
        'summary' => 'support for encrypted submissions',
        'type' => 'leaf',
        'upstream_default' => 'no',
        'value_type' => 'enum'
      },
      # [etc ...]
    ],
    'rw_config' => {
      'backend' => 'ShellVar',
      'config_dir' => '/etc',
      'file' => 'popularity-contest.conf'
    }
  }
];

πŸ’… Configuration instance methods πŸ’…

A configuration instance is created from a model and is the starting point of a configuration tree. It's literally the main character of your whole setup. ✨

πŸ¦‹ instance

An instance must be created with a model name (using the root class name) or an application name (as shown by cme list command).

For example:

my $model = Config::Model->new() ;
$model->instance( application => 'approx');

Or:

my $model = Config::Model->new() ;
# note that the model class is slightly different compared to
# application name
$model->instance( root_class_name => 'Approx');

A custom configuration class can also be used with root_class_name parameter:

my $model = Config::Model->new() ;
# create_config_class is described below
$model ->create_config_class (
  name => "SomeRootClass",
  element => [ ...  ]
) ;

# instance name is 'default'
my $inst = $model->instance (root_class_name => 'SomeRootClass');

You can create several separated instances from a model using name option:

# instance name is 'default'
my $inst = $model->instance (
  root_class_name => 'SomeRootClass',
  name            => 'test1'
);

Usually, model files are loaded automatically using a path matching root_class_name (e.g. configuration class Foo::Bar is stored in Foo/Bar.pl). You can choose to specify the file containing the model with model_file parameter. This is mostly useful for tests. πŸ§ͺ

The instance method can also retrieve an instance that has already been created:

my $inst = $model->instance( name => 'test1' );

πŸŽ€ get_instance

Retrieve an existing instance using its name.

my $inst = $model->get_instance('test1' );

⭐ has_instance

Check if an instance name already exists

  my $maybe = $model->has_instance('test1');

πŸ’€ delete_instance

Delete an instance. (yep, that's the whole vibe, it just deletes it)

πŸ‘‘ cme

This method is syntactic sugar for short program. It creates a new Config::Model object and returns a new instance.

cme arguments are passed to instance method, except force-load.

Like cme command, cme functions accepts force-load parameters. When this argument is true, the instance is created with <check = 'no'>. Hence bad values are stored in cme and must be corrected before saving back the data. ⚠️


πŸ¦‹ Configuration class πŸ¦‹

A configuration class is made of series of elements which are detailed in Config::Model::Node.

Whatever its type (node, leaf,... ), each element of a node has several other properties:

class_description

Description of the configuration class. This description is used while generating user interfaces. πŸ’–

generated_by

Mention with a descriptive string if this class was generated by a program. This parameter is currently reserved for Config::Model::Itself model editor.

include

Include element description from another class.

  include => 'AnotherClass' ,

or

  include => [qw/ClassOne ClassTwo/]

In a configuration class, the order of the element is important. For instance if foo is warped by bar, you must declare bar element before foo.

When including another class, you may wish to insert the included elements after a specific element of your including class:

  # say AnotherClass contains element xyz
  include => 'AnotherClass' ,
  include_after => "foo" ,
  element => [ bar => ... , foo => ... , baz => ... ]

Now the element of your class are:

  ( bar , foo , xyz , baz )

Note that include may not clobber an existing element. 🚫

include_backend

Include read/write specification from another class.

  include_backend => 'AnotherClass' ,

or

  include_backend => [qw/ClassOne ClassTwo/]

Note that include may not clobber an existing read/write specification. 🚫

create_config_class

This method creates configuration classes. The parameters are described above and are forwarded to Config::Model::Node constructor. See "Configuration class declaration" in Config::Model::Node for more details on configuration class parameters.

Example:

  my $model = Config::Model -> new ;

  $model->create_config_class
  (
   config_class_name => 'SomeRootClass',
   description       => [ X => 'X-ray' ],
   level             => [ 'tree_macro' => 'important' ] ,
   class_description => "SomeRootClass description",
   element           => [ ... ]
  ) ;

For convenience, level and description parameters can also be declared within the element declaration:

  $model->create_config_class
  (
   config_class_name => 'SomeRootClass',
   class_description => "SomeRootClass description",
   'element' => [
     tree_macro => { level => 'important'},
     X          => { description => 'X-ray', } ,
   ]
  ) ;

🌈 Load predeclared model 🌈

You can also load predeclared model. Because why declare when someone already did? πŸ’…

πŸ“‚ load( <model_name> )

This method opens the model directory and execute a .pl file containing the model declaration,

This perl file must return an array ref to declare models. E.g.:

[
 [
  name => 'Class_1',
  element => [ ... ]
 ],
 [
  name => 'Class_2',
  element => [ ... ]
 ]
];
do not put 1; at the end or load will not work ⚠️

When a model name contain a :: (e.g Foo::Bar), load looks for a file named Foo/Bar.pl.

This method also searches in Foo/Bar.d directory for additional model information. Model snippet found there are loaded with augment_config_class. 🧚

Returns a list containing the names of the loaded classes. For instance, if Foo/Bar.pl contains a model for Foo::Bar and Foo::Bar2, load returns ( 'Foo::Bar' , 'Foo::Bar2' ).

🌟 augment_config_class (name => '...', class_data )

Enhance the feature of a configuration class. This method uses the same parameters as create_config_class. See "Model Plugin" in Config::Model::Manual::ModelCreationAdvanced for more details on creating model plugins. ✨

πŸ” Model query πŸ”

  • model β€” bestie, this hands you back a hash holding the model declaration of whatever model name you passed in. Heads up though: do not go in there and start editing stuff β€” the contents of that returned data structure are off-limits. Look, don't touch. πŸ™ˆ
my $cloned = $model->model('Foo');
  • get_model_clone β€” literally the same as model, returns a hash with the model declaration of the model name you passed... except this time you get it in a deep clone of the data structure. So you can mess with it all you want, the original will never know. πŸ’…
my $cloned = $model->get_model_clone('Foo');
  • generate_doc ( top_class_name , directory , [ \%done ] ) β€” generates POD documentation for the configuration class top_class_name and every class that top_class_name uses, then writes all of it into the directory you specified. The \%done bit is an optional reference to a hash, and its whole job is keeping you from documenting the same thing twice when you call this method more than once. Reuse protection, iconic. ✨
  • get_element_model( config_class_name , element) β€” gives you back a hash with the model declaration for the class and element you asked about. πŸŽ€
  • get_element_name( class => Foo ) β€” grabs you all the names of the elements of class Foo.
  • get_element_property β€” returns the property of an element, straight from the model. The parameters it wants are:
    • class
    • element
    • property
  • list_class_element β€” returns a string listing every class and element. Perfect for debugging your configuration model when it's acting up. πŸ”₯

πŸ›‘ Error handling πŸ›‘

Errors here are handled with an exception mechanism. Periodt.

When a strongly typed Value object gets an authorized value, it raises an exception. And if nobody catches that exception? The program exits. πŸ’€

Peep Config::Model::Exception for the full deets on all the various exception classes that ship with Config::Model.


πŸ“ Logging πŸ“

See "Logging" in cme.

πŸͺ΅ initialize_log4perl πŸͺ΅

Call this method and it loads your Log::Log4perl configuration from ~/.log4config-model, or from the /etc/log4config-model.conf files, or from the default configuration at https://github.com/dod38fr/config-model/blob/master/lib/Config/Model/log4perl.conf. Options, options, options. πŸ’…

It also accepts a verbose parameter β€” that's a list of log classes that get added onto the log4perl configuration it read above.

So, for instance, if you go verbose => 'Loader', log4perl gets initialised with:

log4perl.logger.Verbose.Loader = INFO, PlainMsgOnScreen

And likewise, if you're feeling extra and go verbose => [ 'Loader', 'Foo' ], log4perl gets initialised with:

log4perl.logger.Verbose.Loader = INFO, PlainMsgOnScreen
log4perl.logger.Verbose.Foo    = INFO, PlainMsgOnScreen

⚠️ Currently, this module supports only Loader as a verbose parameter. That's the whole guest list, bestie.


πŸ› BUGS πŸ›

Murphy's law says the author is fairly confident you will find bugs or that some features are just... missing. πŸ₯² Please report them to https://github.com/dod38fr/config-model/issues. The author gets notified, and then you automatically get notified of all the progress on your bug. Group chat behavior, honestly. 🫢


πŸ’¬ FEEDBACK πŸ’¬

Feedback from users is highly desired. If you find this module useful, please share your use cases and success stories with the author or with the config-model-users mailing list. We wanna hear it. πŸ‘‘


🌟 PROJECT FOUNDER 🌟

Dominique Dumont, ddumont@cpan.org


🫢 CREDITS 🫢

Contributors to this project, listed in alphabetical order:

  • Harley Pig
  • Ilya Arosov
  • Jose Luis Perez Diez
  • Krzysztof Tyszecki
  • Mathieu Arnold
  • Mohammad S Anwar
  • Topi Miettinen

Many thanks for your help πŸ’–


πŸ‘€ SEE ALSO πŸ‘€

🧩 Model elements 🧩

The arrow shows inheritance between classes:

  • Config::Model::Node <- Config::Model::AnyThing
  • Config::Model::HashId <- Config::Model::AnyId <- Config::Model::AnyThing
  • Config::Model::ListId <- Config::Model::AnyId <- Config::Model::AnyThing
  • Config::Model::Value <- Config::Model::AnyThing
  • Config::Model::CheckList <- Config::Model::AnyThing
  • Config::Model::WarpedNode <- Config::Model::AnyThing

⌨️ command line ⌨️

  • cme πŸ–₯️

πŸ“₯ Read and write backends πŸ“₯

  • Config::Model::Backend::Fstab <- Config::Model::Backend::Any
  • Config::Model::Backend::IniFile <- Config::Model::Backend::Any
  • Config::Model::Backend::PlainFile <- Config::Model::Backend::Any
  • Config::Model::Backend::ShellVar <- Config::Model::Backend::Any

πŸ› οΈ Model utilities πŸ› οΈ

  • Config::Model::Annotation
  • Config::Model::BackendMgr β€” used by the Config::Model::Node object
  • Config::Model::Describe
  • Config::Model::Dumper
  • Config::Model::DumpAsData
  • Config::Model::IdElementReference
  • Config::Model::Iterator
  • Config::Model::Loader
  • Config::Model::ObjTreeScanner
  • Config::Model::Report
  • Config::Model::Searcher β€” search element in configuration model.
  • Config::Model::SimpleUI
  • Config::Model::TreeSearcher β€” search string or regexp in configuration tree.
  • Config::Model::TermUI
  • Config::Model::Iterator β€” yes bestie, Config::Model::Iterator is listed twice in the original and I'm not about to erase that kind of chaotic energy. 🌈
  • Config::Model::ValueComputer
  • Config::Model::Warper

πŸ§ͺ Test framework πŸ§ͺ

  • Config::Model::Tester

✍️ 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

πŸ†˜ SUPPORT πŸ†˜

🌐 Websites 🌐

The following websites have more information about this module, and may be of help to you. As always, in addition to those websites please use your favorite search engine to discover more resources.

🐞 Bugs / Feature Requests 🐞

Please report any bugs or feature requests by email to ddumont at cpan.org, or through the web interface at https://github.com/dod38fr/config-model/issues. You will be automatically notified of any progress on the request by the system. πŸ’Œ

πŸ’Ύ Source Code πŸ’Ύ

The code is open to the world, and available for you to hack on. Please feel free to browse it and play with it, or whatever. If you want to contribute patches, please send me a diff or prod me to pull from your repository :)

http://github.com/dod38fr/config-model

git clone git://github.com/dod38fr/config-model.git

and that's a wrap on the whole page, bestie πŸŽ€ this one landed as perl v5.42.3, dated 2026-07-23, and it lives at Config::Model(3pm) β€” now go configure something cute. πŸ’–βœ¨

πŸ“¦ where this page comes from