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   1558274 use utf8;
  7         225  
  7         53  
6 7     7   249 use v5.14;
  7         22  
7              
8 7     7   44 use Carp qw( croak );
  7         15  
  7         332  
9 7     7   2816 use Devel::StrictMode;
  7         2864  
  7         339  
10 7     7   591 use Moo ();
  7         16516  
  7         100  
11 7     7   2675 use Moo::Role ();
  7         51131  
  7         174  
12 7     7   42 use Scalar::Util qw/ blessed /;
  7         13  
  7         375  
13 7     7   3186 use Types::Const qw( Const );
  7         1095462  
  7         53  
14 7     7   3064 use Types::Standard qw( is_CodeRef Value Object Ref );
  7         18  
  7         40  
15              
16             # RECOMMEND PREREQ: Types::Const v0.3.6
17             # RECOMMEND PREREQ: Type::Tiny::XS
18             # RECOMMEND PREREQ: MooX::TypeTiny
19              
20 7     7   19407 use namespace::autoclean;
  7         21  
  7         45  
21              
22             our $VERSION = 'v0.6.2';
23              
24              
25             sub import {
26 7     7   67 my $class = shift;
27              
28 7         26 my $target = caller;
29              
30 7 100       293 my $installer =
31             $target->isa("Moo::Object")
32             ? \&Moo::_install_tracked
33             : \&Moo::Role::_install_tracked;
34              
35 7 50       77 if ( my $has = $target->can('has') ) {
36             my $new_has = sub {
37 45     45   287007 $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   178 my ( $name, %opts ) = @_;
46              
47 45         97 my $strict = STRICT || ( $opts{strict} // 1 );
48              
49 45         75 my $is = $opts{is};
50              
51 45   100     237 my $once = $is && $is eq "once";
52              
53 45 100 66     294 if ($is && $is =~ /^(?:const|once)$/ ) {
54              
55 39 100       272 if ( my $isa = $opts{isa} ) {
56              
57 38 100 100     385 unless ( blessed($isa) && $isa->isa('Type::Tiny') ) {
58 1         16 croak "isa must be a Type::Tiny type";
59             }
60              
61 37 100       482 if ($isa->is_a_type_of(Value)) {
62              
63 10 100       4819 if ($once) {
64              
65 1         10 croak "write-once attributes are not supported for Value types";
66              
67             }
68             else {
69              
70 9         30 $opts{is} = 'ro';
71              
72             }
73              
74             }
75             else {
76              
77 27 100       27946 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       6676 if ( $isa->is_a_type_of(Object) ) {
82 2         468 croak "isa cannot be a type of Types::Standard::Object";
83             }
84              
85 24 100       23787 if ($strict) {
86 21         83 $opts{isa} = Const[$isa];
87 21 100       9271 if ( my $next = $opts{coerce} ) {
88              
89 3 50       32 if (is_CodeRef($next)) {
90 3     3   16 $opts{coerce} = sub { $opts{isa}->coercion->( $next->( $_[0] ) ) };
  3         14661  
91             }
92             else {
93 0     0   0 $opts{coerce} = sub { $opts{isa}->coercion->( $isa->coercion->( $_[0] ) ) };
  0         0  
94             }
95             }
96             else {
97 18         59 $opts{coerce} = $opts{isa}->coercion;
98             }
99             }
100              
101 24 100       4182 $opts{is} = $once ? 'rw' : 'ro';
102              
103             }
104              
105 33 100 100     157 if ($opts{trigger} && ($is ne "once")) {
106 2         20 croak "triggers are not applicable to const attributes";
107             }
108              
109 31 100 100     105 if ($opts{writer} && ($is ne "once")) {
110 2         19 croak "writers are not applicable to const attributes";
111             }
112              
113 29 100       97 if ($opts{clearer}) {
114 2         19 croak "clearers are not applicable to const attributes";
115             }
116              
117             }
118             else {
119              
120 1         19 croak "Missing isa for a const attribute";
121              
122             }
123              
124             }
125              
126 33         318 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.2
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             Future releases may only support Perl versions released in the last ten years.
234              
235             If you need this module on Perl v5.10, please use one of the v0.5.x
236             versions of this module. Significant bug or security fixes may be
237             backported to those versions.
238              
239             =head1 SEE ALSO
240              
241             L<Const::Fast>
242              
243             L<Devel::StrictMode>
244              
245             L<Moo>
246              
247             L<MooseX::SetOnce>
248              
249             L<Sub::Trigger::Lock>
250              
251             L<Types::Const>
252              
253             L<Type::Tiny>
254              
255             =head1 SOURCE
256              
257             The development version is on github at L<https://github.com/robrwo/MooX-Const>
258             and may be cloned from L<git://github.com/robrwo/MooX-Const.git>
259              
260             =head1 BUGS
261              
262             Please report any bugs or feature requests on the bugtracker website
263             L<https://github.com/robrwo/MooX-Const/issues>
264              
265             When submitting a bug or request, please include a test-file or a
266             patch to an existing test-file that illustrates the bug or desired
267             feature.
268              
269             =head1 AUTHOR
270              
271             Robert Rothenberg <rrwo@cpan.org>
272              
273             This module was inspired by suggestions from Kang-min Liu 劉康民
274             <gugod@gugod.org> in a L<blog post|http://blogs.perl.org/users/robert_rothenberg/2018/11/typeconst-released.html>.
275              
276             =head1 CONTRIBUTOR
277              
278             =for stopwords Kang-min Liu 劉康民
279              
280             Kang-min Liu 劉康民 <gugod@gugod.org>
281              
282             =head1 COPYRIGHT AND LICENSE
283              
284             This software is Copyright (c) 2018-2023 by Robert Rothenberg.
285              
286             This is free software, licensed under:
287              
288             The Artistic License 2.0 (GPL Compatible)
289              
290             =cut