File Coverage

blib/lib/Enumeration.pm
Criterion Covered Total %
statement 81 84 96.4
branch 19 22 86.3
condition 11 21 52.3
subroutine 23 24 95.8
pod 9 13 69.2
total 143 164 87.2


line stmt bran cond sub pod time code
1             =for gpg
2             -----BEGIN PGP SIGNED MESSAGE-----
3             Hash: SHA1
4              
5             =head1 NAME
6              
7             Enumeration - Yet Another enumeration class implementation.
8              
9             =head1 VERSION
10              
11             This is version 0.03 of Enumeration, of March 26, 2008.
12              
13             =cut
14              
15 7     7   189479 use strict;
  7         18  
  7         238  
16 7     7   34 use warnings;
  7         13  
  7         342  
17             package Enumeration;
18             $Enumeration::VERSION = '0.03';
19              
20 7     7   42 use Scalar::Util qw(refaddr);
  7         11  
  7         1375  
21              
22 7         66 use overload '""' => \&value,
23             'eq' => \&equals,
24 7     7   11955 'ne' => \¬_equals;
  7         8738  
25              
26             # Auto-croaking saves program startup time:
27 9     9 0 55 sub croak { require Carp; goto &Carp::croak }
  9         1122  
28              
29             # Enumeration constants for each subclass
30             my %class_symbols;
31              
32             # This should ONLY be called by subclasses.
33             # Call as: __PACKAGE__->set_enumerations(@list_of_symbols);
34             sub set_enumerations
35             {
36 7     7 1 2011 my $class = shift;
37 7         102 $class_symbols{$class}{$_} = 1 for @_;
38 7         44 return 1;
39             }
40              
41             # Return a list of enumerations allowable in the given class.
42             sub enumerations
43             {
44 0     0 0 0 my $class = shift;
45 0         0 return keys %{ $class_symbols{$class} };
  0         0  
46             }
47              
48             sub import
49             {
50 11     11   80 my $class = shift;
51 11   66     59 my $import = @_ && $_[0] eq ':all';
52              
53 11         22 my $cpkg = caller;
54 11         41 foreach my $sym (keys %{$class_symbols{$class}})
  11         2210  
55             {
56 7     7   1722 no strict 'refs';
  7         15  
  7         6513  
57 31         49 my $full_name = $cpkg . '::' . $sym;
58 31         47 my $local_name = $class . '::' . $sym;
59              
60 31 100   7   102 *$full_name = sub () { $sym } if $import;
  7         1322  
61 16     16   2363 *$local_name = sub () { $sym }
62 31         9611 }
63             }
64              
65              
66             # OO enclosure.
67             {
68             # Enumeration constants for objects created directly from the Enumeration class.
69             my %instance_symbols;
70             my %instance_value;
71              
72             sub new
73             {
74 10     10 1 1079 my $class = shift;
75 10         14 my $self = bless \do { my $dummy } => $class;
  10         23  
76              
77             # Caller is creating an on-the-fly enumeration
78 10 100       29 if ($class eq 'Enumeration')
79             {
80 3         7 my %values = map {$_ => 1} @_;
  12         31  
81 3         22 $instance_symbols{refaddr $self} = \%values;
82             }
83             else # Caller is using a subclass
84             {
85 7 50       28 croak "Too many arguments to ${class}->new" if @_ > 1;
86 7         34 $instance_symbols{refaddr $self} = $class_symbols{$class};
87 7 100       40 $self->set(shift) if @_;
88             }
89              
90 10         25 return $self;
91             }
92              
93             sub DESTROY
94             {
95 10     10   1568 my $self = shift;
96 10         33 delete $instance_symbols{refaddr $self};
97 10         381 delete $instance_value{refaddr $self};
98             }
99              
100             # Is a given value in the list of enumeration values that are legal
101             # for this class or object?
102             sub is_allowable_value
103             {
104 88     88 1 92 my $what = shift; # may be class name string or an object reference
105 88         95 my $value = shift;
106 88 50       154 return 1 if not defined $value; # undef is always allowed.
107              
108             # It's a "free" enum object -- instance contains the allowable values.
109 88 100       183 if (ref $what eq 'Enumeration')
110             {
111 38         168 return $instance_symbols{refaddr $what}{$value};
112             }
113              
114             # It's a subclass-based object -- enumeration is at the class level.
115 50   33     98 $what = ref ($what) || $what;
116 50         182 return $class_symbols{$what}{$value};
117             }
118              
119             # simple internal routine for generating a consistent error message
120             # throughout.
121             sub _check
122             {
123 88 100   88   183 croak qq{"$_[1]" is not an allowable value}
124             if not $_[0]->is_allowable_value($_[1]);
125             }
126              
127             # Set the object's value.
128             sub set
129             {
130 18     18 1 480 my $self = shift;
131 18         24 my $value = shift;
132              
133 18         52 $self->_check($value);
134 15         54 $instance_value{refaddr $self} = $value;
135             }
136              
137             # Return the object's value.
138             sub value
139             {
140 6     6 1 66 my $self = shift;
141 6         111 return ref($self) . '::' . $instance_value{refaddr $self}
142             }
143             sub bare_value
144             {
145 6     6 1 918 my $self = shift;
146 6         33 return $instance_value{refaddr $self}
147             }
148              
149             # Query the object's status; check to see if it is a given value.
150             sub is
151             {
152 53     53 1 68 my $self = shift;
153 53         86 my $value = shift;
154              
155             # Comparing to another enum object?
156 53 100 66     163 if (ref $value && $value->isa('Enumeration'))
157             {
158             # Compatible classes? Equivalent values?
159 11   100     59 return ref $value eq ref $self &&
160             _defeq($instance_value{refaddr $self},
161             $instance_value{refaddr $value});
162             }
163              
164 42         68 $self->_check($value);
165 40         116 return _defeq($instance_value{refaddr $self}, $value);
166             }
167              
168             # "Complex" equality:
169             # If either value is undef, then they're equal only if both are undef
170             # Otherwise, just a simple string equality.
171             sub _defeq
172             {
173 48     48   546 my ($v1, $v2) = @_;
174              
175 48 50 0     182 return (!defined $v1 && !defined $v2)
      33        
176             if (!defined $v1 || !defined $v2);
177              
178 48         217 return $v1 eq $v2;
179             }
180              
181             # Query the object's status; check to see if it is any of a number
182             # of possible values.
183             # Each status passed must be an allowable value.
184             sub is_any
185             {
186 14     14 1 1157 my $self = shift;
187              
188 14         528 foreach my $value (@_)
189             {
190 34 100 66     91 next if ref $value && $value->isa('Enumeration');
191 28         43 $self->_check($value);
192             }
193              
194 10         42 foreach my $value (@_)
195             {
196 26 100       46 return 1 if $self->is($value);
197             }
198 4         15 return;
199             }
200              
201             # Opposite of is_any. (Duh)
202             sub is_none
203             {
204 7     7 1 1047 my $self = shift;
205 7         15 return ! $self->is_any(@_);
206             }
207              
208             # Overload methods
209              
210             sub equals
211             {
212 8     8 0 25 return $_[0]->is($_[1]);
213             }
214              
215             sub not_equals
216             {
217 7     7 0 17 return ! $_[0]->is($_[1]);
218             }
219              
220             }
221              
222              
223             return 'a true value';
224             __END__