aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Youri/Config.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Youri/Config.pm')
-rw-r--r--lib/Youri/Config.pm235
1 files changed, 235 insertions, 0 deletions
diff --git a/lib/Youri/Config.pm b/lib/Youri/Config.pm
new file mode 100644
index 0000000..dab63aa
--- /dev/null
+++ b/lib/Youri/Config.pm
@@ -0,0 +1,235 @@
+# $Id: Config.pm 961 2006-07-11 09:56:51Z guillomovitch $
+package Youri::Config;
+
+=head1 NAME
+
+Youri::Config - Youri configuration handler
+
+=head1 SYNOPSIS
+
+ use Youri::Config;
+
+ my $config = Youri::Config->new(
+ command_spec => [
+ 'help|h!',
+ ],
+ file_spec => [
+ 'foo=s',
+ ],
+ directories => [ '/etc/youri', "$ENV{HOME}/.youri" ],
+ file_name => 'app.conf',
+ caller => $0,
+ );
+
+ # get configuration directive
+ my $foo = $config->get('foo');
+
+ # get configuration section
+ my %bar = $config->get_section('bar');
+
+=head1 DESCRIPTION
+
+This class handle configuration for all YOURI tools.
+
+It uses distinct command line and config files specification, but merges the
+two inputs transparently, command line directives overriding config file
+directives with the same name.
+
+Given directories are scanned for a file with given name, and only the first
+one found is used. If B<--config> argument is given on command line, no
+scanning occurs. If no readable file is found, an exception is thrown.
+
+==head1 FORMAT
+
+The file format used is the one from AppConfig, with the additional ability to
+use YAML. Here is an exemple configuration file:
+
+ [updates]
+ class = Youri::Check::Check::Updates
+ grabbers = <<EOF
+ --- #YAML:1.0
+ debian:
+ class: Youri::Check::Check::Updates::Debian
+ aliases:
+ fuse: ~
+ cpan:
+ class: Youri::Check::Check::Updates::CPAN
+ fedora:
+ class: Youri::Check::Check::Updates::Fedora
+ gentoo:
+ class: Youri::Check::Check::Updates::Gentoo
+ freshmeat:
+ class: Youri::Check::Check::Updates::Freshmeat
+ aliases:
+ fuse: fuse-emulator
+ EOF
+
+As a side-effect of using YAML, the use of character '~' anywhere is prohibited.
+Use ${HOME} instead.
+
+=head1 SEE ALSO
+
+AppConfig, YAML
+
+=cut
+
+use strict;
+use warnings;
+use AppConfig qw/:argcount :expand/;
+use File::Spec;
+use Pod::Usage;
+use Carp;
+use YAML;
+
+sub new {
+ my ($class, %options) = @_;
+
+ my ($command_config, $file_config);
+
+ # process command line
+ if ($options{command_spec}) {
+ $command_config = AppConfig->new(
+ {
+ CREATE => 1,
+ GLOBAL => {
+ DEFAULT => '',
+ EXPAND => EXPAND_VAR | EXPAND_ENV,
+ ARGCOUNT => ARGCOUNT_ONE,
+ }
+ },
+ @{$options{command_spec}}
+ );
+ $command_config->args();
+
+ pod2usage(
+ -input => $options{caller},
+ -verbose => 0
+ ) if $command_config->get('help');
+ }
+
+ # process config file
+ $file_config = AppConfig->new(
+ {
+ CREATE => 1,
+ GLOBAL => {
+ DEFAULT => '',
+ EXPAND => EXPAND_VAR | EXPAND_ENV,
+ ARGCOUNT => ARGCOUNT_ONE,
+ }
+ },
+ @{$options{file_spec}}
+ );
+
+ # find configuration file to use
+ my $main_file;
+
+ if ($command_config) {
+ my $file = $command_config->get('config');
+ if ($file) {
+ if (! -f $file) {
+ carp "Non-existing file $file, skipping";
+ } elsif (! -r $file) {
+ carp "Non-readable file $file, skipping";
+ } else {
+ $main_file = $file;
+ }
+ };
+ }
+
+ unless ($main_file) {
+ foreach my $directory (@{$options{directories}}) {
+ my $file = "$directory/$options{file_name}";
+ next unless -f $file && -r $file;
+ $main_file = $file;
+ last;
+ }
+ }
+
+ croak 'No config file found, aborting' unless $main_file;
+ $file_config->file($main_file);
+
+ # process inclusions
+ my $need_rescan;
+ foreach my $include_file (split(/\s+/, $file_config->get('includes'))) {
+ # convert relative path to absolute ones
+ $include_file = File::Spec->rel2abs(
+ $include_file, (File::Spec->splitpath($main_file))[1]
+ );
+
+ if (! -f $include_file) {
+ warn "Non-existing file $include_file, skipping";
+ } elsif (! -r $include_file) {
+ warn "Non-readable file $include_file, skipping";
+ } else {
+ $file_config->file($include_file);
+ $need_rescan = 1;
+ }
+ }
+
+ $file_config->file($main_file) if $need_rescan;
+
+ # merge command line configuration
+ if ($command_config) {
+ my %command_vars = $command_config->varlist('.*');
+ while (my ($key, $value) = each %command_vars) {
+ $file_config->set($key, $value);
+ }
+ }
+
+ my $self = bless {
+ _appconfig => $file_config
+ }, $class;
+
+ return $self;
+}
+
+=head2 get_section($id)
+
+Simple wrapper around $config->varlist(), throwing a warning if section I<$id> doesn't exists.
+
+=cut
+
+sub get_section {
+ my ($self, $id) = @_;
+ croak "Not a class method" unless ref $self;
+
+ my %values = $self->{_appconfig}->varlist('^' . $id . '_', 1);
+
+ carp "No such section $id" unless %values;
+
+ foreach my $value (values %values) {
+ $value = _yamlize($value);
+ }
+
+ return %values;
+}
+
+sub get {
+ my ($self, $variable) = @_;
+ croak "Not a class method" unless ref $self;
+
+ return _yamlize($self->{_appconfig}->get($variable));
+}
+
+sub _yamlize {
+ my ($value) = @_;
+
+ if ($value =~ /^--- #YAML:1.0/) {
+ eval {
+ $value = Load($value . "\n");
+ };
+ $value = undef if $@;
+ }
+
+ return $value;
+}
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2002-2006, YOURI project
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=cut
+
+1;