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.014';
4              
5 3     3   163959 use strict;
  3         20  
  3         85  
6 3     3   17 use warnings;
  3         5  
  3         81  
7              
8 3     3   15 use Carp qw(croak);
  3         6  
  3         177  
9 3     3   1461 use Class::Method::Modifiers qw(install_modifier);
  3         4786  
  3         175  
10 3     3   22 use List::Util 1.33;
  3         71  
  3         222  
11 3     3   22 use Scalar::Util qw(blessed);
  3         6  
  3         124  
12              
13 3     3   1357 use namespace::clean;
  3         42228  
  3         21  
14              
15             use overload (
16 3         20 '""' => 'stringify',
17             'cmp' => 'cmp',
18             '0+' => 'numify',
19             fallback => 1,
20 3     3   1917 );
  3         938  
21              
22              
23              
24             sub import {
25 4     4   216 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       17 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         5 my %values;
35              
36 3 50       11 if (ref $params{values} eq 'ARRAY') {
    0          
37 3         5 my $i = 0;
38 3         6 %values = map { $_ => $i++ } @{$params{values}};
  13         31  
  3         7  
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   681 no strict 'refs';
  3         6  
  3         3588  
  3         7  
50 3         4 push @{"${target}::ISA"}, $class;
  3         34  
51             }
52 3     51   19 install_modifier $target, 'fresh', sym_to_ord => sub { \%values };
  51         262  
53 3     12   581 install_modifier $target, 'fresh', ord_to_sym => sub { +{ reverse(%values) } };
  12         507  
54              
55             install_modifier $target, 'fresh', values => sub {
56 2     2   49 my $ord = $_[0]->sym_to_ord;
57 2         12 [ sort { $ord->{$a} <=> $ord->{$b} } keys %values ];
  10         42  
58 3         420 };
59              
60 3         364 for my $value (keys %values) {
61 13     14   1403 install_modifier $target, 'fresh', "is_$value" => sub { $_[0]->is($value) };
  14         770  
62             }
63             }
64              
65              
66              
67             sub new {
68 8     8 1 65 my ($class, $value) = @_;
69              
70 8   66     50 (blessed($class) || $class)->inflate_symbol($value);
71             }
72              
73              
74             sub inflate_symbol {
75 20     20 1 30700 my ($class, $symbol) = @_;
76              
77 20         494 my $ord = $class->sym_to_ord->{$symbol};
78              
79 20 100       609 croak "Value [$symbol] is not valid for enum $class"
80             unless defined $ord;
81              
82 16         143 bless \$ord, $class;
83             }
84              
85              
86             sub inflate_ordinal {
87 6     6 1 14 my ($class, $ord) = @_;
88              
89             croak "Ordinal [$ord] is not valid for enum $class"
90 6 100       168 unless exists $class->ord_to_sym->{$ord};
91              
92 2         11 bless \$ord, $class;
93             }
94              
95              
96             sub list_is_methods {
97 2     2 1 2002 my ($class) = @_;
98              
99 2         6 map "is_$_", @{$class->values};
  2         62  
100             }
101              
102              
103             sub type_constraint {
104 1     1 1 89 my ($class) = @_;
105              
106 1         454 require Type::Tiny::Class;
107 1         2427 require Types::Standard;
108 1   33     60302 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 902 my ($class, $value) = @_;
115              
116 3         79 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 728 my ($class, $value) = @_;
129 3 100       5 return $value if eval { $value->isa($class) };
  3         22  
130              
131 2         10 $class->inflate_symbol($value);
132             }
133              
134              
135             sub coerce_ordinal {
136 3     3 1 8 my ($class, $value) = @_;
137 3 100       5 return $value if eval { $value->isa($class) };
  3         21  
138              
139 2         7 $class->inflate_ordinal($value);
140             }
141              
142              
143             sub coerce_any {
144 5     5 1 13 my ($class, $value) = @_;
145 5 100       8 return $value if eval { $value->isa($class) };
  5         35  
146              
147 4         10 for my $method (qw( inflate_ordinal inflate_symbol )) {
148 7         11 my $enum = eval { $class->$method($value) };
  7         22  
149 7 100       30 return $enum if $enum;
150             }
151 2         142 croak "Could not coerce invalid value [$value] into $class";
152             }
153              
154              
155              
156             sub is {
157 19     19 1 1158 my ($self, $value) = @_;
158 19         354 my $ord = $self->sym_to_ord->{$value};
159              
160 19 50       45 croak "Value [$value] is not valid for enum " . blessed($self)
161             unless defined $ord;
162              
163 19         178 $$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 1093 my ($self) = @_;
176 37         125 $$self;
177             }
178              
179              
180             sub cmp {
181 9     9 1 140 my ($self, $other, $reversed) = @_;
182 9 100       27 return -1 * $self->cmp($other) if $reversed;
183              
184 8 100       25 return $$self <=> $other if blessed($other);
185              
186 7         149 my $ord = $self->sym_to_ord->{$other};
187 7 50       19 croak "Cannot compare to invalid symbol [$other] for " . blessed($self)
188             unless defined $ord;
189              
190 7         26 return $$self <=> $ord;
191             }
192              
193              
194             sub any {
195 1     1 1 4 my ($self, @cases) = @_;
196              
197 1     2   8 List::Util::any { $self->is($_) } @cases;
  2         5  
198             }
199              
200              
201             sub none {
202 1     1 1 4 my ($self, @cases) = @_;
203              
204 1     2   6 List::Util::none { $self->is($_) } @cases;
  2         5  
205             }
206              
207              
208              
209             1;
210              
211             __END__