File Coverage

blib/lib/MooseX/Enumeration.pm
Criterion Covered Total %
statement 10 12 83.3
branch 1 2 50.0
condition 2 3 66.6
subroutine 4 4 100.0
pod n/a
total 17 21 80.9


line stmt bran cond sub pod time code
1 9     9   437937 use 5.008001;
  9         40  
2 9     9   43 use strict;
  9         16  
  9         218  
3 9     9   36 use warnings;
  9         17  
  9         1277  
4              
5             package MooseX::Enumeration;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.009';
9              
10             {
11             my $impl;
12             sub _enum_type_implementation
13             {
14 8         3116 $impl ||= eval { require Type::Tiny::Enum }
15             ? 'Type::Tiny::Enum'
16 9 50 66 9   43 : do {
17 0           require Moose::Meta::TypeConstraint::Enum;
18 0           'Moose::Meta::TypeConstraint::Enum';
19             };
20             }
21             }
22              
23             1;
24              
25             __END__
26              
27             =pod
28              
29             =encoding utf-8
30              
31             =for stopwords enum enums
32              
33             =head1 NAME
34              
35             MooseX::Enumeration - a native attribute trait for enums
36              
37             =head1 SYNOPSIS
38              
39             Given this class:
40              
41             package MyApp::Result {
42             use Moose;
43             use Types::Standard qw(Enum);
44             has status => (
45             is => "rw",
46             isa => Enum[qw/ pass fail /],
47             );
48             }
49              
50             It's quite common to do this kind of thing:
51              
52             if ( $result->status eq "pass" ) { ... }
53              
54             But if you're throwing strings around, it can be quite easy to mistype
55             them:
56              
57             if ( $result->status eq "apss" ) { ... }
58              
59             And the comparison silently fails. Instead, let's define the class like
60             this:
61              
62             package MyApp::Result {
63             use Moose;
64             use Types::Standard qw(Enum);
65             has status => (
66             traits => ["Enumeration"],
67             is => "rw",
68             isa => Enum[qw/ pass fail /],
69             handles => [qw/ is_pass is_fail /],
70             );
71             }
72              
73             So you can use the class like this:
74              
75             if ( $result->is_pass ) { ... }
76              
77             Yay!
78              
79             =head1 DESCRIPTION
80              
81             This attribute trait makes it easier to work with enumerated types in
82             L<Moose>.
83              
84             It will only work on attributes which have an enum type constraint.
85             This may be a L<Type::Tiny::Enum> or may be a type constraint defined
86             using Moose's built-in enum types.
87              
88             =head2 Type Constraint Shortcut
89              
90             This trait gives you a shortcut for specifying an enum type constraint:
91              
92             has status => (
93             traits => ["Enumeration"],
94             is => "rw",
95             enum => [qw/ pass fail /], # instead of isa
96             );
97              
98             =head2 Delegation
99              
100             =over
101              
102             =item C<< is >>
103              
104             The trait also allows you to delegate "is" to the attribute value.
105              
106             # the most longhanded form...
107             #
108             has status => (
109             traits => ["Enumeration"],
110             is => "rw",
111             enum => [qw/ pass fail /],
112             handles => {
113             is_pass => ["is", "pass"],
114             is_fail => ["is", "fail"],
115             }
116             );
117              
118             Note that above, we might have called the delegated method
119             C<< "did_pass" >> instead of C<< "is_pass" >>. You can call it what you
120             like.
121              
122             has status => (
123             traits => ["Enumeration"],
124             is => "rw",
125             enum => [qw/ pass fail /],
126             handles => {
127             did_pass => ["is", "pass"],
128             didnt_pass => ["is", "fail"],
129             }
130             );
131              
132             To save typing, we offer some shorthands for common patterns.
133              
134             has status => (
135             traits => ["Enumeration"],
136             is => "rw",
137             enum => [qw/ pass fail /],
138             handles => {
139             is_pass => "is_pass",
140             is_fail => "is_fail",
141             }
142             );
143              
144             In the hashref values, we implicitly split on the first underscore, so
145             C<< "is_pass" >> is equivalent to C<< ["is", "pass"] >>.
146              
147             This is still repetitive, so how about...
148              
149             has status => (
150             traits => ["Enumeration"],
151             is => "rw",
152             enum => [qw/ pass fail /],
153             handles => [ "is_pass", "is_fail" ],
154             );
155              
156             If an arrayref of delegates is given, it mapped like this:
157              
158             my %delegate_hash = map { $_ => $_ } @delegate_array;
159              
160             We can still go one better...
161              
162             has status => (
163             traits => ["Enumeration"],
164             is => "rw",
165             enum => [qw/ pass fail /],
166             handles => 1,
167             );
168              
169             This will create a delegated method for each value in the enumeration.
170              
171             C<< handles => 1 >> will create methods like C<< $object->is_pass >> while
172             C<< handles => 2 >> will create methods like C<< $object->status_is_pass >>.
173              
174             As a slightly more advanced option, which will only work for the
175             long-hand version, you may match the value against a regular expression
176             or any other value that may serve as a right-hand side for a
177             L<match::simple> match operation:
178              
179             has status => (
180             traits => ["Enumeration"],
181             is => "rw",
182             enum => [qw/ pass fail skip todo /],
183             handles => {
184             is_pass => [ "is", qr{^pass$} ],
185             is_fail => [ "is", "fail" ],
186             is_other => [ "is", [qw(skip todo)] ],
187             }
188             );
189              
190             =item C<< assign >>
191              
192             The Enumeration trait allows you to delegate to "assign":
193              
194             has status => (
195             traits => ["Enumeration"],
196             is => "ro",
197             enum => [qw/ pass fail unknown /],
198             handles => {
199             "set_status_pass" => [ "assign", "pass" ],
200             "set_status_fail" => [ "assign", "fail" ],
201             "clear_status" => [ "assign", "unknown" ],
202             }
203             );
204            
205             ...;
206             $obj->set_status_pass; # sets the object's status to "pass"
207              
208             It is possible to restrict allowed transitions by adding an extra
209             parameter. In the following example you can only set the status to
210             "pass" if the current status is "unknown", and you can only set the
211             status to "fail" if the current status begins with "u" (effectively
212             the same thing).
213              
214             has status => (
215             traits => ["Enumeration"],
216             is => "ro",
217             enum => [qw/ pass fail unknown /],
218             handles => {
219             "set_status_pass" => [ "assign", "pass", "unknown" ],
220             "set_status_fail" => [ "assign", "fail", qr{^u} ],
221             "clear_status" => [ "assign", "unknown" ],
222             }
223             );
224              
225             Calling C<set_status_pass> if the status is already "pass" is
226             conceptually a no-op, so is always allowed.
227              
228             Methods delegated to C<assign> always return C<< $self >> so are
229             suitable for chaining.
230              
231             =back
232              
233             =head1 PERFORMANCE
234              
235             As of version 0.003, C<< $obj->is_pass >> actually benchmarks I<faster>
236             than C<< $obj->status eq "pass" >>. The latter comparison can be
237             accelerated using L<MooseX::XSAccessor> but this module can not (yet)
238             provide an XS version for C<is_pass>. :-(
239              
240             =head1 BUGS
241              
242             Please report any bugs to
243             L<http://rt.cpan.org/Dist/Display.html?Queue=MooseX-Enumeration>.
244              
245             =head1 SEE ALSO
246              
247             L<MooX::Enumeration> — implementation of this for L<Moo>.
248              
249             L<Moose::Meta::TypeConstraint::Enum>,
250             L<Type::Tiny::Enum>,
251             L<Moose::Meta::Attribute::Native>.
252              
253             =head1 AUTHOR
254              
255             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
256              
257             =head1 COPYRIGHT AND LICENCE
258              
259             This software is copyright (c) 2014, 2018 by Toby Inkster.
260              
261             This is free software; you can redistribute it and/or modify it under
262             the same terms as the Perl 5 programming language system itself.
263              
264             =head1 DISCLAIMER OF WARRANTIES
265              
266             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
267             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
268             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
269