Badger...system(3pm)

πŸ“¦
version 0.16 Β· 4 versions
, in debian trixie, ubuntu noble, ubuntu resolute

πŸ’– Badger::Config::Filesystem πŸ’–

okay bestie, gather round β€” this is the Badger::Config::Filesystem page from the User Contributed Perl Documentation, and it's literally just "read your config files out of a folder, but make it fashion." πŸ’…


✨ NAME ✨

Badger::Config::Filesystem β€” reads configuration files in a directory. That's it, that's the whole vibe. πŸŽ€


🌸 SYNOPSIS 🌸

use Badger::Config::Filesystem;

my $config = Badger::Config::Filesystem->new(
    root => 'path/to/some/dir'
);

# Fetch the data in user.[yaml|json] in above dir
my $user = $config->get('user')
    || die "user: not found";

# Fetch sub-data items using dotted syntax
print $config->get('user.name');
print $config->get('user.emails.0');

not the dotted syntax doing the absolute most 😭 user.emails.0 bestie, it's giving "I know exactly where I live." ✨


πŸ¦‹ DESCRIPTION πŸ¦‹

So this module is a subclass of Badger::Config whose whole personality is reading data from configuration files that live in a directory. πŸ—‚οΈ

Picture a directory with these files and sub-directories in it:

config/
    site.yaml
    style.yaml
    pages.yaml
    pages/
        admin.yaml
        developer.yaml

You make a Badger::Config::Filesystem object to read all that config data, like so:

my $config = Badger::Config::Filesystem->new(
    root => 'config'
);

Reading the data out of site.yaml? Could not be easier, bestie:

my $site = $config->get('site');

Now here's the gag: you do not need to type the file extension. You can have a site.yaml or a site.json sitting in that directory and the module will load whichever one it finds first. πŸ‘‘ And if you want to use something that isn't YAML or JSON, you can add other data codecs. Periodt.

You can also reach inside a configuration file. Say site.yaml looks like this:

name:    My Site
version: 314
author:
  name:  Andy Wardley
  email: abw@wardley.org

Then you can read the version and the author name like so:

print $config->get('site.version');
print $config->get('author.name');

🧚 the sub-directory situation 🧚

If the configuration directory has a sub-directory with the same name as the data file being loaded (minus the extension), then every file under that directory gets loaded too. Going back to our earlier example, the pages item is exactly that case:

config/
    site.yaml
    style.yaml
    pages.yaml
    pages/
        admin.yaml
        developer.yaml

So there are three files relevant to pages here. Let's assume the content of each is as follows:

pages.yaml:

one:        Page One
two:        Page Two

pages/admin.yaml:

three:      Page Three
four:       Page Four

pages/developer.yaml:

five:       Page Five

When we load the pages data like so:

my $pages = $config->get('pages');

We end up with a data structure like this:

{
    one   => 'Page One',
    two   => 'Page Two',
    admin => {
        three => 'Page Three',
        four  => 'Page Four',
    },
    developer => {
        five  => 'Page Five',
    },
}

See how the admin and developer items got nested into the data? πŸͺ† The filename base (e.g. admin, developer) is used to define an entry in the parent hash array, and that entry holds the data from the child data file. It's giving nesting dolls, honestly.

🌈 tree_type: the glow-up options 🌈

The tree_type option changes the way this data gets merged. To use it, you put it in a schema section in the top level configuration file, e.g. the pages.yaml:

pages.yaml:

one:        Page One
two:        Page Two
schema:
  tree_type: flat

flat πŸ“

If you don't want the data nested at all, specify a flat value for tree_type. That returns the following data:

{
    one   => 'Page One',
    two   => 'Page Two',
    three => 'Page Three',
    four  => 'Page Four',
    five  => 'Page Five',
}

join πŸ”₯

The join type collapses the nested data files by joining the file path (without extension) onto the data items contained therein. e.g.

{
    one             => 'Page One',
    two             => 'Page Two',
    admin_three     => 'Page Three',
    admin_four      => 'Page Four',
    developer_five  => 'Page Five',
}

You can specify a different character sequence to join paths with via the tree_joint option, e.g.

schema:
  tree_type:  join
  tree_joint: '-'

That would produce this data structure:

{
    one             => 'Page One',
    two             => 'Page Two',
    admin-three     => 'Page Three',
    admin-four      => 'Page Four',
    developer-five  => 'Page Five',
}

