File Coverage

blib/lib/Object/Enum.pm
Criterion Covered Total %
statement 99 99 100.0
branch 34 38 89.4
condition 12 16 75.0
subroutine 24 24 100.0
pod 6 6 100.0
total 175 183 95.6


line stmt bran cond sub pod time code
1             package Object::Enum;
2             $Object::Enum::VERSION = '0.075';
3 6     6   150906 use strict;
  6         9  
  6         158  
4 6     6   22 use warnings;
  6         7  
  6         119  
5 6     6   123 use 5.006001;
  6         16  
6              
7 6     6   17 use Carp ();
  6         6  
  6         62  
8 6     6   2542 use Sub::Install ();
  6         6926  
  6         114  
9              
10 6         2505 use base qw(
11             Class::Data::Inheritable
12             Class::Accessor::Fast
13 6     6   26 );
  6         7  
14              
15             __PACKAGE__->mk_classdata($_) for (
16             '_values',
17             '_unset',
18             '_default',
19             '_readonly',
20             );
21              
22             __PACKAGE__->mk_accessors(
23             'value',
24             );
25              
26             __PACKAGE__->_unset(1);
27              
28             use overload (
29 6         32 q{""} => '_stringify',
30             fallback => 1,
31 6     6   21820 );
  6         2221  
32              
33 6         47 use Sub::Exporter -setup => {
34             exports => [ Enum => \&_build_enum ],
35 6     6   3095 };
  6         33256  
