change rules for cluster accessible dirs.
[cxgn-corelibs.git] / lib / CXGN / Config.pm
blobd669949756065c12982f5c26caac11180ae9c08e
1 =head1 NAME
3 CXGN::Config - L<Config::Any>-based implementation of cascading config files
5 =head1 SYNOPSIS
7 my $cfg = MyProj::MyConfig->load_locked;
8 print "the basepath variable is $cfg->{basepath}\n";
10 =head1 DESCRIPTION
12 Implementation of cascading config files.
14 To use:
16 =over
18 =head2 1. Subclass CXGN::Config for your project's configuration
20 package MyProj::MyConfig;
21 use base 'CXGN::Config';
22 my $defaults = {
24 foo => 'bar',
25 baz => 'quux',
26 # and so on
29 sub defaults { shift->SUPER::defaults( $defaults, @_ )}
32 =head2 2. That's all!
34 When users call
36 my $cfg = MyProj::MyConfig->load_locked;
38 they will get a locked hashref of config values from the following
39 cascading sources, merged with later sources taking precedence:
41 =over
43 =item defaults in CXGN::Config
45 =item defaults in any intermediate classes between CXGN::Config and MyProj::MyConfig
47 =item defaults in MyProj::MyConfig
49 =item values in /etc/cxgn/Global.conf
51 =item values in /etc/cxgn/MyProj.conf
53 =back
55 load() also takes some optional arguments which can be used to tweak
56 which sources are used for configuration, etc.
58 =cut
60 package CXGN::Config;
61 use strict;
63 use Carp;
64 use Cwd ();
65 use File::Spec;
66 use FindBin;
67 use Hash::Merge ();
69 use Memoize ();
71 use Config::Any;
73 our @search_path = $ENV{CXGN_CONF_DIR}
74 ? ( $ENV{CXGN_CONF_DIR} )
75 : map Cwd::realpath(File::Spec->rel2abs(File::Spec->catdir(@$_))),
77 [ File::Spec->rootdir, 'etc', 'cxgn' ], # /etc/cxgn
80 =head1 CLASS METHODS
82 =head2 load
84 Status : public
85 Usage : my $cfg = MyProj::MyConfig->load();
86 Returns : hashref of merged config values
87 Args : optional hash-style list of options as:
89 name => conf file basename to load
90 default: first portion of class name, e.g.
91 'CXGN' for CXGN::Config
92 if 'undef' is passed, searches only Global.conf
94 add_vals => hashref of additional config values to
95 include, overriding any loaded ones
98 the following options are planned but not yet
99 implemented:
101 add_files => arrayref of additional config files to
102 merge in, which will take precedence over
103 ones automatically discovered by this
104 module
106 Side Eff: none
108 Loads and merged multiple cascading config files. L<Config::Any> is
109 used for parsing each one.
111 Merge order (values in later files take precedence):
113 defaults in CXGN::Config
114 defaults in any intermediate classes between CXGN::Config and MyProj::MyConfig
115 defaults in MyProj::MyConfig
116 /etc/cxgn/Global.conf
117 /etc/cxgn/<name>.conf
119 B<NOTE:> For an additional layer of protection, consider using
120 L<"load_locked"> rather than C<load>.
122 =cut
124 sub load {
125 my ( $class, %args ) = @_;
127 my @search_files = $class->_search_files( %args );
129 my @found_files = grep -f, @search_files;
131 my $cfg = $class->defaults;
132 foreach my $file ( @found_files ) {
133 my $c = Config::Any
134 ->load_files({ files => [$file],
135 override => 1,
136 use_ext => 0,
138 $c && @$c or die "could not parse config file '$file'";
140 $class->_merge_hashes($cfg, $c->[0]->{$file});
143 if( my $v = $args{add_vals} ) {
144 ref $v eq 'HASH' or croak 'add_vals must be a hashref';
145 $class->_merge_hashes($cfg, $v);
148 return $cfg;
151 =head2 load_locked
153 Same as load() above, but locks the hash with lock_hash() from
154 L<Hash::Util> before returning it.
156 This means that an error will be thrown if code tries to access a
157 configuration variable that is not set or change a configuration
158 variable, which provides a useful layer of error checking in many
159 situations.
161 Using this also provides a performance benefit, since the return value
162 of C<load_locked> is cached, while C<load> is not.
164 Most users of CXGN::Config-based modules will want to use
165 C<load_locked> rather than L<"load">.
167 =cut
169 Memoize::memoize 'load_locked',
170 # normalize the args by adding the modification time of each file,
171 # so that the result will continue to be cached as long as the
172 # modification times of each of the files stays the same
173 NORMALIZER => sub {
174 my $class = shift;
175 my @search_files = $class->_search_files( @_ );
176 return join ',', ( $class, @_, map { $_ => (stat $_)[9] } @search_files );
180 sub load_locked {
181 my $class = shift;
182 my $cfg = $class->load(@_);
183 { no warnings;
184 require Hash::Util;
185 Hash::Util::lock_hashref( $cfg );
187 return $cfg;
190 # class method, takes the config name (like 'SGN'), and returns the
191 # list of full paths of files to search for
192 my %_search_files_cache;
193 sub _search_files {
194 my $result = $_search_files_cache{ join ',', @_ } ||= [ shift->__search_files(@_) ];
195 return @$result;
197 sub __search_files {
198 my ($class, %args) = @_;
200 #set defaults
201 my $cfg_name = exists $args{name}
202 ? $args{name}
203 : $class->_conf_name;
205 my @conf_basenames = ('Global', defined $cfg_name ? $cfg_name : () );
207 return map {
208 my $path = $_;
209 map File::Spec->catfile($path, "$_.conf" ), @conf_basenames;
210 } @search_path;
213 # e.g. Foo::Conf::Baz->_conf_name returns 'Foo'
214 sub _conf_name {
215 my $classname = shift;
216 return if $classname eq __PACKAGE__; # don't search for CXGN.conf
217 my ($cn) = $classname =~ /^(?:CXGN::)?([^:]+)/
218 or die "error parsing class name $classname";
220 return $cn;
223 # merge all the other hashes into the first hash, modifying the first
224 # hash in place
225 sub _merge_hashes {
226 my $class = shift;
227 my $h1 = shift;
228 for my $h2 (@_) {
229 for ( keys %$h2 ) {
230 $h1->{$_} = $h2->{$_};
233 return $h1;
236 =head2 defaults
238 Status : public
239 Usage : CXGN::Config::MyConfig->merge_defaults({ foo => bar })
240 Returns : hashref of this class's default values merged with any
241 hashrefs that it is passed
242 Args : optional hashrefs of default values to merge in,
243 with the rightmost hash taking precedence
244 Side Eff: none
245 Examples:
246 # recommended implementation in a CXGN::Config subclass:
247 my $defaults = {
248 foo => 'bar',
249 baz => 'boo',
252 sub defaults {
253 # pass this class's defaults to the superclass, which will
254 # merge them into its own defaults and return the result
255 shift->SUPER::defaults( $defaults, @_ )
258 =cut
260 our $defaults; #< this hashref is filled below
261 sub defaults {
262 #operate on a shallow copy of our defaults
263 return shift->_merge_hashes({%$defaults}, @_ );
266 $defaults =
268 #who to contact, these addresses will be used by modules which send us emails, among other things
269 email => 'sgn-feedback@solgenomics.net',
271 #default database to connect to and how to connect to it
272 dbhost => 'db.sgn.cornell.edu',
273 dbname => 'cxgn',
274 dbuser => 'web_usr',
275 #dbpass => undef,
276 dbsearchpath => [qw[
278 public
279 annotation
280 genomic
281 insitu
282 metadata
283 pheno_population
284 phenome
285 physical
286 tomato_gff
287 sgn_people
289 biosource
292 cview_db_backend => 'cxgn',
294 # path for cxgn core perllib
295 cxgn_core_perllib => '/data/local/cxgn/core/cxgn-corelibs/lib',
297 #how to find blast stuff
298 blast_path => '',
299 blast_db_path => '/data/shared/blast/databases/current',
301 #the shared temp directory used by cluster nodes
302 cluster_shared_tempdir => '/data/prod/tmp',
304 #how verbose we want the warnings to be in the apache error log
305 verbose_warnings => 1,
307 # Insitu file locations
308 insitu_fullsize_dir => '/data/prod/public/images/insitu/processed',
309 insitu_fullsize_url => '/data/images/insitu/processed',
310 insitu_display_dir => '/data/prod/public/images/insitu/display',
311 insitu_display_url => '/data/images/insitu/display',
312 insitu_input_dir => '/data/prod/public/images/insitu/incoming',
314 #path to our production ftp site
315 ftpsite_root => '/data/prod/public',
316 ftpsite_url => 'ftp://ftp.solgenomics.net',
318 #path to the pucebaboon temperature sensor file:
319 pucebaboon_file => '/data/prod/public/digitemp.out',
324 1;#do not remove
327 =head1 AUTHOR
329 Robert Buels, E<lt>rmb32@cornell.eduE<gt>
331 =head1 COPYRIGHT & LICENSE
333 Copyright 2009 The Boyce Thompson Institute for Plant Research
335 This program is free software; you can redistribute it and/or modify
336 it under the same terms as Perl itself.