data-path/lib/Data/Path.pm
jeremy 59fac68ef8 test and fix for bug 32911 in rt
darcs-hash:20080221030135-f4041-4e626ffdc29e6616515e0200cdd7c01fed2315f6.gz
2008-02-20 21:01:35 -06:00

232 lines
5.5 KiB
Perl

package Data::Path;
use 5.006001;
use strict;
use warnings;
use Scalar::Util 'blessed';
use Carp;
our $VERSION = '1.1';
sub new {
my ($class,$data,$callback)=@_;
$callback||={};
my $self=
{ data => $data
# set call backs to default if not given
, callback =>
{ key_does_not_exist => $callback->{key_does_not_exist} ||
sub {
my ($data, $key, $index, $value, $rest )=@_;
croak "key $key does not exists\n";
}
, index_does_not_exist => $callback->{index_does_not_exist} ||
sub {
my ($data, $key, $index, $value, $rest )=@_;
croak "key $key\[$index\] does not exists\n";
}
, retrieve_index_from_non_array => $callback->{retrieve_index_from_non_array} ||
sub {
my ($data, $key, $index, $value, $rest )=@_;
croak "trie to retrieve an index $index from a no array value (in key $key)\n";
}
, retrieve_key_from_non_hash => $callback->{retrieve_key_from_non_hash} ||
sub {
my ($data, $key, $index, $value, $rest )=@_;
croak "trie to retrieve a key from a no hash value (in key $key)\n";
}
, not_a_coderef_or_method => $callback->{not_a_coderef_or_method} ||
sub {
my ($data, $key, $index, $value, $rest )=@_;
croak "tried to retrieve from a non-existant coderef or method: $key in $data";
}
}
};
return bless $self,$class;
}
sub get {
my ($self,$rkey,$data)=@_;
# set data to
$data||=$self->{data};
# get key till / or [
my $key = $1 if ( $rkey =~ s/^\/([^\/|\[]+)//o );
# check index for index
my $index = $1 if ( $rkey =~ s/^\[(\d+)\]//o );
# set rest
my $rest = $rkey;
# get key from data
my $value;
if ($key =~ s/(\(\))$//) {
$self->{callback}->{not_a_coderef_or_method}->($data, $key, $index, $value, $rest)
unless exists $data->{$key} or (blessed $data && $data->can($key));
$value = $data->{$key}->() if (exists $data->{$key});
$value = $data->$key() if blessed $data && $data->can($key);
} else {
$value = $data->{$key};
}
# croak if key does not exists and something after that is requested
$self->{callback}->{key_does_not_exist}->($data, $key, $index, $value, $rest)
if not exists $data->{$key} and $rest;
# check index
if (defined $index) {
# croak if index does not exists and something after that is requested
$self->{callback}->{index_does_not_exist}->($data, $key, $index, $value, $rest)
if not exists $value->[$index] and $rest;
if ( ref $value eq 'ARRAY' ) {
$value=$value->[$index];
} else {
$self->{callback}->{retrieve_index_from_non_array}->($data, $key, $index, $value, $rest);
}
}
# check if last element is reached
if ($rest) {
if ( ref $value eq 'HASH' || blessed $value ) {
$value=$self->get($rest,$value);
} else {
$self->{callback}->{retrieve_key_from_non_hash}->($data, $key, $index, $value, $rest);
}
}
return $value;
}
1;
__END__
=head1 NAME
Data::Path - Perl extension for XPath like accessing from complex data structs
=head1 SYNOPSIS
use Data::Path;
my $hashdata={
result => {
msg =>
[ { text => 'msg0' }
, { text => 'msg1' }
, { text => 'msg2' }
]
},
method => sub {'method text'}
}
my $hpath=Data::Path->new($hashdata);
my $value= $hpath->get('/result/msg[1]/text');
my $value2 = $hpath->get('/method()');
print "OK" if $value2 eq 'method text';
print "OK" if $value eq 'msg1';
my $hpath=Data::Path->new($hashdata,$callback);
my $hpath=Data::Path->new
($hashdata,
{ key_does_not_exist => sub { die index not found }
);
=head1 DESCRIPTION
XPath like access to get values from a complex data structs.
key_does_not_exist / index_does_not_exist are only called if it was not the last part of the path.
If the last part of path is not exists undef is returned.
=head2 CALLBACKs
The default callbacks but you can overwrite this.
{ key_does_not_exist =>
sub {
my ($data, $key, $index, $value, $rest )=@_;
croak "key $key does not exists\n";
}
, index_does_not_exist =>
sub {
my ($data, $key, $index, $value, $rest )=@_;
croak "key $key\[$index\] does not exists\n";
}
, retrieve_index_from_non_array =>
sub {
my ($data, $key, $index, $value, $rest )=@_;
croak "trie to retrieve an index $index from a no array value (in key $key)\n";
}
, retrieve_key_from_non_hash =>
sub {
my ($data, $key, $index, $value, $rest )=@_;
croak "trie to retrieve a key from a no hash value (in key $key)\n";
}
, not_a_coderef_or_method => $callback->{not_a_coderef_or_method} ||
sub {
my ($data, $key, $index, $value, $rest )=@_;
croak "tried to retrieve from a non-existant coderef or method";
}
}
=head2 EXMAPLE overwrite callback
my $hpath=Data::Path->new
($hashdata,
{ key_does_not_exist => sub { die key not found }
{ index_does_not_exist => sub { die index not found }
);
=head2 EXPORT
None by default.
=head1 SEE ALSO
XPath
=head1 TODO
Slices of data through /foo[*]/bar syntax. eg. retrieve all the bar keys from each element of the foo
array.
allow accessing the results of coderefs eg. /foo()/bar retreive the result of the foo coderef/method
of the object
=head1 AUTHOR
Marco Schrieck, E<lt>marco.schrieck@gmx.deE<gt>
Jeremy Wall L<< jeremy@marzhillstudios.com >>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2006 by Marco Schrieck
Copyright (C) 2007 by Jeremy Wall
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.4 or,
at your option, any later version of Perl 5 you may have available.
=cut