line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package ZMQ::Declare::ZDCF::Validator; |
2
|
|
|
|
|
|
|
{ |
3
|
|
|
|
|
|
|
$ZMQ::Declare::ZDCF::Validator::VERSION = '0.03'; |
4
|
|
|
|
|
|
|
} |
5
|
1
|
|
|
1
|
|
21270
|
use 5.008001; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
37
|
|
6
|
1
|
|
|
1
|
|
1986
|
use Moose; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
use Data::Rx; |
9
|
|
|
|
|
|
|
use Clone (); |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# Scope for schema snippets |
12
|
|
|
|
|
|
|
SCOPE: { |
13
|
|
|
|
|
|
|
# The following spec snippets are shared between ZDCF 0.1 and ZDCF 1.0 |
14
|
|
|
|
|
|
|
my $context_schema = { # the top level context obj/hash |
15
|
|
|
|
|
|
|
type => '//rec', |
16
|
|
|
|
|
|
|
optional => { # can have these properties |
17
|
|
|
|
|
|
|
iothreads => { type => '//int', range => {min => 1} }, |
18
|
|
|
|
|
|
|
verbose => '//bool', |
19
|
|
|
|
|
|
|
}, |
20
|
|
|
|
|
|
|
}; |
21
|
|
|
|
|
|
|
my $option_schema = { |
22
|
|
|
|
|
|
|
type => '//rec', |
23
|
|
|
|
|
|
|
optional => { |
24
|
|
|
|
|
|
|
"hwm" => { type => '//int' }, |
25
|
|
|
|
|
|
|
"swap" => { type => '//int' }, |
26
|
|
|
|
|
|
|
"affinity" => { type => '//int' }, |
27
|
|
|
|
|
|
|
"identity" => { type => '//str' }, |
28
|
|
|
|
|
|
|
"subscribe" => { type => '//str' }, |
29
|
|
|
|
|
|
|
"rate" => { type => '//int' }, |
30
|
|
|
|
|
|
|
"recovery_ivl" => { type => '//int' }, |
31
|
|
|
|
|
|
|
"mcast_loop" => { type => '//bool' }, |
32
|
|
|
|
|
|
|
"sndbuf" => { type => '//int' }, |
33
|
|
|
|
|
|
|
"rcvbuf" => { type => '//int' }, |
34
|
|
|
|
|
|
|
}, |
35
|
|
|
|
|
|
|
}; |
36
|
|
|
|
|
|
|
my $string_or_value_ary_schema = { |
37
|
|
|
|
|
|
|
type => '//any', |
38
|
|
|
|
|
|
|
of => [ |
39
|
|
|
|
|
|
|
{ type => '//str' }, |
40
|
|
|
|
|
|
|
{ type => '//arr', length => {min => 1}, contents => "//str" }, |
41
|
|
|
|
|
|
|
] |
42
|
|
|
|
|
|
|
}; |
43
|
|
|
|
|
|
|
my $socket_type_schema = { |
44
|
|
|
|
|
|
|
type => '//any', |
45
|
|
|
|
|
|
|
of => [ |
46
|
|
|
|
|
|
|
map { |
47
|
|
|
|
|
|
|
{ type => '//str', value => $_ }, |
48
|
|
|
|
|
|
|
{ type => '//str', value => uc($_) } |
49
|
|
|
|
|
|
|
} qw(sub pub req rep xreq xrep push pull pair router dealer) |
50
|
|
|
|
|
|
|
] |
51
|
|
|
|
|
|
|
}; |
52
|
|
|
|
|
|
|
my $socket_schema = { |
53
|
|
|
|
|
|
|
type => '//any', |
54
|
|
|
|
|
|
|
of => [ |
55
|
|
|
|
|
|
|
{ |
56
|
|
|
|
|
|
|
type => '//rec', |
57
|
|
|
|
|
|
|
required => { |
58
|
|
|
|
|
|
|
type => $socket_type_schema, |
59
|
|
|
|
|
|
|
bind => $string_or_value_ary_schema, |
60
|
|
|
|
|
|
|
}, |
61
|
|
|
|
|
|
|
optional => { |
62
|
|
|
|
|
|
|
connect => $string_or_value_ary_schema, |
63
|
|
|
|
|
|
|
option => $option_schema, |
64
|
|
|
|
|
|
|
}, |
65
|
|
|
|
|
|
|
}, |
66
|
|
|
|
|
|
|
{ |
67
|
|
|
|
|
|
|
type => '//rec', |
68
|
|
|
|
|
|
|
required => { |
69
|
|
|
|
|
|
|
type => $socket_type_schema, |
70
|
|
|
|
|
|
|
connect => $string_or_value_ary_schema, |
71
|
|
|
|
|
|
|
}, |
72
|
|
|
|
|
|
|
optional => { |
73
|
|
|
|
|
|
|
bind => $string_or_value_ary_schema, |
74
|
|
|
|
|
|
|
option => $option_schema, |
75
|
|
|
|
|
|
|
}, |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
] |
78
|
|
|
|
|
|
|
}; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# The following are versioned |
81
|
|
|
|
|
|
|
# First for ZDCF 0.1 |
82
|
|
|
|
|
|
|
my $device_schema_0 = { |
83
|
|
|
|
|
|
|
type => '//rec', |
84
|
|
|
|
|
|
|
# device must have property called 'type' |
85
|
|
|
|
|
|
|
required => { 'type' => {type => '//str'} }, |
86
|
|
|
|
|
|
|
rest => {type => '//map', values => $socket_schema}, # anything else is a socket (sigh) |
87
|
|
|
|
|
|
|
}; |
88
|
|
|
|
|
|
|
my $base_zdcf_schema_0 = { |
89
|
|
|
|
|
|
|
type => '//rec', |
90
|
|
|
|
|
|
|
optional => { |
91
|
|
|
|
|
|
|
context => $context_schema, |
92
|
|
|
|
|
|
|
version => { type => '//num', range => {min => 0} }, |
93
|
|
|
|
|
|
|
}, |
94
|
|
|
|
|
|
|
rest => {type => '//map', values => $device_schema_0}, # anything but the context is a device |
95
|
|
|
|
|
|
|
}; |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# Now ZDCF 1.0 |
98
|
|
|
|
|
|
|
my $device_schema_1 = { |
99
|
|
|
|
|
|
|
type => '//rec', |
100
|
|
|
|
|
|
|
optional => { |
101
|
|
|
|
|
|
|
# device CAN have property called 'type' (no longer required) |
102
|
|
|
|
|
|
|
'type' => {type => '//str'}, |
103
|
|
|
|
|
|
|
'sockets' => { |
104
|
|
|
|
|
|
|
type => '//map', |
105
|
|
|
|
|
|
|
values => $socket_schema |
106
|
|
|
|
|
|
|
}, |
107
|
|
|
|
|
|
|
}, |
108
|
|
|
|
|
|
|
}; |
109
|
|
|
|
|
|
|
my $app_schema_1 = { |
110
|
|
|
|
|
|
|
type => '//rec', |
111
|
|
|
|
|
|
|
optional => { |
112
|
|
|
|
|
|
|
context => $context_schema, |
113
|
|
|
|
|
|
|
devices => { type => '//map', values => $device_schema_1 }, |
114
|
|
|
|
|
|
|
}, |
115
|
|
|
|
|
|
|
}; |
116
|
|
|
|
|
|
|
my $base_zdcf_schema_1 = { |
117
|
|
|
|
|
|
|
type => '//rec', |
118
|
|
|
|
|
|
|
required => { |
119
|
|
|
|
|
|
|
version => { type => '//num', range => {min => 0} }, |
120
|
|
|
|
|
|
|
}, |
121
|
|
|
|
|
|
|
optional => { |
122
|
|
|
|
|
|
|
apps => { type => '//map', values => $app_schema_1 }, |
123
|
|
|
|
|
|
|
}, |
124
|
|
|
|
|
|
|
}; |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# A single Rx object is enough |
127
|
|
|
|
|
|
|
my $rx = Data::Rx->new; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
my %validator_schemata; # schema cache |
130
|
|
|
|
|
|
|
sub _get_validator { |
131
|
|
|
|
|
|
|
my $version = shift; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# normalize version |
134
|
|
|
|
|
|
|
my $major_version = int($version||0); |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
if (not exists $validator_schemata{$major_version}) { |
137
|
|
|
|
|
|
|
if ($major_version == 0) { |
138
|
|
|
|
|
|
|
my $validator_schema = $rx->make_schema($base_zdcf_schema_0); |
139
|
|
|
|
|
|
|
$validator_schemata{$major_version} = $validator_schema; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
elsif ($major_version == 1) { |
142
|
|
|
|
|
|
|
my $validator_schema = $rx->make_schema($base_zdcf_schema_1); |
143
|
|
|
|
|
|
|
$validator_schemata{$major_version} = $validator_schema; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
else { |
146
|
|
|
|
|
|
|
die __PACKAGE__ . " does not support ZDCF specification version $version"; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
return $validator_schemata{$major_version}; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
} # end SCOPE |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub validate { |
155
|
|
|
|
|
|
|
my ($self, $structure, $force_version) = @_; |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# Just extract the spec version so we use the right validation code |
158
|
|
|
|
|
|
|
my $version = defined $force_version ? $force_version : $self->find_spec_version($structure); |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
return _get_validator($version)->check($structure); |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub upgrade_structure { |
164
|
|
|
|
|
|
|
my ($self, $structure) = @_; |
165
|
|
|
|
|
|
|
my $major_version = int( $self->find_spec_version($structure) ); |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
if ($major_version == 0 and keys %$structure) { |
168
|
|
|
|
|
|
|
# introduce "apps", "devices", and "sockets" intermediate layers |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# add "apps" layer |
171
|
|
|
|
|
|
|
my $app = {}; |
172
|
|
|
|
|
|
|
foreach my $key (keys %$structure) { |
173
|
|
|
|
|
|
|
next if $key eq 'version'; |
174
|
|
|
|
|
|
|
$app->{$key} = delete $structure->{$key}; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
$structure->{apps} = {"" => $app}; |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# add "devices" layer |
179
|
|
|
|
|
|
|
my $devices = {}; |
180
|
|
|
|
|
|
|
foreach my $key (keys %$app) { |
181
|
|
|
|
|
|
|
next if $key eq 'context'; |
182
|
|
|
|
|
|
|
my $device = $devices->{$key} = delete $app->{$key}; |
183
|
|
|
|
|
|
|
$devices->{$key} = $device; |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# add "sockets" layer |
186
|
|
|
|
|
|
|
my $sockets = {}; |
187
|
|
|
|
|
|
|
foreach my $key (keys %$device) { |
188
|
|
|
|
|
|
|
next if $key eq 'type'; |
189
|
|
|
|
|
|
|
$sockets->{$key} = delete $device->{$key}; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
$device->{sockets} = $sockets; |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
} # end foreach key in application |
194
|
|
|
|
|
|
|
$app->{devices} = $devices; |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
$structure->{version} = 1.0; |
197
|
|
|
|
|
|
|
} # end if have to upgrade from v0 |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub find_spec_version { |
201
|
|
|
|
|
|
|
my ($self, $structure) = @_; |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
return undef if not ref($structure) eq 'HASH'; |
204
|
|
|
|
|
|
|
my $spec_version = $structure->{version} || '0'; # 0 == pre-versioned spec |
205
|
|
|
|
|
|
|
return $spec_version; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub validate_and_upgrade { |
209
|
|
|
|
|
|
|
my ($self, $structure) = @_; |
210
|
|
|
|
|
|
|
return undef if not $self->validate($structure); |
211
|
|
|
|
|
|
|
my $copy = Clone::clone($structure); |
212
|
|
|
|
|
|
|
$self->upgrade_structure($copy); |
213
|
|
|
|
|
|
|
return $copy; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
no Moose; |
217
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
__END__ |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=head1 NAME |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
ZMQ::Declare::ZDCF::Validator - ZDCF validator |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=head1 SYNOPSIS |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
use ZMQ::Declare; |
228
|
|
|
|
|
|
|
my $validator = ZMQ::Declare::ZDCF::Validator->new; |
229
|
|
|
|
|
|
|
unless ($validator->validate($datastructure)) { |
230
|
|
|
|
|
|
|
die "Input data structure is not ZDCF!" |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=head1 DESCRIPTION |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
Validates that a given nested Perl data structure (arrays, hashes, scalars) |
236
|
|
|
|
|
|
|
is actually a valid ZDCF tree. |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=head1 METHODS |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=head2 validate |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
Returns true if the given Perl data structure is a valid ZDCF tree, false |
243
|
|
|
|
|
|
|
otherwise. |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
Dies if the specification version of the ZDCF tree is unsupported. |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
The second parameter to this method can optionally be a major ZDCF |
248
|
|
|
|
|
|
|
specification version to use for validation instead of auto-detection. |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=head2 validate_and_upgrade |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
Validates the input ZDCF structure, then attempts to upgrade |
253
|
|
|
|
|
|
|
it to the newest supported spec version. Returns a cloned copy |
254
|
|
|
|
|
|
|
of the input structure on success or undef on failure. |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=head2 upgrade_structure |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
Given a ZDCF structure, determines the specification version and |
259
|
|
|
|
|
|
|
tries to upgrade it to the most recent supported version. |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Does not validate the input and works in-place. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=head2 find_spec_version |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
Returns the version of the provided specification. |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
Returns undef on failure. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=head1 SEE ALSO |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
The ZDCF RFC L<http://rfc.zeromq.org/spec:17> |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
L<Data::Rx>, L<http://rx.codesimply.com/index.html> |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
L<ZeroMQ> |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=head1 AUTHOR |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
Steffen Mueller E<lt>smueller@cpan.orgE<gt> |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
Copyright (C) 2012 by Steffen Mueller |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
286
|
|
|
|
|
|
|
it under the same terms as Perl itself, either Perl version 5.8.1 or, |
287
|
|
|
|
|
|
|
at your option, any later version of Perl 5 you may have available. |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=cut |