File Coverage

blib/lib/Config/FreeForm.pm
Criterion Covered Total %
statement 49 61 80.3
branch 5 16 31.2
condition 2 13 15.3
subroutine 10 12 83.3
pod 0 4 0.0
total 66 106 62.2


line stmt bran cond sub pod time code
1             package Config::FreeForm;
2 1     1   43804 use strict;
  1         4  
  1         54  
3 1     1   6 use Carp;
  1         2  
  1         87  
4 1     1   6 use vars qw/$VERSION $CONF_DIR %_Sets %_Stat $DEBUG/;
  1         7  
  1         105  
5             $VERSION = '0.01';
6 1     1   3764 use Data::Dumper;
  1         30264  
  1         1726  
7              
8             sub import {
9 1     1   11 my $class = shift;
10 1 50       7 croak "import: Not an even number of arguments" if @_%2;
11 1         4 my %p = @_;
12 1 50       4 if ($p{dir}) {
13 1         2 $CONF_DIR = $p{dir};
14             }
15             else {
16 0         0 ($CONF_DIR = __FILE__) =~ s/\.pm$//;
17             }
18 1         3 $DEBUG = $p{debug};
19 1         1 for my $set ( @{ $p{sets} } ) {
  1         3  
20 1         4 $_Sets{$set} = "$CONF_DIR/$set.conf";
21 1         2 reload($set);
22             }
23             }
24              
25             sub reload_changed {
26 1     1   12 no strict 'refs';
  1         3  
  1         229  
27 0     0 0 0 for my $set (keys %_Sets) {
28 0         0 my $mtime = (stat $_Sets{$set})[9];
29 0 0 0     0 warn(__PACKAGE__ . ": Can't locate $_Sets{$set}\n"), next
30             unless defined $mtime && $mtime;
31              
32 0 0 0     0 if (!$_Stat{$set} || $mtime > $_Stat{$set}) {
33 0 0 0     0 warn sprintf "%s: process %d reloading %s (%d < %d)\n",
34             __PACKAGE__, $$, $set, $_Stat{$set} || 0, $mtime
35             if $DEBUG;
36 0         0 reload($set);
37             }
38 0         0 $_Stat{$set} = $mtime;
39             }
40             }
41              
42             sub reload {
43 2     2 0 9 my $set = shift;
44 2 50       8 croak "reload: $set not found" unless exists $_Sets{$set};
45 2         627 my $conf = do $_Sets{$set};
46 1     1   5 no strict 'refs';
  1         2  
  1         166  
47 2         13 for my $key (keys %$conf) {
48 2         6 *{ __PACKAGE__ . '::' . $key } = \$conf->{$key};
  2         3239  
49             }
50             }
51              
52             sub handler {
53 0     0 0 0 my $r = shift;
54 0   0     0 $DEBUG = ($r->dir_config("ConfigDebug") || '') eq 'on';
55 0         0 reload_changed();
56 0         0 return 1;
57             }
58              
59             sub rewrite {
60 3     3 0 1461 my $set = shift;
61 3   66     24 my $file = shift || $_Sets{$set};
62 3         4 my $hash;
63             {
64 1     1   4 no strict 'refs';
  1         2  
  1         228  
  3         5  
65 3         4 $hash = { $set => ${ __PACKAGE__ . "::${set}" } };
  3         23  
66             }
67 3         9 local *FH;
68 3 50       458 open FH, ">" . $file
69             or croak "Can't open $file: $!";
70 3         9 local $Data::Dumper::Indent = 1;
71 3         7 local $Data::Dumper::Purity = 1;
72 3         17 print FH Dumper($hash);
73 3 50       909 close FH or croak "Can't close $file: $!";
74             }
75              
76             =head1 NAME
77              
78             Config::FreeForm - Provide in-memory configuration data
79              
80             =head1 SYNOPSIS
81              
82             use Config::FreeForm %options;
83              
84             =head1 DESCRIPTION
85              
86             I provides in-memory configuration data
87             in a free-form manner. Many existing configuration modules
88             attempt to provide some structure to your configuration
89             data; in doing so, they force you to use their own
90             configuration paradigm (association of keywords with values,
91             etc.). Often this isn't what you need in a complex
92             application--you need complete control over your configuration
93             data, and you need the ability to structure it however you
94             like. This is what I gives you.
95              
96             In I configuration data is stored as a
97             Perl data structure. The logic behind this is that you know
98             Perl--you shouldn't need to learn another little language
99             to set up your configuration data, however simple that
100             language may be. Of course, this works best if programmers
101             or tools do the updating of your configuration files; it
102             does make it more difficult for other possible users to edit
103             the files. If this is a problem for you, try some of the
104             other configuration modules listed in I.
105              
106             Still here? Good. You might then ask what I
107             gives you that rolling your own light module using I
108             and I would not. It's a good question, considering in
109             particular that I uses I
110             and I to write and read your data, respectively.
111             I adds some very nice features, though:
112              
113             =over 4
114              
115             =item * Configuration File Management
116              
117             So as not to clutter one file with configuration for all
118             purposes, you can separate your configuration data into
119             multiple files, and specify which files to load when you
120             load in the module:
121              
122             use Config::FreeForm sets => [ ... ];
123              
124             I manages the various configuration files
125             that you've told it to load, and lets you update your
126             data in memory, then write it back to its original location
127             on disk, using the I function (below, in
128             I).
129              
130             =item * Automated Reloading
131              
132             In a I context, your configuration data will be
133             loaded once, at webserver startup; subsequent access to the
134             configuration data will come from memory. If you update
135             your configuration on disk, then, you'll want those
136             changes to be reflected in the in-memory versions of the
137             configuration. I will handle this
138             automatically for you if you install it as a I
139             on your I-enabled server. For more details, see
140             I, below.
141              
142             =back
143              
144             =head1 HOW TO USE IT
145              
146             To create a configuration file, add its configuration to a
147             file like I:
148              
149             $conf = {
150             Foo => { Bar => 1 }
151             }
152              
153             Once you've written your I configuration file,
154             load that file explicitly (without the I<.conf>):
155              
156             use Config::FreeForm sets => [ 'Foo' ];
157              
158             When the module is loaded, it will attempt to find a
159             configuration file for the set I; it will load the
160             data in this file using I; then it will loop over
161             the top-level variables in the tree structure and alias
162             variables into the I namespace to the
163             values in the structure.
164              
165             For example, if you have the above I, the
166             variable I<$Config::FreeForm::Foo> will be equal to the
167             following structure:
168              
169             { Bar => 1 }
170              
171             So you could access the value of the I attribute
172             by treating the aliased variable as a hash
173             reference:
174              
175             my $value = $Config::FreeForm::Foo->{Bar};
176              
177             In addition to specifying which configuration files to
178             load, you can use the I<%options> in the import list to
179             set the directory holding the configuration files. By
180             default I looks in the directory
181             I within the directory from which it was loaded
182             for the files. For example, if the module were loaded
183             from F, the I
184             configuration file would be default be looked up in
185             F.
186              
187             By using the I import list parameter, though, you
188             can override this default behavior:
189              
190             use Config::FreeForm dir => '/foo', sets => [ 'Foo' ];
191              
192             This would look up F in the directory I.
193              
194             =head1 UPDATING CONFIGURATION
195              
196             If you wish to update the configuration files
197             programatically (as opposed to editing them by
198             hand), you can use the I function.
199              
200             This is a two-step process. First, you'll need to update
201             the in-memory configuration--just make a change to one
202             of the variables. For example:
203              
204             $Config::FreeForm::Foo->{Bar} = 2;
205              
206             This updates the configuration in memory; now you need
207             to write the configuration to the disk. You can do
208             that using I, which takes the name of a
209             configuration "set" (which corresponds to the name of
210             the configuration file). In this case, that set would
211             be I:
212              
213             Config::FreeForm::rewrite('Foo');
214              
215             And you're done. The configuration is now updated on
216             disk.
217              
218             If you'd like to write the configuration data to a
219             file different than that from which it was read, you
220             can pass a filepath as a second argument. For example:
221              
222             Config::FreeForm::rewrite('Foo', './myfoo.conf');
223              
224             This will write out the I configuration data to
225             F<./myfoo.conf>.
226              
227             Keep in mind that, if you're rewriting your configuration
228             in a webserver context, you'll want your on-disk changes
229             to propagate to the other webserver children (the children
230             in which you didn't already change the in-memory
231             configuration). Read on--this can be made to happen
232             automatically.
233              
234             =head1 AUTOMATED RELOADING
235              
236             When used in a webserver context, the configuration files
237             are parsed once at webserver startup, then stored in
238             memory. If changes occur in the configuration files, under
239             normal circumstances the configuration stored in memory
240             would not be reloaded. However, I has a
241             built-in capability to automatically reload configuration
242             files that have changed on disk. This allows you to make
243             a change to a config file, then let the webserver
244             automatically pick up the new changes.
245              
246             This is particularly important when using the I
247             function; if you alter the in-memory configuration, then
248             write the file to disk, you want the other webserver
249             children to pick up the changes, in addition to the child
250             where you actually made the in-memory changes. Using the
251             automated reloading, these changes will be automatically
252             picked up by all of the webserver children.
253              
254             To use this capability, just install I as
255             a I in the webserver. Add this to the
256             configuration:
257              
258             PerlInitHandler Config::FreeForm
259              
260             You can either stick this into a I block or make
261             it global for your entire server. The latter may be easier
262             in terms of maintenance, but the former may give you more
263             flexibility.
264              
265             By default, I will go about its business
266             quietly. If you'd like it to write a message to the error
267             log each time it reloads a configuration file, you can
268             add a configuration directive to do so:
269              
270             PerlSetVar ConfigDebug on
271              
272             Now, each time I reloads a configuration
273             file, it will write a message to the log file telling
274             you the process ID, the configuration set, and the
275             modified-time comparison that caused the reload.
276              
277             =head1 MISCELLANEOUS
278              
279             If the so-called freeform nature of I
280             doesn't appeal to you, and you'd like a more structured
281             approach to your configuration files, check out
282             I, I, or I.
283              
284             =head1 AUTHOR
285              
286             Benjamin Trott, ben@rhumba.pair.com
287              
288             =cut
289              
290             1;