Perl: How to get the value of an attribute of an object - perl

In the below example, how would i print out the thread id?
$r_event = {
'type' => 'READ_' . $task . '_STARTED',
'timestamp' => $timestamp,
'threadid' => $threadId,
'fdn' => $fdn
};

You have a hash reference there (and those are often used as objects and that's what JSON calls an "object"). You can use the -> to dereference it and put the key you want in curlies:
print $r_event->{'threaded'};
This is just like a normal hash. Note the % and the parens instead of curlies:
%r_event = (
'type' => 'READ_' . $task . '_STARTED',
'timestamp' => $timestamp,
'threadid' => $threadId,
'fdn' => $fdn
);
In that case it's just $r_event{'threaded'} with no arrow since there's no reference.
My book Intermediate Perl covers this and you'll also find it in perlref.

Related

How can I update a hash value using a hash reference in Perl?

Is there a way to update a value in a hash using a hash reference that points to the hash value?
My hash output looks like this:
'Alternate' => {
'free' => '27.52',
'primary' => 'false',
'used' => '0.01',
'name' => '/mydir/journal2',
'size' => '50.00'
},
'Primary' => {
'free' => '60.57',
'primary' => 'true',
'used' => '0.06',
'name' => '/mydir/journal',
'size' => '64.00'
}
};
I attempted to create a hash reference to the 'used' property in the hash and tried to update the value:
$hash_ref = \%hash->{"Primary"}->{used};
$hash_ref = "99%";
print $$hash_ref, "\n";
This changes the value of the hash, but I get the "Using a hash as a reference is deprecated at line X". I'd like to know if what I'm trying to do is possible and what I'm doing wrong.
...
'Primary' => {
'free' => '60.57',
'primary' => 'true',
'used' => '0.06',
'name' => '/mydir/journal',
'size' => '64.00'
}
...
Try to bypass the deprecation problem doing it like this:
...
my $hash_ref = $hash{'Primary'}; # if you declared `%hash = ( .. );`
# Or my $hash_ref = $hash->{'Primary'}; if you declared `$hash = { .. };`
print $hash_ref->{used}; # Prints 0.06
$hash_ref->{used} = '0.07'; # Update
print $href->{used}; # Prints 0.07
...
See perldsc, if you want to learn more.
Your failure started because you tried to create a hash reference to a scalar. That's kind of a meaningless goal as those are different data types. As Filippo already demonstrated, you already have hash references as values of your greater hash, so you can rely on that.
However, if you really want to create a reference to the scalar, you can just edit that value. This is how you'd do it:
use strict;
use warnings;
my $h = {
'Alternate' => {
'free' => '27.52',
'primary' => 'false',
'used' => '0.01',
'name' => '/mydir/journal2',
'size' => '50.00',
},
'Primary' => {
'free' => '60.57',
'primary' => 'true',
'used' => '0.06',
'name' => '/mydir/journal',
'size' => '64.00',
}
};
my $primary = $h->{Primary};
print $primary->{used}, "\n"; # Outputs 0.06
my $usedref = \$h->{Primary}{used};
$$usedref = '0.07';
print $primary->{used}, "\n"; # Outputs 0.07

How to store Hash of Hashes in Moose?

i was wondering, what is the best way to store Hash of Hashes in Moose. Lets take for example a Hash like this:
my %hash = ('step1' => {'extraction' => \$object1,
'analysis' => \$object2},
'step2' => {'extraction' => \$object3,
'analysis' => \$object4});
but i want to save this one in a moose attribute. How should i organize the access (reading, writing) on this. Examples on the net are mostly for "flat" hashes. But then you can use helpers like Moose::Meta::Attribute::Native::Trait::Hash. Is there something similar for hash of hashes?
Reason for this is, that i want to iterate over the step-keys and access the object-instances in that. Or is there a better, more Moose-like way to do this?
Thanks in advance!!!
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.)
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});

Retrieve the reference

