line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Specio::Declare; |
2
|
|
|
|
|
|
|
|
3
|
28
|
|
|
28
|
|
811748
|
use strict; |
|
28
|
|
|
|
|
728
|
|
|
28
|
|
|
|
|
822
|
|
4
|
28
|
|
|
28
|
|
143
|
use warnings; |
|
28
|
|
|
|
|
44
|
|
|
28
|
|
|
|
|
717
|
|
5
|
|
|
|
|
|
|
|
6
|
28
|
|
|
28
|
|
3451
|
use parent 'Exporter'; |
|
28
|
|
|
|
|
2208
|
|
|
28
|
|
|
|
|
167
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '0.46'; |
9
|
|
|
|
|
|
|
|
10
|
28
|
|
|
28
|
|
1837
|
use Carp qw( croak ); |
|
28
|
|
|
|
|
48
|
|
|
28
|
|
|
|
|
1223
|
|
11
|
28
|
|
|
28
|
|
10978
|
use Specio::Coercion; |
|
28
|
|
|
|
|
68
|
|
|
28
|
|
|
|
|
871
|
|
12
|
28
|
|
|
28
|
|
11052
|
use Specio::Constraint::Simple; |
|
28
|
|
|
|
|
65
|
|
|
28
|
|
|
|
|
856
|
|
13
|
28
|
|
|
28
|
|
4295
|
use Specio::DeclaredAt; |
|
28
|
|
|
|
|
53
|
|
|
28
|
|
|
|
|
722
|
|
14
|
28
|
|
|
28
|
|
143
|
use Specio::Helpers qw( install_t_sub _STRINGLIKE ); |
|
28
|
|
|
|
|
50
|
|
|
28
|
|
|
|
|
1286
|
|
15
|
28
|
|
|
28
|
|
3043
|
use Specio::Registry qw( internal_types_for_package register ); |
|
28
|
|
|
|
|
52
|
|
|
28
|
|
|
|
|
38593
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
## no critic (Modules::ProhibitAutomaticExportation) |
18
|
|
|
|
|
|
|
our @EXPORT = qw( |
19
|
|
|
|
|
|
|
anon |
20
|
|
|
|
|
|
|
any_can_type |
21
|
|
|
|
|
|
|
any_does_type |
22
|
|
|
|
|
|
|
any_isa_type |
23
|
|
|
|
|
|
|
coerce |
24
|
|
|
|
|
|
|
declare |
25
|
|
|
|
|
|
|
enum |
26
|
|
|
|
|
|
|
intersection |
27
|
|
|
|
|
|
|
object_can_type |
28
|
|
|
|
|
|
|
object_does_type |
29
|
|
|
|
|
|
|
object_isa_type |
30
|
|
|
|
|
|
|
union |
31
|
|
|
|
|
|
|
); |
32
|
|
|
|
|
|
|
## use critic |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub import { |
35
|
92
|
|
|
92
|
|
8286
|
my $package = shift; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# What the heck is this monstrosity? |
38
|
|
|
|
|
|
|
# |
39
|
|
|
|
|
|
|
# Moose version 2.0901 included a first pass at support for Specio. This |
40
|
|
|
|
|
|
|
# was based on Specio c. 0.06 when Specio itself still used |
41
|
|
|
|
|
|
|
# Moose. Unfortunately, recent changes to Specio broke this support and |
42
|
|
|
|
|
|
|
# the Moose core needs updating. |
43
|
|
|
|
|
|
|
# |
44
|
|
|
|
|
|
|
# However, stable versions of Moose have since shipped with a test that |
45
|
|
|
|
|
|
|
# attempts to test itself with Specio 0.07+. This was fine until I wanted |
46
|
|
|
|
|
|
|
# to release a non-TRIAL Specio. |
47
|
|
|
|
|
|
|
# |
48
|
|
|
|
|
|
|
# Once that's out, anyone installing Specio will cause future attempts to |
49
|
|
|
|
|
|
|
# install Moose to fail until Moose includes updated Specio support! |
50
|
|
|
|
|
|
|
# Breaking Moose is not acceptable, thus this mess. |
51
|
|
|
|
|
|
|
# |
52
|
|
|
|
|
|
|
# Note that since Moose 2.1207 this test was renamed and the Specio tests |
53
|
|
|
|
|
|
|
# actually run (and pass). We still need to leave this in here for quite |
54
|
|
|
|
|
|
|
# some time. People should be able to install Specio and then install an |
55
|
|
|
|
|
|
|
# older Moose indefinitely (or at least for a year or two). |
56
|
92
|
50
|
33
|
|
|
663
|
if ( $ENV{HARNESS_ACTIVE} |
57
|
|
|
|
|
|
|
&& $0 =~ m{t[\\/]type_constraints[\\/]specio\.t$} ) { |
58
|
|
|
|
|
|
|
|
59
|
0
|
|
|
|
|
0
|
require Test::More; |
60
|
0
|
|
|
|
|
0
|
Test::More::plan( skip_all => |
61
|
|
|
|
|
|
|
'These tests will not pass with this version of Specio' ); |
62
|
0
|
|
|
|
|
0
|
exit 0; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
92
|
|
|
|
|
222
|
my $caller = caller(); |
66
|
|
|
|
|
|
|
|
67
|
92
|
|
|
|
|
9406
|
$package->export_to_level( 1, $package, @_ ); |
68
|
|
|
|
|
|
|
|
69
|
92
|
|
|
|
|
700
|
install_t_sub( |
70
|
|
|
|
|
|
|
$caller, |
71
|
|
|
|
|
|
|
internal_types_for_package($caller) |
72
|
|
|
|
|
|
|
); |
73
|
|
|
|
|
|
|
|
74
|
92
|
|
|
|
|
7976
|
return; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub declare { |
78
|
801
|
50
|
|
801
|
1
|
2071
|
my $name = _STRINGLIKE(shift) |
79
|
|
|
|
|
|
|
or croak 'You must provide a name for declared types'; |
80
|
801
|
|
|
|
|
2105
|
my %p = @_; |
81
|
|
|
|
|
|
|
|
82
|
801
|
|
|
|
|
2016
|
my $tc = _make_tc( name => $name, %p ); |
83
|
|
|
|
|
|
|
|
84
|
801
|
|
|
|
|
5403
|
register( scalar caller(), $name, $tc, 'exportable' ); |
85
|
|
|
|
|
|
|
|
86
|
801
|
|
|
|
|
1848
|
return $tc; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
sub anon { |
90
|
10
|
|
|
10
|
1
|
45
|
return _make_tc(@_); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub enum { |
94
|
3
|
|
|
3
|
1
|
17880
|
my $name; |
95
|
3
|
50
|
|
|
|
15
|
$name = shift if @_ % 2; |
96
|
3
|
|
|
|
|
9
|
my %p = @_; |
97
|
|
|
|
|
|
|
|
98
|
3
|
|
|
|
|
1380
|
require Specio::Constraint::Enum; |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
my $tc = _make_tc( |
101
|
|
|
|
|
|
|
( defined $name ? ( name => $name ) : () ), |
102
|
|
|
|
|
|
|
values => $p{values}, |
103
|
3
|
50
|
|
|
|
25
|
type_class => 'Specio::Constraint::Enum', |
104
|
|
|
|
|
|
|
); |
105
|
|
|
|
|
|
|
|
106
|
3
|
50
|
|
|
|
34
|
register( scalar caller(), $name, $tc, 'exportable' ) |
107
|
|
|
|
|
|
|
if defined $name; |
108
|
|
|
|
|
|
|
|
109
|
3
|
|
|
|
|
9
|
return $tc; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
sub object_can_type { |
113
|
5
|
|
|
5
|
1
|
134592
|
my $name; |
114
|
5
|
100
|
|
|
|
36
|
$name = shift if @_ % 2; |
115
|
5
|
|
|
|
|
23
|
my %p = @_; |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# This cannot be loaded earlier, since it loads Specio::Library::Builtins, |
118
|
|
|
|
|
|
|
# which in turn wants to load Specio::Declare (the current module). |
119
|
5
|
|
|
|
|
1007
|
require Specio::Constraint::ObjectCan; |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
my $tc = _make_tc( |
122
|
|
|
|
|
|
|
( defined $name ? ( name => $name ) : () ), |
123
|
|
|
|
|
|
|
methods => $p{methods}, |
124
|
5
|
100
|
|
|
|
38
|
type_class => 'Specio::Constraint::ObjectCan', |
125
|
|
|
|
|
|
|
); |
126
|
|
|
|
|
|
|
|
127
|
5
|
100
|
|
|
|
58
|
register( scalar caller(), $name, $tc, 'exportable' ) |
128
|
|
|
|
|
|
|
if defined $name; |
129
|
|
|
|
|
|
|
|
130
|
5
|
|
|
|
|
20
|
return $tc; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub object_does_type { |
134
|
7
|
|
|
7
|
1
|
49446
|
my $name; |
135
|
7
|
100
|
|
|
|
32
|
$name = shift if @_ % 2; |
136
|
7
|
|
|
|
|
22
|
my %p = @_; |
137
|
|
|
|
|
|
|
|
138
|
7
|
|
|
|
|
18
|
my $caller = scalar caller(); |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# If we are being called repeatedly with a single argument, then we don't |
141
|
|
|
|
|
|
|
# want to blow up because the type has already been declared. This would |
142
|
|
|
|
|
|
|
# force the user to use t() for all calls but the first, making their code |
143
|
|
|
|
|
|
|
# pointlessly more complicated. |
144
|
7
|
100
|
|
|
|
26
|
unless ( keys %p ) { |
145
|
4
|
100
|
|
|
|
20
|
if ( my $exists = internal_types_for_package($caller)->{$name} ) { |
146
|
2
|
|
|
|
|
10
|
return $exists; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
5
|
|
|
|
|
2091
|
require Specio::Constraint::ObjectDoes; |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
my $tc = _make_tc( |
153
|
|
|
|
|
|
|
( defined $name ? ( name => $name ) : () ), |
154
|
5
|
100
|
|
|
|
45
|
role => ( defined $p{role} ? $p{role} : $name ), |
|
|
100
|
|
|
|
|
|
155
|
|
|
|
|
|
|
type_class => 'Specio::Constraint::ObjectDoes', |
156
|
|
|
|
|
|
|
); |
157
|
|
|
|
|
|
|
|
158
|
5
|
100
|
|
|
|
52
|
register( scalar caller(), $name, $tc, 'exportable' ) |
159
|
|
|
|
|
|
|
if defined $name; |
160
|
|
|
|
|
|
|
|
161
|
5
|
|
|
|
|
18
|
return $tc; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
sub object_isa_type { |
165
|
4
|
|
|
4
|
1
|
36951
|
my $name; |
166
|
4
|
100
|
|
|
|
25
|
$name = shift if @_ % 2; |
167
|
4
|
|
|
|
|
13
|
my %p = @_; |
168
|
|
|
|
|
|
|
|
169
|
4
|
|
|
|
|
11
|
my $caller = scalar caller(); |
170
|
4
|
100
|
|
|
|
18
|
unless ( keys %p ) { |
171
|
3
|
100
|
|
|
|
17
|
if ( my $exists = internal_types_for_package($caller)->{$name} ) { |
172
|
1
|
|
|
|
|
6
|
return $exists; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
3
|
|
|
|
|
1381
|
require Specio::Constraint::ObjectIsa; |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
my $tc = _make_tc( |
179
|
|
|
|
|
|
|
( defined $name ? ( name => $name ) : () ), |
180
|
3
|
100
|
|
|
|
30
|
class => ( defined $p{class} ? $p{class} : $name ), |
|
|
100
|
|
|
|
|
|
181
|
|
|
|
|
|
|
type_class => 'Specio::Constraint::ObjectIsa', |
182
|
|
|
|
|
|
|
); |
183
|
|
|
|
|
|
|
|
184
|
3
|
100
|
|
|
|
33
|
register( $caller, $name, $tc, 'exportable' ) |
185
|
|
|
|
|
|
|
if defined $name; |
186
|
|
|
|
|
|
|
|
187
|
3
|
|
|
|
|
10
|
return $tc; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub any_can_type { |
191
|
2
|
|
|
2
|
1
|
23134
|
my $name; |
192
|
2
|
100
|
|
|
|
170
|
$name = shift if @_ % 2; |
193
|
2
|
|
|
|
|
8
|
my %p = @_; |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# This cannot be loaded earlier, since it loads Specio::Library::Builtins, |
196
|
|
|
|
|
|
|
# which in turn wants to load Specio::Declare (the current module). |
197
|
2
|
|
|
|
|
1247
|
require Specio::Constraint::AnyCan; |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
my $tc = _make_tc( |
200
|
|
|
|
|
|
|
( defined $name ? ( name => $name ) : () ), |
201
|
|
|
|
|
|
|
methods => $p{methods}, |
202
|
2
|
100
|
|
|
|
17
|
type_class => 'Specio::Constraint::AnyCan', |
203
|
|
|
|
|
|
|
); |
204
|
|
|
|
|
|
|
|
205
|
2
|
100
|
|
|
|
26
|
register( scalar caller(), $name, $tc, 'exportable' ) |
206
|
|
|
|
|
|
|
if defined $name; |
207
|
|
|
|
|
|
|
|
208
|
2
|
|
|
|
|
7
|
return $tc; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub any_does_type { |
212
|
7
|
|
|
7
|
1
|
54878
|
my $name; |
213
|
7
|
100
|
|
|
|
56
|
$name = shift if @_ % 2; |
214
|
7
|
|
|
|
|
22
|
my %p = @_; |
215
|
|
|
|
|
|
|
|
216
|
7
|
|
|
|
|
19
|
my $caller = scalar caller(); |
217
|
7
|
100
|
|
|
|
24
|
unless ( keys %p ) { |
218
|
3
|
50
|
|
|
|
11
|
if ( my $exists = internal_types_for_package($caller)->{$name} ) { |
219
|
3
|
|
|
|
|
12
|
return $exists; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
4
|
|
|
|
|
1702
|
require Specio::Constraint::AnyDoes; |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
my $tc = _make_tc( |
226
|
|
|
|
|
|
|
( defined $name ? ( name => $name ) : () ), |
227
|
4
|
100
|
|
|
|
39
|
role => ( defined $p{role} ? $p{role} : $name ), |
|
|
50
|
|
|
|
|
|
228
|
|
|
|
|
|
|
type_class => 'Specio::Constraint::AnyDoes', |
229
|
|
|
|
|
|
|
); |
230
|
|
|
|
|
|
|
|
231
|
4
|
100
|
|
|
|
43
|
register( scalar caller(), $name, $tc, 'exportable' ) |
232
|
|
|
|
|
|
|
if defined $name; |
233
|
|
|
|
|
|
|
|
234
|
4
|
|
|
|
|
16
|
return $tc; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub any_isa_type { |
238
|
5
|
|
|
5
|
1
|
66300
|
my $name; |
239
|
5
|
100
|
|
|
|
30
|
$name = shift if @_ % 2; |
240
|
5
|
|
|
|
|
24
|
my %p = @_; |
241
|
|
|
|
|
|
|
|
242
|
5
|
|
|
|
|
15
|
my $caller = scalar caller(); |
243
|
5
|
100
|
|
|
|
22
|
unless ( keys %p ) { |
244
|
2
|
100
|
|
|
|
12
|
if ( my $exists = internal_types_for_package($caller)->{$name} ) { |
245
|
1
|
|
|
|
|
6
|
return $exists; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
4
|
|
|
|
|
1798
|
require Specio::Constraint::AnyIsa; |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
my $tc = _make_tc( |
252
|
|
|
|
|
|
|
( defined $name ? ( name => $name ) : () ), |
253
|
4
|
100
|
|
|
|
39
|
class => ( defined $p{class} ? $p{class} : $name ), |
|
|
100
|
|
|
|
|
|
254
|
|
|
|
|
|
|
type_class => 'Specio::Constraint::AnyIsa', |
255
|
|
|
|
|
|
|
); |
256
|
|
|
|
|
|
|
|
257
|
4
|
100
|
|
|
|
48
|
register( scalar caller(), $name, $tc, 'exportable' ) |
258
|
|
|
|
|
|
|
if defined $name; |
259
|
|
|
|
|
|
|
|
260
|
4
|
|
|
|
|
15
|
return $tc; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub intersection { |
264
|
3
|
|
|
3
|
1
|
14
|
my $name; |
265
|
3
|
100
|
|
|
|
13
|
$name = shift if @_ % 2; |
266
|
3
|
|
|
|
|
10
|
my %p = @_; |
267
|
|
|
|
|
|
|
|
268
|
3
|
|
|
|
|
30
|
require Specio::Constraint::Intersection; |
269
|
|
|
|
|
|
|
|
270
|
3
|
100
|
|
|
|
16
|
my $tc = _make_tc( |
271
|
|
|
|
|
|
|
( defined $name ? ( name => $name ) : () ), |
272
|
|
|
|
|
|
|
%p, |
273
|
|
|
|
|
|
|
type_class => 'Specio::Constraint::Intersection', |
274
|
|
|
|
|
|
|
); |
275
|
|
|
|
|
|
|
|
276
|
3
|
100
|
|
|
|
51
|
register( scalar caller(), $name, $tc, 'exportable' ) |
277
|
|
|
|
|
|
|
if defined $name; |
278
|
|
|
|
|
|
|
|
279
|
3
|
|
|
|
|
11
|
return $tc; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub union { |
283
|
4
|
|
|
4
|
1
|
15
|
my $name; |
284
|
4
|
100
|
|
|
|
15
|
$name = shift if @_ % 2; |
285
|
4
|
|
|
|
|
16
|
my %p = @_; |
286
|
|
|
|
|
|
|
|
287
|
4
|
|
|
|
|
416
|
require Specio::Constraint::Union; |
288
|
|
|
|
|
|
|
|
289
|
4
|
100
|
|
|
|
25
|
my $tc = _make_tc( |
290
|
|
|
|
|
|
|
( defined $name ? ( name => $name ) : () ), |
291
|
|
|
|
|
|
|
%p, |
292
|
|
|
|
|
|
|
type_class => 'Specio::Constraint::Union', |
293
|
|
|
|
|
|
|
); |
294
|
|
|
|
|
|
|
|
295
|
4
|
100
|
|
|
|
37
|
register( scalar caller(), $name, $tc, 'exportable' ) |
296
|
|
|
|
|
|
|
if defined $name; |
297
|
|
|
|
|
|
|
|
298
|
4
|
|
|
|
|
12
|
return $tc; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub _make_tc { |
302
|
844
|
|
|
844
|
|
2056
|
my %p = @_; |
303
|
|
|
|
|
|
|
|
304
|
844
|
|
100
|
|
|
2354
|
my $class = delete $p{type_class} || 'Specio::Constraint::Simple'; |
305
|
|
|
|
|
|
|
|
306
|
844
|
100
|
|
|
|
1627
|
$p{constraint} = delete $p{where} if exists $p{where}; |
307
|
844
|
50
|
|
|
|
1471
|
$p{message_generator} = delete $p{message} if exists $p{message}; |
308
|
844
|
100
|
|
|
|
1706
|
$p{inline_generator} = delete $p{inline} if exists $p{inline}; |
309
|
|
|
|
|
|
|
|
310
|
844
|
|
|
|
|
3001
|
return $class->new( |
311
|
|
|
|
|
|
|
%p, |
312
|
|
|
|
|
|
|
declared_at => Specio::DeclaredAt->new_from_caller(2), |
313
|
|
|
|
|
|
|
); |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
sub coerce { |
317
|
16
|
|
|
16
|
1
|
25
|
my $to = shift; |
318
|
16
|
|
|
|
|
48
|
my %p = @_; |
319
|
|
|
|
|
|
|
|
320
|
16
|
100
|
|
|
|
48
|
$p{coercion} = delete $p{using} if exists $p{using}; |
321
|
16
|
100
|
|
|
|
41
|
$p{inline_generator} = delete $p{inline} if exists $p{inline}; |
322
|
|
|
|
|
|
|
|
323
|
16
|
|
|
|
|
88
|
return $to->add_coercion( |
324
|
|
|
|
|
|
|
Specio::Coercion->new( |
325
|
|
|
|
|
|
|
to => $to, |
326
|
|
|
|
|
|
|
%p, |
327
|
|
|
|
|
|
|
declared_at => Specio::DeclaredAt->new_from_caller(1), |
328
|
|
|
|
|
|
|
) |
329
|
|
|
|
|
|
|
); |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
1; |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
# ABSTRACT: Specio declaration subroutines |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
__END__ |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=pod |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
=encoding UTF-8 |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=head1 NAME |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
Specio::Declare - Specio declaration subroutines |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=head1 VERSION |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
version 0.46 |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=head1 SYNOPSIS |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
package MyApp::Type::Library; |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
use parent 'Specio::Exporter'; |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
use Specio::Declare; |
357
|
|
|
|
|
|
|
use Specio::Library::Builtins; |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
declare( |
360
|
|
|
|
|
|
|
'Foo', |
361
|
|
|
|
|
|
|
parent => t('Str'), |
362
|
|
|
|
|
|
|
where => sub { $_[0] =~ /foo/i }, |
363
|
|
|
|
|
|
|
); |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
declare( |
366
|
|
|
|
|
|
|
'ArrayRefOfInt', |
367
|
|
|
|
|
|
|
parent => t( 'ArrayRef', of => t('Int') ), |
368
|
|
|
|
|
|
|
); |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
my $even = anon( |
371
|
|
|
|
|
|
|
parent => t('Int'), |
372
|
|
|
|
|
|
|
inline => sub { |
373
|
|
|
|
|
|
|
my $type = shift; |
374
|
|
|
|
|
|
|
my $value_var = shift; |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
return $value_var . ' % 2 == 0'; |
377
|
|
|
|
|
|
|
}, |
378
|
|
|
|
|
|
|
); |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
coerce( |
381
|
|
|
|
|
|
|
t('ArrayRef'), |
382
|
|
|
|
|
|
|
from => t('Foo'), |
383
|
|
|
|
|
|
|
using => sub { [ $_[0] ] }, |
384
|
|
|
|
|
|
|
); |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
coerce( |
387
|
|
|
|
|
|
|
$even, |
388
|
|
|
|
|
|
|
from => t('Int'), |
389
|
|
|
|
|
|
|
using => sub { $_[0] % 2 ? $_[0] + 1 : $_[0] }, |
390
|
|
|
|
|
|
|
); |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
# Specio name is DateTime |
393
|
|
|
|
|
|
|
any_isa_type('DateTime'); |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
# Specio name is DateTimeObject |
396
|
|
|
|
|
|
|
object_isa_type( 'DateTimeObject', class => 'DateTime' ); |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
any_can_type( |
399
|
|
|
|
|
|
|
'Duck', |
400
|
|
|
|
|
|
|
methods => [ 'duck_walk', 'quack' ], |
401
|
|
|
|
|
|
|
); |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
object_can_type( |
404
|
|
|
|
|
|
|
'DuckObject', |
405
|
|
|
|
|
|
|
methods => [ 'duck_walk', 'quack' ], |
406
|
|
|
|
|
|
|
); |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
enum( |
409
|
|
|
|
|
|
|
'Colors', |
410
|
|
|
|
|
|
|
values => [qw( blue green red )], |
411
|
|
|
|
|
|
|
); |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
intersection( |
414
|
|
|
|
|
|
|
'HashRefAndArrayRef', |
415
|
|
|
|
|
|
|
of => [ t('HashRef'), t('ArrayRef') ], |
416
|
|
|
|
|
|
|
); |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
union( |
419
|
|
|
|
|
|
|
'IntOrArrayRef', |
420
|
|
|
|
|
|
|
of => [ t('Int'), t('ArrayRef') ], |
421
|
|
|
|
|
|
|
); |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=head1 DESCRIPTION |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
This package exports a set of type declaration helpers. Importing this package |
426
|
|
|
|
|
|
|
also causes it to create a C<t> subroutine the caller. |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=head1 SUBROUTINES |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
This module exports the following subroutines. |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=head2 t('name') |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
This subroutine lets you access any types you have declared so far, as well as |
435
|
|
|
|
|
|
|
any types you imported from another type library. |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
If you pass an unknown name, it throws an exception. |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=head2 declare(...) |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
This subroutine declares a named type. The first argument is the type name, |
442
|
|
|
|
|
|
|
followed by a set of key/value parameters: |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=over 4 |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=item * parent => $type |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
The parent should be another type object. Specifically, it can be anything |
449
|
|
|
|
|
|
|
which does the L<Specio::Constraint::Role::Interface> role. The parent can be a |
450
|
|
|
|
|
|
|
named or anonymous type. |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
=item * where => sub { ... } |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
This is a subroutine which defines the type constraint. It will be passed a |
455
|
|
|
|
|
|
|
single argument, the value to check, and it should return true or false to |
456
|
|
|
|
|
|
|
indicate whether or not the value is valid for the type. |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
This parameter is mutually exclusive with the C<inline> parameter. |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=item * inline => sub { ... } |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
This is a subroutine that is called to generate inline code to validate the |
463
|
|
|
|
|
|
|
type. Inlining can be I<much> faster than simply providing a subroutine with |
464
|
|
|
|
|
|
|
the C<where> parameter, but is often more complicated to get right. |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
The inline generator is called as a method on the type with one argument. This |
467
|
|
|
|
|
|
|
argument is a I<string> containing the variable name to use in the generated |
468
|
|
|
|
|
|
|
code. Typically this is something like C<'$_[0]'> or C<'$value'>. |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
The inline generator subroutine should return a I<string> of code representing |
471
|
|
|
|
|
|
|
a single term, and it I<should not> be terminated with a semicolon. This |
472
|
|
|
|
|
|
|
allows the inlined code to be safely included in an C<if> statement, for |
473
|
|
|
|
|
|
|
example. You can use C<do { }> blocks and ternaries to get everything into one |
474
|
|
|
|
|
|
|
term. Do not assign to the variable you are testing. This single term should |
475
|
|
|
|
|
|
|
evaluate to true or false. |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
The inline generator is expected to include code to implement both the current |
478
|
|
|
|
|
|
|
type and all its parents. Typically, the easiest way to do this is to write a |
479
|
|
|
|
|
|
|
subroutine something like this: |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
sub { |
482
|
|
|
|
|
|
|
my $self = shift; |
483
|
|
|
|
|
|
|
my $var = shift; |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
return $self->parent->inline_check($var) |
486
|
|
|
|
|
|
|
. ' and more checking code goes here'; |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
Or, more concisely: |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
sub { $_[0]->parent->inline_check( $_[1] ) . 'more code that checks $_[1]' } |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
The C<inline> parameter is mutually exclusive with the C<where> parameter. |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
=item * message_generator => sub { ... } |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
A subroutine to generate an error message when the type check fails. The |
498
|
|
|
|
|
|
|
default message says something like "Validation failed for type named Int |
499
|
|
|
|
|
|
|
declared in package Specio::Library::Builtins |
500
|
|
|
|
|
|
|
(.../Specio/blib/lib/Specio/Library/Builtins.pm) at line 147 in sub named (eval) |
501
|
|
|
|
|
|
|
with value 1.1". |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
You can override this to provide something more specific about the way the |
504
|
|
|
|
|
|
|
type failed. |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
The subroutine you provide will be called as a method on the type with two |
507
|
|
|
|
|
|
|
arguments. The first is the description of the type (the bit in the message |
508
|
|
|
|
|
|
|
above that starts with "type named Int ..." and ends with "... in sub named |
509
|
|
|
|
|
|
|
(eval)". This description says what the thing is and where it was defined. |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
The second argument is the value that failed the type check, after any |
512
|
|
|
|
|
|
|
coercions that might have been applied. |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
=back |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=head2 anon(...) |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
This subroutine declares an anonymous type. It is identical to C<declare> |
519
|
|
|
|
|
|
|
except that it expects a list of key/value parameters without a type name as |
520
|
|
|
|
|
|
|
the first parameter. |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
=head2 coerce(...) |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
This declares a coercion from one type to another. The first argument should |
525
|
|
|
|
|
|
|
be an object which does the L<Specio::Constraint::Role::Interface> role. This |
526
|
|
|
|
|
|
|
can be either a named or anonymous type. This type is the type that the |
527
|
|
|
|
|
|
|
coercion is I<to>. |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
The remaining arguments are key/value parameters: |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
=over 4 |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=item * from => $type |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
This must be an object which does the L<Specio::Constraint::Role::Interface> |
536
|
|
|
|
|
|
|
role. This is type that we are coercing I<from>. Again, this can be either a |
537
|
|
|
|
|
|
|
named or anonymous type. |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=item * using => sub { ... } |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
This is a subroutine which defines the type coercion. It will be passed a |
542
|
|
|
|
|
|
|
single argument, the value to coerce. It should return a new value of the type |
543
|
|
|
|
|
|
|
this coercion is to. |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
This parameter is mutually exclusive with the C<inline> parameter. |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=item * inline => sub { ... } |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
This is a subroutine that is called to generate inline code to perform the |
550
|
|
|
|
|
|
|
coercion. |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
The inline generator is called as a method on the type with one argument. This |
553
|
|
|
|
|
|
|
argument is a I<string> containing the variable name to use in the generated |
554
|
|
|
|
|
|
|
code. Typically this is something like C<'$_[0]'> or C<'$value'>. |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
The inline generator subroutine should return a I<string> of code representing |
557
|
|
|
|
|
|
|
a single term, and it I<should not> be terminated with a semicolon. This |
558
|
|
|
|
|
|
|
allows the inlined code to be safely included in an C<if> statement, for |
559
|
|
|
|
|
|
|
example. You can use C<do { }> blocks and ternaries to get everything into one |
560
|
|
|
|
|
|
|
term. This single term should evaluate to the new value. |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=back |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
=head1 DECLARATION HELPERS |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
This module also exports some helper subs for declaring certain kinds of types: |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
=head2 any_isa_type, object_isa_type |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
The C<any_isa_type> helper creates a type which accepts a class name or |
571
|
|
|
|
|
|
|
object of the given class. The C<object_isa_type> helper creates a type |
572
|
|
|
|
|
|
|
which only accepts an object of the given class. |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
These subroutines take a type name as the first argument. The remaining |
575
|
|
|
|
|
|
|
arguments are key/value pairs. Currently this is just the C<class> key, which |
576
|
|
|
|
|
|
|
should be a class name. This is the class that the type requires. |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
The type name argument can be omitted to create an anonymous type. |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
You can also pass just a single argument, in which case that will be used as |
581
|
|
|
|
|
|
|
both the type's name and the class for the constraint to check. |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
=head2 any_does_type, object_does_type |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
The C<any_does_type> helper creates a type which accepts a class name or |
586
|
|
|
|
|
|
|
object which does the given role. The C<object_does_type> helper creates a |
587
|
|
|
|
|
|
|
type which only accepts an object which does the given role. |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
These subroutines take a type name as the first argument. The remaining |
590
|
|
|
|
|
|
|
arguments are key/value pairs. Currently this is just the C<role> key, which |
591
|
|
|
|
|
|
|
should be a role name. This is the class that the type requires. |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
This should just work (I hope) with roles created by L<Moose>, L<Mouse>, and |
594
|
|
|
|
|
|
|
L<Moo> (using L<Role::Tiny>). |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
The type name argument can be omitted to create an anonymous type. |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
You can also pass just a single argument, in which case that will be used as |
599
|
|
|
|
|
|
|
both the type's name and the role for the constraint to check. |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
=head2 any_can_type, object_can_type |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
The C<any_can_type> helper creates a type which accepts a class name or |
604
|
|
|
|
|
|
|
object with the given methods. The C<object_can_type> helper creates a type |
605
|
|
|
|
|
|
|
which only accepts an object with the given methods. |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
These subroutines take a type name as the first argument. The remaining |
608
|
|
|
|
|
|
|
arguments are key/value pairs. Currently this is just the C<methods> key, |
609
|
|
|
|
|
|
|
which can be either a string or array reference of strings. These strings are |
610
|
|
|
|
|
|
|
the required methods for the type. |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
The type name argument can be omitted to create an anonymous type. |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=head2 enum |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
This creates a type which accepts a string matching a given list of acceptable |
617
|
|
|
|
|
|
|
values. |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
The first argument is the type name. The remaining arguments are key/value |
620
|
|
|
|
|
|
|
pairs. Currently this is just the C<values> key. This should an array |
621
|
|
|
|
|
|
|
reference of acceptable string values. |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
The type name argument can be omitted to create an anonymous type. |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
=head2 intersection |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
This creates a type which is the intersection of two or more other types. A |
628
|
|
|
|
|
|
|
union only accepts values which match all of its underlying types. |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
The first argument is the type name. The remaining arguments are key/value |
631
|
|
|
|
|
|
|
pairs. Currently this is just the C<of> key. This should an array |
632
|
|
|
|
|
|
|
reference of types. |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
The type name argument can be omitted to create an anonymous type. |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
=head2 union |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
This creates a type which is the union of two or more other types. A union |
639
|
|
|
|
|
|
|
accepts any of its underlying types. |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
The first argument is the type name. The remaining arguments are key/value |
642
|
|
|
|
|
|
|
pairs. Currently this is just the C<of> key. This should an array |
643
|
|
|
|
|
|
|
reference of types. |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
The type name argument can be omitted to create an anonymous type. |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=head1 PARAMETERIZED TYPES |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
You can create a parameterized type by calling C<t> with additional |
650
|
|
|
|
|
|
|
parameters, like this: |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
my $arrayref_of_int = t( 'ArrayRef', of => t('Int') ); |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
my $arrayref_of_hashref_of_int = t( |
655
|
|
|
|
|
|
|
'ArrayRef', |
656
|
|
|
|
|
|
|
of => t( |
657
|
|
|
|
|
|
|
'HashRef', |
658
|
|
|
|
|
|
|
of => t('Int'), |
659
|
|
|
|
|
|
|
), |
660
|
|
|
|
|
|
|
); |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
The C<t> subroutine assumes that if it receives more than one argument, it |
663
|
|
|
|
|
|
|
should look up the named type and call C<< $type->parameterize(...) >> with |
664
|
|
|
|
|
|
|
the additional arguments. |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
If the named type cannot be parameterized, it throws an error. |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
You can also call C<< $type->parameterize >> directly if needed. See |
669
|
|
|
|
|
|
|
L<Specio::Constraint::Parameterizable> for details. |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
=head1 SUPPORT |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>. |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>. |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=head1 SOURCE |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>. |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=head1 AUTHOR |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
Dave Rolsky <autarch@urth.org> |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
This software is Copyright (c) 2012 - 2020 by Dave Rolsky. |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
This is free software, licensed under: |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
The Artistic License 2.0 (GPL Compatible) |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
The full text of the license can be found in the |
694
|
|
|
|
|
|
|
F<LICENSE> file included with this distribution. |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=cut |