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.05';
4 9     9   2934 use strict;
  9         16  
  9         221  
5 9     9   40 use warnings;
  9         12  
  9         173  
6              
7 9     9   324 use Carp;
  9         17  
  9         413  
8 9     9   51 use Scalar::Util qw/looks_like_number/;
  9         24  
  9         512  
9              
10 9     9   45 use overload fallback => 1, q/""/ => sub { $_[0]->to_string };
  9     174   78  
  9         57  
  174         3486  
11              
12 9   50 9   578 use constant DEBUG => $ENV{DATA_DYNAMICVALIDATOR_DEBUG} || 0;
  9         69  
  9         6638  
13              
14             sub new {
15 89     89 0 3929 my ($class, $path) = @_;
16 89         125 my $self = { };
17 89         132 bless $self => $class;
18 89         192 $self->_build_components($path);
19 89         212 return $self;
20             }
21              
22             sub _build_components {
23 89     89   132 my ($self, $path) = @_;
24              
25             # handle escaped path components
26 89         131 $_ = $path;
27             # substitute all '/'-symbols to "`" and strip
28             # surrounding(wraping) `
29 89         220 s[`(.+?)`][my $x=$1;$x=~ tr{/}{`};$x]ge;
  23         56  
  23         41  
  23         71  
30 89         266 my @elements = split '/';
31 89         176 for(@elements) {
32 293         435 tr {`}{/}; # roll back slashes again
33             }
34 89         201 for my $i (0..@elements-1) {
35 293         483 my @parts = split(':', $elements[$i]);
36 293 100       600 if (@parts > 1) {
37 40         50 $elements[$i] = $parts[1];
38 40         120 $self->{_labels}->{ $parts[0] } = $i;
39             }
40             }
41             # special name _ for the last route component
42 89         360 $self->{_labels}->{'_'} = @elements-1;
43 89         196 $self->{_components} = \@elements;
44             }
45              
46 296     296 0 605 sub components { shift->{_components} }
47              
48             sub to_string {
49             join('/',
50 603 100       1787 map { /\// ? "`$_`" : $_ }
51 184     184 0 2765 @{ shift->{_components} })
  184         350  
52             }
53              
54             sub labels {
55 24     24 0 32 my $self = shift;
56 24         39 my $labels = $self->{_labels};
57 24         61 sort { $labels->{$a} <=> $labels->{$b} } grep { $_ ne '_' } keys %$labels;
  17         46  
  50         128  
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       16 unless exists $self->{_labels}->{$label};
64 6         14 return $self->_clone_to($self->{_labels}->{$label});
65             }
66              
67             sub named_component {
68 38     38 0 69 my ($self, $label) = @_;
69             croak("No label '$label' in path '$self'")
70 38 50       69 unless exists $self->{_labels}->{$label};
71 38         53 my $idx = $self->{_labels}->{$label};
72 38         198 return $self->{_components}->[$idx];
73             }
74              
75             sub _clone_to {
76 6     6   10 my ($self, $index) = @_;
77 6         6 my @components;
78 6         15 for my $i (0 .. $index) {
79 19         68 push @components, $self->{_components}->[$i]
80             }
81 6         9 while ( my ($name, $idx) = each(%{ $self->{_labels} })) {
  26         68  
82 20 100 100     70 $components[$idx] = join(':', $name, $components[$idx])
83             if( $idx <= $index && $name ne '_');
84             }
85 6         13 my $path = join('/', @components);
86 6         17 return Data::DynamicValidator::Path->new($path);
87             }
88              
89              
90             sub value {
91 98     98 1 188 my ($self, $data, $label) = @_;
92 98   100     305 $label //= '_';
93             croak("No label '$label' in path '$self'")
94 98 50       194 if(!exists $self->{_labels}->{$label});
95 98         139 my $idx = $self->{_labels}->{$label};
96 98         120 my $value = $data;
97 98         186 for my $i (1 .. $idx) {
98 224         333 my $element = $self->{_components}->[$i];
99 224 100 66     1067 if (ref($value) && ref($value) eq 'HASH' && exists $value->{$element}) {
    50 66        
      33        
      33        
      33        
100 150         208 $value = $value->{$element};
101 150         246 next;
102             }
103             elsif (ref($value) && ref($value) eq 'ARRAY'
104             && looks_like_number($element) && $element < @$value) {
105 74         139 $value = $value->[$element];
106 74         116 next;
107             }
108 0         0 croak "I don't know how to get element#$i ($element) at $self";
109             }
110 98         116 if (DEBUG) {
111             warn "-- value for $self is "
112             . (defined($value)? $value : 'undefined') . "\n"
113             }
114 98         263 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.05
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