uri ⭐

The uri type is a slightly smarter version of the join type. It joins path elements with the / character to create URI paths.

{
    one             => 'Page One',
    two             => 'Page Two',
    admin/three     => 'Page Three',
    admin/four      => 'Page Four',
    developer/five  => 'Page Five',
}

What makes it special is that it follows the standard rules for URI resolution and recognises a path with a leading slash to be absolute rather than relative to the current location. πŸ‘‘

For example, the pages/admin.yaml file could contain something like this:

pages/admin.yaml:

three:      Page Three
/four:      Page Four

The three entry is considered to be relative to the admin file so it results in a final path of admin/three, same as before. But /four is an absolute path, so the admin path gets ignored entirely. The end result is a data structure like this:

{
    one             => 'Page One',
    two             => 'Page Two',
    admin/three     => 'Page Three',
    /four           => 'Page Four',
    developer/five  => 'Page Five',
}

And in this example we've ended up with an annoying inconsistency 😭 because our /four path has a leading slash when the other items don't. Rude. The uri_paths option can be set to relative or absolute to remove or add leading slashes respectively, effectively standardising all paths as one or the other.

schema:
  tree_type:  uri
  uri_paths:  absolute

The data would then be returned like so:

{
    /one            => 'Page One',
    /two            => 'Page Two',
    /admin/three    => 'Page Three',
    /four           => 'Page Four',
    /developer/five => 'Page Five',
}

consistency slay ✨


πŸ’… CONFIGURATION OPTIONS πŸ’…

🌸 root / directory / dir 🌸

The root (or directory or dir if you prefer) option must be provided to specify the directory that the module should load configuration files from. Directories can be specified as absolute paths or relative to the current working directory.

my $config = Badger::Config::Filesystem->new(
    dir => 'path/to/config/dir'
);

three names, one option, choose your fighter 🫢

πŸŽ€ data πŸŽ€

Any additional configuration data can be provided via the data named parameter:

my $config = Badger::Config::Filesystem->new(
    dir  => 'path/to/config/dir'
    data => {
        name  => 'Arthur Dent',
        email => 'arthur@dent.org',
    },
);

πŸ¦‹ encoding πŸ¦‹

The character encoding of the configuration files. Defaults to utf8.

πŸ“ extensions πŸ“

A list of file extensions to try in addition to yaml and json. Note that you may also need to define a codecs entry to map the file extension to a data encoder/decoder module.

my $config = Badger::Config::Filesystem->new(
    dir        => 'path/to/config/dir'
    extensions => ['str'],
    codecs     => {
        str    => 'storable',
    }
);

πŸ’œ codecs πŸ’œ

File extensions like .yaml and .json are recognised by Badger::Codecs, which can then provide the appropriate Badger::Codec module to handle the encoding and decoding of data in the file. The codecs option can be used to provide a mapping from other file extensions to Badger::Codec modules.

my $config = Badger::Config::Filesystem->new(
    dir        => 'path/to/config/dir'
    extensions => ['str'],
    codecs     => {
        str    => 'storable',   # *.str files loaded via storable codec
    }
);

You may need to write a simple codec module yourself if there isn't one for the data format you want, but it's usually just a few lines of code required to provide the Badger::Codec wrapper module around whatever other Perl module or custom code you're using to load and save the data format. iykyk 🧚

🌈 schemas 🌈

TODO: document specification of item schemas. The items below (tree_type through uri_paths) must now be defined in a schema. Support for a default schema has temporarily been disabled/broken.

not the docs being honest about being broken πŸ’€ iconic honestly.

πŸ‘‘ tree_type πŸ‘‘

This option can be used to set the default tree type for any configuration items that don't explicitly declare it by other means. The default tree type is nest.

NOTE: this has been changed. Don't trust these docs.

bestie said don't trust me 😭 okay but here are the supported tree types anyway:

nest

This is the default tree type, creating nested hash arrays of data.

flat

Creates a flat hash array by merging all nested hash arrays of data into one.

join

Joins data paths together using the tree_joint string which is _ by default.

uri

Joins data paths together using slash characters to create URI paths. An item in a sub-directory can have a leading slash (i.e. an absolute path) and it will be promoted to the top-level data hash.

e.g.

foo/bar + baz  = foo/bar/baz
foo/bar + /bam = /bam

none

No tree is created. No sub-directories are scanned. You never saw me. I wasn't here. πŸ«₯

