File Coverage

blib/lib/Youri/Config.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             # $Id: Config.pm 2458 2017-11-13 18:30:13Z guillomovitch $
2             package Youri::Config;
3              
4             =head1 NAME
5              
6             Youri::Config - Youri configuration handler
7              
8             =head1 SYNOPSIS
9              
10             use Youri::Config;
11              
12             my $app = Youri::Config->new(
13             options => {
14             help => '|h!'
15             },
16             directories => [ '/etc/youri', "$ENV{HOME}/.youri" ],
17             file => 'app.conf',
18             );
19              
20             # get command line argument
21             my $foo = $app->get_arg('foo');
22              
23             # get configuration file parameter
24             my $bar = $app->get_param('bar');
25              
26             =head1 DESCRIPTION
27              
28             This class handle configuration for all YOURI applications.
29              
30             The command line specification is used to manage arguments through
31             Getopt::Long. Unless B<--config> argument is given, the list of directories is
32             then scanned for a file with given name, and halt as soon as it find one. If no
33             readable file is found, an exception is thrown. The file is then processed
34             through YAML::AppConfig. If parsing fails, an exception is thrown.
35              
36             =head1 CONFIGURATION FILE FORMAT
37              
38             =head2 SHARED KEYS
39              
40             In addition to the application-specific optional or mandatory parameters, all
41             YOURI applications support the following optional top-level parameters:
42              
43             =over
44              
45             =item B
46              
47             A list of additional configuration files.
48              
49             =item B
50              
51             An arbitrary variable, usable everywhere else in the file.
52              
53             =back
54              
55             =head2 PLUGIN DEFINITION
56              
57             All YOURI application heavily rely on plugins defined in their configuration
58             files. A plugin definition is composed from the following parameters:
59              
60             =over
61              
62             =item B
63              
64             The class of this plugin.
65              
66             =item B
67              
68             The options of this plugin.
69              
70             =back
71              
72             =head1 SEE ALSO
73              
74             YAML::AppConfig, Getopt::Long
75              
76             =cut
77              
78 1     1   64991 use strict;
  1         10  
  1         23  
79 1     1   4 use warnings;
  1         2  
  1         18  
80 1     1   93 use YAML::AppConfig;
  0            
  0            
81             use Getopt::Long;
82             use File::Spec;
83             use Pod::Usage;
84             use Carp;
85             use version; our $VERSION = qv('0.2.4');
86              
87             =head2 new(%args)
88              
89             Creates and returns a new Youri::Config object.
90              
91             =cut
92              
93             sub new {
94             my ($class, %options) = @_;
95              
96             # command line arguments
97             my $args = {
98             verbose => 0
99             };
100             my @args;
101             if ($options{args}) {
102             while (my ($arg, $spec) = each %{$options{args}}) {
103             push(@args, ($arg . $spec) => \$args->{$arg});
104             }
105             }
106             push(@args,
107             'config=s' => \$args->{config},
108             'h|help' => \$args->{help},
109             'v|verbose+' => \$args->{verbose}
110             );
111             GetOptions(@args);
112              
113             if ($args->{help}) {
114             if (!@ARGV) {
115             # standard help, available immediatly
116             my $filename = (caller)[1];
117             pod2usage(
118             -input => $filename,
119             -verbose => 0
120             );
121             }
122             }
123              
124             # config files parameters
125            
126             # find configuration file to use
127             my $main_file;
128             if ($args->{config}) {
129             if (! -f $args->{config}) {
130             croak "Non-existing file $args->{config}";
131             } elsif (! -r $args->{config}) {
132             croak "Non-readable file $args->{config}";
133             } else {
134             $main_file = $args->{config};
135             }
136             } else {
137             foreach my $directory (@{$options{directories}}) {
138             my $file = "$directory/$options{file}";
139             next unless -f $file && -r $file;
140             $main_file = $file;
141             last;
142             }
143             }
144              
145             my $params;
146             if ($main_file) {
147             eval {
148             $params = YAML::AppConfig->new(file => $main_file);
149             };
150             if ($@) {
151             croak
152             "Invalid configuration file $main_file, aborting. " .
153             "The parser error was:\n" . $@;
154             }
155              
156             # process inclusions
157             my $includes = $params->get('includes');
158             if ($includes) {
159             foreach my $include_file (@{$includes}) {
160             # convert relative path to absolute ones
161             $include_file = File::Spec->rel2abs(
162             $include_file, (File::Spec->splitpath($main_file))[1]
163             );
164              
165             if (! -f $include_file) {
166             warn "Non-existing file $include_file, skipping";
167             } elsif (! -r $include_file) {
168             warn "Non-readable file $include_file, skipping";
169             } else {
170             eval {
171             $params->merge(file => $include_file);
172             };
173             if ($@) {
174             carp "Invalid included configuration file $include_file, skipping";
175             }
176             }
177             }
178             }
179             } else {
180             croak 'No config file found, aborting' if $options{mandatory};
181             }
182              
183             my $self = bless {
184             _args => $args,
185             _params => $params
186             }, $class;
187              
188             return $self;
189             }
190              
191             =head1 INSTANCE METHODS
192              
193             =head2 get_arg($arg)
194              
195             Returns the command-line argument $arg.
196              
197             =cut
198              
199             sub get_arg {
200             my ($self, $arg) = @_;
201             croak "Not a class method" unless ref $self;
202              
203             return $self->{_args}->{$arg};
204             }
205              
206             =head2 get_param($param)
207              
208             Returns the configuration file parameter $param.
209              
210             =cut
211              
212              
213             sub get_param {
214             my ($self, $param) = @_;
215             croak "Not a class method" unless ref $self;
216              
217             return $self->{_params} ?
218             $self->{_params}->get($param) :
219             undef;
220             }
221              
222             =head1 COPYRIGHT AND LICENSE
223              
224             Copyright (C) 2002-2006, YOURI project
225              
226             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
227              
228             =cut
229              
230             1;