From 0a44a5718469afd7ba370a44510454a2ffe91aef Mon Sep 17 00:00:00 2001 From: Florent Villard Date: Mon, 16 Oct 2006 16:33:43 +0000 Subject: merging dev with upstream --- lib/Youri/Config.pm | 295 +++++++++++++++++++++++----------------------------- 1 file changed, 131 insertions(+), 164 deletions(-) diff --git a/lib/Youri/Config.pm b/lib/Youri/Config.pm index 004f240..09cfbe6 100644 --- a/lib/Youri/Config.pm +++ b/lib/Youri/Config.pm @@ -3,225 +3,192 @@ package Youri::Config; =head1 NAME -Youri::Config - Youri configuration handler +Youri::Application - Youri application handler =head1 SYNOPSIS - use Youri::Config; + use Youri::Application; - my $config = Youri::Config->new( - command_spec => [ - 'help|h!', - ], - file_spec => [ - 'foo=s', - ], + my $app = Youri::Application->new( + options => { + help => '|h!' + }, directories => [ '/etc/youri', "$ENV{HOME}/.youri" ], - file_name => 'app.conf', - caller => $0, + file => 'app.conf', ); - # get configuration directive - my $foo = $config->get('foo'); + # get command line argument + my $foo = $app->get_arg('foo'); - # get configuration section - my %bar = $config->get_section('bar'); + # get configuration file parameter + my $bar = $app->get_param('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 = < argument is given, the list of directories is +then scanned for a file with given name, and halt as soon as it find one. If no +readable file is found, an exception is thrown. The file is then processed +through YAML::AppConfig. If parsing fails, an exception is thrown. + +=head1 CONFIGURATION FILE FORMAT + +=head2 SHARED KEYS + +In addition to the application-specific optional or mandatory parameters, all +YOURI applications support the following optional top-level parameters: + +=over + +=item B + +A list of additional configuration files. + +=item B + +An arbitrary variable, usable everywhere else in the file. + +=back + +=head2 PLUGIN DEFINITION + +All YOURI application heavily rely on plugins defined in their configuration +files. A plugin definition is composed from the following parameters: + +=over + +=item B + +The class of this plugin. + +=item B + +The options of this plugin. + +=back =head1 SEE ALSO -AppConfig, YAML +YAML::AppConfig, Getopt::Long =cut use strict; use warnings; -use AppConfig qw/:argcount :expand/; +use YAML::AppConfig; +use Getopt::Long; 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}} + # command line arguments + my $args = { + verbose => 0 + }; + my @args; + if ($options{args}) { + while (my ($arg, $spec) = each %{$options{args}}) { + push(@args, ($arg . $spec) => \$args->{$arg}); + } + } + push(@args, + 'config=s' => \$args->{config}, + 'h|help' => \$args->{help}, + 'v|verbose+' => \$args->{verbose} ); + GetOptions(@args); + + if ($args->{help}) { + if (!@ARGV) { + # standard help, available immediatly + my $filename = (caller)[1]; + pod2usage( + -input => $filename, + -verbose => 0 + ); + } + } + # config files parameters + # 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) { + if ($args->{config}) { + if (! -f $args->{config}) { + croak "Non-existing file $args->{config}"; + } elsif (! -r $args->{config}) { + croak "Non-readable file $args->{config}"; + } else { + $main_file = $args->{config}; + } + } else { foreach my $directory (@{$options{directories}}) { - my $file = "$directory/$options{file_name}"; + my $file = "$directory/$options{file}"; next unless -f $file && -r $file; $main_file = $file; last; } + croak 'No config file found, aborting' unless $main_file; } - 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; - } + my $params; + eval { + $params = YAML::AppConfig->new(file => $main_file); + }; + if ($@) { + croak "Invalid configuration file $main_file, aborting"; } - $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); + # process inclusions + my $includes = $params->get('includes'); + if ($includes) { + foreach my $include_file (@{$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 { + eval { + $params->merge(file => $include_file); + }; + if ($@) { + carp "Invalid included configuration file $include_file, skipping"; + } + } } } my $self = bless { - _appconfig => $file_config + _args => $args, + _params => $params }, $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) = @_; +sub get_arg { + my ($self, $arg) = @_; 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; + return $self->{_args}->{$arg}; } -sub get { - my ($self, $variable) = @_; +sub get_param { + my ($self, $param) = @_; 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; + return $self->{_params}->get($param); } =head1 COPYRIGHT AND LICENSE -- cgit v1.2.1