✨ tree_joint ✨

This option can be used to set the default character sequence for joining paths.

πŸŽ€ uri_paths πŸŽ€

This option can be used to set the default uri_paths option for joining paths as URIs. It should be set to relative or absolute. It can be over-ridden in a schema section of a top-level configuration file.


🫢 METHODS 🫢

The module inherits all methods defined in the Badger::Config and Badger::Workplace base classes.


okay and there's more, so don't go anywhere bestie β€” πŸŒΈπŸ¦‹βœ¨

✨ INTERNAL METHODS ✨

These are the internal-use methods, bestie. Like, you're not really supposed to call them from the outside, but here's the tea anyway. β˜•οΈπŸ’…

init($config)

This overrides the default initialisation method inherited from Badger::Config. It calls the init_config() method to perform the base class Badger::Config initialisation and then the init_filesystem() method to perform initialisation specific to the Badger::Config::Filesystem module.

init_filesystem($config)

This performs the initialisation of the object specific to the filesystem object.

head($item)

This redefines the head() method in the Badger::Config base class. The method is called by get() to fetch a top-level data item (e.g. "user" in $config->get('user.name')). This implementation looks for existing data items as usual, but additionally falls back on a call to fetch($item) to load additional data (or attempt to load it).

tail($item, $data)

This is a do-nothing stub for subclasses to redefine. It is called after a successful call to fetch().

fetch($item)

This is the main method called to load a configuration file (or tree of files) from the filesystem. It looks to see if a configuration file (with one of the known extensions appended, e.g. $item.yaml, $item.json, etc) exists and/or a directory named $item.

If the file exists but the directory doesn't then the configuration data is read from the file. If the directory exists

config_tree($item, $file, $dir)

This scans a configuration tree comprising of a configuration file and/or a directory. The $file and $dir arguments are optional and are only supported as an internal optimisation. The method can safely be called with a single $item argument and the relevant file and directory will be determined automatically.

The configuration file is loaded (via scan_config_file()). If the directory exists then it is also scanned (via scan_config_dir()) and the files contained therein are loaded.

scan_config_file($file, $data, $path, $schema, $binder)

Loads the data in a configuration $file and merges it into the common $data hash under the $path prefix (a reference to an array). The $schema contains any schema rules for this data item. The $binder is a reference to a tree_binder() method to handle the data merge.

scan_config_dir($dir, $data, $path, $schema, $binder)

Scans the diles in a configuration directory, $dir and recursively calls scan_config_dir() for each sub-directory found, and scan_config_file() for each file.

tree_binder($name)

This method returns a reference to one of the binder methods below based on the $name parameter provided.

# returns a reference to the nest_binder() method
my $binder = $config->tree_binder('nest');

If no $name is specified then it uses the default "tree_type" of "nest". This can be changed via the tree_type configuration option.

nest_tree_binder($parent, $path, $child, $schema)

This handles the merging of data for the nest tree_type.

flat_tree_binder($parent, $path, $child, $schema)

This handles the merging of data for the flat tree_type.

uri_tree_binder($parent, $path, $child, $schema)

This handles the merging of data for the uri tree_type.

join_tree_binder($parent, $path, $child, $schema)

This handles the merging of data for the join tree_type.

config_file($name)

This method returns a Badger::Filesystem::File object representing a configuration file in the configuration directory. It will automatically have the correct filename extension added (via a call to config_filename) and the correct "codec" and "encoding" parameters set (via a call to config_filespec) so that the data in the configuration file can be automatically loaded (see config_data($name)).

config_file_data($name)

This method fetches a configuration file via a call to config_file() and then returns the data contained therein.

config_filespec($params)

Returns a reference to a hash array containing appropriate initialisation parameters for Badger::Filesystem::File objects created to read general and resource-specific configuration files. The parameters are constructed from the "codecs" (default: "yaml") and "encoding" (default: "utf8") configuration options. These can be overridden or augmented by extra parameters passed as arguments.


πŸ‘‘ AUTHOR πŸ‘‘

Andy Wardley (http://wardley.org/)

Copyright (C) 2008-2014 Andy Wardley. All Rights Reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


That's the whole vibe, bestie. This page came from perl v5.36.0, dated 2023-08-28, and it's the Badger...system(3pm) manual. Now go slay those config files. πŸ’–βœ¨πŸ¦‹

πŸ“¦ where this page comes from