File Coverage

blib/lib/Data/LNPath.pm
Criterion Covered Total %
statement 91 91 100.0
branch 56 56 100.0
condition 19 21 90.4
subroutine 9 9 100.0
pod 1 1 100.0
total 176 178 98.8


line stmt bran cond sub pod time code
1             package Data::LNPath;
2              
3 8     8   525247 use 5.006;
  8         90  
4 8     8   45 use strict;
  8         16  
  8         162  
5 8     8   40 use warnings;
  8         14  
  8         238  
6 8     8   60 use Scalar::Util qw/blessed/;
  8         14  
  8         8148  
7              
8             our $VERSION = '1.02';
9              
10             our (%ERROR, %METH, $caller);
11              
12             BEGIN {
13 8     8   76 %ERROR = (
14             invalid_key => 'A miserable death %s - %s - %s',
15             invalid_index => 'A slightly more miserable death %s - %s - %s',
16             invalid_method => 'A horrible horrible miserable death %s - %s - %s',
17             allow_meth_keys => 'jump from a high building',
18             );
19             %METH = (
20             extract_path => sub {
21 120         273 my ($follow, $end, $data, @path) = @_;
22              
23 120 100 100     387 if (scalar @path && !$end) {
24 85         185 my ($key, $ref) = (shift @path, ref $data);
25 85         257 $follow = sprintf "%s/%s", $follow, $key;
26 85 100 100     294 if ($ref eq 'HASH') {
    100          
    100          
27 54         93 $data = $data->{$key};
28 54 100       130 $METH{error}->('invalid_key', $key, $follow) if ! defined $data;
29             }
30             elsif ( $ref eq 'ARRAY' ) {
31 6         19 $data = $data->[$key - 1];
32 6 100       18 $METH{error}->('invalid_index', $key, $follow) if ! defined $data;
33             }
34             elsif ( $ref && blessed $data ) {
35 22         53 my ($meth, $params) = $METH{meth_params}->($key, $data);
36 21 100       30 $data = scalar @{ $params || [] } ? $data->$meth(@{ $params }) : $data->$meth;
  21 100       77  
  20         66  
37 21 100       153 $METH{error}->('invalid_method', $key, $follow) if ! defined $data;
38             }
39             else {
40 3 100       20 $METH{error}->('invalid_path', $key, $follow) if (exists $ERROR{invalid_path});
41 2         4 $end = 1;
42             }
43 79         213 return $METH{extract_path}->($follow, $end, $data, @path);
44             }
45              
46 35         121 return $data;
47             },
48             unescape => sub {
49 46 100       218 return '' unless defined $_[0];
50 45         128 $_[0] =~ s/^\///g;
51 45         98 $_[0] =~ s/\+/ /g;
52 45         77 $_[0] =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
  2         15  
53 45         197 return $_[0];
54             },
55             meth_params => sub {
56 22         52 my ($key, $obj) = @_;
57 22         184 my ($method, $args) = $key =~ /^(.*?)\((.*)\)$/;
58 22 100       66 $args = $METH{generate_params}->($args, $obj) if $args;
59 21   33     70 return ($method || $key, $args);
60             },
61             generate_params => sub {
62 41         86 my ($string, $obj, @world, $current) = @_;
63 41         152 foreach ( split /(?![^\(\[\{]+[\]\}\)]),/, $string ) {
64 57 100 100     355 if ( $_ =~ m/^\s*\[\s*(.*)\]/ ) {
    100          
    100          
65 6         19 $current = $METH{generate_params}->($1, $obj);
66 6         14 push @world, $current;
67             }
68             elsif ( $_ =~ m/^\s*\{\s*(.*)\s*\}/ ) {
69 5         10 $current = {};
70 5         28 my %temp = split '=>', $1;
71 5 100       23 do { $current->{$METH{generate_params}->(defined $ERROR{allow_meth_keys} ? sprintf("'%s'", $_) : ($_, $obj))->[0] } = $METH{generate_params}->($temp{$_}, $obj)->[0] } for keys %temp;
  5         18  
72 4         12 push @world, $current;
73             }
74             elsif ( ($_ =~ m/^\s*(\d+)\s*$/) || ($_ =~ m/^\s*[\'\"]+\s*(.*?)\s*[\'\"]+\s*$/) ) {
75 33         103 push @world, $1;
76             }
77             else {
78 13         44 my $ex = $_ =~ s/^\s*\&//;
79 13         39 my ($method, $args) = $_ =~ /^\s*(.*?)\((.*)\)$/;
80 13 100       46 ($method) = $_ =~ m/\s*(.*)\s*/ unless $method;
81 13 100       47 $args = $args ? $METH{generate_params}->($args, $obj) : [];
82 8 100   8   67 push @world, $ex ? do { no strict 'refs'; *{"${caller}::${method}"}->(@{ $args }); } : $obj->$method(@{ $args });
  8         23  
  8         1719  
  13         33  
  6         11  
  6         25  
  6         14  
  7         55  
83             }
84             }
85 39         161 return \@world;
86             },
87             error => sub {
88 5         13 my ($error) = @_;
89 5         12 my $find = $ERROR{$error};
90 5 100       53 return ref $find eq 'CODE'
91             ? $find->(@_)
92             : die sprintf $find, @_;
93             }
94 8         1155 );
95             }
96              
97             sub import {
98 10     10   135 my ($pkg, $sub) = shift;
99 10 100       1440 return unless my @export = @_;
100 8 100       33 my $opts = ref $export[scalar @export - 1] ? pop @export : {};
101 8 100       40 $ERROR{no_error} = 1 if $opts->{return_undef};
102 8 100       40 %ERROR = (%ERROR, %{ $opts->{errors} }) if $opts->{errors};
  3         16  
103 8 100 100     67 @export = qw/lnpath/ if scalar @export == 1 && $export[0] eq 'all';
104 8         17 $caller = scalar caller();
105             {
106 8     8   58 no strict 'refs';
  8         23  
  8         2158  
  8         15  
107 8         16 for ( @export ) {
108 9 100 100     1350 if ( $sub = $pkg->can($_) ? $_ : $opts->{as} && $opts->{as}->{$_} ) {
    100          
109 6         9 *{"${caller}::${_}"} = \&{"${pkg}::${sub}"};
  6         5563  
  6         27  
110             }
111             }
112             }
113             }
114              
115             sub lnpath {
116 41     41 1 1308 my ($data, $key) = @_;
117 41         116 my $val = eval {
118 41         112 $METH{extract_path}->('', 0, $data, split('/', $METH{unescape}->($key)))
119             };
120 41 100 100     139 if ($@ && !$ERROR{no_error}) {
121 4         24 die $@;
122             }
123 37         159 return $val;
124             }
125              
126             1;
127              
128             __END__