NAME
Badger::Config::Filesystem - reads configuration files in a directory
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');
DESCRIPTION
This module is a subclass of Badger::Config for reading data from configuration files in a directory.
Consider a directory that contains the following files and sub-directories:
config/
site.yaml
style.yaml
pages.yaml
pages/
admin.yaml
developer.yaml
We can create a Badger::Config::Filesystem object to read the configuration data from the files in this directory like so:
my $config = Badger::Config::Filesystem->new(
root => 'config'
);
Reading the data from site.yaml
is as simple as this:
my $site = $config->get('site');
Note that the file extension is not required. You can have either a site.yaml
or a site.json
file in the directory and the module will load whichever one it finds first. It's possible to add other data codecs if you want to use something other than YAML or JSON.
You can also access data from within a configuration file. If the site.yaml
file contains the following:
name: My Site
version: 314
author:
name: Andy Wardley
email: abw@wardley.org
Then we can read the version and author name like so:
print $config->get('site.version');
print $config->get('author.name');
If the configuration directory contains a sub-directory with the same name as the data file being loaded (minus the extension) then any files under that directory will also be loaded. Going back to our earlier example, the pages
item is such a case:
config/
site.yaml
style.yaml
pages.yaml
pages/
admin.yaml
developer.yaml
There are three files relevant to pages
here. Let's assume the content of each is as follow:
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',
},
}
Note how the admin
and developer
items have been nested into the data. The filename base (e.g. admin
, developer
) is used to define an entry in the "parent" hash array containing the data in the "child" data file.
The tree_type
option can be used to change the way that this data is merged. To use this option, 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
If you don't want the data nested at all then specify a flat
value for tree_type
. This would return the following data:
{
one => 'Page One',
two => 'Page Two',
three => 'Page Three',
four => 'Page Four',
five => 'Page Five',
}
The join
type collapses the nested data files by joining the file path (without extension) onto the data items contain 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 via the tree_joint
option, e.g.
schema:
tree_type: join
tree_joint: '-'
That would producing this data structure:
{
one => 'Page One',
two => 'Page Two',
admin-three => 'Page Three',
admin-four => 'Page Four',
developer-five => 'Page Five',
}
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 results in a final path of admin/three
as before. However, /four
is an absolute path so the admin
path is ignored. 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',
}
In this example we've ended up with an annoying inconsistency in that our /four
path has a leading slash when the other items don't. 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',
}
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'
);
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 options can be used to provide 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 that are required to provide the Badger::Codec wrapper module around whatever other Perl module or custom code you've using to load and save the data format.
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.
tree_type
This option can be used to sets 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.
The following tree types are supported:
nest
This is the default tree type, creating nested hash arrays of data.
flat
Creates a flat hash array by merging all nested hash array 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.
INTERNAL METHODS
The following methods are defined for internal use.
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
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.