1package SPOPS::Initialize; 2 3# $Id: Initialize.pm,v 3.4 2004/06/02 00:48:21 lachoy Exp $ 4 5use strict; 6use Log::Log4perl qw( get_logger ); 7use SPOPS; 8use SPOPS::ClassFactory; 9use SPOPS::Exception qw( spops_error ); 10 11my $log = get_logger(); 12 13$SPOPS::Initialize::VERSION = sprintf("%d.%02d", q$Revision: 3.4 $ =~ /(\d+)\.(\d+)/); 14 15# Main interface -- take the information read in from 'read_config()' 16# and create SPOPS classes, then initialize them 17 18sub process { 19 my ( $class, $p ) = @_; 20 $p ||= {}; 21 my $config = $p->{config}; 22 if ( $p->{directory} or $p->{filename} ) { 23 $config = $class->read_config( $p ); 24 return unless ( ref $config eq 'HASH' ); 25 delete $p->{filename}; 26 delete $p->{directory}; 27 delete $p->{pattern}; 28 } 29 30 # We were given more than one configuration to process, so merge 31 32 if ( ref $config eq 'ARRAY' ) { 33 my $full_config = {}; 34 foreach my $single_config ( @{ $config } ) { 35 next unless ( ref $single_config eq 'HASH' ); 36 foreach my $object_key ( keys %{ $single_config } ) { 37 $full_config->{ $object_key } = $single_config->{ $object_key }; 38 } 39 } 40 $config = $full_config; 41 } 42 43 my $class_created_ok = SPOPS::ClassFactory->create( $config, $p ) || []; 44 unless ( scalar @{ $class_created_ok } ) { 45 $log->warn( "No classes were created by 'SPOPS::ClassFactory->create()'" ); 46 return undef; 47 } 48 49 # Now go through each of the classes created and initialize 50 51 my @full_success = (); 52 foreach my $spops_class ( @{ $class_created_ok } ) { 53 eval { $spops_class->class_initialize() }; 54 if ( $@ ) { 55 spops_error "Failed to run class_initialize() on ", 56 "[$spops_class]: $@"; 57 } 58 push @full_success, $spops_class; 59 } 60 return \@full_success; 61} 62 63 64# Read in one or more configuration files (see POD) 65 66sub read_config { 67 my ( $class, $p ) = @_; 68 my @config_files = (); 69 70 # You can specify one or more filenames to read 71 72 if ( $p->{filename} ) { 73 if ( ref $p->{filename} eq 'ARRAY' ) { 74 push @config_files, @{ $p->{filename} }; 75 } 76 else { 77 push @config_files, $p->{filename}; 78 } 79 } 80 81 # Or specify a directory and, optionally, a pattern to match for 82 # files to read 83 84 elsif ( $p->{directory} and -d $p->{directory} ) { 85 my $dir = $p->{directory}; 86 $log->is_info && 87 $log->info( "Reading configuration files from ($dir) with pattern ($p->{pattern})" ); 88 opendir( CONF, $dir ) 89 || spops_error "Cannot read configuration files from directory [$dir]: $!"; 90 my @directory_files = readdir( CONF ); 91 close( CONF ); 92 foreach my $file ( @directory_files ) { 93 my $full_filename = "$dir/$file"; 94 next unless ( -f $full_filename ); 95 if ( $p->{pattern} ) { 96 next unless ( $file =~ /$p->{pattern}/ ); 97 } 98 push @config_files, $full_filename; 99 } 100 } 101 102 # Now read in each of the files and assign the values to the main 103 # $spops_config. 104 105 my %spops_config = (); 106 foreach my $file ( @config_files ) { 107 $log->is_info && 108 $log->info( "Reading configuration from file: ($file)" ); 109 my $data = $class->read_perl_file( $file ); 110 if ( ref $data eq 'HASH' ) { 111 foreach my $spops_key ( keys %{ $data } ) { 112 $spops_config{ $spops_key } = $data->{ $spops_key }; 113 } 114 } 115 } 116 117 return \%spops_config; 118} 119 120 121# Read in a Perl data structure from a file and return 122 123sub read_perl_file { 124 my ( $class, $filename ) = @_; 125 return undef unless ( -f $filename ); 126 eval { open( INFO, $filename ) || die $! }; 127 if ( $@ ) { 128 warn "Cannot open config file for evaluation ($filename): $@ "; 129 return undef; 130 } 131 local $/ = undef; 132 no strict; 133 my $info = <INFO>; 134 close( INFO ); 135 my $data = eval $info; 136 if ( $@ ) { 137 spops_error "Cannot read data structure from [$filename]: $@"; 138 } 139 return $data; 140} 141 1421; 143 144__END__ 145 146=pod 147 148=head1 NAME 149 150SPOPS::Initialize - Provide methods for initializing groups of SPOPS objects at once 151 152=head1 SYNOPSIS 153 154 # Bring in the class 155 156 use SPOPS::Initialize; 157 158 # Assumes that all your SPOPS configuration information is collected 159 # in a series of files 'spops/*.perl' 160 161 my $config = SPOPS::Initialize->read_config({ 162 directory => '/path/to/spops', 163 pattern => '\.perl' }); 164 165 # You could also have all your SPOPS classes in a single file: 166 167 my $config = SPOPS::Initialize->read_config({ 168 filename => '/path/to/my/spops.config' }); 169 170 # Or in a number of files: 171 172 my $config = SPOPS::Initialize->read_config({ 173 filename => [ '/path/to/my/spops.config.1', 174 '/path/to/my/spops.config.2' ] }); 175 176 # As a shortcut, you read the config and process all at once 177 178 SPOPS::Initialize->process({ directory => '/path/to/spops', 179 pattern => '\.perl' }); 180 181 SPOPS::Initialize->process({ filename => '/path/to/my/spops.config' }); 182 183 SPOPS::Initialize->process({ filename => [ '/path/to/my/spops.config.1', 184 '/path/to/my/spops.config.2' ] }); 185 186 # Use an already-formed config hashref from somewhere else 187 188 SPOPS::Initialize->process({ config => \%spops_config }); 189 190 # You can also pass in multiple config hashrefs that get processed at 191 # once, taking care of circular relationship problems (e.g., 'user' 192 # links-to 'group', 'group' links-to 'user'). 193 194 SPOPS::Initialize->process({ config => [ $config1, $config2 ] }); 195 196=head1 DESCRIPTION 197 198This class makes it simple to initialize SPOPS classes and should be 199suitable for utilizing at a server (or long-running process) startup. 200 201Initialization of a SPOPS class consists of four steps: 202 203=over 4 204 205=item 1. 206 207Read in the configuration. The configuration can be in a separate 208file, read from a database or built on the fly. 209 210=item 2. 211 212Ensure that the classes used by SPOPS are 'require'd. 213 214=item 3. 215 216Build the SPOPS class, using L<SPOPS::ClassFactory|SPOPS::ClassFactory>. 217 218=item 4. 219 220Initialize the SPOPS class. This ensures any initial work the class 221needs to do on behalf of its objects is done. Once this step is 222complete you can instantiate objects of the class and use them at 223will. 224 225=back 226 227=head1 METHODS 228 229B<process( \%params )> 230 231The configuration parameter 'config' can refer to one or more SPOPS 232object configuration hashrefs. These can be already-formed 233configuration hashrefs which, if there are more than one,are merged. 234 235Example: 236 237 SPOPS::Initialize->process({ config => $spops_config }); 238 SPOPS::Initialize->process({ config => [ $spops_config, $spops_config ] }); 239 240You can also pass one or more filenames of SPOPS information (using 241'filename', or a combination of 'directory' and 242'pattern'). Filename/directory processing parameters are passed 243directly to C<read_config()>. 244 245Examples: 246 247 # Process configurations in files 'user/spops.perl' and 248 # 'group/spops.perl' 249 250 SPOPS::Initialize->process({ filename => [ 'user/spops.perl', 251 'group/spops.perl' ] }); 252 253 # Process all configuration files ending in .perl in the directory 254 # 'conf/spops/': 255 256 SPOPS::Initialize->process({ directory => 'conf/spops/', 257 pattern => q(\.perl$) }); 258 259Other parameters in C<\%params> depend on C<SPOPS::ClassFactory> -- 260any values you pass will be passed through. This is fairly rare -- the 261only one you might ever want to pass is 'alias_list', which is an 262arrayref of aliases in the (merged or not) SPOPS config hashref to 263process. 264 265Example: 266 267 # We're just clowning around, so only process 'bozo' -- 'user' and 268 # 'group' aren't touched 269 270 my $config = { user => { ... }, 271 group => { ... }, 272 bozo => { ... } }; 273 SPOPS::Initialize->process({ config => $config, alias_list => [ 'bozo' ] }); 274 275B<read_config( \%params )> 276 277Read in SPOPS configuration information from one or more files in the 278filesystem. 279 280Parameters: 281 282=over 4 283 284=item * 285 286B<filename> ($ or \@) 287 288One or more filenames, each with a fully-qualified path. 289 290=item * 291 292B<directory> ($) 293 294Directory to read files from. If no B<pattern> given, we try to read 295all the files from this directory. 296 297B<pattern> ($) 298 299Regular expression pattern to match the files in the directory 300B<directory>. For instance, you can use 301 302 \.perl$ 303 304to match all the files ending in '.perl' and read them in. 305 306=back 307 308=head1 SEE ALSO 309 310L<SPOPS::ClassFactory|SPOPS::ClassFactory> 311 312=head1 COPYRIGHT 313 314Copyright (c) 2001-2004 Chris Winters. All rights reserved. 315 316This library is free software; you can redistribute it and/or modify 317it under the same terms as Perl itself. 318 319=head1 AUTHORS 320 321Chris Winters E<lt>chris@cwinters.comE<gt> 322 323=cut 324