|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package Data::DynamicValidator;  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   $Data::DynamicValidator::VERSION = '0.03';  | 
| 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #ABSTRACT: JPointer-like and Perl union for flexible perl data structures validation  | 
| 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
7
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
311440
 | 
 use strict;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
314
 | 
    | 
| 
8
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
179
 | 
 use warnings;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
266
 | 
    | 
| 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
10
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
59
 | 
 use Carp;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
804
 | 
    | 
| 
11
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
9161
 | 
 use Devel::LexAlias qw(lexalias);  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
75674
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
584
 | 
    | 
| 
12
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
107
 | 
 use PadWalker qw(peek_sub);  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
27
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
455
 | 
    | 
| 
13
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
48
 | 
 use Scalar::Util qw/looks_like_number/;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
880
 | 
    | 
| 
14
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
236005
 | 
 use Storable qw(dclone);  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
78924
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
896
 | 
    | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
16
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
7123
 | 
 use aliased qw/Data::DynamicValidator::Error/;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6235
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
63
 | 
    | 
| 
17
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
741
 | 
 use aliased qw/Data::DynamicValidator::Filter/;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
45
 | 
    | 
| 
18
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
1256
 | 
 use aliased qw/Data::DynamicValidator::Label/;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
50
 | 
    | 
| 
19
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
13012
 | 
 use aliased qw/Data::DynamicValidator::Path/;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51
 | 
    | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 use overload  | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     fallback => 1,  | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     '&{}' => sub {  | 
| 
24
 | 
36
 | 
 
 | 
 
 | 
  
36
  
 | 
 
 | 
246
 | 
         my $self = shift;  | 
| 
25
 | 
36
 | 
 
 | 
 
 | 
  
36
  
 | 
 
 | 
129
 | 
         return sub { $self->validate(@_) }  | 
| 
26
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
1021
 | 
     };  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
57
 | 
    | 
| 
 
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
172
 | 
    | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
28
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
527
 | 
 use parent qw/Exporter/;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
58
 | 
    | 
| 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our @EXPORT_OK = qw/validator/;  | 
| 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
31
 | 
8
 | 
 
 | 
  
 50
  
 | 
  
8
  
 | 
 
 | 
