How to store Hash of Hashes in Moose?

女生的网名这么多〃 提交于 2019-12-04 11:25:39

You can store a hash of hashes in a Moose object in pretty much the same way as you would store any other hash:

has steps => ( is => 'ro', isa => 'HashRef' );

You can, however, be more specific to declare it as the specific kind of hash you need to store as a way to verify that anything stored in that slot is the right kind of thing:

has steps => ( is => 'ro', isa => 'HashRef[HashRef[Object]]' );

Depending on the data, I might also change Object here to the class name. You can get even fancier and use MooseX::Types and MooseX::Types::Structured to specify an even more exacting structure.

As for helpers to to step over your structure, I don't know of anything in Moose or MooseX to do that. If you know the structure of your data, it's probably best to just implement a subroutine to do what you need yourself. Your code will likely perform better and do what you need better than any generic traversal.

Edit/Additional Info: Each Moose attribute creates an accessor method no your class which returns the stored value, so accessing the data is:

# Assuming we put the attribute in a package named StepTool
my $step_tool = StepTool->new(
    steps => { 'step1' => {'extraction' => \$object1,
                             'analysis' => \$object2},
               'step2' => {'extraction' => \$object3,
                             'analysis' => \$object4} },
);

# To do something one of the values
do_something($step_tool->steps->{step1}{extraction});

# To iterate over the structure, could be done in a method on StepTool
for my $step_name (keys %{ $step_tool->steps }) {
    my $step = $step_tool->steps->{ $step_name };

    for my $action_name (keys %$step) {
        my $object = $step->{ $action_name };

        do_something($object);
    }
}

# If doing the above as a method, $self is the Moose object, so...
sub traverse_steps {
    my ($self) = @_;

    for my $step_name (keys %{ $self->steps }) {
        ... # just like above
    }
}

And one other note, you could still use traits => [ 'Hash' ] and add some handles to give yourself some additional helpers, if you want.

If the data structure is more free form than that, you might want to look into something like Data::Visitor to iterate over your structure in your subroutine. (I have had some difficult to debug, weird problems with Data::Visitor, so I try to avoid it when I can.)

Peter V. Mørch

There is also a type-safe approach inspired by Moose: How to get an array of objects? Traits?

There is a class to hold the outer hash (StepTool::Steps) that has traits => ['Hash']. This approach can be nested infinitely deep using e.g. Arrays and Hashes:

package StepTool;

use Moose;

has 'steps' => (
    'is' => 'rw',
    'isa' => 'StepTool::Steps',
    'default' => sub { StepTool::Steps->new() },
);

package StepTool::Steps;

use Mouse;

has '_steps' => (
    is => 'ro',
    isa => 'HashRef[StepTool::Step]',
    traits => ['Hash'],
    default => sub { {} },
    handles => {
        # You'll probably want a fuller set here...
        get => 'get',
        set => 'set',
        keys => 'keys',
    }
);

package StepTool::Step;

use Mouse;

has 'extraction' => (
    is => 'rw',
);

has 'analysis' => (
    is => 'rw',
);

package main;

my $object1 = bless {}, 'Foobar1';
my $object2 = bless {}, 'Foobar2';
my $object3 = bless {}, 'Foobar3';
my $object4 = bless {}, 'Foobar4';

my $stepTool = StepTool->new();

# set up step1 one field at a time.
$stepTool->steps->set('step1', StepTool::Step->new());
# I have no idea why the OP wants references to objects
# everywhere but he does...
$stepTool->steps->get('step1')->extraction(\$object1);
$stepTool->steps->get('step1')->analysis(\$object2);

# set up step2 all at once
$stepTool->steps->set('step2', StepTool::Step->new(
    extraction => \$object3,
    analysis => \$object4
));

# or, less elegantly, initialize an entire StepTool:
my $stepTool2 = StepTool->new(
    steps => StepTool::Steps->new(
        _steps => {
            step1 => StepTool::Step->new(
                extraction => \$object1,
                analysis => \$object2
            ),
            step2 => StepTool::Step->new(
                extraction => \$object3,
                analysis => \$object4
            ),
        }
    ),
);

printf "step1->analysis is a ref to an instance of class: %s\n",
    ref(${$stepTool->steps->get('step1')->analysis});
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!