File Coverage

blib/lib/Data/DynamicValidator/Path.pm
Criterion Covered Total %
statement 79 80 98.7
branch 12 16 75.0
condition 13 22 59.0
subroutine 16 16 100.0
pod 1 7 14.2
total 121 141 85.8


line stmt bran cond sub pod time code
1             package Data::DynamicValidator::Path;
2             # ABSTRACT: Class represents "splitted" to labelled componets path.
3             $Data::DynamicValidator::Path::VERSION = '0.04';
4 9     9   2734 use strict;
  9         19  
  9         266  
5 9     9   42 use warnings;
  9         16  
  9         191  
6              
7 9     9   44 use Carp;
  9         18  
  9         460  
8 9     9   56 use Scalar::Util qw/looks_like_number/;
  9         28  
  9         537  
9              
10 9     9   47 use overload fallback => 1, q/""/ => sub { $_[0]->to_string };
  9     174   79  
  9         62  
  174         3517  
11              
12 9   50 9   587 use constant DEBUG => $ENV{DATA_DYNAMICVALIDATOR_DEBUG} || 0;
  9         66  
  9         6323  
13              
14             sub new {
15 89     89 0 3500 my ($class, $path) = @_;
16 89         136 my $self = { };
17 89         156 bless $self => $class;
18 89         182 $self->_build_components($path);
19 89         214 return $self;
20             }
21              
22             sub _build_components {
23 89     89   145 my ($self, $path) = @_;
24              
25             # handle escaped path components
26 89         125 $_ = $path;
27             # substitute all '/'-symbols to "`" and strip
28             # surrounding(wraping) `
29 89         219 s[`(.+?)`][my $x=$1;$x=~ tr{/}{`};$x]ge;
  23         53  
  23         39  
  23         64  
30 89         284 my @elements = split '/';
31 89         170 for(@elements) {
32 293         436 tr {`}{/}; # roll back slashes again
33             }
34 89         199 for my $i (0..@elements-1) {
35 293         507 my @parts = split(':', $elements[$i]);
36 293 100       622 if (@parts > 1) {
37 40         63 $elements[$i] = $parts[1];
38 40         136 $self->{_labels}->{ $parts[0] } = $i;
39             }
40             }
41             # special name _ for the last route component
42 89         343 $self->{_labels}->{'_'} = @elements-1;
43 89         186 $self->{_components} = \@elements;
44             }
45              
46 296     296 0 652 sub components { shift->{_components} }
47              
48             sub to_string {
49             join('/',
50 603 100       2009 map { /\// ? "`$_`" : $_ }
51 184     184 0 2714 @{ shift->{_components} })
  184         359  
52             }
53              
54             sub labels {
55 24     24 0 36 my $self = shift;
56 24         38 my $labels = $self->{_labels};
57 24         72 sort { $labels->{$a} <=> $labels->{$b} } grep { $_ ne '_' } keys %$labels;
  17         60  
  50         153  
58             }
59              
60             sub named_route {
61 6     6 0 13 my ($self, $label) = @_;
62             croak("No label '$label' in path '$self'")
63 6 50       18 unless exists $self->{_labels}->{$label};
64 6         15 return $self->_clone_to($self->{_labels}->{$label});
65             }
66              
67             sub named_component {
68 38     38 0 78 my ($self, $label) = @_;
69             croak("No label '$label' in path '$self'")
70 38 50       88 unless exists $self->{_labels}->{$label};
71 38         67 my $idx = $self->{_labels}->{$label};
72 38         226 return $self->{_components}->[$idx];
73             }
74              
75             sub _clone_to {
76 6     6   10 my ($self, $index) = @_;
77 6         8 my @components;
78 6         11 for my $i (0 .. $index) {
79 19         33 push @components, $self->{_components}->[$i]
80             }
81 6         10 while ( my ($name, $idx) = each(%{ $self->{_labels} })) {
  26         65  
82 20 100 100     63 $components[$idx] = join(':', $name, $components[$idx])
83             if( $idx <= $index && $name ne '_');
84             }
85 6         11 my $path = join('/', @components);
86 6         17 return Data::DynamicValidator::Path->new($path);
87             }
88              
89              
90             sub value {
91 98     98 1 201 my ($self, $data, $label) = @_;
92 98   100     330 $label //= '_';
93             croak("No label '$label' in path '$self'")
94 98 50       211 if(!exists $self->{_labels}->{$label});
95 98         164 my $idx = $self->{_labels}->{$label};
96 98         129 my $value = $data;
97 98         198 for my $i (1 .. $idx) {
98 224         347 my $element = $self->{_components}->[$i];
99 224 100 66     1173 if (ref($value) && ref($value) eq 'HASH' && exists $value->{$element}) {
    50 66        
      33        
      33        
      33        
100 150         234 $value = $value->{$element};
101 150         253 next;
102             }
103             elsif (ref($value) && ref($value) eq 'ARRAY'
104             && looks_like_number($element) && $element < @$value) {
105 74         123 $value = $value->[$element];
106 74         130 next;
107             }
108 0         0 croak "I don't know how to get element#$i ($element) at $self";
109             }
110 98         122 if (DEBUG) {
111             warn "-- value for $self is "
112             . (defined($value)? $value : 'undefined') . "\n"
113             }
114 98         291 return $value;
115             }
116              
117             1;
118              
119             __END__
120              
121             =pod
122              
123             =encoding UTF-8
124              
125             =head1 NAME
126              
127             Data::DynamicValidator::Path - Class represents "splitted" to labelled componets path.
128              
129             =head1 VERSION
130              
131             version 0.04
132              
133             =head1 METHODS
134              
135             =head2 value
136              
137             Returns data value under named label or (if undefined)
138             the value under path itself
139              
140             =head1 AUTHOR
141              
142             Ivan Baidakou <dmol@gmx.com>
143              
144             =head1 COPYRIGHT AND LICENSE
145              
146             This software is copyright (c) 2017 by Ivan Baidakou.
147              
148             This is free software; you can redistribute it and/or modify it under
149             the same terms as the Perl 5 programming language system itself.
150              
151             =cut