694
 | 
 use constant DEBUG => $ENV{DATA_DYNAMICVALIDATOR_DEBUG} || 0;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19660
 | 
    | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub validator {  | 
| 
41
 | 
48
 | 
 
 | 
 
 | 
  
48
  
 | 
  
1
  
 | 
54237
 | 
     return Data::DynamicValidator->new(@_);  | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub new {  | 
| 
45
 | 
48
 | 
 
 | 
 
 | 
  
48
  
 | 
  
0
  
 | 
99
 | 
     my ($class, $data) = @_;  | 
| 
46
 | 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
203
 | 
     my $self = {  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         _data   => $data,  | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         _errors => [],  | 
| 
49
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         _bases  => [],  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     };  | 
| 
51
 | 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1610
 | 
     return bless $self => $class;  | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub validate {  | 
| 
56
 | 
40
 | 
 
 | 
 
 | 
  
40
  
 | 
  
1
  
 | 
166
 | 
     my ($self, %args) = @_;  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
58
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
66
 | 
     my $on      = $args{on     };  | 
| 
59
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
58
 | 
     my $should  = $args{should };  | 
| 
60
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
59
 | 
     my $because = $args{because};  | 
| 
61
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51
 | 
     my $each    = $args{each   };  | 
| 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
63
 | 
40
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
218
 | 
     croak("Wrong arguments: 'on', 'should', 'because' should be specified")  | 
| 
 
 | 
 
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if(!$on || !$should || !$because);  | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
66
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
41
 | 
     warn "-- validating : $on \n" if DEBUG;  | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
68
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
82
 | 
     my $errors = $self->{_errors};  | 
| 
69
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
49
 | 
     my $selection_results;  | 
| 
70
 | 
40
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
85
 | 
     if ( !@$errors ) {  | 
| 
71
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
648
 | 
         my $success;  | 
| 
72
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
99
 | 
         my $current_base = $self->current_base;  | 
| 
73
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
101
 | 
         my $selector = $self->_rebase_selector($on);  | 
| 
74
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
108
 | 
         ($success, $selection_results) = $self->_apply($selector, $should);  | 
| 
75
 | 
40
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
135
 | 
         $self->report_error($because, $selector)  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             unless $success;  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # OK, now going to child rules if there is no errors  | 
| 
79
 | 
40
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
194
 | 
     if ( !@$errors && $each  ) {  | 
| 
80
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
         warn "-- no errors, will check children\n" if DEBUG;  | 
| 
81
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
         $self->_validate_children($selection_results, $each);  | 
| 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
84
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
287
 | 
     return $self;  | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub report_error {  | 
| 
89
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
  
1
  
 | 
17
 | 
     my ($self, $reason, $path) = @_;  | 
| 
90
 | 
6
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
18
 | 
     $path //= $self->{_current_path};  | 
| 
91
 | 
6
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
13
 | 
     croak "Can't report error unless path is undefined"  | 
| 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         unless defined $path;  | 
| 
93
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
     push @{ $self->{_errors} }, Error->new($reason, $path);  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
37
 | 
    | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
97
 | 
9
 | 
 
 | 
 
 | 
  
9
  
 | 
  
1
  
 | 
318
 | 
 sub is_valid { @{ $_[0]->{_errors} } == 0; }  | 
| 
 
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
58
 | 
    | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
101
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
  
1
  
 | 
51
 | 
 sub errors { $_[0]->{_errors} }  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub rebase {  | 
| 
105
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
1
  
 | 
24
 | 
     my ($self, $expandable_route, $rule) = @_;  | 
| 
106
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
     my $current_base = $self->current_base;  | 
| 
107
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
     my $selector = $self->_rebase_selector($expandable_route);  | 
| 
108
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
     my $scenario = $self->_select($selector);  | 
| 
109
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
50
 | 
     my $number_of_routes = @{ $scenario->{routes} };  | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
    | 
| 
110
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
16
 | 
     carp "The route '$expandable_route' is ambigious for rebasing (should be unique)"  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if $number_of_routes > 1;  | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
113
 | 
5
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
17
 | 
     return $self if $number_of_routes == 0;  | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
115
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
     push @{ $self->{_bases} }, $scenario->{routes}->[0];  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
    | 
| 
116
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
     $rule->($self);  | 
| 
117
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
37
 | 
     pop @{ $self->{_bases} };  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
    | 
| 
118
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
31
 | 
     return $self;  | 
| 
119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
121
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
122
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub current_base {  | 
| 
123
 | 
92
 | 
 
 | 
 
 | 
  
92
  
 | 
  
1
  
 | 
231
 | 
     my $bases = $_[0]->{_bases};  | 
| 
124
 | 
92
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
218
 | 
     return undef unless @$bases;  | 
| 
125
 | 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
116
 | 
     return $bases->[-1];  | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
127
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ### private/implementation methods  | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _rebase_selector {  | 
| 
131
 | 
45
 | 
 
 | 
 
 | 
  
45
  
 | 
 
 | 
74
 | 
     my ($self, $selector) = @_;  | 
| 
132
 | 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
84
 | 
     my $current_base = $self->current_base;  | 
| 
133
 | 
45
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
129
 | 
     my $add_base = $current_base && $selector !~ /^\/{2,}/;  | 
| 
134
 | 
45
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
469
 | 
     my $rebased = $add_base ? $current_base . $selector : $selector;  | 
| 
135
 | 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
61
 | 
     warn "-- Rebasing selector $selector to $rebased \n" if DEBUG;  | 
| 
136
 | 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
111
 | 
     return $rebased;  | 
| 
137
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
138
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
139
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _validate_children {  | 
| 
140
 | 
11
 | 
 
 | 
 
 | 
  
11
  
 | 
 
 | 
19
 | 
     my ($self, $selection_results, $each) = @_;  | 
| 
141
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
     my ($routes, $values) = @{$selection_results}{qw/routes values/};  | 
| 
 
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
    | 
| 
142
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
26
 | 
     my $errors = $self->{_errors};  | 
| 
143
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
     my $data = $self->{_data};  | 
| 
144
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
     for my $i (0 .. @$routes-1) {  | 
| 
145
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
70
 | 
         my $route = $routes->[$i];  | 
| 
146
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25
 | 
         push @{ $self->{_bases} }, $route;  | 
| 
 
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
48
 | 
    | 
| 
147
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
38
 | 
         my $value = $values->[$i];  | 
| 
148
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
81
 | 
         my $label_for = { map { $_ => 1 } ($route->labels) };  | 
| 
 
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
179
 | 
    | 
| 
149
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # prepare context  | 
| 
150
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
152
 | 
         my $pad = peek_sub($each);  | 
| 
151
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
102
 | 
         while (my ($var, $ref) = each %$pad) {  | 
| 
152
 | 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
202
 | 
             my $var_name = substr($var, 1); # chomp sigil  | 
| 
153
 | 
38
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
135
 | 
             next unless exists $label_for->{$var_name};  | 
| 
154
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
104
 | 
             my $label_obj = Label->new($var_name, $route, $data);  | 
| 
155
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
86
 | 
             lexalias($each, $var, \$label_obj);  | 
| 
156
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
157
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # call  | 
| 
158
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
114
 | 
         $self->{_current_path} = $route;  | 
| 
159
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
104
 | 
         $each->($self, local $_ = Label->new('_', $route, $data));  | 
| 
160
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1004
 | 
         pop @{ $self->{_bases} };  | 
| 
 
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
63
 | 
    | 
| 
161
 | 
23
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
144
 | 
         last if(@$errors);  | 
| 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
163
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
164
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
165
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Takes path-like expandable expression and returns hashref of path with corresponding  | 
| 
167
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # values from data, e.g.  | 
| 
168
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  validator({ a => [5,'z']})->_select('/a/*');  | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  # will return  | 
| 
171
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  # {  | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  #   routes => ['/a/0', '/a/1'],  | 
| 
173
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  #   values => [5, z],  | 
| 
174
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  # }  | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Actualy routes are presented by Path objects.  | 
| 
177
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
178
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _select {  | 
| 
179
 | 
58
 | 
 
 | 
 
 | 
  
58
  
 | 
 
 | 
82
 | 
     my ($self, $expession) = @_;  | 
| 
180
 | 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
230
 | 
     my $data = $self->{_data};  | 
| 
181
 | 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
130
 | 
     my $routes = $self->_expand_routes($expession);  | 
| 
182
 | 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
138
 | 
     my $values = [ map { $_->value($data) } @$routes ];  | 
| 
 
 | 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
249
 | 
    | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return {  | 
| 
184
 | 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
245
 | 
         routes => $routes,  | 
| 
185
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         values => $values,  | 
| 
186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     };  | 
| 
187
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
189
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
191
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Takes xpath-like expandable expression and sorted array of exapnded path e.g.  | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  validator({ a => [5,'z']})->_expand_routes('/a/*');  | 
| 
193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  # will return [ '/a/0', '/a/1' ]  | 
| 
194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  validator({ a => [5,'z']})->_expand_routes('/a');  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  # will return [ '/a' ]  | 
| 
196
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  validator({ a => { b => [5,'z'], c => ['y']} })->_expand_routes('/a/*/*');  | 
| 
197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  # will return [ '/a/b/0', '/a/b/1', '/a/c/0' ]  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
199
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _expand_routes {  | 
| 
200
 | 
78
 | 
 
 | 
 
 | 
  
78
  
 | 
 
 | 
128
 | 
     my ($self, $expression) = @_;  | 
| 
201
 | 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
86
 | 
     warn "-- Expanding routes for $expression\n" if DEBUG;  | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # striping leading slashes  | 
| 
203
 | 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
195
 | 
     $expression =~ s/\/{2,}/\//;  | 
| 
204
 | 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
346
 | 
     my @routes = ( Path->new($expression) );  | 
| 
205
 | 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
150
 | 
     my $result = [];  | 
| 
206
 | 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
195
 | 
     while (@routes) {  | 
| 
207
 | 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
276
 | 
         my $route = shift(@routes);  | 
| 
208
 | 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
490
 | 
         my $current = $self->{_data};  | 
| 
209
 | 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
539
 | 
         my $elements = $route->components;  | 
| 
210
 | 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
217
 | 
         my $i;  | 
| 
211
 | 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
229
 | 
         my $can_be_accessed = 0;  | 
| 
212
 | 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
481
 | 
         for ($i = 0; $i < @$elements; $i++) {  | 
| 
213
 | 
587
 | 
 
 | 
 
 | 
 
 | 
 
 | 
602
 | 
             $can_be_accessed = 0;  | 
| 
214
 | 
587
 | 
 
 | 
 
 | 
 
 | 
 
 | 
840
 | 
             my $element = $elements->[$i];  | 
| 
215
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # no futher examination if current value is undefined  | 
| 
216
 | 
587
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1680
 | 
             last unless defined($current);  | 
| 
217
 | 
581
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1340
 | 
             next if($element eq '');  | 
| 
218
 | 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
387
 | 
             my $filter;  | 
| 
219
 | 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
683
 | 
             ($element, $filter) = _filter($element);  | 
| 
220
 | 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
752
 | 
             my $type = ref($current);  | 
| 
221
 | 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
412
 | 
             my $generator;  | 
| 
222
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $advancer;  | 
| 
223
 | 
399
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
2649
 | 
             if ($element eq '*') {  | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
224
 | 
73
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
231
 | 
                 if ($type eq 'HASH') {  | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
225
 | 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
155
 | 
                     my @keys = keys %$current;  | 
| 
226
 | 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
70
 | 
                     my $idx = 0;  | 
| 
227
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                     $generator = sub {  | 
| 
228
 | 
93
 | 
 
 | 
 
 | 
  
93
  
 | 
 
 | 
226
 | 
                         while($idx < @keys) {  | 
| 
229
 | 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
146
 | 
                             my $key = $keys[$idx++];  | 
| 
230
 | 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
427
 | 
                             my $match = $filter->($current->{$key}, {key => $key});  | 
| 
231
 | 
80
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
489
 | 
                             return $key if($match);  | 
| 
232
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                         }  | 
| 
233
 | 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
122
 | 
                         return undef;  | 
| 
234
 | 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
189
 | 
                     };  | 
| 
235
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 } elsif ($type eq 'ARRAY') {  | 
| 
236
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
                     my $idx = 0;  | 
| 
237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                     $generator = sub {  | 
| 
238
 | 
84
 | 
 
 | 
 
 | 
  
84
  
 | 
 
 | 
227
 | 
                         while($idx < @$current) {  | 
| 
239
 | 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
95
 | 
                             my $index = $idx++;  | 
| 
240
 | 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
371
 | 
                             my $match = $filter->($current->[$index], {index => $index});  | 
| 
241
 | 
65
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
346
 | 
                             return $index if($match);  | 
| 
242
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                         }  | 
| 
243
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
91
 | 
                         return undef;  | 
| 
244
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
145
 | 
                     };  | 
| 
245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 }  | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }elsif ($type eq 'HASH' && exists $current->{$element}) {  | 
| 
247
 | 
225
 | 
 
 | 
 
 | 
  
225
  
 | 
 
 | 
669
 | 
                 $advancer = sub { $current->{$element} };  | 
| 
 
 | 
225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
427
 | 
    | 
| 
248
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }elsif ($type eq 'ARRAY' && looks_like_number($element)  | 
| 
249
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 && (  | 
| 
250
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                     ($element >= 0 && $element < @$current)  | 
| 
251
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                     || ($element < 0 && abs($element) <= @$current)  | 
| 
252
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                    )  | 
| 
253
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 ){  | 
| 
254
 | 
92
 | 
 
 | 
 
 | 
  
92
  
 | 
 
 | 
271
 | 
                 $advancer = sub { $current->[$element] };  | 
| 
 
 | 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
181
 | 
    | 
| 
255
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
256
 | 
399
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1001
 | 
             if ($generator) {  | 
| 
257
 | 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
176
 | 
                 while ( defined( my $new_element = $generator->()) ) {  | 
| 
258
 | 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6680
 | 
                     my $new_path = dclone($route);  | 
| 
259
 | 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
596
 | 
                     $new_path->components->[$i] = $new_element;  | 
| 
260
 | 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
432
 | 
                     push @routes, $new_path;  | 
| 
261
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 }  | 
| 
262
 | 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
178
 | 
                 $current = undef;  | 
| 
263
 | 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
412
 | 
                 last;  | 
| 
264
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
265
 | 
326
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
595
 | 
             if ($advancer) {  | 
| 
266
 | 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
660
 | 
                 $current = $advancer->();  | 
| 
267
 | 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
397
 | 
                 $can_be_accessed = 1;  | 
| 
268
 | 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2086
 | 
                 next;  | 
| 
269
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
270
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # the current element isn't hash nor array  | 
| 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # we can't traverse further, because there is more  | 
| 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # else current path  | 
| 
273
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
             $current = undef;  | 
| 
274
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
33
 | 
             $can_be_accessed = 0;  | 
| 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
276
 | 
182
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
586
 | 
         my $do_expansion = defined $current  | 
| 
277
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             || ($can_be_accessed && $i == @$elements);  | 
| 
278
 | 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
156
 | 
         warn "-- Expanded route : $route \n" if(DEBUG && $do_expansion);  | 
| 
279
 | 
182
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
858
 | 
         push @$result, $route if($do_expansion);  | 
| 
280
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
281
 | 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
368
 | 
     return [ sort @$result ];  | 
| 
282
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _filter {  | 
| 
285
 | 
399
 | 
 
 | 
 
 | 
  
399
  
 | 
 
 | 
495
 | 
     my $element = shift;  | 
| 
286
 | 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
375
 | 
     my $filter;  | 
| 
287
 | 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2015
 | 
     my $condition_re = qr/(.+?)(\[(.+)\])/;  | 
| 
288
 | 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1601
 | 
     my @parts = $element =~ /$condition_re/;  | 
| 
289
 | 
399
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
1349
 | 
     if (@parts == 3 && defined($parts[2])) {  | 
| 
290
 | 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
31
 | 
         $element = $parts[0];  | 
| 
291
 | 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
         my $condition = $parts[2];  | 
| 
292
 | 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
120
 | 
         $filter = Filter->new($condition);  | 
| 
293
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
294
 | 
381
 | 
 
 | 
 
 | 
  
83
  
 | 
 
 | 
1181
 | 
         $filter = sub { 1 }; # always true  | 
| 
 
 | 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
161
 | 
    | 
| 
295
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
296
 | 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1495
 | 
     return ($element, $filter);  | 
| 
297
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
298
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
299
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
300
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Takes the expandable expression and validation closure, then  | 
| 
301
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # expands it, and applies the closure for every data piese,  | 
| 
302
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # obtainted from expansion.  | 
| 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
304
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Returns the list of success validation mark and the hash  | 
| 
305
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # of details (obtained via _select).  | 
| 
306
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
307
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _apply {  | 
| 
308
 | 
52
 | 
 
 | 
 
 | 
  
52
  
 | 
 
 | 
83
 | 
     my ($self, $on, $should) = @_;  | 
| 
309
 | 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
121
 | 
     my $selection_results = $self->_select($on);  | 
| 
310
 | 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
97
 | 
     my $values = $selection_results->{values};  | 
| 
311
 | 
52
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
346
 | 
     my $result = $values && @$values && $should->( @$values );  | 
| 
312
 | 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1346
 | 
     return ($result, $selection_results);  | 
| 
313
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 };  | 
| 
314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
315
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 __END__  | 
| 
318
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
319
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =pod  | 
| 
320
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =encoding UTF-8  | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 NAME  | 
| 
324
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
325
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Data::DynamicValidator - JPointer-like and Perl union for flexible perl data structures validation  | 
| 
326
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
327
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 VERSION  | 
| 
328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
329
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 version 0.03  | 
| 
330
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
331
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 SYNOPSIS  | 
| 
332
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
333
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  my $my_complex_config = {  | 
| 
334
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     features => [  | 
| 
335
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         "a/f",  | 
| 
336
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         "application/feature1",  | 
| 
337
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         "application/feature2",  | 
| 
338
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ],  | 
| 
339
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     service_points => {  | 
| 
340
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         localhost => {  | 
| 
341
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             "a/f" => { job_slots => 3, },  | 
| 
342
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             "application/feature1" => { job_slots => 5 },  | 
| 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             "application/feature2" => { job_slots => 5 },  | 
| 
344
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
345
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         "127.0.0.1" => {  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             "application/feature2" => { job_slots => 5 },  | 
| 
347
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
348
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     },  | 
| 
349
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     mojolicious => {  | 
| 
350
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     	hypnotoad => {  | 
| 
351
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             pid_file => '/tmp/hypnotoad-ng.pid',  | 
| 
352
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             listen  => [ 'http://localhost:3000' ],  | 
| 
353
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
354
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     },  | 
| 
355
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  };  | 
| 
356
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
357
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  use Data::DynamicValidator qw/validator/;  | 
| 
358
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  use Net::hostent;  | 
| 
359
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
360
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  my $errors = validator($cfg)->(  | 
| 
361
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    on      => '/features/*',  | 
| 
362
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    should  => sub { @_ > 0 },  | 
| 
363
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    because => "at least one feature should be defined",  | 
| 
364
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    each    => sub {  | 
| 
365
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      my $f = $_->();  | 
| 
366
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      shift->(  | 
| 
367
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        on      => "//service_points/*/`$f`/job_slots",  | 
| 
368
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        should  => sub { defined($_[0]) && $_[0] > 0 },  | 
| 
369
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        because => "at least 1 service point should be defined for feature '$f'",  | 
| 
370
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      )  | 
| 
371
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
372
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  )->(  | 
| 
373
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    on      => '/service_points/sp:*',  | 
| 
374
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    should  => sub { @_ > 0 },  | 
| 
375
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    because => "at least one service point should be defined",  | 
| 
376
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    each    => sub {  | 
| 
377
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      my $sp;  | 
| 
378
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      shift->report_error("SP '$sp' isn't resolvable")  | 
| 
379
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        unless gethost($sp);  | 
| 
380
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
381
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  )->(  | 
| 
382
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   on      => '/service_points/sp:*/f:*',  | 
| 
383
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   should  => sub { @_ > 0 },  | 
| 
384
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   because => "at least one feature under service point should be defined",  | 
| 
385
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   each    => sub {  | 
| 
386
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($sp, $f);  | 
| 
387
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     shift->(  | 
| 
388
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       on      => "//features/`*[value eq '$f']`",  | 
| 
389
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       should  => sub { 1 },  | 
| 
390
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       because => "Feature '$f' of service point '$sp' should be decrlared in top-level features list",  | 
| 
391
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     )  | 
| 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   },  | 
| 
393
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   })->rebase('/mojolicious/hypnotoad' => sub {  | 
| 
394
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     shift->(  | 
| 
395
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       on      => '/pid_file',  | 
| 
396
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       should  => sub { @_ == 1 },  | 
| 
397
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       because => "hypnotoad pid_file should be defined",  | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     )->(  | 
| 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       on      => '/listen/*',  | 
| 
400
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       should  => sub { @_ > 0 },  | 
| 
401
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       because => "hypnotoad listening interfaces defined",  | 
| 
402
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
403
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   })->errors;  | 
| 
404
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
405
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  print "all OK\n"  | 
| 
406
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   unless(@$errors);  | 
| 
407
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
408
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 RATIONALE  | 
| 
409
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
410
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 There are complex data configurations, e.g. application configs. Not to  | 
| 
411
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 check them on applicaiton startup is B<wrong>, because of sudden  | 
| 
412
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 unexpected runtime errors can occur, which are not-so-pleasent to detect.  | 
| 
413
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Write the code, that does full exhaustive checks, is B<boring>.  | 
| 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
415
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This module tries to offer to use DLS, that makes data validation fun  | 
| 
416
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 for developer yet understandable for the person, which provides the data.  | 
| 
417
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
418
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 DESCRIPTION  | 
| 
419
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
420
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 First of all, you should create Validator instance:  | 
| 
421
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
422
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  use Data::DynamicValidator qw/validator/;  | 
| 
423
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
424
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  my $data = { ports => [2222] };  | 
| 
425
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  my $v = validator($data);  | 
| 
426
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
427
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Then, actually do validation:  | 
| 
428
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
429
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $v->(  | 
| 
430
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    on      => '/ports/*',  | 
| 
431
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    should  => sub { @_ > 0 },  | 
| 
432
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    because => 'At least one port should be defined at "ports" section',  | 
| 
433
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  );  | 
| 
434
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
435
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The C<on> parameter defines the data path, via JSON-pointer like expression;  | 
| 
436
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 the C<should> parameter provides the closure, which will check the values  | 
| 
437
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 gathered on via pointer. If the closure returns false, then the error will  | 
| 
438
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 be recorded, with description, provided by C<because> parameter.  | 
| 
439
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
440
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 To get the results of validation, you can call:  | 
| 
441
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
442
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $v->is_valid; # returns true, if there is no validation errors  | 
| 
443
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $v->errors;   # returns array reference, consisting of the met Errors  | 
| 
444
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
445
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 C<on>/C<should> parameters are convenient for validation of presense of  | 
| 
446
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 something, but they aren't so handy in checking of B<individual> values.  | 
| 
447
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 It should be mentioned, that C<should> closure, always takes an array of  | 
| 
448
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 the selected by C<on>, even if only one element has been selected.  | 
| 
449
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
450
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 To handle B<individual> values in more convenient way  the optional  | 
| 
451
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 C<each> parameter has been introduced.  | 
| 
452
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
453
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  my $data = { ports => [2222, 3333] };  | 
| 
454
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $v->(  | 
| 
455
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    on      => '/ports/*',  | 
| 
456
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    should  => sub { @_ > 0 },  | 
| 
457
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    because => 'At least one port should be defined at "ports" section',  | 
| 
458
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    each    => sub {  | 
| 
459
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      my $port = $_->();  | 
| 
460
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      $v->report_error("All ports should be greater than 1000")  | 
| 
461
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       unless $port > 1000;  | 
| 
462
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    },  | 
| 
463
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  );  | 
| 
464
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
465
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 So, C<report_error> could be used for custom errors reporting on current  | 
| 
466
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 path or current data value. The C<$_> is the an implicit alias or B<label>  | 
| 
467
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 to the last componenet of the current path, i.e. on our case the current  | 
| 
468
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 path in C<each> closure will be C</ports/0> and C</ports/1>, so the C<$_>  | 
| 
469
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 will be 0 and 1 respectively. To get the I<value> of the label, you should  | 
| 
470
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 "invoke" it, as showed previously. A label stringizes to the last data  | 
| 
471
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 path component, e.g. to "0" and "1" respectively.  | 
| 
472
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
473
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The C<each> closure single argrument is the validator instance itself. The  | 
| 
474
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 previous example could be rewriten with explicit label like:  | 
| 
475
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
476
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $v->(  | 
| 
477
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    on      => '/ports/port:*',  | 
| 
478
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    should  => sub { @_ > 0 },  | 
| 
479
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    because => 'At least one port should be defined at "ports" section',  | 
| 
480
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    each    => sub {  | 
| 
481
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      my $port;  | 
| 
482
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      my $port_value = $port->();  | 
| 
483
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      shift->report_error("All ports should be greater than 1000")  | 
| 
484
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       unless $port_value > 1000;  | 
| 
485
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    },  | 
| 
486
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  );  | 
| 
487
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
488
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Providing aliases for array indices may be not so handy as for keys  | 
| 
489
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 of hashes. Please note, that the label C<port> was previously "declated"  | 
| 
490
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 in C<on> rule, and only then "injected" into C<$port> variable in  | 
| 
491
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 C<each> closure.  | 
| 
492
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
493
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Consider the following example:  | 
| 
494
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
495
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  my $data = {  | 
| 
496
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   ports => [2000, 3000],  | 
| 
497
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   2000  => 'tcp',  | 
| 
498
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   3000  => 'udp',  | 
| 
499
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  };  | 
| 
500
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
501
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Let's validate it. The validation rule sounds as: there is 'ports' section,  | 
| 
502
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 where at least one port > 1000 should be declated, and then the same port  | 
| 
503
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 should appear at top-level, and it should be either 'tcp' or 'upd' type.  | 
| 
504
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  use List::MoreUtils qw/any/;  | 
| 
506
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
507
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  my $errors = validator($data)->(  | 
| 
508
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    on      => '/ports/*[value > 1000 ]',  | 
| 
509
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    should  => sub { @_ > 0 },  | 
| 
510
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    because => 'At least one port > 1000 should be defined in "ports" section',  | 
| 
511
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    each    => sub {  | 
| 
512
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      my $port = $_->();  | 
| 
513
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      shift->(  | 
| 
514
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        on      => "//*[key eq $port]",  | 
| 
515
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        should  => sub { @_ == 1 && any { $_[0] eq $_ } (qw/tcp udp/)  },  | 
| 
516
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        because => "The port $port should be declated at top-level as tcp or udp",  | 
| 
517
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       )  | 
| 
518
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
519
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   )->errors;  | 
| 
520
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
521
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 As you probably noted, the the path expression contains two slashes at C<on> rule  | 
| 
522
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 inside C<each> rule. This is required to search data from the root, because  | 
| 
523
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 the current element is been set as B<base> before calling C<each>, so all expressions  | 
| 
524
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 inside C<each> are relative to the current element (aka base).  | 
| 
525
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
526
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 You can change the base explicit way via C<rebase> method:  | 
| 
527
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
528
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  my $data = {  | 
| 
529
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     mojolicious => {  | 
| 
530
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     	hypnotoad => {  | 
| 
531
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             pid_file => '/tmp/hypnotoad-ng.pid',  | 
| 
532
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             listen  => [ 'http://localhost:3000' ],  | 
| 
533
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
534
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     },  | 
| 
535
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  };  | 
| 
536
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
537
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $v->rebase('/mojolicious/hypnotoad' => sub {  | 
| 
538
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     shift->(  | 
| 
539
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       on      => '/pid_file',  | 
| 
540
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       should  => sub { @_ == 1 },  | 
| 
541
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       because => "hypnotoad pid_file should be defined",  | 
| 
542
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     )->(  | 
| 
543
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       on      => '/listen/*',  | 
| 
544
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       should  => sub { @_ > 0 },  | 
| 
545
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       because => "hypnotoad listening interfaces defined",  | 
| 
546
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
547
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  })->errors;  | 
| 
548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
549
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 DATA PATH EXPRESSIONS  | 
| 
550
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  my $data = [qw/a b c d e/];  | 
| 
552
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  '/2'   # selects the 'c' value in $data array  | 
| 
553
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  '/-1'  # selects the 'e' value in $data array  | 
| 
554
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
555
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $data = { abc => 123 };  | 
| 
556
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  '/abc' # selects the '123' value in hashref under key 'abc'  | 
| 
557
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
558
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $data = {  | 
| 
559
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    mojolicious => {  | 
| 
560
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      hypnotoad => {  | 
| 
561
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        pid_file => '/tmp/hypnotoad-ng.pid',  | 
| 
562
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      }  | 
| 
563
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
564
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  };  | 
| 
565
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  '/mojolicious/hypnotoad/pid_file'  # point to pid_file  | 
| 
566
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  '//mojolicious/hypnotoad/pid_file' # point to pid_file (independently of current base)  | 
| 
567
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
568
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  # Escaping by back-quotes sample  | 
| 
569
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $data => { "a/b" => { c => 5 } }  | 
| 
570
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  '/`a/b`/c' # selects 5  | 
| 
571
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
572
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $data = {abc => [qw/a b/]};   # 1  | 
| 
573
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $data = {abc => { c => 'd'}}; # 2  | 
| 
574
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $data = {abc => 7};           # 3  | 
| 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  '/abc/*' # selects 'a' and 'b' in 1st case  | 
| 
576
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # the 'd' in 2nd case  | 
| 
577
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # the number 7 in 3rd case  | 
| 
578
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
579
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  # Filtering capabilities samples:  | 
| 
580
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
581
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  '/abc/*[size == 5]'    # filter array/hash by size  | 
| 
582
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  '/abc/*[value eq "z"]' # filter array/hash by value equality  | 
| 
583
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  '/abc/*[index > 5]'    # finter array by index  | 
| 
584
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  '/abc/*[key =~ /def/]' # finter hash by key  | 
| 
585
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
586
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 DEBUGGING  | 
| 
587
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
588
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 You can set the DATA_DYNAMICVALIDATOR_DEBUG environment variable  | 
| 
589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 to get some advanced diagnostics information printed to "STDERR".  | 
| 
590
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
591
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  DATA_DYNAMICVALIDATOR_DEBUG=1  | 
| 
592
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
593
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 METHODS  | 
| 
594
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
595
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 validate  | 
| 
596
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
597
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Performs validation based on C<on>, C<should>, C<because> and optional C<each>  | 
| 
598
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 parameters. Returns the validator itself (C<$self>), to allow further C<chain>  | 
| 
599
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 invocations. The validation will not be performed, if some errors already  | 
| 
600
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 have been detected.  | 
| 
601
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
602
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 It is recommended to use overloaded function call, instead of this method  | 
| 
603
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 call. (e.g. C<$validator->(...)> instead of C<$validato->validate(...)> )  | 
| 
604
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
605
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 report_error  | 
| 
606
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
607
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The method is used for custom errors reporing. It is mainly usable in C<each>  | 
| 
608
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 closure.  | 
| 
609
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
610
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  validator({ ports => [1000, 2000, 3000] })->(  | 
| 
611
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    on      => '/ports/port:*',  | 
| 
612
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    should  => sub { @_ > 0 },  | 
| 
613
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    because => "At least one listening port should be defined",  | 
| 
614
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    each    => sub {  | 
| 
615
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      my $port;  | 
| 
616
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      my $port_value = $port->();  | 
| 
617
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      shift->report_error("Port value $port_value isn't acceptable, because < 1000")  | 
| 
618
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        if($port_value < 1000);  | 
| 
619
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
620
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  );  | 
| 
621
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
622
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 is_valid  | 
| 
623
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
624
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Checks, whether validator already has errors  | 
| 
625
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
626
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 errors  | 
| 
627
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
628
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns internal array of errors  | 
| 
629
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
630
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 rebase  | 
| 
631
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
632
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Temporaly sets the new base to the specified route, and invokes the closure  | 
| 
633
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 with the validator instance, i.e.  | 
| 
634
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
635
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  $v->('/a' => $closure->($v))  | 
| 
636
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
637
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 If the data can't be found at the specified route, the C<closure> is not  | 
| 
638
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 invoked.  | 
| 
639
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
640
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 current_base  | 
| 
641
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
642
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns the current base, which is set only inside C<rebase> call or C<each> closure.  | 
| 
643
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns undef is there is no current base.  | 
| 
644
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
645
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 FUNCTIONS  | 
| 
646
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
647
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 validator  | 
| 
648
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
649
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The enter point for DynamicValidator.  | 
| 
650
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
651
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  my $errors = validator(...)->(  | 
| 
652
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    on => "...",  | 
| 
653
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    should => sub { ... },  | 
| 
654
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    because => "...",  | 
| 
655
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  )->errors;  | 
| 
656
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
657
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 RESOURCES  | 
| 
658
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
659
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
660
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
661
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * Data::DPath  | 
| 
662
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
663
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 L<https://metacpan.org/pod/Data::DPath>  | 
| 
664
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
665
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
666
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
667
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 AUTHOR  | 
| 
668
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
669
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Ivan Baidakou <dmol@gmx.com>  | 
| 
670
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
671
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 COPYRIGHT AND LICENSE  | 
| 
672
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
673
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This software is copyright (c) 2014 by Ivan Baidakou.  | 
| 
674
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
675
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This is free software; you can redistribute it and/or modify it under  | 
| 
676
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 the same terms as the Perl 5 programming language system itself.  | 
| 
677
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
678
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  |