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