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.012';
4              
5 3     3   164306 use strict;
  3         24  
  3         89  
6 3     3   15 use warnings;
  3         6  
  3         85  
7              
8 3     3   19 use Carp qw(croak);
  3         13  
  3         168  
9 3     3   1557 use Class::Method::Modifiers qw(install_modifier);
  3         4905  
  3         179  
10 3     3   21 use List::Util 1.33;
  3         82  
  3         244  
11 3     3   21 use Scalar::Util qw(blessed);
  3         7  
  3         128  
12              
13 3     3   1405 use namespace::clean;
  3         42778  
  3         18  
14              
15             use overload (
16 3         20 '""' => 'stringify',
17             'cmp' => 'cmp',
18             '0+' => 'numify',
19             fallback => 1,
20 3     3   1938 );
  3         977  
21              
22              
23              
24             sub import {
25 4     4   201 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       18 return unless $class eq __PACKAGE__;
30             # If there's a use case for it, we can still allow extending CTE subclasses.
31              
32 3         7 my $target = caller;
33              
34 3         5 my %values;
35              
36 3 50       11 if (ref $params{values} eq 'ARRAY') {
    0          
37 3         6 my $i = 0;
38 3         5 %values = map { $_ => $i++ } @{$params{values}};
  13         34  
  3         8  
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         8  
  3         3671  
  3         6  
50 3         5 push @{"${target}::ISA"}, $class;
  3         29  
51             }
52 3     51   18 install_modifier $target, 'fresh', sym_to_ord => sub { \%values };
  51         254  
53 3     12   587 install_modifier $target, 'fresh', ord_to_sym => sub { +{ reverse(%values) } };
  12         504  
54              
55             install_modifier $target, 'fresh', values => sub {
56 2     2   52 my $ord = $_[0]->sym_to_ord;
57 2         16 [ sort { $ord->{$a} <=> $ord->{$b} } keys %values ];
  8         43  
58 3         389 };
59              
60 3         373 for my $value (keys %values) {
61 13     14   1440 install_modifier $target, 'fresh', "is_$value" => sub { $_[0]->is($value) };
  14         741  
62             }
63             }
64              
65              
66              
67             sub new {
68 8     8 1 67 my ($class, $value) = @_;
69              
70 8   66     51 (blessed($class) || $class)->inflate_symbol($value);
71             }
72              
73              
74             sub inflate_symbol {
75 20     20 1 29778 my ($class, $symbol) = @_;
76              
77 20         453 my $ord = $class->sym_to_ord->{$symbol};
78              
79 20 100       581 croak "Value [$symbol] is not valid for enum $class"
80             unless defined $ord;
81              
82 16         142 bless \$ord, $class;
83             }
84              
85              
86             sub inflate_ordinal {
87 6     6 1 13 my ($class, $ord) = @_;
88              
89             croak "Ordinal [$ord] is not valid for enum $class"
90 6 100       169 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 2011 my ($class) = @_;
98              
99 2         5 map "is_$_", @{$class->values};
  2         59  
100             }
101              
102              
103             sub type_constraint {
104 1     1 1 95 my ($class) = @_;
105              
106 1         458 require Type::Tiny::Class;
107 1         2514 require Types::Standard;
108 1   33     58246 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 937 my ($class, $value) = @_;
115              
116 3         76 exists($class->sym_to_ord->{$value})
117             }
118              
119              
120             sub test_ordinal {
121 3     3 1 10 my ($class, $value) = @_;
122              
123 3         75 exists($class->ord_to_sym->{$value})
124             }
125              
126              
127             sub coerce_symbol {
128 3     3 1 701 my ($class, $value) = @_;
129 3 100       5 return $value if eval { $value->isa($class) };
  3         34  
130              
131 2         8 $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         23  
138              
139 2         8 $class->inflate_ordinal($value);
140             }
141              
142              
143             sub coerce_any {
144 5     5 1 14 my ($class, $value) = @_;
145 5 100       9 return $value if eval { $value->isa($class) };
  5         33  
146              
147 4         11 for my $method (qw( inflate_ordinal inflate_symbol )) {
148 7         13 my $enum = eval { $class->$method($value) };
  7         21  
149 7 100       31 return $enum if $enum;
150             }
151 2         134 croak "Could not coerce invalid value [$value] into $class";
152             }
153              
154              
155              
156             sub is {
157 19     19 1 1150 my ($self, $value) = @_;
158 19         344 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         141 $$self == $ord;
164             }
165              
166              
167              
168             sub stringify {
169 3     3 1 7 my ($self) = @_;
170 3         70 $self->ord_to_sym->{$$self};
171             }
172              
173              
174             sub numify {
175 37     37 1 1095 my ($self) = @_;
176 37         125 $$self;
177             }
178              
179              
180             sub cmp {
181 9     9 1 132 my ($self, $other, $reversed) = @_;
182 9 100       27 return -1 * $self->cmp($other) if $reversed;
183              
184 8 100       27 return $$self <=> $other if blessed($other);
185              
186 7         129 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 5 my ($self, @cases) = @_;
196              
197 1     2   9 List::Util::any { $self->is($_) } @cases;
  2         6  
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         5  
205             }
206              
207              
208              
209             1;
210              
211             __END__