File Coverage

blib/lib/Class/Type/Enum.pm
Criterion Covered Total %
statement 106 109 97.2
branch 21 26 80.7
condition 3 6 50.0
subroutine 32 32 100.0
pod 16 16 100.0
total 178 189 94.1


line stmt bran cond sub pod time code
1             package Class::Type::Enum;
2             # ABSTRACT: Build Enum-like classes
3             $Class::Type::Enum::VERSION = '0.013';
4              
5 3     3   160190 use strict;
  3         18  
  3         86  
6 3     3   17 use warnings;
  3         5  
  3         84  
7              
8 3     3   24 use Carp qw(croak);
  3         8  
  3         161  
9 3     3   1502 use Class::Method::Modifiers qw(install_modifier);
  3         5027  
  3         175  
10 3     3   20 use List::Util 1.33;
  3         67  
  3         214  
11 3     3   20 use Scalar::Util qw(blessed);
  3         7  
  3         120  
12              
13 3     3   1355 use namespace::clean;
  3         42605  
  3         21  
14              
15             use overload (
16 3         19 '""' => 'stringify',
17             'cmp' => 'cmp',
18             '0+' => 'numify',
19             fallback => 1,
20 3     3   1887 );
  3         937  
21              
22              
23              
24             sub import {
25 4     4   199 my ($class, %params) = @_;
26              
27             # import is inherited, but we don't want to do all this to everything that
28             # uses a subclass of Class::Type::Enum.
29 4 100       19 return unless $class eq __PACKAGE__;
30             # If there's a use case for it, we can still allow extending CTE subclasses.
31              
32 3         8 my $target = caller;
33              
34 3         6 my %values;
35              
36 3 50       10 if (ref $params{values} eq 'ARRAY') {
    0          
37 3         5 my $i = 0;
38 3         6 %values = map { $_ => $i++ } @{$params{values}};
  13         35  
  3         6  
39             }
40             elsif (ref $params{values} eq 'HASH') {
41 0         0 %values = %{$params{values}};
  0         0  
42             }
43             else {
44 0         0 croak "Enum values must be provided either as an array or hash ref.";
45             }
46              
47             ## the bits that are installed into the target class, plus @ISA
48             {
49 3     3   634 no strict 'refs';
  3         6  
  3         3509  
  3         6  
50 3         5 push @{"${target}::ISA"}, $class;
  3         29  
51             }
52 3     51   21 install_modifier $target, 'fresh', sym_to_ord => sub { \%values };
  51         255  
53 3     12   565 install_modifier $target, 'fresh', ord_to_sym => sub { +{ reverse(%values) } };
  12         501  
54              
55             install_modifier $target, 'fresh', values => sub {
56 2     2   51 my $ord = $_[0]->sym_to_ord;
57 2         13 [ sort { $ord->{$a} <=> $ord->{$b} } keys %values ];
  8         41  
58 3         461 };
59              
60 3         363 for my $value (keys %values) {
61 13     14   1386 install_modifier $target, 'fresh', "is_$value" => sub { $_[0]->is($value) };
  14         763  
62             }
63             }
64              
65              
66              
67             sub new {
68 8     8 1 65 my ($class, $value) = @_;
69              
70 8   66     49 (blessed($class) || $class)->inflate_symbol($value);
71             }
72              
73              
74             sub inflate_symbol {
75 20     20 1 29690 my ($class, $symbol) = @_;
76              
77 20         482 my $ord = $class->sym_to_ord->{$symbol};
78              
79 20 100       554 croak "Value [$symbol] is not valid for enum $class"
80             unless defined $ord;
81              
82 16         188 bless \$ord, $class;
83             }
84              
85              
86             sub inflate_ordinal {
87 6     6 1 12 my ($class, $ord) = @_;
88              
89             croak "Ordinal [$ord] is not valid for enum $class"
90 6 100       175 unless exists $class->ord_to_sym->{$ord};
91              
92 2         10 bless \$ord, $class;
93             }
94              
95              
96             sub list_is_methods {
97 2     2 1 1863 my ($class) = @_;
98              
99 2         6 map "is_$_", @{$class->values};
  2         59  
100             }
101              
102              
103             sub type_constraint {
104 1     1 1 96 my ($class) = @_;
105              
106 1         454 require Type::Tiny::Class;
107 1         2328 require Types::Standard;
108 1   33     58451 Type::Tiny::Class->new(class => blessed($class) || $class)
109             ->plus_constructors(Types::Standard::Str(), 'inflate_symbol');
110             }
111              
112              
113             sub test_symbol {
114 3     3 1 870 my ($class, $value) = @_;
115              
116 3         84 exists($class->sym_to_ord->{$value})
117             }
118              
119              
120             sub test_ordinal {
121 3     3 1 8 my ($class, $value) = @_;
122              
123 3         77 exists($class->ord_to_sym->{$value})
124             }
125              
126              
127             sub coerce_symbol {
128 3     3 1 697 my ($class, $value) = @_;
129 3 100       6 return $value if eval { $value->isa($class) };
  3         22  
130              
131 2         6 $class->inflate_symbol($value);
132             }
133              
134              
135             sub coerce_ordinal {
136 3     3 1 8 my ($class, $value) = @_;
137 3 100       4 return $value if eval { $value->isa($class) };
  3         23  
138              
139 2         9 $class->inflate_ordinal($value);
140             }
141              
142              
143             sub coerce_any {
144 5     5 1 14 my ($class, $value) = @_;
145 5 100       7 return $value if eval { $value->isa($class) };
  5         32  
146              
147 4         10 for my $method (qw( inflate_ordinal inflate_symbol )) {
148 7         11 my $enum = eval { $class->$method($value) };
  7         21  
149 7 100       28 return $enum if $enum;
150             }
151 2         136 croak "Could not coerce invalid value [$value] into $class";
152             }
153              
154              
155              
156             sub is {
157 19     19 1 1144 my ($self, $value) = @_;
158 19         367 my $ord = $self->sym_to_ord->{$value};
159              
160 19 50       46 croak "Value [$value] is not valid for enum " . blessed($self)
161             unless defined $ord;
162              
163 19         139 $$self == $ord;
164             }
165              
166              
167              
168             sub stringify {
169 3     3 1 7 my ($self) = @_;
170 3         71 $self->ord_to_sym->{$$self};
171             }
172              
173              
174             sub numify {
175 37     37 1 1109 my ($self) = @_;
176 37         130 $$self;
177             }
178              
179              
180             sub cmp {
181 9     9 1 142 my ($self, $other, $reversed) = @_;
182 9 100       28 return -1 * $self->cmp($other) if $reversed;
183              
184 8 100       25 return $$self <=> $other if blessed($other);
185              
186 7         134 my $ord = $self->sym_to_ord->{$other};
187 7 50       22 croak "Cannot compare to invalid symbol [$other] for " . blessed($self)
188             unless defined $ord;
189              
190 7         28 return $$self <=> $ord;
191             }
192              
193              
194             sub any {
195 1     1 1 4 my ($self, @cases) = @_;
196              
197 1     2   9 List::Util::any { $self->is($_) } @cases;
  2         5  
198             }
199              
200              
201             sub none {
202 1     1 1 5 my ($self, @cases) = @_;
203              
204 1     2   6 List::Util::none { $self->is($_) } @cases;
  2         6  
205             }
206              
207              
208              
209             1;
210              
211             __END__