1Config::Model(3) User Contributed Perl Documentation Config::Model(3)
2
3
4
6 Config::Model - a framework to validate, migrate and edit configuration
7 files
8
10 version 2.140
11
13 Perl program to use an existing model
14 use Config::Model qw(cme);
15 # load, modify and save popcon configuration file
16 cme('popcon')->modify("PARTICIPATE=yes");
17
18 Command line to use an existing model
19 # with App::Cme
20 cme modify popcon 'PARTICIPATE=yes'
21
22 Perl program with a custom model
23 use Config::Model;
24
25 # create new Model object
26 my $model = Config::Model->new() ; # Config::Model object
27
28 # create config model. A more complex model should be stored in a
29 # file in lib/Config/Model/models. Then, run cme as explained below
30 $model ->create_config_class (
31 name => "MiniModel",
32 element => [ [qw/foo bar baz/ ] => { type => 'leaf', value_type => 'uniline' }, ],
33 rw_config => { backend => 'IniFile', auto_create => 1,
34 config_dir => '.', file => 'mini.ini',
35 }
36 ) ;
37
38 # create instance (Config::Model::Instance object)
39 my $instance = $model->instance (root_class_name => 'MiniModel');
40
41 # get configuration tree root
42 my $cfg_root = $instance -> config_root ; # C::M:Node object
43
44 # load some dummy data
45 $cfg_root -> load("bar=BARV foo=FOOV baz=BAZV") ;
46
47 # write new ini file
48 $instance -> write_back;
49
50 # now look for new mini.ini file un current directory
51
52 Create a new model file and use it
53 $ mkdir -p lib/Config/Model/models/
54 $ echo "[ { name => 'MiniModel', \
55 element => [ [qw/foo bar baz/ ] => { type => 'leaf', value_type => 'uniline' }, ], \
56 rw_config => { backend => 'IniFile', auto_create => 1, \
57 config_dir => '.', file => 'mini.ini', \
58 } \
59 } \
60 ] ; " > lib/Config/Model/models/MiniModel.pl
61 # require App::Cme
62 $ cme modify -try MiniModel -dev bar=BARV foo=FOOV baz=BAZV
63 $ cat mini.ini
64
65 Note that model creation is easier running "cme meta edit" with
66 App::Cme and Config::Model::Itself.
67
69 Config::Model enables a project developer to provide an interactive
70 configuration editor (graphical, curses based or plain terminal) to
71 users.
72
73 To provide these tools, Config::Model needs:
74
75 · A description of the structure and constraints of the project's
76 configuration (fear not, a GUI is available with App::Cme)
77
78 · A module to read and write configuration data (aka a backend
79 class).
80
81 With the elements above, Config::Model generates interactive
82 configuration editors (with integrated help and data validation).
83 These editors can be graphical (with Config::Model::TkUI), curses based
84 (with Config::Model::CursesUI) or based on ReadLine.
85
86 Smaller models targeted for configuration upgrades can also be created:
87
88 · only upgrade and migration specifications are required
89
90 · unknown parameters can be accepted
91
92 A command line is provided to perform configuration upgrade with a
93 single command.
94
95 How does this work ?
96 Using this project, a typical configuration editor/validator/upgrader
97 is made of 3 parts :
98
99 GUI <--------> |---------------|
100 CursesUI <---> | |---------| |
101 | | Model | |
102 ShellUI <----> | |---------| |<-----read-backend------- |-------------|
103 | |----write-backend-------> | config file |
104 FuseUI <-----> | Config::Model | |-------------|
105 |---------------|
106
107 1. A reader and writer that parse the configuration file and transform
108 its data into a tree representation within Config::Model. The
109 values contained in this configuration tree can be written back in
110 the configuration file(s).
111
112 2. A validation engine which is in charge of validating the content
113 and structure of configuration stored in the configuration tree.
114 This validation engine follows the structure and constraint
115 declared in a configuration model. This model is a kind of schema
116 for the configuration tree.
117
118 3. A user interface to modify the content of the configuration tree. A
119 modification is validated immediately by the validation engine.
120
121 The important part is the configuration model used by the validation
122 engine. This model can be created or modified with a graphical editor
123 (Config::Model::Iself).
124
126 Don't we already have some configuration validation tools ?
127 You're probably thinking of tools like webmin. Yes, these tools exist
128 and work fine, but they have their set of drawbacks.
129
130 Usually, the validation of configuration data is done with a script
131 which performs semantic validation and often ends up being quite
132 complex (e.g. 2500 lines for Debian's xserver-xorg.config script which
133 handles "xorg.conf" file).
134
135 In most cases, the configuration model is expressed in instructions
136 (whatever programming language is used) and interspersed with a lot of
137 processing to handle the actual configuration data.
138
139 What's the advantage of this project ?
140 Config::Model projects provide a way to get a validation engine where
141 the configuration model is completely separated from the actual
142 processing instructions.
143
144 A configuration model can be created and modified with the graphical
145 interface provide by Config::Model::Itself. The model is saved in a
146 declarative form (currently, a Perl data structure). Such a model is
147 easier to maintain than a lot of code.
148
149 The model specifies:
150
151 · The structure of the configuration data (which can be queried by
152 generic user interfaces)
153
154 · The properties of each element (boundaries check, integer or
155 string, enum like type, default value ...)
156
157 · The targeted audience (beginner, advanced, master)
158
159 · The on-line help
160
161 So, in the end:
162
163 · Maintenance and evolution of the configuration content is easier
164
165 · User sees a *common* interface for *all* programs using this
166 project.
167
168 · Upgrade of configuration data is easier and sanity check is
169 performed during the upgrade.
170
171 · Audit of configuration is possible to check what was modified by
172 the user compared to default values
173
174 What about the user interface ?
175 Config::Model interface can be:
176
177 · a shell-like interface (plain or based on Term::ReadLine).
178
179 · Graphical with Config::Model::TkUI (Perl/Tk interface).
180
181 · based on curses with Config::Model::CursesUI. This interface can be
182 handy if your X server is down.
183
184 · Through a virtual file system where every configuration parameter
185 is mapped to a file. (Linux only)
186
187 All these interfaces are generated from the configuration model.
188
189 And configuration model can be created or modified with a graphical
190 user interface (with "cme meta edit" once Config::Model::Itself is
191 installed)
192
193 What about configuration data storage ?
194 Since the syntax of configuration files vary wildly form one
195 application to another, people who want to use this framework may have
196 to provide a dedicated parser/writer.
197
198 To help with this task, this project provides writer/parsers for common
199 format: INI style file and perl file. With the additional
200 Config::Model::Backend::Augeas, Augeas library can be used to read and
201 write some configuration files. See http://augeas.net for more details.
202
203 Is there an example of a configuration model ?
204 The "example" directory contains a configuration model example for
205 "/etc/fstab" file. This example includes a small program that use this
206 model to show some ways to extract configuration information.
207
209 For more question, please send a mail to:
210
211 config-model-users at lists.sourceforge.net
212
214 Beginners
215 · Config::Model::Manual::ModelCreationIntroduction
216
217 · Config::Model::Cookbook::CreateModelFromDoc
218
219 Advanced
220 · Config::Model::models::Itself::Class: This doc and its siblings
221 describes all parameters available to create a model. These are the
222 parameters available in the GUI launched by "cme meta edit"
223 command.
224
225 · Config::Model::Manual::ModelCreationAdvanced
226
227 Masters
228 use the source, Luke
229
231 The documentation below is quite detailed and is more a reference doc
232 regarding "Config::Model" class.
233
234 For an introduction to model creation, please check:
235 Config::Model::Manual::ModelCreationIntroduction
236
238 See Config::Model::BackendMgr for details
239
241 "Config::Model" provides a way to get a validation engine from a set of
242 rules. This set of rules is called the configuration model.
243
245 The user interface uses some parts of the API to set and get
246 configuration values. More importantly, a generic user interface needs
247 to analyze the configuration model to be able to generate at run-time
248 relevant configuration screens.
249
250 A command line interface is provided in this module. Curses and Tk
251 interfaces are provided by Config::Model::CursesUI and
252 Config::Model::TkUI.
253
255 my $model = Config::Model -> new ;
256
257 creates an object to host your model.
258
259 Constructor parameters
260 log_level
261 Specify minimal log level. Default is "WARN". Can be "INFO",
262 "DEBUG" or "TRACE" to get more logs. Can also be "ERROR" to get
263 less traces.
264
265 This parameter is used to override the log level specified in log
266 configuration file.
267
269 To validate a configuration tree, we must create a configuration model
270 that defines all the properties of the validation engine you want to
271 create.
272
273 The configuration model is expressed in a declarative form (i.e. a Perl
274 data structure which should be easier to maintain than a lot of code)
275
276 Each configuration class may contain a set of:
277
278 · node elements that refer to another configuration class
279
280 · value elements that contain actual configuration data
281
282 · list or hash elements that also contain several node or value
283 elements
284
285 The structure of your configuration tree is shaped by the a set of
286 configuration classes that are used in node elements,
287
288 The structure of the configuration data must be based on a tree
289 structure. This structure has several advantages:
290
291 · Unique path to get to a node or a leaf.
292
293 · Simpler exploration and query
294
295 · Simple hierarchy. Deletion of configuration items is simpler to
296 grasp: when you cut a branch, all the leaves attached to that
297 branch go down.
298
299 But using a tree has also some drawbacks:
300
301 · A complex configuration cannot be mapped on a tree. Some more
302 relation between nodes and leaves must be added.
303
304 · A configuration may actually be structured as a graph instead as a
305 tree (for instance, any configuration that maps a service to a
306 resource). The graph relation must be decomposed in a tree with
307 special reference relations that complete the tree to form a graph.
308 See "Value Reference" in Config::Model::Value
309
310 Note: a configuration tree is a tree of objects. The model is declared
311 with classes. The classes themselves have relations that closely match
312 the relation of the object of the configuration tree. But the class
313 need not to be declared in a tree structure (always better to reuse
314 classes). But they must be declared as a DAG (directed acyclic graph).
315 See also Directed acyclic graph on Wikipedia
316 <http://en.wikipedia.org/wiki/Directed_acyclic_graph">More on DAGs>
317
318 Each configuration class declaration specifies:
319
320 · The "name" of the class (mandatory)
321
322 · A "class_description" used in user interfaces (optional)
323
324 · Optional include specification to avoid duplicate declaration of
325 elements.
326
327 · The class elements
328
329 Each element specifies:
330
331 · Most importantly, the type of the element (mostly "leaf", or
332 "node")
333
334 · The properties of each element (boundaries, check, integer or
335 string, enum like type ...)
336
337 · The default values of parameters (if any)
338
339 · Whether the parameter is mandatory
340
341 · Targeted audience (beginner, advance, master), i.e. the level of
342 expertise required to tinker a parameter (to hide expert parameters
343 from newbie eyes)
344
345 · On-line help (for each parameter or value of parameter)
346
347 See Config::Model::Node for details on how to declare a configuration
348 class.
349
350 Example:
351
352 $ cat lib/Config/Model/models/Xorg.pl
353 [
354 {
355 name => 'Xorg',
356 class_description => 'Top level Xorg configuration.',
357 include => [ 'Xorg::ConfigDir'],
358 element => [
359 Files => {
360 type => 'node',
361 description => 'File pathnames',
362 config_class_name => 'Xorg::Files'
363 },
364 # snip
365 ]
366 },
367 {
368 name => 'Xorg::DRI',
369 element => [
370 Mode => {
371 type => 'leaf',
372 value_type => 'uniline',
373 description => 'DRI mode, usually set to 0666'
374 }
375 ]
376 }
377 ];
378
380 A configuration instance is created from a model and is the starting
381 point of a configuration tree.
382
383 instance
384 An instance must be created with a model name (using the root class
385 name) or an application name (as shown by "cme "list"" command).
386
387 For example:
388
389 my $model = Config::Model->new() ;
390 $model->instance( application => 'approx');
391
392 Or:
393
394 my $model = Config::Model->new() ;
395 # note that the model class is slightly different compared to
396 # application name
397 $model->instance( root_class_name => 'Approx');
398
399 A custom configuration class can also be used with "root_class_name"
400 parameter:
401
402 my $model = Config::Model->new() ;
403 # create_config_class is described below
404 $model ->create_config_class (
405 name => "SomeRootClass",
406 element => [ ... ]
407 ) ;
408
409 # instance name is 'default'
410 my $inst = $model->instance (root_class_name => 'SomeRootClass');
411
412 You can create several separated instances from a model using "name"
413 option:
414
415 # instance name is 'default'
416 my $inst = $model->instance (
417 root_class_name => 'SomeRootClass',
418 name => 'test1'
419 );
420
421 Usually, model files are loaded automatically using a path matching
422 "root_class_name" (e.g. configuration class "Foo::Bar" is stored in
423 "Foo/Bar.pl". You can choose to specify the file containing the model
424 with "model_file" parameter. This is mostly useful for tests.
425
426 The "instance" method can also retrieve an instance that has already
427 been created:
428
429 my $inst = $model->instance( name => 'test1' );
430
431 get_instance
432 Retrieve an existing instance using its name.
433
434 my $inst = $model->get_instance('test1' );
435
436 has_instance
437 Check if an instance name already exists
438
439 my $maybe = $model->has_instance('test1');
440
441 cme
442 This method is syntactic sugar for short program. It creates a new
443 "Config::Model" object and returns a new instance. See "instance" for
444 the parameters.
445
447 A configuration class is made of series of elements which are detailed
448 in Config::Model::Node.
449
450 Whatever its type (node, leaf,... ), each element of a node has several
451 other properties:
452
453 level
454 Level is "important", "normal" or "hidden".
455
456 The level is used to set how configuration data is presented to the
457 user in browsing mode. "Important" elements are shown to the user
458 no matter what. "hidden" elements are well, hidden. Their purpose
459 is explained with the warp notion.
460
461 status
462 Status is "obsolete", "deprecated" or "standard" (default).
463
464 Using a deprecated element raises a warning. Using an obsolete
465 element raises an exception.
466
467 description
468 Description of the element. This description is used while
469 generating user interfaces.
470
471 summary
472 Summary of the element. This description is used while generating a
473 user interfaces and may be used in comments when writing the
474 configuration file.
475
476 class_description
477 Description of the configuration class. This description is used
478 while generating user interfaces.
479
480 generated_by
481 Mention with a descriptive string if this class was generated by a
482 program. This parameter is currently reserved for
483 Config::Model::Itself model editor.
484
485 include
486 Include element description from another class.
487
488 include => 'AnotherClass' ,
489
490 or
491
492 include => [qw/ClassOne ClassTwo/]
493
494 In a configuration class, the order of the element is important.
495 For instance if "foo" is warped by "bar", you must declare "bar"
496 element before "foo".
497
498 When including another class, you may wish to insert the included
499 elements after a specific element of your including class:
500
501 # say AnotherClass contains element xyz
502 include => 'AnotherClass' ,
503 include_after => "foo" ,
504 element => [ bar => ... , foo => ... , baz => ... ]
505
506 Now the element of your class are:
507
508 ( bar , foo , xyz , baz )
509
510 Note that include may not clobber an existing element.
511
512 include_backend
513 Include read/write specification from another class.
514
515 include_backend => 'AnotherClass' ,
516
517 or
518
519 include_backend => [qw/ClassOne ClassTwo/]
520
521 Note that include may not clobber an existing read/write specification.
522
523 create_config_class
524 This method creates configuration classes. The parameters are described
525 above and are forwarded to Config::Model::Node constructor. See
526 "Configuration class declaration" in Config::Model::Node for more
527 details on configuration class parameters.
528
529 Example:
530
531 my $model = Config::Model -> new ;
532
533 $model->create_config_class
534 (
535 config_class_name => 'SomeRootClass',
536 description => [ X => 'X-ray' ],
537 level => [ 'tree_macro' => 'important' ] ,
538 class_description => "SomeRootClass description",
539 element => [ ... ]
540 ) ;
541
542 For convenience, "level" and "description" parameters can also be
543 declared within the element declaration:
544
545 $model->create_config_class
546 (
547 config_class_name => 'SomeRootClass',
548 class_description => "SomeRootClass description",
549 'element'
550 => [
551 tree_macro => { level => 'important'},
552 X => { description => 'X-ray', } ,
553 ]
554 ) ;
555
557 You can also load predeclared model.
558
559 load( <model_name> )
560 This method opens the model directory and execute a ".pl" file
561 containing the model declaration,
562
563 This perl file must return an array ref to declare models. E.g.:
564
565 [
566 [
567 name => 'Class_1',
568 element => [ ... ]
569 ],
570 [
571 name => 'Class_2',
572 element => [ ... ]
573 ]
574 ];
575
576 do not put "1;" at the end or "load" will not work
577
578 When a model name contain a "::" (e.g "Foo::Bar"), "load" looks for a
579 file named "Foo/Bar.pl".
580
581 This method also searches in "Foo/Bar.d" directory for additional model
582 information. Model snippet found there are loaded with
583 augment_config_class.
584
585 Returns a list containing the names of the loaded classes. For
586 instance, if "Foo/Bar.pl" contains a model for "Foo::Bar" and
587 "Foo::Bar2", "load" returns "( 'Foo::Bar' , 'Foo::Bar2' )".
588
589 augment_config_class (name => '...', class_data )
590 Enhance the feature of a configuration class. This method uses the same
591 parameters as create_config_class. See "Model Plugin" in
592 Config::Model::Manual::ModelCreationAdvanced for more details on
593 creating model plugins.
594
596 model
597 Returns a hash containing the model declaration of the passed model
598 name. Do not modify the content of the returned data structure.
599
600 my $cloned = $model->model('Foo');
601
602 get_model_clone
603 Like "model", returns a hash containing the model declaration of the
604 passed model name, this time in a deep clone of the data structure.
605
606 my $cloned = $model->get_model_clone('Foo');
607
608 generate_doc ( top_class_name , directory , [ \%done ] )
609 Generate POD document for configuration class top_class_name and all
610 classes used by top_class_name, and write them in specified directory.
611
612 "\%done" is an optional reference to a hash used to avoid writing twice
613 the same documentation when this method is called several times.
614
615 get_element_model( config_class_name , element)
616 Return a hash containing the model declaration for the specified class
617 and element.
618
619 get_element_name( class => Foo )
620 Get all names of the elements of class "Foo".
621
622 get_element_property
623 Returns the property of an element from the model.
624
625 Parameters are:
626
627 class
628 element
629 property
630
631 list_class_element
632 Returns a string listing all the class and elements. Useful for
633 debugging your configuration model.
634
636 Errors are handled with an exception mechanism.
637
638 When a strongly typed Value object gets an authorized value, it raises
639 an exception. If this exception is not caught, the programs exits.
640
641 See Config::Model::Exception for details on the various exception
642 classes provided with "Config::Model".
643
645 See "Logging" in cme
646
647 initialize_log4perl
648 This method can be called to load Log::Log4perl configuration from
649 "~/.log4config-model", or from "/etc/log4config-model.conf" files or
650 from default configuration <https://github.com/dod38fr/config-
651 model/blob/master/lib/Config/Model/log4perl.conf>.
652
653 Accepts "verbose" parameter with a list of log classes that are added
654 to the log4perl configuration read above.
655
656 For instance, with "verbose => 'Loader'", log4perl is initialised with
657
658 log4perl.logger.Verbose.Loader = INFO, PlainMsgOnScreen
659
660 Likewise, with "verbose => [ 'Loader', 'Foo' ]", log4perl is
661 initialised with:
662
663 log4perl.logger.Verbose.Loader = INFO, PlainMsgOnScreen
664 log4perl.logger.Verbose.Foo = INFO, PlainMsgOnScreen
665
666 Currently, this module supports only "Loader" as verbose parameters.
667
669 Given Murphy's law, the author is fairly confident that you will find
670 bugs or miss some features. Please report them to
671 https://github.com/dod38fr/config-model/issues The author will be
672 notified, and then you'll automatically be notified of progress on your
673 bug.
674
676 Feedback from users are highly desired. If you find this module useful,
677 please share your use cases, success stories with the author or with
678 the config-model- users mailing list.
679
681 Dominique Dumont, "ddumont@cpan.org"
682
684 In alphabetical order:
685
686 Harley Pig
687
688 Ilya Arosov
689
690 Jose Luis Perez Diez
691
692 Krzysztof Tyszecki
693
694 Mathieu Arnold
695
696 Mohammad S Anwar
697
699 Copyright (c) 2005-2016 Dominique Dumont.
700
701 Config-Model is free software; you can redistribute it and/or
702 modify it under the terms of the GNU Lesser General Public License as
703 published by the Free Software Foundation; either version 2.1 of
704 the License, or (at your option) any later version.
705
706 Config-Model is distributed in the hope that it will be useful,
707 but WITHOUT ANY WARRANTY; without even the implied warranty of
708 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
709 Lesser Public License for more details.
710
711 You should have received a copy of the GNU Lesser General Public License
712 along with Config-Model; if not, write to the Free Software
713 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
714 02110-1301 USA
715
717 Config::Model::Instance,
718
719 <https://github.com/dod38fr/config-model/wiki>
720
721 <https://github.com/dod38fr/config-model/wiki/Creating-models>
722
723 Model elements
724 The arrow shows inheritance between classes
725
726 · Config::Model::Node <- Config::Model::AnyThing
727
728 · Config::Model::HashId <- Config::Model::AnyId <-
729 Config::Model::AnyThing
730
731 · Config::Model::ListId <- Config::Model::AnyId <-
732 Config::Model::AnyThing
733
734 · Config::Model::Value <- Config::Model::AnyThing
735
736 · Config::Model::CheckList <- Config::Model::AnyThing
737
738 · Config::Model::WarpedNode <- Config::Model::AnyThing
739
740 command line
741 cme.
742
743 Read and write backends
744 · Config::Model::Backend::Fstab <- Config::Model::Backend::Any
745
746 · Config::Model::Backend::IniFile <- Config::Model::Backend::Any
747
748 · Config::Model::Backend::PlainFile <- Config::Model::Backend::Any
749
750 · Config::Model::Backend::ShellVar <- Config::Model::Backend::Any
751
752 Model utilities
753 · Config::Model::Annotation
754
755 · Config::Model::BackendMgr: Used by "Config::Model::Node" object
756
757 · Config::Model::Describe
758
759 · Config::Model::Dumper
760
761 · Config::Model::DumpAsData
762
763 · Config::Model::IdElementReference
764
765 · Config::Model::Iterator
766
767 · Config::Model::Loader
768
769 · Config::Model::ObjTreeScanner
770
771 · Config::Model::Report
772
773 · Config::Model::Searcher: Search element in configuration model.
774
775 · Config::Model::SimpleUI
776
777 · Config::Model::TreeSearcher: Search string or regexp in
778 configuration tree.
779
780 · Config::Model::TermUI
781
782 · Config::Model::Iterator
783
784 · Config::Model::ValueComputer
785
786 · Config::Model::Warper
787
788 Test framework
789 · Config::Model::Tester
790
792 Dominique Dumont
793
795 This software is Copyright (c) 2005-2020 by Dominique Dumont.
796
797 This is free software, licensed under:
798
799 The GNU Lesser General Public License, Version 2.1, February 1999
800
802 Websites
803 The following websites have more information about this module, and may
804 be of help to you. As always, in addition to those websites please use
805 your favorite search engine to discover more resources.
806
807 · CPANTS
808
809 The CPANTS is a website that analyzes the Kwalitee ( code metrics )
810 of a distribution.
811
812 <http://cpants.cpanauthors.org/dist/Config-Model>
813
814 · CPAN Testers
815
816 The CPAN Testers is a network of smoke testers who run automated
817 tests on uploaded CPAN distributions.
818
819 <http://www.cpantesters.org/distro/C/Config-Model>
820
821 · CPAN Testers Matrix
822
823 The CPAN Testers Matrix is a website that provides a visual
824 overview of the test results for a distribution on various
825 Perls/platforms.
826
827 <http://matrix.cpantesters.org/?dist=Config-Model>
828
829 · CPAN Testers Dependencies
830
831 The CPAN Testers Dependencies is a website that shows a chart of
832 the test results of all dependencies for a distribution.
833
834 <http://deps.cpantesters.org/?module=Config::Model>
835
836 Bugs / Feature Requests
837 Please report any bugs or feature requests by email to "ddumont at
838 cpan.org", or through the web interface at
839 <https://github.com/dod38fr/config-model/issues>. You will be
840 automatically notified of any progress on the request by the system.
841
842 Source Code
843 The code is open to the world, and available for you to hack on. Please
844 feel free to browse it and play with it, or whatever. If you want to
845 contribute patches, please send me a diff or prod me to pull from your
846 repository :)
847
848 <http://github.com/dod38fr/config-model>
849
850 git clone git://github.com/dod38fr/config-model.git
851
852
853
854perl v5.32.0 2020-08-02 Config::Model(3)