File Coverage

lib/Badger/Hub.pm
Criterion Covered Total %
statement 110 144 76.3
branch 20 50 40.0
condition 16 44 36.3
subroutine 24 29 82.7
pod 11 22 50.0
total 181 289 62.6


line stmt bran cond sub pod time code
1             #========================================================================
2             #
3             # Badger::Hub
4             #
5             # DESCRIPTION
6             # A hub provides a central configuration and management point for
7             # Badger components to access other Badger components.
8             #
9             # AUTHOR
10             # Andy Wardley
11             #
12             #========================================================================
13              
14             package Badger::Hub;
15              
16 70     70   20718 use Badger::Debug ':dump';
  70         132  
  70         518  
17 70     70   26519 use Badger::Config;
  70         190  
  70         3534  
18             use Badger::Class
19 70         503 version => 0.01,
20             debug => 0,
21             base => 'Badger::Prototype',
22             import => 'class',
23             auto_can => 'auto_can',
24             constants => 'HASH ARRAY REFS PKG',
25             utils => 'blessed params is_object',
26             words => 'COMPONENTS DELEGATES COMP_CACHE DELG_CACHE',
27             constant => {
28             CONFIG_MODULE => 'Badger::Config',
29             },
30             messages => {
31             no_module => 'No %s module defined.',
32             bad_method => "Invalid method '%s' called on %s at %s line %s",
33 70     70   438 };
  70         110  
