NAME
Hash::AsObject - hashes with accessors/mutators
SYNOPSIS
$h = Hash::AsObject->new;
$h->foo(123);
print $h->foo; # prints 123
print $h->{'foo'}; # prints 123
$h->{'bar'}{'baz'} = 456;
print $h->bar->baz; # prints 456
DESCRIPTION
A Hash::AsObject is a blessed hash that provides read-write access to its elements using accessors. (Actually, they're both accessors and mutators.)
It's designed to act as much like a plain hash as possible; this means, for example, that you can use methods like DESTROY
and if the Hash::AsObject has an element with that name, it'll get or set it. See below for more information.
METHODS
The whole point of this module is to provide arbitrary methods. For the most part, these are defined at runtime by a specially written AUTOLOAD
function.
In order to behave properly in all cases, however, a number of special methods and functions must be supported. Some of these are defined while others are simply emulated in AUTOLOAD.
All of the following work Do The Right Thing when called on an instance of Hash::AsObject and when called as class methods. What this means in practical terms is that you never have to worry that you'll accidentally call a "magic" method
- new
-
$h = Hash::AsObject->new; $h = Hash::AsObject->new(\%some_hash); $h = Hash::AsObject->new(%some_other_hash);
Create a new Hash::AsObject.
If called as an instance method, this accesses a hash element 'new':
$h->{'new'} = 123; $h->new; # 123 $h->new(456); # 456
- AUTOLOAD
-
$h->AUTOLOAD($val); $val = $h->AUTOLOAD;
- DESTROY
-
DESTROY
is called automatically by the Perl runtime when an object goes out of scope. A Hash::AsObject can't distinguish this from a call to access the element $h->{'DESTROY'}, but this isn't a problem. - VERSION
-
When called as a class method, this returns
$Hash::AsObject::VERSION
; when called as an instance method, it gets or sets the hash element 'VERSION'; - import
- can
-
The methods
can()
andisa()
are special, because they're defined in theUNIVERSAL
class that all packages automatically inherit from. In Perl, you can usually use calls$object->isa('Foo')
and$object->can('bar')
and get the desired results, but you have the options to useUNIVERSAL::can()
andUNIVERSAL::isa()
directly instead.In Hash::AsObject, you must use the
UNIVERSAL
functions - unless, of course, you want to access hash elements 'can' and 'isa'!Just as in
UNIVERSAL::can()
, a call toHash::AsObject->can()
throws an exception if no argument is provided. The same is true forisa()
. - isa
-
See
can()
above.
CAVEATS
No distinction is made between non-existent elements and those that are present but undefined. Furthermore, there's no way to delete an element without resorting to delete $h->{'foo'}
.
Storing a hash directly into an element of a Hash::AsObject instance has the effect of blessing that hash into Hash::AsObject.
For example, the following code:
my $h = Hash::AsObject->new;
my $foo = { 'bar' => 1, 'baz' => 2 };
print ref($foo), "\n";
$h->foo($foo);
print ref($foo), "\n";
Produces the following output:
HASH
Hash::AsObject
I could fix this, but then code like the following would throw an exception, because $h->foo($foo)
will return a plain hash reference, not an object:
$h->foo($foo)->bar;
Well, I can make $h->foo($foo)->bar
work, but then code like this won't have the desired effect:
my $foo = { 'bar' => 123 };
$h->foo($foo);
$h->foo->bar(456);
print $foo->{'bar'}; # prints 123
print $h->foo->bar; # prints 456
I suppose I could fix that, but that's an awful lot of work for little apparent benefit.
Let me know if you have any thoughts on this.
BUGS
Autovivification is probably not emulated correctly.
The blessing of hashes stored in a Hash::AsObject might be considered a bug. Or a feature; it depends on your point of view.
TO DO
Add the capability to delete elements, perhaps like this:
use Hash::AsObject 'deleter' => 'kill'; $h = Hash::AsObject->new({'one' => 1, 'two' => 2}); kill $h, 'one';
That might seem to violate the prohibition against exporting functions from object-oriented packages, but then technically it wouldn't be exporting it from anywhere since the function would be constructed by hand. Alternatively, it could work like this:
use Hash::AsObject 'deleter' => 'kill'; $h = Hash::AsObject->new({'one' => 1, 'two' => 2}); $h->kill('one');
But, again, what if the hash contained an element named 'kill'?
Define multiple classes in
Hash/AsObject.pm
? For example, there could be one package for read-only access to a hash, one for hashes that throw exceptions when accessors for non-existent keys are called, etc. But this is hard to do fully without (a) altering the underlying hash, or (b) defining methods besides AUTOLOAD. Hmmm...
VERSION
0.05
AUTHOR
Paul Hoffman <nkuitse AT cpan DOT org>
CREDITS
Andy Wardley for Template::Stash, which was my inspiration. Writing template code like this:
[% foo.bar.baz(qux) %]
Made me yearn to write Perl code like this:
foo->bar->baz($qux);
COPYRIGHT
Copyright 2003 Paul M. Hoffman. All rights reserved.
This program is free software; you can redistribute it and modify it under the same terms as Perl itself.