File Coverage

blib/lib/Class/ObjectTemplate.pm
Criterion Covered Total %
statement 168 204 82.3
branch 39 74 52.7
condition n/a
subroutine 32 37 86.4
pod 0 17 0.0
total 239 332 71.9


line stmt bran cond sub pod time code
1             package Class::ObjectTemplate;
2             require Exporter;
3              
4 1     1   6 use vars qw(@ISA @EXPORT $VERSION $DEBUG);
  1         1  
  1         75  
5 1     1   5 use Carp;
  1         2  
  1         73  
6 1     1   5 use strict;
  1         2  
  1         35  
7 1     1   4 no strict 'refs';
  1         1  
  1         916  
8              
9             @ISA = qw(Exporter);
10             @EXPORT = qw(attributes);
11             $VERSION = 0.7;
12              
13             $DEBUG = 0; # assign 1 to it to see code generated on the fly
14              
15             # Create accessor functions
16             sub attributes {
17 7     7 0 22 my ($pkg) = caller;
18              
19 7         289 croak "Error: attributes() invoked multiple times"
20 7 100       12 if scalar @{"${pkg}::_ATTRIBUTES_"};
21              
22             #
23             # We must define a constructor for the class, because we must
24             # declare the variables used for the free list, $_max_id and
25             # @_free. If we don't, we will get compile errors for any class
26             # that declares itself a subclass of any Class::ObjectTemplate
27             # class
28             #
29 6         15 my $code .= _define_constructor($pkg);
30              
31             # _defined_constructor() may have added attributes that we inherited
32             # from any superclasses now add the new attributes
33 6         11 push(@{"${pkg}::_ATTRIBUTES_"},@_);
  6         16  
34              
35             # now define any accessor methods
36 6 50       14 print STDERR "Creating methods for $pkg\n" if $DEBUG;
37 6         12 foreach my $attr (@_) {
38 12 50       25 print STDERR " defining method $attr\n" if $DEBUG;
39             # If a field name is "color", create a global list in the
40             # calling package called @_color
41 12         11 @{"${pkg}::_$attr"} = ();
  12         50  
42              
43             # If the accessor is already present, give a warning
44 12 100       98 if (UNIVERSAL::can($pkg,"$attr")) {
45 1         199 carp "$pkg already has method: $attr";
46             } else {
47 11         22 $code .= _define_accessor ($pkg, $attr);
48             }
49             }
50 1 0   1 0 4 eval $code;
  1 50   1 0 2  
  1 50   1 0 310  
  1 50   1 0 6  
  1 50   1 0 2  
  1 50   1 0 273  
  1 50   0 0 5  
  1 0   1 0 2  
  1 0   1 0 136  
  1 0   0 0 6  
  1 50   1   2  
  1 50   1   305  
  1 50   1   5  
  1 50   2   2  
  1 50   8   304  
  1 50   0   5  
  1 50   2   1  
  1 50   0   277  
  6 50   2   453  
  0 100   0   0  
  0 100   2   0  
  0 0   3   0  
  0 100   2   0  
  1 0       4  
  1 100       4  
  1 0       5  
  0 100       0  
  1 100       2  
  1 100       4  
  1         5  
  1         10  
  1         6  
  1         4  
  1         37  
  1         3  
  1         579  
  0         0  
  1         2  
  1         4  
  1         4  
  1         8  
  1         5  
  1         3  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         2  
  1         1  
  1         3  
  0         0  
  1         2  
  1         2  
  1         3  
  1         7  
  1         4  
  1         2  
  1         29  
  1         2  
  1         3  
  0         0  
  1         2  
  1         2  
  1         8  
  1         7  
  1         6  
  1         14  
  1         28  
  1         2  
  1         4  
  0         0  
  1         2  
  1         2  
  1         3  
  1         7  
  1         3  
  1         3  
  2         31  
  2         7  
  1         38  
  1         5  
  8         152  
  8         31  
  4         27  
  4         16  
  0         0  
  0         0  
  0         0  
  0         0  
  2         6  
  2         8  
  1         3  
  1         17  
  0         0  
  0         0  
  0         0  
  0         0  
  2         5  
  2         8  
  1         3  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  2         51  
  2         8  
  1         7  
  1         5  
  3         7  
  3         8  
  1         3  
  2         7  
  2         6  
  2         8  
  1         3  
  1         4  
51 6 50       24 if ($@) {
52 0         0 die "ERROR defining constructor and attributes for '$pkg':\n"
53             . "\t$@\n"
54             . "-----------------------------------------------------"
55             . $code;
56             }
57             }
58              
59             # $obj->set_attributes (name => 'John', age => 23);
60             # Or, $obj->set_attributes (['name', 'age'], ['John', 23]);
61             sub set_attributes {
62 1     1 0 2 my $obj = shift;
63 1         1 my $attr_name;
64 1 50       3 if (ref($_[0])) {
65 0         0 my ($attr_name_list, $attr_value_list) = @_;
66 0         0 my $i = 0;
67 0         0 foreach $attr_name (@$attr_name_list) {
68 0         0 $obj->$attr_name($attr_value_list->[$i++]);
69             }
70             } else {
71 1         17 my ($attr_name, $attr_value);
72 1         8 while (@_) {
73 1         2 $attr_name = shift;
74 1         2 $attr_value = shift;
75 1         27 $obj->$attr_name($attr_value);
76             }
77             }
78             }
79              
80              
81             # @attrs = $obj->get_attributes (qw(name age));
82             sub get_attributes {
83 1     1 0 10 my $obj = shift;
84 1         2 my $pkg = ref($obj);
85 1         2 my (@retval);
86 1         3 return map {$ {"${pkg}::_$_"}[$$obj]} @_;
  2         3  
  2         12  
87             }
88              
89             sub get_attribute_names {
90 10     10 0 16 my $pkg = shift;
91 10 50       26 $pkg = ref($pkg) if ref($pkg);
92 10         11 return @{"${pkg}::_ATTRIBUTES_"};
  10         53  
93             }
94              
95             sub set_attribute {
96 1     1 0 3 my ($obj, $attr_name, $attr_value) = @_;
97 1         4 my ($pkg) = ref($obj);
98 1         1 return $ {"${pkg}::_$attr_name"}[$$obj] = $attr_value;
  1         6  
99             }
100              
101             sub get_attribute {
102 2     2 0 5 my ($obj, $attr_name, $attr_value) = @_;
103 2         5 my ($pkg) = ref($obj);
104 2         2 return $ {"${pkg}::_$attr_name"}[$$obj];
  2         19  
105             }
106              
107             sub DESTROY {
108             # release id back to free list
109 4     4   53 my $obj = shift;
110 4         8 my $pkg = ref($obj);
111 4         8 my $inst_id = $$obj;
112              
113             # Release all the attributes in that row
114 4         10 my (@attributes) = get_attribute_names($pkg);
115 4         10 foreach my $attr (@attributes) {
116 12         13 undef $ {"${pkg}::_$attr"}[$inst_id];
  12         44  
117             }
118              
119             # The free list is *always* maintained independently by each base
120             # class
121 4         20 push(@{"${pkg}::_free"},$inst_id);
  4         151  
122             }
123              
124 5     5 0 146 sub initialize { }; # dummy method, if subclass doesn't define one.
125              
126             #################################################################
127              
128             sub _define_constructor {
129 6     6   7 my $pkg = shift;
130 6         12 my $free = "\@${pkg}::_free";
131              
132             # inherit any attributes from our superclasses
133 6 50       6 if (defined (@{"${pkg}::ISA"})) {
  6         20  
134 6         6 foreach my $base_pkg (@{"${pkg}::ISA"}) {
  6         17  
135 6         6 push (@{"${pkg}::_ATTRIBUTES_"}, get_attribute_names($base_pkg));
  6         17  
136             }
137             }
138              
139 6         19 my $code = <<"CODE";
140             package $pkg;
141             use vars qw(\$_max_id \@_free);
142             sub new {
143             my \$class = shift;
144             my \$inst_id;
145             if (scalar $free) {
146             \$inst_id = shift($free);
147             } else {
148             \$inst_id = \$_max_id++;
149             }
150             my \$obj = bless \\\$inst_id, \$class;
151             \$obj->set_attributes(\@_) if \@_;
152             my \$rc = \$obj->initialize;
153             return undef if \$rc == -1;
154             \$obj;
155             }
156              
157             # Set up the free list, and the ID counter
158             \@_free = ();
159             \$_max_id = 0;
160              
161             CODE
162 6         14 return $code;
163             }
164              
165             sub _define_accessor {
166 11     11   15 my ($pkg, $attr) = @_;
167              
168             # This code creates an accessor method for a given
169             # attribute name. This method returns the attribute value
170             # if given no args, and modifies it if given one arg.
171             # Either way, it returns the latest value of that attribute
172              
173 11         23 my $code = <<"CODE";
174             package $pkg;
175             sub $attr { # Accessor ...
176             my \$name = ref(\$_[0]) . "::_$attr";
177             \@_ > 1 ? \$name->[\${\$_[0]}] = \$_[1] # set
178             : \$name->[\${\$_[0]}]; # get
179             }
180             CODE
181 11         37 return $code;
182             }
183              
184             1;
185             __END__