File Coverage

blib/lib/MooX/Const.pm
Criterion Covered Total %
statement 68 70 97.1
branch 30 32 93.7
condition 14 15 93.3
subroutine 15 16 93.7
pod n/a
total 127 133 95.4


line stmt bran cond sub pod time code
1             package MooX::Const;
2              
3             # ABSTRACT: Syntactic sugar for constant and write-once Moo attributes
4              
5 7     7   1610932 use utf8;
  7         223  
  7         42  
6 7     7   290 use v5.14;
  7         26  
7              
8 7     7   37 use Carp qw( croak );
  7         14  
  7         518  
9 7     7   2930 use Devel::StrictMode;
  7         2985  
  7         368  
10 7     7   546 use Moo ();
  7         16313  
  7         110  
11 7     7   2706 use Moo::Role ();
  7         52487  
  7         223  
12 7     7   62 use Scalar::Util qw/ blessed /;
  7         15  
  7         473  
13 7     7   3573 use Types::Const qw( Const );
  7         1104248  
  7         83  
14 7     7   3590 use Types::Standard qw( is_CodeRef Value Object Ref );
  7         19  
  7         47  
15              
16             # RECOMMEND PREREQ: Types::Const v0.3.6
17             # RECOMMEND PREREQ: Type::Tiny::XS
18             # RECOMMEND PREREQ: MooX::TypeTiny
19              
20 7     7   20396 use namespace::autoclean;
  7         19  
  7         81  
