The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Data::OpenStruct::Deep;

use 5.008001;
use strict;
use warnings;
use Storable ();
use Want ();

our $VERSION = '0.03';

sub new {
    my $class  = shift;
    my $fields = ref $_[0] eq 'HASH' ? $_[0] : { @_ };
    return bless { __stack => [], __fields => Storable::dclone($fields) }, $class;
}

sub to_hash {
    my $self = shift;
    return Storable::dclone($self->{__fields});
}

our $AUTOLOAD;
sub AUTOLOAD {
    my ($self, $value) = @_;

    my ($method) = $AUTOLOAD =~ /([^:]+)$/;
    return if $method eq 'DESTROY';

    if (Want::want('OBJECT', 'SCALAR')) {
        # method chain
        push @{ $self->{__stack} }, $method;
        return $self;
    }
    else {
        my $node = $self->{__fields}; # root node

        # calc chain
        for my $name (@{ $self->{__stack} }) {
            # override for chain
            unless (ref $node->{$name} eq 'HASH') {
                $node->{$name} = {};
            }

            $node = $node->{$name};
        }
        $self->{__stack} = []; # clear stack

        $node->{$method} = $value if defined $value;
        return $node->{$method};
    }
}

1;

=head1 NAME

Data::OpenStruct::Deep - allows you to create data objects and set arbitrary attributes deeply

=head1 SYNOPSIS

    use Data::OpenStruct::Deep;

    my %hash = (
        foo => 1,
        bar => {
            baz => 2,
        },
    );

    my $struct = Data::OpenStruct::Deep->new(%hash);
    my $foo = $struct->foo;      #=> 1
    my $bar = $struct->bar;      #=> { baz => 2 }
    my $baz = $struct->bar->baz; #=> 2

    my $empty = Data::OpenStruct::Deep->new;
    $empty->foo->bar->baz->quux('deeply'); # deeply, ok
    print $empty->foo->bar;            #=> { baz => { quux => "deeply" } }
    print $empty->foo->bar->baz->quux; #=> "deeply"

=head1 DESCRIPTION

This module allows you to create data objects and set arbitrary attributes.

It is like a hash with a different way to access the data.
In fact, it is implemented with a hash and C<AUTOLOAD>,
and you can initialize it with one.

=head1 METHODS

=head2 new(%hash?)

=head2 to_hash

=head1 AUTHOR

NAKAGAWA Masaki E<lt>masaki@cpan.orgE<gt>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=head1 SEE ALSO

L<Hash::AsObject>, L<Object::AutoAccessor>, L<Hash::Inflator>,

L<http://www.ruby-doc.org/stdlib/libdoc/ostruct/rdoc/classes/OpenStruct.html>

=cut