19.3. Configuration Files
If you're going to provide a configuration mechanism for your application, make it declarative and minimal. Keep in mind that configuration files are one of the few components of your system that are directly read by end-users, so they need to be simple. They're also one of the few components of your system that are directly written by end-users. So they need to be even simpler. It's almost always enough to just support some variation on the widely used INI file format: named sections, individual key/value pairs, multiline values, repeated values (or lists), and comments. Example 19-3 shows a typical configuration file with all of those features. Example 19-3. A simple configuration language > cat ~/.demorc [Interface] # Configurable bits that others will see... Author: Jan-Yu Eyrie E-mail: eju@calnet Disclaimer: This code is provided AS IS, and comes with : ABSOLUTELY NO WARRANTY OF ANY KIND WHATSOEVER! : It's buggy, slow, and will almost certainly : break your computer. Use at your own risk! [Internals] # Stuff no-one else sees... # Look-up path for plug-ins... lib: ~/lib/perl5 lib: ~/lib/perl lib: /usr/share/lib/perl [strict] # Don't allow malformed inputs [verbose] # Report every step [log] # And log every transaction Fancier features like nested or hierarchical data structures, separate syntaxes for lists and scalar values, special notations for boolean configuration variables, or character escapes, are almost always a bad idea. The extra syntax will confuse most users andworsemake it far more likely that they'll inadvertently type something that's valid, but not what they intended. Don't use XML as your configuration file format. It may be human-readable, but it's almost never human-comprehensible, and the ratio of mark-up to content is vastly too high. No-one wants to write or maintain a configuration file that looks like Example 19-4. Example 19-4. An XML-based configuration language> cat ~/.demoxml <section name="Interface"> <!-- Configurable bits that others will see... --> <var> <name>author</name> <value>Jan-Yu Eyrie</value> </var> <var> <name>e-mail</name> <value>eju@calnet</value> </var> <var> <name>disclaimer</name> <value> This code is provided AS IS, and comes with ABSOLUTELY NO WARRANTY OF ANY KIND WHATSOEVER! It's buggy, slow, and will almost certainly break your computer. Use at your own risk! </value> </var> </section> <section name="Internals"> <!-- Stuff no-one else sees... --> <!-- Look-up path for plug-ins... --> <var> <name>lib</name> <value> <list> <item>~/lib/perl5</item> <item>~/lib/perl</item> <item>~/lib/perl5</item> <item>/usr/share/lib/perl</item> </list> </value> </var> </section> <section name="strict"> <!-- Don't allow malformed inputs --> </section> <section name="verbose"> <!-- Report</comment --> </section> <section name="log"> <!-- Report every step --> </section> Whatever format you choose, don't ever parse configuration files "manually" (i.e., with readlines and regexes and loops and all the other associated forms of torture). Don't write your own configuration file parsing module, either; there are already far too many configuration-file tools available on CPAN (see http://search.cpan.org/search?q=Config). Evaluating the available modules to determine the best fit for your particular application is an onerous task, but in most cases it's probably sufficient to look at just three of them:
All three of these alternatives allow you to read configuration files into an internal data structure, update that data structure, and then write it back in the appropriate configuration file syntax. For example, the program: use Config::Std; would update the configuration file shown in Example 19-3, to produce the file shown in Example 19-5. Example 19-5. The configuration file, reloaded > cat ~/.demorc [Interface] # Configurable bits that others will see... Author: Jan-Yu Eyrie E-mail: eju@calnet Disclaimer: Whatever, dude! [Internals] # Stuff no-one else sees... # Look-up path for plug-ins... lib: ~/.plugins lib: /lib/share/plugins [strict] # Don't allow malformed inputs [log] # And log every transaction [Limits] max_time: 1000 max_space: 1000000 Note that Config::Std has an important advantage here compared to most other configuration-file parsers. When it writes back the configuration file, it always preserves the original comments, as well as the order in which sections and their associated configuration variables appear. |