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 -- leonerd@leonerd.org.uk |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Object::Pad::FieldAttr::Checked 0.04; |
7
|
|
|
|
|
|
|
|
8
|
4
|
|
|
4
|
|
826955
|
use v5.14; |
|
4
|
|
|
|
|
36
|
|
9
|
4
|
|
|
4
|
|
26
|
use warnings; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
123
|
|
10
|
|
|
|
|
|
|
|
11
|
4
|
|
|
4
|
|
716
|
use Object::Pad 0.802; # requires pre-filled ctx->bodyop when invoking gen_accessor_ops |
|
4
|
|
|
|
|
11011
|
|
|
4
|
|
|
|
|
299
|
|
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 Types::Standard 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
|
|
|
|
|
|
|
Or, standalone: |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
use Object::Pad; |
40
|
|
|
|
|
|
|
use Object::Pad::FieldAttr::Checked; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
package Numerical { |
43
|
|
|
|
|
|
|
use Scalar::Util qw( looks_like_number ); |
44
|
|
|
|
|
|
|
sub check { looks_like_number $_[1]; } |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
class Point { |
48
|
|
|
|
|
|
|
field $x :param :reader :Checked('Numerical'); |
49
|
|
|
|
|
|
|
field $y :param :reader :Checked('Numerical'); |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
... |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=head1 DESCRIPTION |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
This module provides a third-party field attribute for L-based |
57
|
|
|
|
|
|
|
classes, which declares that values assigned to the field must conform to a |
58
|
|
|
|
|
|
|
given constraint check. |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
B The ability for L to take third-party field attributes |
61
|
|
|
|
|
|
|
is still new and highly experimental, and subject to much API change in |
62
|
|
|
|
|
|
|
future. As a result, this module should be considered equally experimental. |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
Additionally, the behaviour provided by this module should be considered more |
65
|
|
|
|
|
|
|
of a work-in-progress stepping stone. Ideally, constraint syntax ought |
66
|
|
|
|
|
|
|
to be provided in a much more fundamental way by Perl itself, allowing it to |
67
|
|
|
|
|
|
|
be used on C lexicals, subroutine parameters, and other places as well as |
68
|
|
|
|
|
|
|
object fields. This module is more of a placeholder to allow some part of that |
69
|
|
|
|
|
|
|
behaviour to be specified for object fields, while not getting in the way of a |
70
|
|
|
|
|
|
|
more general, more powerful system being added in future. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=head1 FIELD ATTRIBUTES |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=head2 :Checked |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
field $name :Checked(EXPRESSION) ...; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
Declares that any value assigned to the field during the constructor or using |
79
|
|
|
|
|
|
|
an accessor method must conform to the constraint checking object specified by |
80
|
|
|
|
|
|
|
the expression. Attempts to assign a non-conforming value will throw an |
81
|
|
|
|
|
|
|
exception and the field will not be modified. Currently only scalar fields are |
82
|
|
|
|
|
|
|
supported. |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
At compiletime, the string given by I is C'ed in scalar |
85
|
|
|
|
|
|
|
context, and its result is stored as part of the field's definition. The |
86
|
|
|
|
|
|
|
expression must yield either an object reference, or a string containing the |
87
|
|
|
|
|
|
|
name of a package. In either case, a method called C must exist on it. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
During the string C of the expression, the C pragma is |
90
|
|
|
|
|
|
|
specifically disabled, allowing a bareword to be used as the name of a |
91
|
|
|
|
|
|
|
package. This feature may be removed in future, so be sure to quote package |
92
|
|
|
|
|
|
|
names if required. |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
field $x :Checked('CheckerPackage'); |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
At runtime, this constraint checker is used every time an attempt is made to |
97
|
|
|
|
|
|
|
assign a value to the field I, whether that is |
98
|
|
|
|
|
|
|
from C<:param> initialisation, or invoking a C<:writer> accessor. The checker |
99
|
|
|
|
|
|
|
is used as the invocant for invoking a C method, and the new value for |
100
|
|
|
|
|
|
|
the field is passed as an argument. If the method returns true, the assignment |
101
|
|
|
|
|
|
|
is allowed. If false, it is rejected with an exception and the field itself |
102
|
|
|
|
|
|
|
remains unmodified. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
$ok = $checker->check( $value ); |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
As this is the interface supported by L, any constraint |
107
|
|
|
|
|
|
|
object provided by that module is already supported here. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
use Types::Standard qw( Str Num ); |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
field $name :Checked(Str); |
112
|
|
|
|
|
|
|
field $age :Checked(Num); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
B that direct assignment into the field variable by code |
115
|
|
|
|
|
|
|
within the class is not checked. This is partly because of design |
116
|
|
|
|
|
|
|
considerations, and partly because any way to implement that would be horribly |
117
|
|
|
|
|
|
|
slow, or flat-out impossible. I this module used to |
118
|
|
|
|
|
|
|
claim that even direct assignments would be checked. but this gave a false |
119
|
|
|
|
|
|
|
sense of safety if deeply-nested containers were involved and modified from |
120
|
|
|
|
|
|
|
within. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
(For performance reasons, the C method is actually resolved into a |
123
|
|
|
|
|
|
|
function at compiletime when the C<:Checked> attribute is applied, and this |
124
|
|
|
|
|
|
|
stored function is the one that is called at assignment time. If the method |
125
|
|
|
|
|
|
|
itself is replaced later by globref assignment or other trickery, this updated |
126
|
|
|
|
|
|
|
function will not be used.) |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=cut |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub import |
131
|
|
|
|
|
|
|
{ |
132
|
3
|
|
|
3
|
|
2343
|
$^H{"Object::Pad::FieldAttr::Checked/Checked"}++; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub unimport |
136
|
|
|
|
|
|
|
{ |
137
|
0
|
|
|
0
|
|
|
delete $^H{"Object::Pad::FieldAttr::Checked/Checked"}; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=head1 TODO |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=over 4 |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=item * |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
Support C<:accessor> and C<:mutator> accessors as well as C<:writer>. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=back |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=head1 SEE ALSO |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=over 4 |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=item * |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
L - apply class type constraints to C fields |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=back |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=head1 AUTHOR |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
Paul Evans |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=cut |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
0x55AA; |