with the hash below, I would like the clients array's reference :
my $this =
{
'name' => $name,
'max_clients' => $max_clients,
'clients' => ()
};
I can't do "\$this{'clients'};" to retrieve the reference.
You cannot store an array as a value in a hash table. You can only store a scalar value such as a reference to an array:
my $this =
{
'name' => $name,
'max_clients' => $max_clients,
'clients' => [],
};
See also perldoc perldsc.

Is it possible to retrieve existing moose objects, rather than create a new one, when the same required attributes are provided?

Suppose i have the following Moose package:
package GSM::Cell;
use Moose;
has 'ID' => (is => 'ro', required => 1);
has [qw(BCCH NEIGHBOUR)] => (is => 'rw', default => undef);
no Moose;
__PACKAGE__->meta->make_immutable;
1;
I then create two objects and add the one as the 'NEIGHBOUR' attribute of the other:
my $a = GSM::Cell->new(ID => 20021, BCCH => 1);
my $b = GSM::Cell->new(ID => 20022, BCCH => 2);
$a->NEIGHBOUR($b);
Somewhere else, e.g. in another procedure, the BCCH attribute of $b could be updated to another value:
$b->BCCH(3);
Now, if i refer to
$a->NEIGHBOUR->BCCH
then i will still get back the initial value of the BCCH attribute instead of the updated value.
I guess the sensible thing to do is to add a reference to $b instead of $b itself which would solve the problem:
$a->NEIGHBOUR(\$b);
However, i have the scenario in a web application where an object equivalent to $b (same ID) is instantiated in a multitude of methods and changes could be done in any one, making it difficult to pass around references of all your created objects.
Ideally, when a call to
my $somevar = GSM::Cell->new(ID => 20022);
is made, an object should only be created if one with the same ID does not already exist.
Is a dictionary the way to go, something like this:
$id = 20022;
my $somevar = $already_created{$id} || GSM::Cell->new(ID => $id);
or are there neater solutions?
It sounds like something MooseX::NaturalKey was designed for.
package GSM::Cell;
use MooseX::NaturalKey;
has 'ID' => (is => 'ro', required => 1);
has [qw(BCCH NEIGHBOUR)] => (is => 'rw', default => undef);
primary_key => ('ID');
no Moose;
__PACKAGE__->meta->make_immutable;
1;
Then later:
my $a = GSM::Cell->new(ID => 20021, BCCH => 1);
my $b = GSM::Cell->new(ID => 20022, BCCH => 2);
$a->NEIGHBOUR($b);
$b->BCCH(3);
say $a->NEIGHBOR->BCCH; # prints 3
my $c = GSM::Cell->new(ID => 20022);
$c->BCCH(4);
say $a->NEIGHBOR->BCCH; # prints 4
Isn't the neighbour relationship between two cells in itself an object, that needs to be referenced by cells 20021 and 20022? Changing the BCC value on one cell could then be passed through to the relationship-object, thus updating both cells.
what you should do is store object references to your cells in a hash say $Network and control the cell creation through a factory class, that knows to check the $Network hash for existing cells...
I think the problem described in the first half of your post is a non-problem.
If I run your code:
package GSM::Cell;
use Moose;
has 'ID' => (is => 'ro', required => 1);
has [qw(BCCH NEIGHBOUR)] => (is => 'rw', default => undef);
no Moose;
__PACKAGE__->meta->make_immutable;
1;
package main;
my $a = GSM::Cell->new(ID => 20021, BCCH => 1);
my $b = GSM::Cell->new(ID => 20022, BCCH => 2);
$a->NEIGHBOUR($b);
$b->BCCH(3);
print $a->NEIGHBOUR->BCCH, "\n"; # 3
It prints the updated value, not the old value. It works because $b is an object, and all Perl objects are blessed references. When you run $a->NEIGHBOUR($b) you are already passing a reference; there is no need to pass a reference to a reference.

How do I best make triggered accessors with defaults in Moose?

