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   1607148 use utf8;
  7         237  
  7         38  
6 7     7   276 use v5.14;
  7         59  
7              
8 7     7   47 use Carp qw( croak );
  7         20  
  7         359  
9 7     7   3245 use Devel::StrictMode;
  7         3103  
  7         387  
10 7     7   617 use Moo ();
  7         16861  
  7         114  
11 7     7   2752 use Moo::Role ();
  7         54097  
  7         182  
12 7     7   47 use Scalar::Util qw/ blessed /;
  7         13  
  7         359  
13 7     7   3162 use Types::Const qw( Const );
  7         1108390  
  7         66  
14 7     7   3213 use Types::Standard qw( is_CodeRef Value Object Ref );
  7         18  
  7         38  
15              
16             # RECOMMEND PREREQ: Types::Const v0.3.6
17             # RECOMMEND PREREQ: Type::Tiny::XS
18             # RECOMMEND PREREQ: MooX::TypeTiny
19              
20 7     7   19874 use namespace::autoclean;
  7         25  
  7         58  
21              
22             our $VERSION = 'v0.6.0'; # TRIAL
23              
24              
25             sub import {
26 7     7   69 my $class = shift;
27              
28 7         37 my $target = caller;
29              
30 7 100       247 my $installer =
31             $target->isa("Moo::Object")
32             ? \&Moo::_install_tracked
33             : \&Moo::Role::_install_tracked;
34              
35 7 50       82 if ( my $has = $target->can('has') ) {
36             my $new_has = sub {
37 45     45   293962 $has->( _process_has(@_) );
        15      
38 7         29 };
39 7         49 $installer->( $target, "has", $new_has );
40             }
41              
42             }
43              
44             sub _process_has {
45 45     45   202 my ( $name, %opts ) = @_;
46              
47 45         101 my $strict = STRICT || ( $opts{strict} // 1 );
48              
49 45         83 my $is = $opts{is};
50              
51 45   100     221 my $once = $is && $is eq "once";
52              
53 45 100 66     288 if ($is && $is =~ /^(?:const|once)$/ ) {
54              
55 39 100       291 if ( my $isa = $opts{isa} ) {
56              
57 38 100 100     393 unless ( blessed($isa) && $isa->isa('Type::Tiny') ) {
58 1         18 croak "isa must be a Type::Tiny type";
59             }
60              
61 37 100       509 if ($isa->is_a_type_of(Value)) {
62              
63 10 100       4996 if ($once) {
64              
65 1         10 croak "write-once attributes are not supported for Value types";
66              
67             }
68             else {
69              
70 9         41 $opts{is} = 'ro';
71              
72             }
73              
74             }
75             else {
76              
77 27 100       28438 unless ( $isa->is_a_type_of(Ref) ) {
78 1         752 croak "isa must be a type of Types::Standard::Ref";
79             }
80              
81 26 100       6470 if ( $isa->is_a_type_of(Object) ) {
82 2         466 croak "isa cannot be a type of Types::Standard::Object";
83             }
84              
85 24 100       23706 if ($strict) {
86 21         111 $opts{isa} = Const[$isa];
87 21 100       9570 if ( my $next = $opts{coerce} ) {
88              
89 3 50       58 if (is_CodeRef($next)) {
90 3     3   24 $opts{coerce} = sub { $opts{isa}->coercion->( $next->( $_[0] ) ) };
  3         15782  
91             }
92             else {
93 0     0   0 $opts{coerce} = sub { $opts{isa}->coercion->( $isa->coercion->( $_[0] ) ) };
  0         0  
94             }
95             }
96             else {
97 18         85 $opts{coerce} = $opts{isa}->coercion;
98             }
99             }
100              
101 24 100       4479 $opts{is} = $once ? 'rw' : 'ro';
102              
103             }
104              
105 33 100 100     173 if ($opts{trigger} && ($is ne "once")) {
106 2         21 croak "triggers are not applicable to const attributes";
107             }
108              
109 31 100 100     158 if ($opts{writer} && ($is ne "once")) {
110 2         23 croak "writers are not applicable to const attributes";
111             }
112              
113 29 100       113 if ($opts{clearer}) {
114 2         20 croak "clearers are not applicable to const attributes";
115             }
116              
117             }
118             else {
119              
120 1         12 croak "Missing isa for a const attribute";
121              
122             }
123              
124             }
125              
126 33         344 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.0
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