Perl: how to increment a Class::Struct field? - perl

How do I increment a field in a Class::Struct object?
For now I am stuck with
use Class::Struct foo => [
counter => '$',
];
my $bar = foo->new(counter => 5);
$bar->counter($bar->counter()+1);
I wonder if there is something more expressive than the last line (the obvious $bar->counter++ results in Can't modify non-lvalue subroutine call).
EDIT: of course, I am not interested in $bar->[0]++ et al - what if I add a field before counter? I don't want to have to hunt my code for all such "bugs-in-waiting".

You can add an increment method to foo:
#!/usr/bin/env perl
package foo;
use strict; use warnings;
sub increment_counter {
my $self = shift;
my $val = $self->counter + 1;
$self->counter($val);
return $val;
}
package main;
use 5.012;
use strict;
use warnings;
use Class::Struct foo => [
counter => '$',
];
my $bar = foo->new(counter => 5);
$bar->increment_counter;
say $bar->counter;
__END__

Alternatively, try doing this :
use strict; use warnings;
use Class::Struct foo => [
counter => '$',
];
my $bar = foo->new(counter => 5);
print ++$bar->[0];
or using a SCALAR ref (no need to hard-code the "path" like the previous snippet) :
use strict; use warnings;
$\ = "\n";
use Class::Struct foo => [
counter => '*$',
];
my $bar = foo->new(counter => 5);
print ++${ $bar->counter };

Related

Transform a string to a hash

Is there any way/lib to transform a string to a hash ?
I have such string
{"hello"=>["world","perl"],"foo"=>"bar"}
and I would like to access the different values like if it was a hash
Thank you
Convert the string with Perl data structure to a string with JSON data structure by substitute => with : and decode it with JSON package.
#!/usr/bin/env perl
use warnings FATAL => 'all';
use strict;
use Data::Dumper;
use JSON qw(decode_json); # use JSON::XS for more performance
my $string = '{"hello"=>["world","perl"],"foo"=>"bar"}';
$string =~ s/"=>/":/g;
print Dumper(decode_json($string));
Output
$VAR1 = {
'hello' => [
'world',
'perl'
],
'foo' => 'bar'
};
Using eval():
#!/usr/bin/env perl
use strict;
use Data::Dumper;
my $string = qw( {"hello"=>["world","perl"],"foo"=>"bar"} );
print "String: $string\n";
my $hash = eval($string);
print "Hash: ", Dumper($hash), "\n";
Output
String: {"hello"=>["world","perl"],"foo"=>"bar"}
Hash: $VAR1 = {
'foo' => 'bar',
'hello' => [
'world',
'perl'
]
};
Using reval() and Safe if you are at all concerned about the input:
#!/usr/bin/env perl
use strict;
use Safe;
use Data::Dumper;
my $string = qw( {"hello"=>["world","perl"],"foo"=>"bar"} );
print "String: $string\n";
my $compartment = new Safe;
my $hash = $compartment->reval($string);
print $# ? "reval error: $#" : ("Hash: ", Dumper($hash)), "\n";
If you don't mind me plugging one of my own modules: Config::Perl uses PPI to parse strings like that, with no need for eval:
use warnings;
use strict;
use Data::Dumper; # Debug
use Config::Perl;
my $str = q( {"hello"=>["world","perl"],"foo"=>"bar"} );
my $data = Config::Perl->new->parse_or_die(\$str)->{_}[0];
print Dumper($data); # Debug
Output:
$VAR1 = {
'hello' => [
'world',
'perl'
],
'foo' => 'bar'
};
(The above code assumes that you've only got a single hash ref in your data, if you've got variations, you'll have to look at the whole data structure returned by parse_or_die.)

How to insert directly into a stash?

I'm trying to write a pragma for defining a bunch of constants, like this:
use many::constant
one_constant => 1,
other_constant => 2,
;
The relevant portion of my import looks like
package many::constant;
use strict;
use warnings;
sub import {
my ($class, #constants) = #_;
my $caller_nms = do {
no strict 'refs';
\%{caller.'::'}
};
while (my ($name, $value) = splice #constants, 0, 2) {
*{$caller_nms->{$name}} = sub () { $value };
}
}
I expect the $caller_nms stash to auto-vivify when assigned to like this, but I'm getting an error "Can't use an undefined value as a symbol reference". Is there a way to get this assignment to work like I expect? I ended up changing the assignment to:
my $caller_glob = do {
no strict 'refs';
\*{caller.'::'.$name}
};
*$caller_glob = sub () { $value };
but that feels less elegant to me.
Just use use constant as a baseline and actually examine the source: constant.pm.
That's essentially what it does as well:
my $pkg = caller;
# ...
{
no strict 'refs';
my $full_name = "${pkg}::$name";
# ...
my #list = #_;
*$full_name = sub () { #list };
}
Also, note that the constant module has this feature: constant #Defining multiple constants at once
use strict;
use warnings;
use constant {
one_constant => 1,
other_constant => 2,
};
print one_constant, ' - ', other_constant, "\n";
Outputs:
1 - 2

Perl Storable retrieve an array of Moose Objects

I tried to store an array of Moose Objects to YAML or JSON.
The saving works very well, but when I try to restore my Objects, they're empty:
$VAR1 = bless({}, 'Note');
$VAR2 = bless({}, 'Note');
Here is my code:
Note.pm:
package Note;
use strict;
use warnings;
use Moose;
use MooseX::Storage;
with Storage('format' => 'JSON', 'io' => 'File');
has 'message' =>(is=> 'rw', isa =>'Str');
1;
testNote.pl:
use strict;
use warnings;
use utf8;
use feature 'say';
use Note;
use Storable;
use MooseX::Storage;
use Data::Dumper;
use JSON;
my #container=();
my $obj = Note->new;
$obj->message("firstmessage");
say $obj->message;
push(#container,$obj);
my $obj2 = Note->new;
$obj2->message("secondmessage");
push(#container,$obj2);
my #output=();
for my $counter (0 .. $#container){
push(#output,$container[$counter]->pack());
}
say "Output packed strings:" ;
for my $counter(0 .. $#output){
say $output[$counter];
}
store \#output, 'saveNotes';
my #Notes=();
my #fin=#{retrieve('saveNotes') };
say "After reading file:";
#Arr=();
for my $counter (0 .. $#fin){
push(#Arr,Note->unpack($fin[$counter]));
}
say Dumper(#Arr);
Hope someone could help :)

How can I create a nested hash as a constant in Perl?

I want to do, in Perl, the equivalent of the following Ruby code:
class Foo
MY_CONST = {
'foo' => 'bar',
'baz' => {
'innerbar' => 'bleh'
},
}
def some_method
a = MY_CONST[ 'foo' ]
end
end
# In some other file which uses Foo...
b = Foo::MY_CONST[ 'baz' ][ 'innerbar' ]
That is, I just want to declare a constant, nested hash structure for use both in the class and outside. How to?
You can also do this entirely with builtins:
package Foo;
use constant MY_CONST =>
{
'foo' => 'bar',
'baz' => {
'innerbar' => 'bleh',
},
};
sub some_method
{
# presumably $a is defined somewhere else...
# or perhaps you mean to dereference a parameter passed in?
# in that case, use ${$_[0]} = MY_CONST->{foo} and call some_method(\$var);
$a = MY_CONST->{foo};
}
package Main; # or any other namespace that isn't Foo...
# ...
my $b = Foo->MY_CONST->{baz}{innerbar};
You can use the Hash::Util module to lock and unlock a hash (keys, values, or both).
package Foo;
use Hash::Util;
our %MY_CONST = (
foo => 'bar',
baz => {
innerbar => 'bleh',
}
);
Hash::Util::lock_hash_recurse(%MY_CONST);
Then in some other file:
use Foo;
my $b = $Foo::MY_CONST{baz}{innerbar};
See Readonly:
#!/usr/bin/perl
package Foo;
use strict;
use warnings;
use Readonly;
Readonly::Hash our %h => (
a => { b => 1 }
);
package main;
use strict;
use warnings;
print $Foo::h{a}->{b}, "\n";
$h{a}->{b} = 2;
Output:
C:\Temp> t
1
Modification of a read-only value attempted at C:\Temp\t.pl line 21
Here is a guide to hashes in perl. Hash of Hashes

Can I access a static method in a dynamically specified class in Perl?

Is it possible to dynamically specify a class in Perl and access a static method in that class? This does not work, but illustrates what I'd like to do:
use Test::Class1;
my $class = 'Test::Class1';
$class::static_method();
I know I can do this:
$class->static_method();
and ignore the class name passed to static_method, but I wonder if there's a better way.
Yup! The way to do it with strictures is to use can.
package Foo::Bar;
use strict;
use warnings;
sub baz
{
return "Passed in '#_' and ran baz!";
}
package main;
use strict;
use warnings;
my $class = 'Foo::Bar';
if (my $method = $class->can('baz'))
{
print "yup it can, and it ";
print $method->();
}
else
{
print "No it can't!";
}
can returns a reference to the method, undef / false. You then just have to call the method with the dereferene syntax.
It gives:
> perl foobar.pl
yup it can, and it Passed in '' and ran baz!
As always with Perl, there is more than one way to do it.
use strict;
use warnings;
{
package Test::Class;
sub static_method{ print join(' ', #_), "\n" }
}
You can use the special %:: variable to access the symbol table.
my $class = 'Test::Class';
my #depth = split '::', $class;
my $ref = \%::;
$ref = $glob->{$_.'::'} for #depth; # $::{'Test::'}{'Class::'}
$code = $glob->{'static_method'};
$code->('Hello','World');
You could just simply use a symbolic reference;
no strict 'refs';
my $code = &{"${class}::static_method"};
# or
my $code = *{"${class}::static_method"}{CODE};
$code->('Hello','World');
You could also use a string eval.
eval "${class}::static_method('Hello','World')";
The simplest in this case, would be to use UNIVERSAL::can.
$code = $class->can('static_method');
$code->('Hello','World');
I am unaware of a particularly nice way of doing this, but there are some less nice ways, such as this program:
#!/usr/bin/perl -w
use strict;
package Test::Class1;
sub static_method {
print join(", ", #_) . "\n";
}
package main;
my $class = "Test::Class1";
{
no strict "refs";
&{${class}. "::static_method"}(1, 2, 3);
}
I have included a $class variable, as that was how you asked the question, and it illustrates how the class name can be chosen at runtime, but if you know the class beforehand, you could just as easily call &{"Test::Class1::static_method"}(1, 2, 3);
Note that you have to switch off strict "refs" if you have it on.
There are three main ways to call a static function:
$object->static_method()
Classname->static_method()
Classname::static_method()
You could define your function like this:
# callable as $object->static_method() or Classname->static_method()
sub static_method
{
my $class = shift; # ignore; not needed
# ...
}
or like this, which works in all three calling scenarios, and doesn't incur any overhead on the caller's side like Robert P's solution does:
use UNIVERSAL qw(isa);
sub static_method
{
my $class = shift if $_[0] and isa($_[0], __PACKAGE__);
# ...
}
You can use string eval:
#!/usr/bin/perl
use strict; use warnings;
package Test::Class1;
sub static_method {
print join(", ", #_) . "\n";
}
package main;
my $class = 'Test::Class1';
my $static_method = 'static_method';
my $subref = eval q{ \&{ "${class}::${static_method}" } };
$subref->(1, 2, 3);
Output:
C:\Temp> z
1, 2, 3
Benchmarks:
#!/usr/bin/perl
use strict; use warnings;
package Test::Class1;
sub static_method { "#_" }
package main;
use strict; use warnings;
use Benchmark qw( cmpthese );
my $class = 'Test::Class1';
my $static_method = 'static_method';
cmpthese -1, {
'can' => sub { my $r = $class->can($static_method); $r->(1, 2, 3) },
'eval' => sub {
my $r = eval q/ \&{ "${class}::${static_method}" } /;
$r->(1, 2, 3);
},
'nostrict' => sub {
no strict "refs";
my $r = \&{ "${class}::static_method" };
$r->(1, 2, 3);
}
};
Output:
Rate eval can nostrict
eval 12775/s -- -94% -95%
can 206355/s 1515% -- -15%
nostrict 241889/s 1793% 17% --