File Coverage

blib/lib/XAO/DO/Config.pm
Criterion Covered Total %
statement 73 79 92.4
branch 20 26 76.9
condition 3 6 50.0
subroutine 17 23 73.9
pod 7 17 41.1
total 120 151 79.4


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             XAO::DO::Config - Base object for all configurations
4              
5             =head1 SYNOPSIS
6              
7             Useful in tandem with XAO::Projects to describe contexts.
8              
9             use XAO::Projects qw(:all);
10              
11             my $config=XAO::Objects->new(objname => 'Config',
12             sitename => 'test');
13              
14             create_project(name => 'test',
15             object => $config,
16             set_current => 1);
17              
18             my $webconfig=XAO::Objects->new(objname => 'Web::Config');
19             my $fsconfig=XAO::Objects->new(objname => 'FS::Config');
20              
21             $config->embed(web => $webconfig,
22             fs => $fsconfig);
23              
24             # Now we have web and fs methods on the config itself:
25             #
26             my $cgi=$config->cgi;
27             my $odb=$config->odb;
28              
29             =head1 DESCRIPTION
30              
31             This object provides storage for project specific configuration
32             variables and clipboard mechanism.
33              
34             It can ``embed'' other configuration objects that describe specific
35             parts of the system -- such as database, web or something else. This is
36             done by using method embed() -- see below.
37              
38             =head1 METHODS
39              
40             XAO::DO::Config provides the following methods:
41              
42             =over
43              
44             =cut
45              
46             ###############################################################################
47             package XAO::DO::Config;
48 3     3   20 use strict;
  3         11  
  3         122  
49 3     3   18 use XAO::Utils;
  3         7  
  3         366  
50 3     3   19 use XAO::Objects;
  3         7  
  3         56  
51 3     3   1100 use XAO::Cache;
  3         8  
  3         136  
52              
53 3     3   19 use base XAO::Objects->load(objname => 'Atom');
  3         6  
  3         28  
54              
55             ###############################################################################
56             # Prototypes
57             #
58             sub cache ($%);
59             sub cleanup ($;@);
60             sub embed ($%);
61             sub embedded ($$);
62             sub is_embedded ($$);
63             sub new ($);
64              
65             ###############################################################################
66              
67             =item cache (%)
68              
69             Creates or retrieves a cache for use in various other XAO objects.
70             Arguments are directly passed to XAO::Cache's new() method (see
71             L).
72              
73             The 'name' argument is required and is used to identify the requested
74             cache. If a cache with the same name was requested before its previously
75             created object is returned and all new arguments are silently ignored
76             without making sure they match the previous request.
77              
78             B Retrieve method SHOULD NOT rely on any locally available
79             lexical variables, they will be taken from whatever scope existed first
80             time cache() was called!
81              
82             Example:
83              
84             my $cache=$self->cache(
85             name => 'fubar',
86             retrieve => \&real_retrieve,
87             coords => ['foo','bar'],
88             expire => 60
89             );
90              
91             Caches are kept between executions in mod_perl environment.
92              
93             =cut
94              
95             sub cache ($%) {
96 3     3 1 100 my $self=shift;
97 3         24 my $args=get_args(\@_);
98              
99 3   33     18 my $name=$args->{'name'} ||
100             throw $self "cache - no 'name' argument";
101              
102 3         12 my $cache_list=$self->{'cache_list'};
103 3 100       20 if(! $cache_list) {
104 1         11 $cache_list=$self->{'cache_list'}={};
105             }
106              
107 3         13 my $cache=$cache_list->{$name};
108              
109 3 100       12 if(! $cache) {
110             $cache=$cache_list->{$name}=XAO::Cache->new($args,{
111 1         26 sitename => $self->{'sitename'},
112             });
113             }
114              
115 3         12 return $cache;
116             }
117              
118             ###############################################################################
119              
120             =item cleanup ()
121              
122             Calls cleanup method on all embedded configurations if it is
123             available. Order of calls is random.
124              
125             =cut
126              
127             sub cleanup ($;@) {
128 1     1 1 3 my $self=shift;
129 1         3 foreach my $name (keys %{$self->{names}}) {
  1         11  
130 2         7 my $obj=$self->{names}->{$name}->{obj};
131 2 50       20 $obj->cleanup(@_) if $obj->can('cleanup');
132             }
133             }
134              
135             ###############################################################################
136              
137             =item embed (%)
138              
139             This method allows to embed other configuration objects into
140             Config. After embedding certain methods of embedded object become
141             available as Config methods. For example, if you embed Web::Config into
142             Config and Web::Config provides a method called cgi(), then you will be
143             able to call that method on Config:
144              
145             my $config=XAO::Objects->new(objname => 'Config');
146             my $webconfig=XAO::Objects->new(objname => 'Web::Config');
147              
148             $config->embed('Web::Config' => $webconfig);
149              
150             my $cgi=$config->cgi();
151              
152             In order to support that hte object being embedded must have a method
153             embeddable_methods() that returns an array of method names to be
154             embedded.
155              
156             sub embeddable_methods ($) {
157             my $self=shift;
158             return qw(cgi add_cookie del_cookie);
159             }
160              
161             The idea behind embedding is to allow easy access to arbitrary context
162             description objects (Configs). For example XAO::FS would provide its own
163             config that creates and caches its database handler. Some other database
164             module might provide its own config if for some reason XAO::FS can't be
165             used.
166              
167             =cut
168              
169 3     3   24 use vars qw(%global_methods);
  3         7  
  3         1977  