36              
37             sub _build_enum {
38 3     3   348 my ($class, undef, $arg) = @_;
39 3 100   11   10 return sub { $class->new({ %$arg, %{shift || {} } }) };
  11         1632  
  11         62  
40             }
41              
42             =head1 NAME
43              
44             Object::Enum - replacement for C<< if ($foo eq 'bar') >>
45              
46             =head1 SYNOPSIS
47              
48             use Object::Enum qw(Enum);
49              
50             my $color = Enum([ qw(red yellow green) ]);
51             # ... later
52             if ($color->is_red) {
53             # it can't be yellow or green
54              
55             =head1 EXPORTS
56              
57             See L<Sub::Exporter> for ways to customize this module's
58             exports.
59              
60             =head2 Enum
61              
62             An optional shortcut for C<< Object::Enum->new >>.
63              
64             =head1 CLASS METHODS
65              
66             =head2 new
67              
68             my $obj = Object::Enum->new(\@values);
69             # or
70             $obj = Object::Enum->new(\%arg);
71              
72             Return a new Object::Enum, with one or more sets of possible
73             values.
74              
75             The simplest case is to pass an arrayref, which returns an
76             object capable of having any one of the given values or of
77             being unset.
78              
79             The more complex cases involve passing a hashref, which may
80             have the following keys:
81              
82             =over
83              
84             =item * unset
85              
86             whether this object can be 'unset' (defaults to true)
87              
88             =item * default
89              
90             this object's default value is (defaults to undef)
91              
92             =item * values
93              
94             an arrayref, listing the object's possible values (at least
95             one required)
96              
97             =item * readonly
98              
99             boolean value to indicate if the object is read-only. If set
100             to read-only the objects C<value> and C<set_*> methods become ineffectual.
101              
102             =back
103              
104             =cut
105              
106             my $id = 0;
107             sub _generate_class {
108 11     11   9 my $class = shift;
109 6     6   1747 no strict 'refs';
  6         7  
  6         2567  
110 11         39 my $gen = sprintf "%s::obj_%08d", $class, ++$id;
111 11         10 push @{$gen."::ISA"}, $class;
  11         119  
112 11         19 return $gen;
113             }
114              
115             sub _mk_values {
116 11     11   13 my $class = shift;
117 11         23 for my $value (keys %{ $class->_values }) {
  11         17  
118             Sub::Install::install_sub({
119             into => $class,
120             as => "set_$value",
121 8     8   583 code => sub { $_[0]->value($value); return $_[0] },
  8         41  
122 23         412 });
123             Sub::Install::install_sub({
124             into => $class,
125             as => "is_$value",
126 14   50 14   1869 code => sub { (shift->value || '') eq $value },
127 23 50       824 }) unless $class->can("is_$value");
128             }
129             }
130              
131             sub new {
132 14     14 1 826 my ($class, $arg) = @_;
133 14   50     24 $arg ||= [];
134 14 100       35 if (ref $arg eq 'ARRAY') {
135 2         4 $arg = { values => $arg };
136             }
137              
138 14 100       11 unless (@{$arg->{values} || []}) {
  14 100       41  
139 1         176 Carp::croak("at least one possible value must be provided");
140             }
141              
142 13 100       27 exists $arg->{unset} or $arg->{unset} = 1;
143 13 100       24 exists $arg->{default} or $arg->{default} = undef;
144 13 100       22 exists $arg->{readonly} or $arg->{readonly} = 0;
145              
146 13 100 66     25 if (!$arg->{unset} && !defined $arg->{default}) {
147 1         113 Carp::croak("must supply a defined default for 'unset' to be false");
148             }
149              
150 12 100 100     25 if (defined($arg->{default}) && ! grep {
151             $_ eq $arg->{default}
152 14         47 } @{$arg->{values}}) {
  7         9  
153 1         95 Carp::croak("default value must be listed in 'values' or undef");
154             }
155              
156 11         22 my $gen = $class->_generate_class;
157 11         70 $gen->_unset($arg->{unset});
158 11         251 $gen->_default($arg->{default});
159 11         196 $gen->_readonly($arg->{readonly});
160 11         157 $gen->_values({ map { $_ => 1 } @{$arg->{values}} });
  23         74  
  11         17  
161 11         174 $gen->_mk_values;
162              
163             # constructors shouldn't call cloners
164             #return $gen->spawn;
165 11         262 return $gen->_curried;
166             }
167              
168             sub _stringify {
169 9     9   604 my $self = shift;
170 9 50       17 return '(undef)' unless defined $self->value;
171 9         42 return $self->value;
172             }
173              
174             =head1 OBJECT METHODS
175              
176             =head2 spawn
177              
178             =head2 clone
179              
180             my $new = $obj->clone;
181              
182             my $new = $obj->clone($value);
183              
184             Create a new Enum from an existing object, using the same arguments as were
185             originally passed to C<< new >> when that object was created.
186              
187             An optional value may be passed in; this is identical to (but more convenient
188             than) calling C<value> with the same argument on the newly cloned object.
189              
190             This method was formerly named C<spawn>. That name will still work but is
191             deprecated.
192              
193             =cut
194              
195             sub _curried {
196 18     18   20 my $class = shift;
197 18 100 66     45 my $self = bless {
198             value => ref($class)? $class->value : $class->_default,
199             } => ref($class) || $class;
200 18 100       248 $self->value(@_) if @_;
201              
202 18         45 return $self;
203             }
204              
205             sub clone {
206 7     7 1 753 my $self = shift->_curried(@_);
207 7 100       15 $self->_readonly(0)
208             if $self->_readonly;
209              
210 7         46 return $self;
211             }
212              
213 6     6   1209 BEGIN { *spawn = \&clone }
214              
215             =head2 readonly
216              
217             my $obj = $obj->readonly(\@values, $value)
218              
219             Creates a read-only enum object, also known as immutable. When enum objects are created
220             with this C<< set_* >> methods, and the C<value> method will become ineffectual.
221              
222             If you want a mutable version, simply clone the immutable version
223              
224             my $new_obj = $readonly_obj->clone;
225             $new_obj->set_red;
226              
227             =cut
228              
229             sub readonly {
230 1     1 1 449 my $class = shift;
231 1         1 my $values = shift;
232 1         1 my $value = shift;
233              
234 1 50       4 $values = []
235             unless ref($values) eq 'ARRAY';
236              
237 1         3 return $class->new({
238             values => $values,
239             default => $value,
240             readonly => 1
241             });
242             }
243              
244             =head2 value
245              
246             The current value as a string (or undef)
247              
248             Note: don't pass in undef; use the L<unset|/unset> method instead.
249              
250             =cut
251              
252             sub value {
253 59     59 1 796 my $self = shift;
254 59 100 100     124 if (@_ && !$self->_readonly) {
255 10         64 my $val = shift;
256 10 50       21 Carp::croak("object $self cannot be set to undef") unless defined $val;
257 10 100       16 unless ($self->_values->{$val}) {
258 1         7 Carp::croak("object $self cannot be set to '$val'");
259             }
260 9         53 return $self->_value_accessor($val);
261             }
262 49         91 return $self->_value_accessor;
263             }
264              
265             =head2 values
266              
267             The possible values for this object
268              
269             =cut
270              
271             sub values {
272 1     1 1 303 my $self = shift;
273 1         1 return keys %{ $self->_values };
  1         4  
274             }
275              
276             =head2 unset
277              
278             Unset the object's value (set to undef)
279              
280             =cut
281              
282             sub unset {
283 3     3 1 173 my $self = shift;
284 3 100       6 unless ($self->_unset) {
285 2         49 Carp::croak("object $self cannot be unset");
286             }
287 1         8 $self->_value_accessor(undef);
288             }
289              
290             =head2 is_*
291              
292             =head2 set_*
293              
294             Automatically generated from the values passed into C<< new
295             >>.
296              
297             None of these methods take any arguments.
298              
299             The C<< set_* >> methods are chainable; that is, they return
300             the object on which they were called. This lets you do useful things like:
301              
302             use Object::Enum Enum => { -as => 'color', values => [qw(red blue)] };
303              
304             print color->set_red->value; # prints 'red'
305              
306             =cut
307              
308             =head1 AUTHOR
309              
310             Hans Dieter Pearcey, C<< <hdp at cpan.org> >>
311              
312             =head1 BUGS
313              
314             Please report any bugs or feature requests to
315             C<bug-object-enum at rt.cpan.org>, or through the web interface at
316             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Object-Enum>.
317             I will be notified, and then you'll automatically be notified of progress on
318             your bug as I make changes.
319              
320             =head1 SUPPORT
321              
322             You can find documentation for this module with the perldoc command.
323              
324             perldoc Object::Enum
325              
326             You can also look for information at:
327              
328             =over 4
329              
330             =item * AnnoCPAN: Annotated CPAN documentation
331              
332             L<http://annocpan.org/dist/Object-Enum>
333              
334             =item * CPAN Ratings
335              
336             L<http://cpanratings.perl.org/d/Object-Enum>
337              
338             =item * RT: CPAN's request tracker
339              
340             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Object-Enum>
341              
342             =item * Search CPAN
343              
344             L<http://search.cpan.org/dist/Object-Enum>
345              
346             =item * GitHub
347              
348             L<https://github.com/jmmills/object-enum/>
349              
350             =back
351              
352             =head1 ACKNOWLEDGEMENTS
353              
354             =head1 COPYRIGHT & LICENSE
355              
356             Copyright 2006 Hans Dieter Pearcey, all rights reserved.
357              
358             This program is free software; you can redistribute it and/or modify it
359             under the same terms as Perl itself.
360              
361             =cut
362              
363             1; # End of Object::Enum