File Coverage

blib/lib/Object/Pad/FieldAttr/Checked.pm
Criterion Covered Total %
statement 9 10 90.0
branch n/a
condition n/a
subroutine 4 5 80.0
pod n/a
total 13 15 86.6


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2023-2024 -- leonerd@leonerd.org.uk
5              
6             package Object::Pad::FieldAttr::Checked 0.12;
7              
8 6     6   1824416 use v5.14;
  6         25  
9 6     6   56 use warnings;
  6         13  
  6         423  
10              
11 6     6   1324 use Object::Pad 0.802; # requires pre-filled ctx->bodyop when invoking gen_accessor_ops
  6         19062  
  6         303  
12              
13             require XSLoader;
14             XSLoader::load( __PACKAGE__, our $VERSION );
15              
16             =head1 NAME
17              
18             C - apply value constraint checks to C fields
19              
20             =head1 SYNOPSIS
21              
22             With L:
23              
24             use Object::Pad;
25             use Object::Pad::FieldAttr::Checked;
26             use Data::Checks qw( Num );
27              
28             class Point {
29             field $x :param :reader :Checked(Num);
30             field $y :param :reader :Checked(Num);
31             }
32              
33             Point->new( x => 123, y => "456" ); # this is fine
34              
35             Point->new( x => "hello", y => "world" ); # throws an exception
36              
37             =head1 DESCRIPTION
38              
39             This module provides a third-party field attribute for L-based
40             classes, which declares that values assigned to the field must conform to a
41             given constraint check.
42              
43             B The ability for L to take third-party field attributes
44             is still new and highly experimental, and subject to much API change in
45             future. As a result, this module should be considered equally experimental.
46              
47             Additionally, the behaviour provided by this module should be considered more
48             of a work-in-progress stepping stone. Ideally, constraint syntax ought
49             to be provided in a much more fundamental way by Perl itself, allowing it to
50             be used on C lexicals, subroutine parameters, and other places as well as
51             object fields. This module is more of a placeholder to allow some part of that
52             behaviour to be specified for object fields, while not getting in the way of a
53             more general, more powerful system being added in future.
54              
55             =head1 FIELD ATTRIBUTES
56              
57             =head2 :Checked
58              
59             field $name :Checked(EXPRESSION) ...;
60              
61             Declares that any value assigned to the field during the constructor or using
62             an accessor method must conform to the constraint checker specified by the
63             expression. Attempts to assign a non-conforming value will throw an exception
64             and the field will not be modified. Currently only scalar fields are
65             supported.
66              
67             At compiletime, the string given by I is C'ed in scalar
68             context, and its result is stored as part of the field's definition. The
69             expression must yield a value usable by L. Namely, one of:
70              
71             =over 4
72              
73             =item *
74              
75             Any of the constraint checkers provided by the L module itself.
76              
77             =item *
78              
79             An B reference with a C method:
80              
81             $ok = $checkerobj->check( $value );
82              
83             =item *
84              
85             A B giving the name of a package with a C method:
86              
87             $ok = $checkerpkg->check( $value );
88              
89             If using a plain package name as a checker, be sure to quote package names so
90             it will not upset C.
91              
92             field $x :Checked('CheckerPackage');
93              
94             =back
95              
96             As this is the interface supported by L, any constraint
97             object provided by that module is already supported here.
98              
99             use Types::Standard qw( Str Num );
100              
101             field $name :Checked(Str);
102             field $age :Checked(Num);
103              
104             At runtime, this constraint checker is used every time an attempt is made to
105             assign a value to the field I, whether that is
106             from C<:param> initialisation, or invoking a C<:writer>, C<:accessor> or
107             C<:mutator>. The checker is used as the invocant for invoking a C
108             method, and the new value for the field is passed as an argument. If the
109             method returns true, the assignment is allowed. If false, it is rejected with
110             an exception and the field itself remains unmodified.
111              
112             (For performance reasons, the C method is actually resolved into a
113             function at compiletime when the C<:Checked> attribute is applied, and this
114             stored function is the one that is called at assignment time. If the method
115             itself is replaced later by globref assignment or other trickery, this updated
116             function will not be used.)
117              
118             B that direct assignment into the field variable by code
119             within the class is not checked. This is partly because of design
120             considerations, and partly because any way to implement that would be horribly
121             slow, or flat-out impossible. I this module used to
122             claim that even direct assignments would be checked. but this gave a false
123             sense of safety if deeply-nested containers were involved and modified from
124             within.
125              
126             =cut
127              
128             sub import
129             {
130 5     5   5044 $^H{"Object::Pad::FieldAttr::Checked/Checked"}++;
131             }
132              
133             sub unimport
134             {
135 0     0     delete $^H{"Object::Pad::FieldAttr::Checked/Checked"};
136             }
137              
138             =head1 SEE ALSO
139              
140             =over 4
141              
142             =item *
143              
144             L - apply class type constraints to C fields
145              
146             =back
147              
148             =head1 AUTHOR
149              
150             Paul Evans
151              
152             =cut
153              
154             0x55AA;