I have a situation where I'd like to cache some calculations for use
later. Let's say I have a list of allowed values. Since I'm going to
be checking to see if anything is in that list I'm going to want it as
a hash for efficiency and convenience. Otherwise I'd have to grep.
If I'm using Moose it would be nice if the cache was recalculated each
time the list of allowed values is changed. I can do that with a
trigger easy enough...
has allowed_values => (
is => 'rw',
isa => 'ArrayRef',
trigger => sub {
my %hash = map { $_ => 1 } #{$_[1]};
$_[0]->allowed_values_cache(\%hash);
}
);
has allowed_values_cache => (
is => 'rw',
isa => 'HashRef',
);
And the two will stay in sync...
$obj->allowed_values([qw(up down left right)]);
print keys %{ $obj->allowed_values_cache }; # up down left right
Now let's say I want a default for allowed_values, simple enough
change...
has allowed_values => (
is => 'rw',
isa => 'ArrayRef',
trigger => sub {
my %hash = map { $_ => 1 } #{$_[1]};
$_[0]->allowed_values_cache(\%hash);
},
default => sub {
return [qw(this that whatever)]
},
);
...except setting the default doesn't call the trigger. To get it to
DWIM I need to duplicate the caching.
has allowed_values => (
is => 'rw',
isa => 'ArrayRef',
trigger => sub {
$_[0]->cache_allowed_values($_[1]);
},
default => sub {
my $default = [qw(this that whatever)];
$_[0]->cache_allowed_values($default);
return $default;
},
);
sub cache_allowed_values {
my $self = shift;
my $values = shift;
my %hash = map { $_ => 1 } #$values;
$self->allowed_values_cache(\%hash);
return;
}
The Moose docs are explicit about trigger not getting called when
the default is set, but it gets in the way. I don't like the
duplication there.
Is there a better way to do it?
I was recently faced with this, and after asking on the #moose channel, was told to handle it this way:
Mark cache_allowed_values as a lazy_build, have _build_cache_allowed_values reference the current allowed_values, and put a write-trigger on allowed_values that clears cache_allowed_values.
That way, no matter what order the values are asked for or saved, they'll always be right with the least amount of work.
Example:
has cache_allowed_values => (is => 'ro', lazy_build => 1);
sub _build_cache_allowed_values {
return { map { $_ => 1 } #{shift->allowed_values} };
}
has allowed_values => (
is => 'rw',
trigger => sub { shift->clear_cache_allowed_values },
default => ...,
);
I think you really want allowed_values to be a separate data structure with the efficiency and ordering properties you desire. Since it doesn't look like you care about the ordering, why not:
has 'allowed_values' => (
traits => ['Hash'],
isa => HashRef[Bool],
default => sub { +{} },
handles => {
_add_allowed_value => 'set',
remove_allowed_value => 'delete',
value_is_allowed => 'exists',
allowed_values => 'keys',
},
);
method add_allowed_value(Str $value){
$self->_add_allowed_value( $value, 1 );
}
In general, anything not specific to the class being implemented should probably be implemented elsewhere. Making arrays have faster lookup times is not really the job of whatever class you are writing, so it should be implemented elsewhere, and this class should use that class. (In the simple case, like the hash above, maybe it's OK to ignore this rule. But if it were any more complicated, you would definitely want to factor it out.)
Edit:
If you want the user to think this is a list, how about:
use MooseX::Types::Moose qw(Bool ArrayRef HashRef);
use MooseX::Types -declare => ['ListHash'];
subtype ListHash, as HashRef[Bool];
coerce ListHash, from ArrayRef, via { +{ map { $_ => 1 } #$_ } };
has 'allowed_values' => (
# <same as above>
isa => ListHash,
writer => 'set_allowed_values',
coerce => 1,
);
Now you can set allowed_values like:
my $instance = Class->new( allowed_values => [qw/foo bar/] );
$instance->set_allowed_values([qw/foo bar baz/]);
And access them like:
my #allowed_values = $instance->allowed_values;
... if $instance->value_is_allowed('foo');
And modify them:
$instance->remove_allowed_value('foo');
$instance->add_allowed_value('gorch');
This hides any underlying implementation details from the user.
BTW, is building the hash actually and using it significantly faster than a linear scan over 3 elements?