NAME Tie::SentientHash - Perl module implementing intelligent objects SYNOPSIS use Tie::SentientHash; tie %hash, 'Tie::SentientHash', $meta_data, $initial_data; $hashref = Tie::SentientHash->new($meta_data, $initial_data); $modified = $hashref->modified($key [, $bool]) $untiedhash = $hashref->export; $metadata = $hashref->_metadata; $modified = $hashref->_modified; $hashref->{key} = 'value'; $hashref->{key1}{key2} = $value; $value2 = $hashref->{key}; undef $hashref; DESCRIPTION The `Tie::SentientHash' package provides intelligent objects. The objects are represented as hashes which: * provide read-only elements * provide 'special' elements that are handled by user-supplied functions * disallow changes to the data as specified by metadata * track changes and call a 'commit changes' function when the object is destroyed References to scalars, arrays, hashes and objects can be stored in hash elements in which case the referenced object is tied to an internal class of the appropriate type (Tie::SentientHash::NestedHash, ::NestedArray or ::NestedScalar), so that changes to the nested data structures can be tracked. The constructor is invoked with two hash references: the first contains metadata and the second the initial data values. The metadata hash may contain the following flags: READONLY a list of hash entries that are read-only (read-only elements cannot be modified -- except by special element handlers -- or deleted and are not deleted when the CLEAR method is called) SPECIAL a hash of name/subroutine-refs pairs that specifies elements that are handled specially (special elements also cannot be deleted). The user function is called both for STORE (with four arguments) and for FETCH (with three arguments). The arguments are: a reference to the metadata hash, a reference to the data hash, the element key and if the funtion is being called for a STORE operation, the value to be stored. SPECIAL elements can be used to implement calculated attributes. TRACK_CHANGES flag to indicate that the class should keep track of the keys of modified (top-level) hash elements COMMIT_SUB a reference to a subroutine to commit changes (called with a reference to the data hash and a reference to the metadata hash) FORBID_INSERTS forbid inserts into hash and sub-hashes/arrays FORBID_DELETES forbid deletes from hash FORBID_CHANGES forbid any changes Trying to change an object in a way that is forbidden by the metadata will cause the module to croak. Changes are only tracked at the top level. The API is as follows: tie %hash, 'Tie::SentientHash', $meta_data, $initial_data Functional interface to create a new sentient hash. $meta_data describes the properties of the sentient hash (as outlined above) and $initial_data is the initial content of the sentient hash. Tie::SentientHash->new($meta_data, $initial_data) Object oriented constructor for a sentient hash. $hashref->modified([$key [, $bool]]) If called with no arguments in a scalar returns an indication of whether the sentient hash has been modified. If called with no arguments in an array context returns the list of elements that have been modified. Otherwise queries or sets the modification status of a specific top level element. $untiedhash = $hashref->export Creates an "untied" copy of the sentient hash. If a commit function is specified when the sentient hash is created it will be called when the destructor is called (normall when it is garbage-collected). EXAMPLE I use Tie::SentientHash as the basis for implementing persistent objects in my CGI/mod_perl scripts. The details of reading and writing the objects from and to the database is handled by a class, but neither the class nor the high level code needs to keep track of whether the object has been changed in any way. For example if you had a pay per view system of some kind you could have a script that contained the following fragment: sub pay_per_view ($$) { my($cust_id, $cost) = @_; my $cust = load Customer $cust_id; $cust->{CREDIT} -= $cost; } The customer object would be implemented in a module sketched out below. A commit function is specified on the call to create a new sentient object, and that function will be called when $cust goes out of scope at the end of the pay_per_view function and can write the modified object back to the database. If none of the attributes had been modified then the commit function would not be invoked. package Customer; sub load ($$) { my ($class, $cust_id) = @_; my $data = {}; # read customer data from a database into $data my $meta = { COMMIT_SUB => \&_commit, READONLY => [ qw( CUST_ID ) ], FORBID_INSERTS => 1 }; return bless Tie::SentientHash->new($meta, $data), $class; } sub _commit ($$) { my ($meta, $data) = @_; # As we have been called, something has changed. The names of # the modified fields are the keys of $meta->{MODIFIED}. We had # better write the data back out to the database. } RESTRICTIONS Full array semantics are only supported for Perl version 5.005. Starting with version 0.54 blessed objects may be stored in the *sentient hash*, however this functionality is experimental, has not been exhaustively tested, may not work, and may be subject to change. Use at your own peril! Tie::SentientHash ties nested elements to internal subclasses so it can track changes. If you keep references to such elements and modify them directly then Tie::SentientHash may not be aware of the changes. Objects of classes that use the tie mechanism may not work when stored in a sentient hash. If you use an object as the data for a new sentient hash, then the hash will not be re-blessed, i.e. if $object is an object then after $href = Tie::SentientHash->new($meta, $object); $href will be blessed in the same class as $object. This means you cannot use the `modified' or `export' methods on $href. However you can use them on the tied array, e.g.: @keys = (tied %$href)->modified; $newhash = (tied %$href)->export; As Tie::Sentient recursively ties nested elements to internal subclasses it may not be very efficent on large, deeply nested data structures. (If I find the time I may provide a C implementation that would be faster in this regard). AUTHOR Andrew Ford Please let me know if you use this module. SEE ALSO perl(1). COPYRIGHT Copyright 1999-2001 Andrew Ford and Ford & Mason Ltd. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.