34              
35             our $COMPONENTS = { };
36             our $RESOURCES = { };
37             our $DELEGATES = { };
38             our $LOADED = { };
39             #our $COLLECTIONS = [qw( components resources delegates )];
40              
41              
42             sub init {
43 4     4 1 9 my ($self, $config) = @_;
44 4         11 $self->init_hub($config);
45             }
46              
47             sub init_hub {
48 4     4 0 7 my ($self, $config) = @_;
49 4         12 $self->init_config($config);
50 4         11 $self->init_items($config);
51             # $self->init_collections($config); # init on demand
52             }
53              
54             sub init_config {
55 4     4 0 6 my ($self, $args) = @_;
56 4         11 my $class = $self->class;
57              
58             # We're looking for a specific 'config' item which the user can provide to
59             # points to a master configuration object or class name. We default to the
60             # value in the $CONFIG package variable, which in this case is Badger::Config,
61             # but could be re-defined by a subclass to be something else.
62              
63             my $config = delete($args->{ config })
64 4   100     15 || $self->class->any_var('CONFIG')
65             || { };
66              
67             my $module = delete($args->{ config_module })
68 4   33     15 || $class->any_var('CONFIG_MODULE')
69             || $self->CONFIG_MODULE;
70              
71 4 50       12 if (is_object($module, $config)) {
72             # $config is already a config module object of the right heritage
73 0         0 $self->debug("config is already a $module object") if DEBUG;
74             }
75             else {
76 4 100       12 if (ref $config eq HASH) {
    50          
77             # cool
78             }
79             elsif (! ref $config) {
80             # $config is a module name
81 1         1 $module = $config;
82 1         2 $config = { };
83             }
84             else {
85 0         0 return $self->error_msg( invalid => config => $config );
86             }
87 4         4 $self->debug("loading and instantiating a custom $module config object") if DEBUG;
88 4         8 class($module)->load;
89 4         18 $config = $module->new($config);
90             }
91              
92 4         15 $self->{ config } = $config;
93 4         6 $self->debug("hub config: $self->{ config }\n") if DEBUG;
94             }
95              
96             sub init_items {
97 4     4 0 8 my ($self, $args) = @_;
98              
99             # merge all values in $CONFIG_ITEMS in with $args->{ items };
100             $args->{ items } = $self->class->list_vars(
101             CONFIG_ITEMS => delete($args->{ config_items }), $args->{ items }
102 4         7 );
103              
104             $self->debug(
105             "hub config items: ",
106             $self->dump_data($args->{ items })
107 4         7 ) if DEBUG;
108              
109 4         8 return $self;
110             }
111              
112             sub init_collections {
113 0     0 0 0 my ($self, $config) = @_;
114 0         0 $self->init_components($config);
115 0         0 $self->init_resource($config);
116 0         0 $self->init_delegates($config);
117             }
118              
119             sub init_components {
120 2     2 0 6 shift->init_collection( components => @_ );
121             }
122              
123             sub init_resources {
124 0     0 0 0 shift->init_collection( resources => @_ );
125             }
126              
127             sub init_delegates {
128 1     1 0 3 shift->init_collection( delegates => @_ );
129             }
130              
131             sub init_collection {
132 3     3 0 5 my ($self, $type) = @_;
133 3         6 my $TYPE = uc $type;
134 3         6 my $cfg = $self->config($type);
135 3         9 my $bits = $self->{ $type } = $self->class->hash_vars(
136             $TYPE => $self->config($type)
137             );
138 3         3 $self->debug("init_collection: $type: ", $self->dump_data($bits)) if DEBUG;
139 3         7 return $bits;
140             }
141              
142             sub configure {
143 0     0 0 0 my ($self, $config) = self_params(@_);
144 0         0 my $item;
145              
146 0 0       0 if ($item = delete $config->{ components }) {
147 0         0 $self->components($item);
148             }
149 0 0       0 if ($item = delete $config->{ resources }) {
150 0         0 $self->resources($item);
151             }
152 0 0       0 if ($item = delete $config->{ delegates }) {
153 0         0 $self->delegates($item);
154             }
155             }
156              
157             #-----------------------------------------------------------------------------
158             # comment
159             #-----------------------------------------------------------------------------
160              
161             sub components {
162 2     2 1 6 my $self = shift->prototype;
163             my $comps = $self->{ components }
164 2   33     10 || $self->init_components;
165              
166 2 50       5 if (@_) {
167 0 0       0 my $args = ref $_[0] eq HASH ? shift : { @_ };
168 0         0 @$comps{ keys %$args } = values %$args;
169             }
170              
171 2         3 return $comps;
172             }
173              
174              
175             sub component {
176 2     2 1 2 my $self = shift;
177 2         4 my $comps = $self->components;
178 2         2 $self->debug("components: ", $self->dump_data($comps)) if DEBUG;
179             return @_
180 2 50       26 ? $comps->{ $_[0] }
181             : $comps;
182             }
183              
184              
185             sub delegates {
186 1     1 1 4 my $self = shift->prototype;
187 1         3 my $class = $self->class;
188             my $delgs = $self->{ delegates }
189 1   33     5 || $self->init_delegates;
190              
191 1 50       3 if (@_) {
192 0 0       0 my $args = ref $_[0] eq HASH ? shift : { @_ };
193 0         0 @$delgs{ keys %$args } = values %$args;
194             }
195              
196 1         2 return $delgs;
197             }
198              
199              
200             sub delegate {
201 1     1 1 3 my $self = shift;
202 1         15 my $delegs = $self->delegates;
203 1         1 $self->debug("delegates: ", $self->dump_data($delegs)) if DEBUG;
204             return @_
205 1 50       5 ? $delegs->{ $_[0] }
206             : $delegs;
207             }
208              
209              
210             sub auto_can {
211 2     2 1 4 my ($self, $name) = @_;
212 2         3 my $target;
213              
214 2 100       5 if ($target = $self->component($name)) {
    50          
215 1         1 $self->debug("creating component method in $self for $name") if DEBUG;
216 1         6 return $self->auto_component( $name => $target );
217             }
218             elsif ($target = $self->delegate($name)) {
219 0         0 $self->debug("creating delegate method in $self for $name") if DEBUG;
220 0         0 return $self->auto_delegate( $name => $target );
221             }
222 0         0 elsif (DEBUG) {
223             $self->debug("no component or delegate found in $self for $name");
224             }
225            
226 1         4 return undef;
227             }
228              
229              
230             sub auto_component {
231 1     1 1 3 my ($self, $name, $comp) = @_;
232 1   33     3 my $class = ref $self || $self;
233              
234 1   33     9 $LOADED->{ $name } ||= class($comp)->load;
235              
236             return sub {
237 1     1   2 my $self = shift;
238 1 50 33     5 my $args = @_ && ref $_[0] eq HASH ? shift : { @_ };
239 1 50       3 $self = $self->prototype unless ref $self;
240              
241 1   33     12 return $self->{ $name }
242             ||= $self->construct(
243             $name => {
244             # TODO: figure out what's going on here in terms of
245             # possible combinations of configuration options
246             %$args,
247             hub => $self,
248             module => $comp
249             }
250             );
251             }
252 1         12 }
253              
254              
255             sub auto_delegate {
256 0     0 1 0 my ($self, $name, $deleg) = @_;
257 0   0     0 my $class = ref $self || $self;
258            
259             # foo => bar is mapped to $self->bar->foo
260             # foo => [bar, baz] is mapped to $self->bar->baz
261 0 0       0 my ($m1, $m2) = ref $deleg eq ARRAY ? @$deleg : ($deleg, $name);
262              
263 0 0       0 return $self->error("Cannot auto_delegate() a method to itself: $m1 -> $m2")
264             if $m1 eq $m2;
265              
266             return sub {
267 0     0   0 shift->$m1->$m2(@_);
268 0         0 };
269             }
270              
271              
272              
273             # Configure and create a sub-component identified by $name, using
274             # any configuration items in $params and any values defined locally
275             # in a configuration hash, object or class.
276             #
277             # The $self->{ config } can contain a hash array of configuration
278             # items, or it can be a Badger::Config object or the class name of a
279             # Badger::Config object. We look in the hash, or call the object/class
280             # method to find $name (e.g. $hash->{ $name }, $object->$name, or
281             # $class->name()). This is merged with $params.
282              
283             sub construct {
284 1     1 1 2 my $self = shift;
285 1         2 my $name = shift;
286 1         2 my $config = $self->config($name, @_);
287              
288 1         2 $self->debug("$name module config: ", $self->dump_data($config)) if DEBUG;
289            
290             # see if a module name is specified in $args, config hash or use $pkgmod
291             my $module = $config->{ module }
292 1   50     3 || return $self->error_msg( no_module => $name );
293              
294             # load the module
295 1         2 class($module)->load;
296              
297 1         4 return $module->new($config);
298             }
299              
300             sub config {
301 7     7 1 15 my $self = shift->prototype;
302 7 50       10 my $config = $self->{ config }; return $config unless @_;
  7         19  
303 7         8 my $name = shift;
304 7         14 my $params = params(@_);
305 7         9 my $defaults;
306             my $method;
307              
308              
309 7 50 33     59 if ($config && ref $config eq HASH) {
    50 33        
    0 0        
310             # $self->{ config } can be a hash ref with a $name item
311 0         0 $defaults = $config->{ $name };
312             }
313             elsif (blessed $config && ($method = $config->can('get'))) {
314             # $self->{ config } can be an object with a get($name) method which we call
315 7         8 $self->debug("calling config->get($name)") if DEBUG;
316 7         13 $defaults = $method->($config, $name);
317             }
318             elsif (blessed $config && ($method = $config->can($name))) {
319             # $self->{ config } can be an object with a $name method which we call
320 0         0 $self->debug("calling config->$name") if DEBUG;
321 0         0 $defaults = $method->($config);
322             }
323             # no defaults
324 7 50       20 return $self->no_config($name, $params)
325             unless $defaults;
326              
327 0         0 $self->debug(
328             "config for $name: DEFAULTS: ",
329             $self->dump_data($defaults),
330             "\nPARAMS: ", $self->dump_data($params)
331             ) if DEBUG;
332              
333             return {
334 0         0 %$defaults,
335             %$params
336             };
337             }
338              
339             sub no_config {
340 7     7 0 12 my ($self, $name, $params) = @_;
341              
342 7         4 $self->debug(
343             "no_config for $name: ",
344             "PARAMS: ", $self->dump_data($params)
345             ) if DEBUG;
346              
347             # TODO: option to make this a failure
348 7   33     13 return $self->pkgvar_config($name, $params)
349             || ($params ? { %$params } : { });
350             }
351              
352             sub pkgvar_config {
353 7     7 0 8 my ($self, $name, $params) = @_;
354 7   100     11 my $pkgvar = $self->class->any_var(uc $name) || return $params;
355 6         15 my $config;
356              
357 6 50       12 if (ref $pkgvar eq HASH) {
358             # package variable can be a hash ref of config options
359 6         6 $config = $pkgvar;
360             }
361             else {
362             # or the name of a module
363 0         0 $config = {
364             module => $pkgvar
365             };
366             };
367              
368             return {
369 6         28 %$config,
370             %$params
371             };
372             }
373              
374              
375              
376             #------------------------------------------------------------------------
377             # destroy()
378             #
379             # Destroy the hub and cleanup any cache items we may have stored.
380             #------------------------------------------------------------------------
381              
382             sub destroy {
383 3     3 1 7 my $self = shift;
384              
385             # if called as a class method we cleanup any prototype object
386             # stored as a singleton in the $PROTOTYPE package variable
387 3 100       7 unless (ref $self) {
388 70     70   994 no strict 'refs';
  70         141  
  70         10696  
389 1         1 my $class = $self;
390 1   50     2 $self = ${"$class\::PROTOTYPE"} || return;
391 1         1 $self->debug("deleting hub prototype from \$$class\::PROTOTYPE\n") if DEBUG;
392 1         1 ${"$class\::PROTOTYPE"} = undef;
  1         4  
393             }
394              
395 3         3 $self->debug("destroying hub: $self\n") if DEBUG;
396              
397             # empty content of $self to break any circular references that
398             # we may have established with other items that point back to us
399 3         108 %$self = ();
400             }
401              
402              
403             sub DESTROY {
404 2     2   4 my $self = shift;
405 2         6 $self->destroy;
406             }
407              
408              
409             1;
410             __END__