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   933400 use 5.006;
  8         32  
4 8     8   46 use strict;
  8         21  
  8         298  
5 8     8   45 use warnings;
  8         33  
  8         498  
6 8     8   57 use Scalar::Util qw/blessed/;
  8         67  
  8         11885  
7              
8             our $VERSION = '1.03';
9              
10             our (%ERROR, %METH, $caller);
11              
12             BEGIN {
13 8     8   70 %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         300 my ($follow, $end, $data, @path) = @_;
22              
23 120 100 100     397 if (scalar @path && !$end) {
24 85         205 my ($key, $ref) = (shift @path, ref $data);
25 85         188 $follow = sprintf "%s/%s", $follow, $key;
26 85 100 100     277 if ($ref eq 'HASH') {
    100          
    100          
27 54         129 $data = $data->{$key};
28 54 100       122 $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       19 $METH{error}->('invalid_index', $key, $follow) if ! defined $data;
33             }
34             elsif ( $ref && blessed $data ) {
35 22         63 my ($meth, $params) = $METH{meth_params}->($key, $data);
36 21 100       50 $data = scalar @{ $params || [] } ? $data->$meth(@{ $params }) : $data->$meth;
  21 100       79  
  20         76  
37 21 100       149 $METH{error}->('invalid_method', $key, $follow) if ! defined $data;
38             }
39             else {
40 3 100       15 $METH{error}->('invalid_path', $key, $follow) if (exists $ERROR{invalid_path});
41 2         6 $end = 1;
42             }
43 79         272 return $METH{extract_path}->($follow, $end, $data, @path);
44             }
45              
46 35         149 return $data;
47             },
48             unescape => sub {
49 46 100       175707 return '' unless defined $_[0];
50 45         126 $_[0] =~ s/^\///g;
51 45         104 $_[0] =~ s/\+/ /g;
52 45         118 $_[0] =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
  2         13  
53 45         212 return $_[0];
54             },
55             meth_params => sub {
56 22         47 my ($key, $obj) = @_;
57 22         147 my ($method, $args) = $key =~ /^(.*?)\((.*)\)$/;
58 22 100       69 $args = $METH{generate_params}->($args, $obj) if $args;
59 21   33     77 return ($method || $key, $args);
60             },
61             generate_params => sub {
62 41         89 my ($string, $obj, @world, $current) = @_;
63 41         168 foreach ( split /(?![^\(\[\{]+[\]\}\)]),/, $string ) {
64 57 100 100     372 if ( $_ =~ m/^\s*\[\s*(.*)\]/ ) {
    100          
    100          
65 6         43 $current = $METH{generate_params}->($1, $obj);
66 6         15 push @world, $current;
67             }
68             elsif ( $_ =~ m/^\s*\{\s*(.*)\s*\}/ ) {
69 5         9 $current = {};
70 5         26 my %temp = split '=>', $1;
71 5 100       14 do { $current->{$METH{generate_params}->(defined $ERROR{allow_meth_keys} ? sprintf("'%s'", $_) : ($_, $obj))->[0] } = $METH{generate_params}->($temp{$_}, $obj)->[0] } for keys %temp;
  5         17  
72 4         13 push @world, $current;
73             }
74             elsif ( ($_ =~ m/^\s*(\d+)\s*$/) || ($_ =~ m/^\s*[\'\"]+\s*(.*?)\s*[\'\"]+\s*$/) ) {
75 33         124 push @world, $1;
76             }
77             else {
78 13         38 my $ex = $_ =~ s/^\s*\&//;
79 13         40 my ($method, $args) = $_ =~ /^\s*(.*?)\((.*)\)$/;
80 13 100       43 ($method) = $_ =~ m/\s*(.*)\s*/ unless $method;
81 13 100       45 $args = $args ? $METH{generate_params}->($args, $obj) : [];
82 8 100   8   75 push @world, $ex ? do { no strict 'refs'; *{"${caller}::${method}"}->(@{ $args }); } : $obj->$method(@{ $args });
  8         14  
  8         2815  
  13         31  
  6         25  
  6         36  
  6         14  
  7         58  
83             }
84             }
85 39         180 return \@world;
86             },
87             error => sub {
88 5         15 my ($error) = @_;
89 5         27 my $find = $ERROR{$error};
90 5 100       100 return ref $find eq 'CODE'
91             ? $find->(@_)
92             : die sprintf $find, @_;
93             }
94 8         1430 );
95             }
96              
97             sub import {
98 10     10   108 my ($pkg, $sub) = shift;
99 10 100       2140 return unless my @export = @_;
100 8 100       28 my $opts = ref $export[scalar @export - 1] ? pop @export : {};
101 8 100       26 $ERROR{no_error} = 1 if $opts->{return_undef};
102 8 100       27 %ERROR = (%ERROR, %{ $opts->{errors} }) if $opts->{errors};
  3         12  
103 8 100 100     61 @export = qw/lnpath/ if scalar @export == 1 && $export[0] eq 'all';
104 8         18 $caller = scalar caller();
105             {
106 8     8   50 no strict 'refs';
  8         17  
  8         2768  
  8         10  
107 8         16 for ( @export ) {
108 9 100 100     1279 if ( $sub = $pkg->can($_) ? $_ : $opts->{as} && $opts->{as}->{$_} ) {
    100          
109 6         10 *{"${caller}::${_}"} = \&{"${pkg}::${sub}"};
  6         7429  
  6         20  
110             }
111             }
112             }
113             }
114              
115             sub lnpath {
116 41     41 1 1163760 my ($data, $key) = @_;
117 41         68 my $val = eval {
118 41         133 $METH{extract_path}->('', 0, $data, split('/', $METH{unescape}->($key)))
119             };
120 41 100 100     149 if ($@ && !$ERROR{no_error}) {
121 4         25 die $@;
122             }
123 37         204 return $val;
124             }
125              
126             1;
127              
128             __END__