21              
22             our $VERSION = 'v0.6.1';
23              
24              
25             sub import {
26 7     7   71 my $class = shift;
27              
28 7         31 my $target = caller;
29              
30 7 100       243 my $installer =
31             $target->isa("Moo::Object")
32             ? \&Moo::_install_tracked
33             : \&Moo::Role::_install_tracked;
34              
35 7 50       90 if ( my $has = $target->can('has') ) {
36             my $new_has = sub {
37 45     45   297368 $has->( _process_has(@_) );
        15      
38 7         29 };
39 7         45 $installer->( $target, "has", $new_has );
40             }
41              
42             }
43              
44             sub _process_has {
45 45     45   209 my ( $name, %opts ) = @_;
46              
47 45         150 my $strict = STRICT || ( $opts{strict} // 1 );
48              
49 45         90 my $is = $opts{is};
50              
51 45   100     247 my $once = $is && $is eq "once";
52              
53 45 100 66     307 if ($is && $is =~ /^(?:const|once)$/ ) {
54              
55 39 100       327 if ( my $isa = $opts{isa} ) {
56              
57 38 100 100     392 unless ( blessed($isa) && $isa->isa('Type::Tiny') ) {
58 1         21 croak "isa must be a Type::Tiny type";
59             }
60              
61 37 100       520 if ($isa->is_a_type_of(Value)) {
62              
63 10 100       4990 if ($once) {
64              
65 1         12 croak "write-once attributes are not supported for Value types";
66              
67             }
68             else {
69              
70 9         26 $opts{is} = 'ro';
71              
72             }
73              
74             }
75             else {
76              
77 27 100       28388 unless ( $isa->is_a_type_of(Ref) ) {
78 1         759 croak "isa must be a type of Types::Standard::Ref";
79             }
80              
81 26 100       6552 if ( $isa->is_a_type_of(Object) ) {
82 2         472 croak "isa cannot be a type of Types::Standard::Object";
83             }
84              
85 24 100       24783 if ($strict) {
86 21         109 $opts{isa} = Const[$isa];
87 21 100       9994 if ( my $next = $opts{coerce} ) {
88              
89 3 50       39 if (is_CodeRef($next)) {
90 3     3   23 $opts{coerce} = sub { $opts{isa}->coercion->( $next->( $_[0] ) ) };
  3         14758  
91             }
92             else {
93 0     0   0 $opts{coerce} = sub { $opts{isa}->coercion->( $isa->coercion->( $_[0] ) ) };
  0         0  
94             }
95             }
96             else {
97 18         100 $opts{coerce} = $opts{isa}->coercion;
98             }
99             }
100              
101 24 100       4528 $opts{is} = $once ? 'rw' : 'ro';
102              
103             }
104              
105 33 100 100     167 if ($opts{trigger} && ($is ne "once")) {
106 2         21 croak "triggers are not applicable to const attributes";
107             }
108              
109 31 100 100     104 if ($opts{writer} && ($is ne "once")) {
110 2         20 croak "writers are not applicable to const attributes";
111             }
112              
113 29 100       141 if ($opts{clearer}) {
114 2         20 croak "clearers are not applicable to const attributes";
115             }
116              
117             }
118             else {
119              
120 1         16 croak "Missing isa for a const attribute";
121              
122             }
123              
124             }
125              
126 33         373 return ( $name, %opts );
127             }
128              
129              
130             1;
131              
132             __END__
133              
134             =pod
135              
136             =encoding UTF-8
137              
138             =head1 NAME
139              
140             MooX::Const - Syntactic sugar for constant and write-once Moo attributes
141              
142             =head1 VERSION
143              
144             version v0.6.1
145              
146             =head1 SYNOPSIS
147              
148             use Moo;
149             use MooX::Const;
150              
151             use Types::Standard -types;
152              
153             has thing => (
154             is => 'const',
155             isa => ArrayRef[HashRef],
156             );
157              
158             =head1 DESCRIPTION
159              
160             This is syntactic sugar for using L<Types::Const> with L<Moo>. The
161             SYNOPSIS above is equivalent to:
162              
163             use Types::Const -types;
164              
165             has thing => (
166             is => 'ro',
167             isa => Const[ArrayRef[HashRef]],
168             coerce => 1,
169             );
170              
171             It modifies the C<has> function to support "const" attributes. These
172             are read-only ("ro") attributes for references, where the underlying
173             data structure has been set as read-only.
174              
175             This will return an error if there is no "isa", the "isa" is not a
176             L<Type::Tiny> type, if it is not a reference, or if it is blessed
177             object.
178              
179             Simple value types such as C<Int> or C<Str> are silently converted to
180             read-only attributes.
181              
182             As of v0.5.0, it also supports write-once ("once") attributes for
183             references:
184              
185             has setting => (
186             is => 'once',
187             isa => HashRef,
188             );
189              
190             This allows you to set the attribute I<once>. The value is coerced
191             into a constant, and cannot be changed again.
192              
193             Note that "wo" is a removed synonym for "once". It no longer works in
194             v0.6.0, since "wo" is used for "write-only" in some Moose-like
195             extensions.
196              
197             As of v0.4.0, this now supports the C<strict> setting:
198              
199             has thing => (
200             is => 'const',
201             isa => ArrayRef[HashRef],
202             strict => 0,
203             );
204              
205             When this is set to a false value, then the read-only constraint will
206             only be applied when running in strict mode, see L<Devel::StrictMode>.
207              
208             If omitted, C<strict> is assumed to be true.
209              
210             =head1 KNOWN ISSUES
211              
212             Accessing non-existent keys for hash references will throw an
213             error. This is a feature, not a bug, of read-only hash references, and
214             it can be used to catch mistakes in code that refer to non-existent
215             keys.
216              
217             Unfortunately, this behaviour is not replicated with array references.
218              
219             See L<Types::Const> for other known issues related to the C<Const>
220             type.
221              
222             =head2 Using with Moose and Mouse
223              
224             This module appears to work with L<Moose>, and there is now a small
225             test suite.
226              
227             It does not work with L<Mouse>. Pull requests are welcome.
228              
229             =head1 SUPPORT FOR OLDER PERL VERSIONS
230              
231             Since v0.6.0, the this module requires Perl v5.14 or later.
232              
233             If you need this module on Perl v5.10, please use one of the v0.5.x
234             versions of this module. Significant bug or security fixes may be
235             backported to those versions.
236              
237             =head1 SEE ALSO
238              
239             L<Const::Fast>
240              
241             L<Devel::StrictMode>
242              
243             L<Moo>
244              
245             L<MooseX::SetOnce>
246              
247             L<Sub::Trigger::Lock>
248              
249             L<Types::Const>
250              
251             L<Type::Tiny>
252              
253             =head1 SOURCE
254              
255             The development version is on github at L<https://github.com/robrwo/MooX-Const>
256             and may be cloned from L<git://github.com/robrwo/MooX-Const.git>
257              
258             =head1 BUGS
259              
260             Please report any bugs or feature requests on the bugtracker website
261             L<https://github.com/robrwo/MooX-Const/issues>
262              
263             When submitting a bug or request, please include a test-file or a
264             patch to an existing test-file that illustrates the bug or desired
265             feature.
266              
267             =head1 AUTHOR
268              
269             Robert Rothenberg <rrwo@cpan.org>
270              
271             This module was inspired by suggestions from Kang-min Liu 劉康民
272             <gugod@gugod.org> in a L<blog post|http://blogs.perl.org/users/robert_rothenberg/2018/11/typeconst-released.html>.
273              
274             =head1 CONTRIBUTOR
275              
276             =for stopwords Kang-min Liu 劉康民
277              
278             Kang-min Liu 劉康民 <gugod@gugod.org>
279              
280             =head1 COPYRIGHT AND LICENSE
281              
282             This software is Copyright (c) 2018-2023 by Robert Rothenberg.
283              
284             This is free software, licensed under:
285              
286             The Artistic License 2.0 (GPL Compatible)
287              
288             =cut