|
Data::DRef - Delimited-key access to complex data structures |
Data::DRef - Delimited-key access to complex data structures
use Data::DRef qw( :dref_access );
my $hash = { 'items' => [ 'first' ] };
print get_value_for_dref($hash, 'items.0');
set_value_for_dref( $hash, 'items.1', 'second' );
set_value_for_root_dref( 'myhash', $hash );
print get_value_for_root_dref('myhash.items.0');
use Data::DRef qw( :select ); matching_keys($target, %filter_criteria) : $key or @keys matching_values($target, %filter_criteria) : $item or @items
use Data::DRef qw( :index ); index_by_drefs($target, @drefs) : $index unique_index_by_drefs($target, @drefs) : $index ordered_index_by_drefs( $target, $index_dref ) : $entry_ary
use Data::DRef qw( :leaf ); leaf_drefs($target) : @drefs leaf_values( $target ) : @values leaf_drefs_and_values( $target ) : %dref_value_pairs
Data::DRef provides a streamlined interface for accessing values within nested Perl data structures. These structures are generally networks of hashes and arrays, some of which may be blessed into various classes, containing a mix of simple scalar values and references to other items in the structure.
The Data::DRef functions allow you to use delimited key strings to set and
retrieve values at desired nodes within these structures. These functions
are slower than direct variable access, but provide additional flexibility
for high-level scripting and other late-binding behaviour. For example,
a web-based application could use DRefs to simplify customization,
allowing the user to refer to arguments processed by CGI.pm in fairly
readable way, such as query.param.foo.
A suite of utility functions, previous maintained in a separate Data::Collection module, performs a variety of operations across nested data structures. Because the Data::DRef abstraction layer is used, these functions should work equally well with arrays, hashes, or objects that provide their own key-value interface.
The first set of functions define our core key-value interface, and provide its implementation for references to Perl arrays and hashes. For example, direct access to array and hash keys usually looks like this:
print $employee->[3];
$person->{'name'} = 'Joe';
Using these functions, you could replace the above statements with:
print get_value_for_key( $employee, 3 );
set_value_for_key( $person, 'name', 'Joe' );
Each of these functions checks for object methods as described below.
get_keys($target) : @keysget_values($target) : @values
Frequently we wish to access values at some remove within a structure by chaining through a list of references. Programmatic access to these values within Perl usually looks something like this:
print $report->{'employees'}[3]{'id'};
$report->{'employees'}[3]{'name'} = 'Joe';
Using these functions, you could replace the above statements with:
print get_value_for_keys( $report, 'employees', 3, 'id' );
set_value_for_keys( $report, 'Joe', 'employees', 3, 'name' );
These functions also support the ``m_*'' method delegation described above.
Each of the value-for-key and multiple-key functions first check for
methods with similar names preceeded by ``m_'' and, if present, uses
that implementation. For example, callers can consistently request
get_value_for_key($foo, $key), but in cases where $foo supports a
method named m_get_value_for_key, its results will be returned instead.
Classes that wish to provide alternate DRef-like behavior or generate values on demand should implement these methods in their packages. A Data::DRef::MethodBased class is provided for use by objects which use methods to get and set attributes. By making your package a subclass of MethodBased you'll inherit m_get_value_for_key and m_set_value_for_key methods which treat the key as a method name to invoke.
In order to simplify expression of the lists of keys used above,
we define a string format in which they may be represented. A DRef
string is composed of a series of simple scalar keys, each escaped
with String::Escape's printable() function, joined with the $Separator
character, '.'.
., the period
character.
get_key_drefs($target) : @drefs
Several types of parenthesized expressions are supported as extension mechanisms for dref strings. Nested parentheses are supported, with the innermost parentheses resolved first.
Continuing the above example, one could write:
set_value_for_root_dref('empl_number', 3);
...
print get_value_for_dref($report, 'employees.(#empl_number).name');
resolve_pragmas() causes these expressions to be evaluated,
and an expanded version of the dref is returned. In a list context, also
returns a list of key-value pairs that may contain pragma information.
These functions provide the main public interface for dref-based access to values in nested data structures. They invoke the equivalent ..._value_for_keys() function after expanding and spliting the provided drefs.
Using these functions, you could replace the above statements with:
print get_value_for_dref( $report, 'employees.3.id' );
set_value_for_dref( $report, 'employees.3.name', 'Joe' );
Data::DRef also provides a common point-of-entry datastructure, refered to as $Root. Objects or structures accessible through $Root can be refered to identically from any package using the get_value_for_root_dref and set_value_for_root_dref functions. Here's another example:
set_value_for_root_dref('report', $report);
print get_value_for_root_dref('report.employees.3.name');
get_value_for_root_dref($dref) : $valueget_value_for_optional_dref($literal_or_prefixed_dref) : $value
The selection functions extract and return elements of a collection by evaluating them against a provided hash of criteria. When called in a scalar context, they will return the first sucessful match; in a list context, they will return all sucessful matches.
The keys in the criteria hash are drefs to check for each candidate; a match is sucessful if for each of the provided drefs, the candidate returns the same value that is associated with that dref in the criteria hash. To check the value itself, rather than looking up a dref, use undef as the hash key.
The indexing functions extract the values from some target structure, then return a new structure containing references to those same values.
These functions explore all of the references in the network of structures accessible from some starting point, and provide access to the outermost (non-reference) items. For a tree structure, this is equivalent to listing the leaf nodes, but these functions can also be used in structures with circular references.
leaf_drefs($target) : @drefs
To provide compatibility with earlier versions of this module, many of the functions above are also accesible through an alias with the old name.
Here is a sample data structure which will be used to illustrate various example function calls. Note that the individual hashes shown below are only refered to in the following example results, not completely copied.
$spud : {
'type'=>'tubers', 'name'=>'potatoes', 'color'=>'red', 'size'=>[2,3,5]
}
$apple : {
'type'=>'fruit', 'name'=>'apples', 'color'=>'red', 'size'=>[2,2,2]
}
$orange : {
'type'=>'fruit', 'name'=>'oranges', 'color'=>'orange', 'size'=>[1,1,1]
}
$produce_info : [ $spud, $apple, $orange, ];
matching_keys($produce_info, 'type'=>'tubers') : ( 0 ) matching_keys($produce_info, 'type'=>'fruit') : ( 1, 2 ) matching_keys($produce_info, 'type'=>'fruit', 'color'=>'red' ) : ( 1 ) matching_keys($produce_info, 'type'=>'tubers', 'color'=>'orange' ) : ( )
matching_values($produce_info, 'type'=>'fruit') : ( $apple, $orange ) matching_values($produce_info, 'type'=>'fruit', 'color'=>'red' ) : ( $apple )
index_by_drefs($produce_info, 'type') : {
'fruit' => [ $apple, $orange ],
'tubers' => [ $spud ],
}
index_by_drefs($produce_info, 'color', 'type') : {
'red' => {
'fruit' => [ $apple ],
'tubers' => [ $spud ],
},
'orange' => {
'fruit' => [ $orange ],
},
}
unique_index_by_drefs($produce_info, 'type') : {
'fruit' => $orange,
'tubers' => $spud,
}
ordered_index_by_drefs($produce_info, 'type') : [
{
'value' => 'tubers',
'items' => [ $spud ],
},
{
'value' => 'fruit',
'items' => [ $orange, $apple ],
},
]
leaf_drefs($spud) : ( 'type', 'name', 'color', 'size.0', 'size.1', 'size.2' )
leaf_values($spud) : ( 'tubers', 'potatoes', 'red', '2', '3', '5' )
leaf_drefs_and_values($spud) : (
'type' => 'tubers', 'name' => 'potatoes', 'color' => 'red',
'size.0' => 2, 'size.1' => 3, 'size.2' => 5
)
Here's a get_value_for_key method for an object which provides a calculated timestamp value:
package Clock;
sub new { bless { @_ }; }
sub m_get_value_for_key {
my ($self, $key) = @_;
return time() if ( $key eq 'timestamp' );
return $self->{ $key };
}
package main;
set_value_for_root_dref( 'clock', new Clock ( name => "Clock 1" ) );
...
print get_value_for_root_dref('clock.timestamp');
This release of Data::DRef is intended for public review and feedback. This is the most recent version of code that has been used for several years and thoroughly tested, however, the interface has recently been overhauled and it should be considered ``alpha'' pending that feedback.
Name DSLI Description -------------- ---- --------------------------------------------- Data:: ::DRef adph Nested data access using delimited strings
You will also need the String::Escape module from CPAN or www.evoscript.com.
Further information and support for this module is available at <www.evoscript.com>.
Please report bugs or other problems to <bugs@evoscript.com>.
There is one known bug in this version:
There is one major change under consideration:
Copyright 1996, 1997, 1998, 1999 Evolution Online Systems, Inc. <www.evolution.com>
You may use this software for free under the terms of the Artistic License.
Contributors: M. Simon Cavalletto <simonm@evolution.com>, E. J. Evans <piglet@evolution.com>
|
Data::DRef - Delimited-key access to complex data structures |