The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Data::Reach - Walk down a datastructure, without autovivification

SYNOPSIS

    # regular use
    use Data::Reach;
    my $node = reach $data_tree, @path;

    # import under a different name
    use Data::Reach as => 'walk_down';
    my $node = walk_down $data_tree, @path;

    # optional changes of algorithm, lexically scoped
    { no Data::Reach  qw/peek_blessed use_overloads/;
      use Data::Reach call_method => [qw/foo bar/];
      my $node = reach $object_tree, @path;
    }
    # after end of scope, back to the regular algorithm

DESCRIPTION

The reach function walks down a nested datastructure of hashrefs and arrayrefs, choosing the next subnode at each step according to the next key supplied in @path. If there is no such sequence of subnodes, undef is returned. No autovivification nor any writing into the datastructure is ever performed. Missing data merely returns undef, while wrong use of data (for example looking into an arrayref with a non-numerical index) generates an exception. Blessed objects within the datastructure are generally treated just like raw, unblessed datastructures; however that behaviour can be changed through pragma options.

Note: this code doesn't do much, actually; but after having copy-pasted similar stuff into several of my applications, I finally decided that it was worth a CPAN distribution on its own. The "SEE ALSO" section below discusses some alternative implementations.

FUNCTIONS

reach

  my $node = reach $data_tree, @path;

Tries to find a node under root $data_tree, walking down the tree and choosing subnodes according to values given in @path (which should be a list of scalar values). At each step :

  • if the root is undef, then undef is returned (even if there are remaining items in @path)

  • if @path is empty, then the root $data_tree is returned

  • if the first item in @path is undef, then undef is returned (even if there are remaining items in @path).

  • if $data_tree is a hashref or can behave as a hashref, then $data_tree->{$path[0]} becomes the new root, and the first item from @path is removed. No distinction is made between a missing or an undefined $data_tree->{$path[0]} : in both cases the result will be undef.

  • if $data_tree is an arrayref or can behave as an arrayref, then $data_tree->[$path[0]] becomes the new root, and the first item from @path is removed. The value in $path[0] must be an integer; otherwise it is improper as an array index and an error is generated. No distinction is made between a missing or an undefined $data_tree->[$path[0]] : in both cases the result will be undef.

  • if $data_tree is any other kind of data (scalar, reference to a scalar, reference to a reference, etc.), an error is generated.

By default, blessed objects are treated just like raw, unblessed datastructures; however that behaviour can be changed through pragma options, as described below.

IMPORT INTERFACE

Exporting the 'reach' function

The 'reach' function is exported by default when useing this module, as in :

  use Data::Reach;
  use Data::Reach qw/reach/; # equivalent to the line above

However the exported name can be changed through the as option :

  use Data::Reach as => 'walk_down';
  my $node = walk_down $data, @path;

The same can be done with an empty string in order to prevent any export. In that case, the fully qualified name must be used to call the reach function :

  use Data::Reach as => '';      # equivalent to "use Data::Reach ();"
  my $node = Data::Reach::reach $data, @path;

Pragma options for reaching within objects

Arguments to the import method may also change the algorithm used to reach within objects. These options can be turned on or off as lexical pragmata; this means that the effect of change of algorithm is valid until the end of the current scope (see "use" in perlfunc, "no" in perlfunc and perlpragma).

call_method
  use Data::Reach call_method => 'foo';         # just one method
  use Data::Reach call_method => [qw/foo bar/]; # an ordered list of methods

If the target object possesses a method corresponding to the name(s) specified, that method will be called, with a single argument corresponding to the current value in path. The method is supposed to reach down one step into the datastructure and return the next data subtree or leaf.

The presence of one of the required methods is the first choice for reaching within an object. If this cannot be applied, either because there was no required method, or because the target object has none of them, then the second choice is to use overloads, as described below.

use_overloads
  use Data::Reach qw/use_overloads/; # turn the option on
  no  Data::Reach qw/use_overloads/; # turn the option off

This option is true by default; it means that if the object has an overloaded hash or array dereferencing function, that function will be called (see overload). This feature distinguishes Data::Reach from other similar modules listed in the "SEE ALSO" section.

peek_blessed
  use Data::Reach qw/peek_blessed/; # turn the option on
  no  Data::Reach qw/peek_blessed/; # turn the option off

This option is true by default; it means that the reach functions will go down into object implementations (i.e. reach internal attributes within the object's hashref). Turn it off if you want objects to stay opaque, with public methods as the only way to reach internal information.

Note that several options can be tuned in one single statement :

  no  Data::Reach qw/use_overloads peek_blessed/; # turn both options off

SEE ALSO

There are many similar modules on CPAN, each of them having some variations in the set of features. Here are a few pointers, and the reasons why I didn't use them :

Data::Diver

Does quite a similar job, with a richer API (can also write into the datastructure or use it as a lvalue). Return values may be complex to decode (distinctions between an empty list, an undef, or a singleton containing an undef). It uses eval internally, without taking care of eval pitfalls (see "BACKGROUND" in Try::Tiny for explanations).

Data::DRef

An old module (last update was in 1999), still relevant for modern Perl, except that it does not handle overloads, which were not available at that time. The API is a bit too rich to my taste (many different ways to get or set data).

Data::DPath or Data::Path

Two competing modules for accessing nested data through expressions similar to XPath. Very interesting, but a bit overkill for the needs I wanted to cover here.

Data::Focus

Creates a "focus" object that walks through the data using various "lenses". An interesting approach, inspired by Haskell, but also a bit overkill.

Data::PathSimple

Very concise. The path is expressed as a '/'-separated string instead of an array of values. Does not handle overloads.

AUTHOR

Laurent Dami, <dami at cpan.org>

BUGS

Please report any bugs or feature requests to bug-data-reach at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Data-Reach. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Data::Reach

You can also look for information at:

RT: CPAN's request tracker (report bugs here)

http://rt.cpan.org/NoAuth/Bugs.html?Dist=Data-Reach

AnnoCPAN: Annotated CPAN documentation

http://annocpan.org/dist/Data-Reach

CPAN Ratings

http://cpanratings.perl.org/d/Data-Reach

METACPAN

https://metacpan.org/pod/Data::Reach

The source code is at https://github.com/damil/Data-Reach.

LICENSE AND COPYRIGHT

Copyright 2015 Laurent Dami.

This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:

http://www.perlfoundation.org/artistic_license_2_0