The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

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 plain hashes as possible: for example, non-existing elements are autovivified just as they would be in a regular hash.

PUBLIC METHODS

new
   $h = Hash::AsObject->new;
   $h = Hash::AsObject->new(\%some_hash);
   $h = Hash::AsObject->new(%some_other_hash);

Create a new Hash::AsObject.

NOTE: Technically, there aren't any public methods defined in the Hash::AsObject package, but a standard new class method is emulated using AUTOLOAD.

CAVEATS

No distinction is made between non-existent elements and those 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

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.04

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.