# $Id: Config.pm 1709 2006-10-16 16:33:43Z warly $ package Youri::Config; =head1 NAME Youri::Application - Youri application handler =head1 SYNOPSIS use Youri::Application; my $app = Youri::Application->new( options => { help => '|h!' }, directories => [ '/etc/youri', "$ENV{HOME}/.youri" ], file => 'app.conf', ); # get command line argument my $foo = $app->get_arg('foo'); # get configuration file parameter my $bar = $app->get_param('bar'); =head1 DESCRIPTION This class handle configuration for all YOURI applications. The command line specification is used to manage arguments through Getopt::Long. Unless B<--config> 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 YAML::AppConfig, Getopt::Long =cut use strict; use warnings; use YAML::AppConfig; use Getopt::Long; use File::Spec; use Pod::Usage; use Carp; sub new { my ($class, %options) = @_; # 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 ($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}"; next unless -f $file && -r $file; $main_file = $file; last; } croak 'No config file found, aborting' unless $main_file; } my $params; eval { $params = YAML::AppConfig->new(file => $main_file); }; if ($@) { croak "Invalid configuration file $main_file, aborting"; } # 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 { _args => $args, _params => $params }, $class; return $self; } sub get_arg { my ($self, $arg) = @_; croak "Not a class method" unless ref $self; return $self->{_args}->{$arg}; } sub get_param { my ($self, $param) = @_; croak "Not a class method" unless ref $self; return $self->{_params}->get($param); } =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;