170              
171             sub embed ($%) {
172 7     7 1 43 my $self=shift;
173 7         108 my $args=get_args(\@_);
174              
175 7         38 foreach my $name (keys %$args) {
176              
177             throw $self "embed - object with that name ($name) was already embedded before"
178 7 50       26 if $self->{$name};
179              
180 7         22 my $obj=$args->{$name};
181 7 50       90 $obj->can('embeddable_methods') ||
182             throw $self "embed - object (".ref($obj).") does not have embeddable_methods() method";
183              
184             ##
185             # Setting base for the object we embed, it might need it
186             #
187 7 100       57 $obj->set_base_config($self) if $obj->can('set_base_config');
188              
189             ##
190             # Building perl code for proxy methods definitions
191             #
192 7         40 my @list=$obj->embeddable_methods();
193 7         49 my $code='';
194 7         15 foreach my $mn (@list) {
195 48 50       233 $obj->can($mn) ||
196             throw $self "embed - object (".ref($obj).") doesn't have embeddable method $mn()";
197              
198 48 50       116 $self->{methods}->{$mn} &&
199             throw $self "embed - method with such name ($mn) already exists, can't be embedded from ".ref($obj);
200              
201 48         205 $self->{methods}->{$mn}=$obj;
202              
203             ##
204             # We only add code if it is required, if that subroutine was
205             # not defined before in another instance of Config object.
206             #
207 48 100       117 if(! $global_methods{$mn}) {
208 29         82 $code.="sub $mn { shift->{methods}->{$mn}->$mn(\@_); }\n";
209 29         65 $global_methods{$mn}=1;
210             }
211             }
212              
213             ##
214             # Now a bit of black magic, evaluating the code in the current
215             # package context to add appropriate proxy methods.
216             #
217 7 100       21 if($code) {
218 5     0 0 973 eval $code;
  0     0 0 0  
  0     0 0 0  
  0     0 0 0  
  0     32 0 0  
  32     0 0 31821  
  0     1 0 0  
  1     1 0 36  
  1     0 0 42  
  0     1 0 0  
  1         5  
219 5 50       32 $@ && throw $self "embed - internal error; name=$name, obj=".ref($obj);
220             }
221              
222             ##
223             # To operate with sub-configs by name later on.
224             #
225 7         38 $self->{names}->{$name}->{obj}=$obj;
226 7         51 $self->{names}->{$name}->{methods}=\@list;
227             }
228             }
229              
230             ###############################################################################
231              
232             =item embedded ($)
233              
234             Returns a reference to a previously embedded object by name. Can be used
235             to call non-embedded method on that object. Throws an error if there is
236             no such embedded object.
237              
238             =cut
239              
240             sub embedded ($$) {
241 4     4 1 261 my $self=shift;
242 4         20 my $name=shift;
243              
244 4   66     63 my $desc=$self->{names}->{$name} ||
245             throw $self "embedded - no configuration with such name ($name)";
246 3         40 $desc->{obj};
247             }
248              
249             ###############################################################################
250              
251             =item is_embedded ($)
252              
253             Check if a named object is embedded.
254              
255             =cut
256              
257             sub is_embedded ($$) {
258 22     22 1 42 my $self=shift;
259 22         44 my $name=shift;
260 22 100       437 return $self->{'names'}->{$name} ? 1 : 0;
261             }
262              
263             ###############################################################################
264              
265             =item init (%)
266              
267             Default method for project specific Config implementation
268             initialization. This method would normally be called by various handlers
269             after creating configuration and before making it current. It calls
270             init() method on all embedded configs in unpredictable order.
271              
272             =cut
273              
274             sub init ($) {
275 3     3 1 70 my $self=shift;
276 3         12 foreach my $name (keys %{$self->{names}}) {
  3         30  
277 2         8 my $obj=$self->{names}->{$name}->{obj};
278 2 100       16 $obj->init() if $obj->can('init');
279             }
280             }
281              
282             ###############################################################################
283              
284             =item new ()
285              
286             Creates new instance of abstract Config.
287              
288             =cut
289              
290             sub new ($) {
291 9     9 1 34 my $proto=shift;
292 9         61 my $args=get_args(\@_);
293 9         247 $proto->SUPER::new(merge_refs($args,{
294             methods => {
295             embed => 1,
296             embedded => 1,
297             new => 1,
298             BEGIN => 1,
299             END => 1,
300             DESTROY => 1,
301             AUTOLOAD => 1,
302             },
303             }));
304             }
305              
306             ###############################################################################
307             1;
308             __END__