File Coverage

lib/App/Reference.pm
Criterion Covered Total %
statement 85 121 70.2
branch 46 90 51.1
condition 11 24 45.8
subroutine 10 13 76.9
pod 9 9 100.0
total 161 257 62.6


line stmt bran cond sub pod time code
1              
2             #############################################################################
3             ## $Id: Reference.pm 9683 2007-06-26 15:30:18Z spadkins $
4             #############################################################################
5              
6             package App::Reference;
7             $VERSION = (q$Revision: 9683 $ =~ /(\d[\d\.]*)/)[0]; # VERSION numbers generated by svn
8              
9 8     8   25371 use strict;
  8         17  
  8         284  
10              
11 8     8   675 use App;
  8         16  
  8         10493  
12              
13             =head1 NAME
14              
15             App::Reference - a Perl reference, blessed so it can be accessed with methods
16              
17             =head1 SYNOPSIS
18              
19             use App::Reference;
20              
21             $ref = App::Reference->new();
22             $ref = App::Reference->new("file" => $file);
23             print $ref->dump(), "\n"; # use Data::Dumper to spit out the Perl representation
24              
25             # accessors
26             $property_value = $ref->get($property_name);
27             $branch = $ref->get_branch($branch_name,$create_flag); # get hashref
28             $ref->set($property_name, $property_value);
29              
30             # on-demand loading helper methods (private methods)
31             $ref->overlay($ref2); # merge the two structures using overlay rules
32             $ref->overlay($ref1, $ref2); # merge $ref2 onto $ref1
33             $ref->graft($branch_name, $ref2); # graft new structure onto branch
34              
35             =head1 DESCRIPTION
36              
37             App::Reference is a very thin class which wraps a few simple
38             methods around a perl reference which may contain a multi-level data
39             structure.
40              
41             =cut
42              
43             #############################################################################
44             # CLASS
45             #############################################################################
46              
47             =head1 Class: App::Reference
48              
49             * Throws: App::Exception
50             * Since: 0.01
51              
52             =head2 Requirements
53              
54             The App::Reference class satisfies the following requirements.
55              
56             o Minimum performance penalty to access perl data
57             o Ability to bless any reference into this class
58             o Ability to handle ARRAY and HASH references
59              
60             =cut
61              
62             #############################################################################
63             # CONSTRUCTOR METHODS
64             #############################################################################
65              
66             =head1 Constructor Methods:
67              
68             =cut
69              
70             #############################################################################
71             # new()
72             #############################################################################
73              
74             =head2 new()
75              
76             This constructor is used to create Reference objects.
77             Customized behavior for a particular type of Reference
78             is achieved by overriding the _init() method.
79              
80             * Signature: $ref = App::Reference->new($array_ref)
81             * Signature: $ref = App::Reference->new($hash_ref)
82             * Signature: $ref = App::Reference->new("array",@args)
83             * Signature: $ref = App::Reference->new(%named)
84             * Param: $array_ref []
85             * Param: $hash_ref {}
86             * Return: $ref App::Reference
87             * Throws: App::Exception
88             * Since: 0.01
89              
90             Sample Usage:
91              
92             use "App::Reference";
93              
94             $ref = App::Reference->new("array", "x", 1, -5.4, { pi => 3.1416 });
95             $ref = App::Reference->new( [ "x", 1, -5.4 ] );
96             $ref = App::Reference->new(
97             arg1 => 'value1',
98             arg2 => 'value2',
99             );
100              
101             =cut
102              
103             sub new {
104 11     11 1 38 my $this = shift;
105 11   33     82 my $class = ref($this) || $this;
106              
107             # bootstrap phase: bless an empty hash
108 11         26 my $self = {};
109 11         32 bless $self, $class;
110              
111             # create phase: replace empty hash with created hash, bless again
112 11         52 $self = $self->create(@_);
113 11         119 bless $self, $class;
114              
115 11         61 $self->_init(@_); # allows a subclass to override this portion
116              
117 11         32 return $self;
118             }
119              
120             #############################################################################
121             # PUBLIC METHODS
122             #############################################################################
123              
124             =head1 Public Methods:
125              
126             =cut
127              
128             #############################################################################
129             # get()
130             #############################################################################
131              
132             =head2 get()
133              
134             * Signature: $property_value = $ref->get($property_name);
135             * Param: $property_name string
136             * Return: $property_value string
137             * Throws: App::Exception
138             * Since: 0.01
139              
140             Sample Usage:
141              
142             $dbi = $ref->get("Repository.default.dbi");
143             $dbuser = $ref->get("Repository.default.dbuser");
144             $dbpass = $ref->get("Repository.default.dbpass");
145              
146             =cut
147              
148             sub get {
149 1 50   1 1 8 print "get(@_)\n" if ($App::DEBUG);
150 1         3 my ($self, $property_name, $ref) = @_;
151 1 50       4 $ref = $self if (!defined $ref);
152 1 50       7 if ($property_name =~ /^(.*)([\.\{\[])([^\.\[\]\{\}]+)([\]\}]?)$/) {
153 1         2 my ($branch_name, $attrib, $type, $branch);
154 1         2 $branch_name = $1;
155 1         2 $type = $2;
156 1         3 $attrib = $3;
157 1 50       4 $branch = ref($ref) eq "ARRAY" ? undef : $ref->{_branch}{$branch_name};
158 1 50       3 $branch = $self->get_branch($1,0,$ref) if (!defined $branch);
159 1 50 33     17 return undef if (!defined $branch || ref($branch) eq "");
160 1 50       4 return $branch->[$attrib] if (ref($branch) eq "ARRAY");
161 1         6 return $branch->{$attrib};
162             }
163             else {
164 0         0 return $self->{$property_name};
165             }
166             }
167              
168             #############################################################################
169             # get_branch()
170             #############################################################################
171              
172             =head2 get_branch()
173              
174             * Signature: $branch = $ref->get_branch($branch_name);
175             * Param: $branch_name string
176             * Return: $branch {}
177             * Throws: App::Exception
178             * Since: 0.01
179              
180             Sample Usage:
181              
182             $branch_name = "Repository.default";
183             $branch = $ref->get_branch($branch_name);
184             foreach $key (keys %$branch) {
185             $property = "${branch_name}.${key}";
186             print $property, "=", $branch->{$key}, "\n";
187             }
188             $dbi = $branch->{dbi};
189             $dbuser = $branch->{dbuser};
190             $dbpass = $branch->{dbpass};
191              
192             =cut
193              
194             sub get_branch {
195 88 50   88 1 1064 print "get_branch(@_)\n" if ($App::DEBUG);
196 88         212 my ($self, $branch_name, $create, $ref) = @_;
197 88         97 my ($sub_branch_name, $branch_piece, $attrib, $type, $branch, $cache_ok);
198 88 100       171 $ref = $self if (!defined $ref);
199              
200             # check the cache quickly and return the branch if found
201 88   66     475 $cache_ok = (ref($ref) ne "ARRAY" && $ref eq $self); # only cache from $self
202 88 100       169 $branch = $ref->{_branch}{$branch_name} if ($cache_ok);
203 88 100       184 return ($branch) if (defined $branch);
204              
205             # not found, so we need to parse the $branch_name and walk the $ref tree
206 87         100 $branch = $ref;
207 87         111 $sub_branch_name = "";
208              
209             # these: "{field1}" "[3]" "field2." are all valid branch pieces
210 87         461 while ($branch_name =~ s/^([\{\[]?)([^\.\[\]\{\}]+)([\.\]\}]?)//) {
211              
212 166         413 $branch_piece = $2;
213 166         240 $type = $3;
214 166 100       499 $sub_branch_name .= ($3 eq ".") ? "$1$2" : "$1$2$3";
215              
216 166 50       323 if (ref($branch) eq "ARRAY") {
217 0 0       0 if (! defined $branch->[$branch_piece]) {
218 0 0       0 if ($create) {
219 0 0       0 $branch->[$branch_piece] = ($type eq "]") ? [] : {};
220 0         0 $branch = $branch->[$branch_piece];
221 0 0       0 $ref->{_branch}{$sub_branch_name} = $branch if ($cache_ok);
222             }
223             else {
224 0         0 return(undef);
225             }
226             }
227             else {
228 0         0 $branch = $branch->[$branch_piece];
229 0         0 $sub_branch_name .= "$1$2$3"; # accumulate the $sub_branch_name
230             }
231             }
232             else {
233 166 100       395 if (! defined $branch->{$branch_piece}) {
234 74 100       127 if ($create) {
235 73 50       262 $branch->{$branch_piece} = ($type eq "]") ? [] : {};
236 73         132 $branch = $branch->{$branch_piece};
237 73 100       160 $ref->{_branch}{$sub_branch_name} = $branch if ($cache_ok);
238             }
239             else {
240 1         3 return(undef);
241             }
242             }
243             else {
244 92         208 $branch = $branch->{$branch_piece};
245             }
246             }
247 165 100       788 $sub_branch_name .= $type if ($type eq ".");
248             }
249 86         225 return $branch;
250             }
251              
252             #############################################################################
253             # set()
254             #############################################################################
255              
256             =head2 set()
257              
258             * Signature: $ref->get($property_name, $property_value);
259             * Param: $property_name string
260             * Param: $property_value string
261             * Throws: App::Exception
262             * Since: 0.01
263              
264             Sample Usage:
265              
266             $dbi = $ref->get("Repository.default.dbi");
267             $dbuser = $ref->get("Repository{default}{dbuser}");
268             $dbpass = $ref->get("Repository.default{dbpass}");
269              
270             =cut
271              
272             sub set {
273 85 50   85 1 1336 print "set(@_)\n" if ($App::DEBUG);
274 85         223 my ($self, $property_name, $property_value, $ref) = @_;
275 85 100       171 $ref = $self if (!defined $ref);
276              
277 85         108 my ($branch_name, $attrib, $type, $branch, $cache_ok);
278 85 50       489 if ($property_name =~ /^(.*)([\.\{\[])([^\.\[\]\{\}]+)([\]\}]?)$/) {
279 85         149 $branch_name = $1;
280 85         115 $type = $2;
281 85         117 $attrib = $3;
282 85   66     433 $cache_ok = (ref($ref) ne "ARRAY" && $ref eq $self);
283 85 100       154 $branch = $ref->{_branch}{$branch_name} if ($cache_ok);
284 85 50       560 $branch = $self->get_branch($1,1,$ref) if (!defined $branch);
285             }
286             else {
287 0         0 $branch = $ref;
288 0         0 $attrib = $property_name;
289             }
290              
291 85 50       257 if (ref($branch) eq "ARRAY") {
292 0         0 $branch->[$attrib] = $property_value;
293             }
294             else {
295 85         442 $branch->{$attrib} = $property_value;
296             }
297             }
298              
299             #############################################################################
300             # overlay()
301             #############################################################################
302              
303             =head2 overlay()
304              
305             * Signature: $ref->overlay($ref2);
306             * Signature: $ref->overlay($ref1, $ref2);
307             * Param: $ref1 {}
308             * Param: $ref2 {}
309             * Return: void
310             * Throws: App::Exception
311             * Since: 0.01
312              
313             Sample Usage:
314              
315             # merge the two config structures using overlay rules
316             $ref->overlay($ref2);
317              
318             # merge $ref2 onto $ref1
319             $ref->overlay($ref1, $ref2);
320              
321             NOTE: right now, this just copies top-level keys of a hash reference
322             from one hash to the other.
323              
324             TODO: needs to nested/recursive overlaying
325              
326             =cut
327              
328             sub overlay {
329 6 50   6 1 458 &App::sub_entry if ($App::trace);
330 6         19 my ($self, $ref1, $ref2) = @_;
331 6 50       18 if (!defined $ref2) {
332 0         0 $ref2 = $ref1;
333 0         0 $ref1 = $self;
334             }
335 6         12 my $ref1type = ref($ref1);
336 6         13 my $ref2type = ref($ref2);
337 6 50 33     61 if ($ref1type eq "" || $ref2type eq "") {
    50 33        
338             # scalar: nothing to do
339             }
340             elsif ($ref1type eq "ARRAY" || $ref2type eq "ARRAY") {
341             # array: nothing to do
342             }
343             else { # assume they are both hashes
344 6         23 foreach my $key (keys %$ref2) {
345 8 100       23 if (!exists $ref1->{$key}) {
346 5         27 $ref1->{$key} = $ref2->{$key};
347             }
348             else {
349 3         6 $ref1type = ref($ref1->{$key});
350 3 100 66     16 if ($ref1type && $ref1type ne "ARRAY") {
351 2         3 $ref2type = ref($ref2->{$key});
352 2 50 33     10 if ($ref2type && $ref2type ne "ARRAY") {
353 2         7 $self->overlay($ref1->{$key}, $ref2->{$key});
354             }
355             }
356             }
357             }
358             }
359 6 50       31 &App::sub_exit() if ($App::trace);
360             }
361              
362             #############################################################################
363             # graft()
364             #############################################################################
365              
366             =head2 graft()
367              
368             * Signature: $ref->graft($branch_name, $ref2);
369             * Param: $branch_name string
370             * Param: $ref2 {}
371             * Return: void
372             * Throws: App::Exception
373             * Since: 0.01
374              
375             Sample Usage:
376              
377             # graft new config structure onto branch
378             $ref->graft($branch_name, $ref2);
379              
380             =cut
381              
382 0     0 1 0 sub graft {
383             }
384              
385             #############################################################################
386             # dump()
387             #############################################################################
388              
389             =head2 dump()
390              
391             * Signature: $perl = $ref->dump();
392             * Param: void
393             * Return: $perl text
394             * Throws: App::Exception
395             * Since: 0.01
396              
397             Sample Usage:
398              
399             $ref = $context->config();
400             print $ref->dump(), "\n";
401              
402             =cut
403              
404 8     8   3997 use Data::Dumper;
  8         31770  
  8         4094  
