File Coverage

blib/lib/Data/LNPath.pm
Criterion Covered Total %
statement 91 91 100.0
branch 52 52 100.0
condition 18 18 100.0
subroutine 9 9 100.0
pod 1 1 100.0
total 171 171 100.0


line stmt bran cond sub pod time code
1             package Data::LNPath;
2              
3 8     8   546018 use 5.006;
  8         96  
4 8     8   43 use strict;
  8         18  
  8         173  
5 8     8   39 use warnings;
  8         12  
  8         238  
6 8     8   60 use Scalar::Util qw/blessed/;
  8         14  
  8         8029  
7              
8             our $VERSION = '1.01';
9              
10             our (%ERROR, %METH, $caller);
11              
12             BEGIN {
13 8     8   84 %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         279 my ($follow, $end, $data, @path) = @_;
22              
23 120 100 100     426 if (scalar @path && !$end) {
24 85         187 my ($key, $ref) = (shift @path, ref $data);
25 85         252 $follow = sprintf "%s/%s", $follow, $key;
26 85 100 100     288 if ($ref eq 'HASH') {
    100          
    100          
27 54         88 $data = $data->{$key};
28 54 100       134 $METH{error}->('invalid_key', $key, $follow) if ! defined $data;
29             }
30             elsif ( $ref eq 'ARRAY' ) {
31 6         20 $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         50 my ($meth, $params) = $METH{meth_params}->($key, $data);
36 21 100       31 $data = scalar @{ $params } ? $data->$meth(@{ $params }) : $data->$meth;
  21         63  
  20         61  
37 21 100       150 $METH{error}->('invalid_method', $key, $follow) if ! defined $data;
38             }
39             else {
40 3 100       14 $METH{error}->('invalid_path', $key, $follow) if (exists $ERROR{invalid_path});
41 2         4 $end = 1;
42             }
43 79         209 return $METH{extract_path}->($follow, $end, $data, @path);
44             }
45              
46 35         152 return $data;
47             },
48             unescape => sub {
49 46 100       207 return '' unless defined $_[0];
50 45         127 $_[0] =~ s/^\///g;
51 45         97 $_[0] =~ s/\+/ /g;
52 45         84 $_[0] =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
  2         13  
53 45         181 return $_[0];
54             },
55             meth_params => sub {
56 22         40 my ($key, $obj) = @_;
57 22         141 my ($method, $args) = $key =~ /^(.*?)\((.*)\)$/;
58 22         58 $args = $METH{generate_params}->($args, $obj);
59 21         58 return ($method, $args);
60             },
61             generate_params => sub {
62 42         91 my ($string, $obj, @world, $current) = @_;
63 42         137 foreach ( split /(?![^\(\[\{]+[\]\}\)]),/, $string ) {
64 57 100 100     355 if ( $_ =~ m/^\s*\[\s*(.*)\]/ ) {
    100          
    100          
65 6         15 $current = $METH{generate_params}->($1, $obj);
66 6         13 push @world, $current;
67             }
68             elsif ( $_ =~ m/^\s*\{\s*(.*)\s*\}/ ) {
69 5         11 $current = {};
70 5         23 my %temp = split '=>', $1;
71 5 100       20 do { $current->{$METH{generate_params}->(defined $ERROR{allow_meth_keys} ? sprintf("'%s'", $_) : ($_, $obj))->[0] } = $METH{generate_params}->($temp{$_}, $obj)->[0] } for keys %temp;
  5         16  
72 4         12 push @world, $current;
73             }
74             elsif ( ($_ =~ m/^\s*(\d+)\s*$/) || ($_ =~ m/^\s*[\'\"]+\s*(.*?)\s*[\'\"]+\s*$/) ) {
75 33         111 push @world, $1;
76             }
77             else {
78 13         42 my $ex = $_ =~ s/^\s*\&//;
79 13         40 my ($method, $args) = $_ =~ /^\s*(.*?)\((.*)\)$/;
80 13 100       45 ($method) = $_ =~ m/\s*(.*)\s*/ unless $method;
81 13 100       42 $args = $args ? $METH{generate_params}->($args, $obj) : [];
82 8 100   8   62 push @world, $ex ? do { no strict 'refs'; *{"${caller}::${method}"}->(@{ $args }); } : $obj->$method(@{ $args });
  8         24  
  8         1735  
  13         28  
  6         8  
  6         58  
  6         11  
  7         57  
83             }
84             }
85 40         159 return \@world;
86             },
87             error => sub {
88 5         12 my ($error) = @_;
89 5         12 my $find = $ERROR{$error};
90 5 100       55 return ref $find eq 'CODE'
91             ? $find->(@_)
92             : die sprintf $find, @_;
93             }
94 8         1199 );
95             }
96              
97             sub import {
98 10     10   110 my ($pkg, $sub) = shift;
99 10 100       1501 return unless my @export = @_;
100 8 100       35 my $opts = ref $export[scalar @export - 1] ? pop @export : {};
101 8 100       35 $ERROR{no_error} = 1 if $opts->{return_undef};
102 8 100       26 %ERROR = (%ERROR, %{ $opts->{errors} }) if $opts->{errors};
  3         16  
103 8 100 100     50 @export = qw/lnpath/ if scalar @export == 1 && $export[0] eq 'all';
104 8         18 $caller = scalar caller();
105             {
106 8     8   61 no strict 'refs';
  8         19  
  8         2221  
  8         14  
107 8         16 for ( @export ) {
108 9 100 100     1312 if ( $sub = $pkg->can($_) ? $_ : $opts->{as} && $opts->{as}->{$_} ) {
    100          
109 6         13 *{"${caller}::${_}"} = \&{"${pkg}::${sub}"};
  6         5658  
  6         25  
110             }
111             }
112             }
113             }
114              
115             sub lnpath {
116 41     41 1 1293 my ($data, $key) = @_;
117 41         111 my $val = eval {
118 41         126 $METH{extract_path}->('', 0, $data, split('/', $METH{unescape}->($key)))
119             };
120 41 100 100     144 if ($@ && !$ERROR{no_error}) {
121 4         26 die $@;
122             }
123 37         196 return $val;
124             }
125              
126             1;
127              
128             __END__