405              
406             sub dump {
407 0     0 1 0 my ($self, $ref) = @_;
408 0 0       0 $ref = $self if (!$ref);
409 0         0 my $d = Data::Dumper->new([ $ref ], [ "ref" ]);
410 0         0 $d->Indent(1);
411 0         0 return $d->Dump();
412             }
413              
414             #############################################################################
415             # print()
416             #############################################################################
417              
418             =head2 print()
419              
420             * Signature: $ref->print();
421             * Param: void
422             * Return: void
423             * Throws: App::Exception
424             * Since: 0.01
425              
426             Sample Usage:
427              
428             $context->print();
429              
430             =cut
431              
432             sub print {
433 0     0 1 0 my ($self, $ref) = @_;
434 0 0       0 $ref = $self if (!$ref);
435 0         0 print $self->dump($ref);
436             }
437              
438             #############################################################################
439             # PROTECTED METHODS
440             #############################################################################
441              
442             =head1 Protected Methods:
443              
444             The following methods are intended to be called by subclasses of the
445             current class.
446              
447             =cut
448              
449             #############################################################################
450             # create()
451             #############################################################################
452              
453             =head2 create()
454              
455             The create() method is used to create the Perl structure that will
456             be blessed into the class and returned by the constructor.
457             It may be overridden by a subclass to provide customized behavior.
458              
459             * Signature: $ref = App::Reference->create("array", @args)
460             * Signature: $ref = App::Reference->create($arrayref)
461             * Signature: $ref = App::Reference->create($hashref)
462             * Signature: $ref = App::Reference->create($hashref, %named)
463             * Signature: $ref = App::Reference->create(%named)
464             * Param: $hashref {}
465             * Param: $arrayref []
466             * Return: $ref ref
467             * Throws: App::Exception
468             * Since: 0.01
469              
470             Sample Usage:
471              
472             =cut
473              
474             sub create {
475 5     5 1 13 my $self = shift;
476 5 50       21 print "create(@_)\n" if ($App::DEBUG);
477 5 50       27 return {} if ($#_ == -1);
478 0 0       0 if (ref($_[0]) ne "") {
479 0 0       0 return $_[0] if ($#_ == 0);
480 0 0       0 App::Exception->throw(error => "Reference->create(): args supplied with an ARRAY ref\n")
481             if (ref($_[0]) eq "ARRAY");
482 0         0 my ($ref, $i);
483 0         0 $ref = shift;
484 0         0 for ($i = 0; $i < $#_; $i += 2) {
485             #print "arg: $_[$i] => $_[$i+1]\n";
486 0         0 $ref->{$_[$i]} = $_[$i+1];
487             }
488 0         0 return $ref;
489             }
490 0 0       0 if ($_[0] eq "array") {
    0          
491 0         0 shift;
492 0         0 return [ @_ ];
493             }
494             elsif ($#_ % 2 == 0) {
495 0         0 App::Exception->throw(error => "Reference->create(): Odd number of named parameters\n");
496             }
497 0         0 return { @_ };
498             }
499              
500             #############################################################################
501             # _init()
502             #############################################################################
503              
504             =head2 _init()
505              
506             The _init() method is called from within the standard Reference constructor.
507             The _init() method in this class does nothing.
508             It allows subclasses of the Reference to customize the behavior of the
509             constructor by overriding the _init() method.
510              
511             * Signature: _init($named)
512             * Param: $named {} [in]
513             * Return: void
514             * Throws: App::Exception
515             * Since: 0.01
516              
517             Sample Usage:
518              
519             $ref->_init($args);
520              
521             =cut
522              
523             sub _init {
524 11     11   23 my $self = shift;
525             }
526              
527             #############################################################################
528             # PRIVATE METHODS
529             #############################################################################
530              
531             =head1 Private Methods:
532              
533             The following methods are intended to be called only within this class.
534              
535             =cut
536              
537             =head1 ACKNOWLEDGEMENTS
538              
539             * Author: Stephen Adkins
540             * License: This is free software. It is licensed under the same terms as Perl itself.
541              
542             =head1 SEE ALSO
543              
544             none
545              
546             =cut
547              
548             1;
549