I'm trying to print the outputs from an API which are in multidimensional format.
use strict;
use warnings;
use Data::Dumper;
my $content={
'school_set' => 'SSET1234',
'result' => [
{
'school_name' => 'school_abc',
'display_value' => 'IL25',
'school_link' => 'example.com',
'status' => 'registerd',
'status_message' => 'only arts',
'school_id' => '58c388d40596191f',
}
],
'school_table' => 'arts_schools'
};
print "school_name is=".$content{result}[0]{school_name};
print "school_status is=".$content{result}[3]{status};
output
Global symbol "%content" requires explicit package name (did you forget to declare "my %content"?) at test8.pl line 20.
Global symbol "%content" requires explicit package name (did you forget to declare "my %content"?) at test8.pl line 21.
I have to print the outputs like below from the result.
school_name = school_abc
school_status = registered
If $content is a hash reference, you need to dereference it first. Use the arrow operator for that:
$content->{result}[0]{school_name}
The syntax without the arrow is only possible for %content.
my %content = ( result => [ { school_name => 'abc' } ] );
print $content{result}[0]{school_name};
If you want to print all the results, you have to loop over the array somehow. For example
#!/usr/bin/perl
use warnings;
use strict;
my $content = {
'result' => [
{
'school_name' => 'school_abc',
'status' => 'registerd',
},
{
'school_name' => 'school_def',
'status' => 'pending',
}
],
};
for my $school (#{ $content->{result} }) {
print "school_name is $school->{school_name}, status is $school->{status}\n";
}
Your data structure assumes an array, perhaps it would be useful to utilize loop output for the data of interest.
The data presented as hash reference and will require de-referencing to loop through an array.
Following code snippet is based on your posted code and demonstrates how desired output can be achieved.
use strict;
use warnings;
use feature 'say';
my $dataset = {
'school_set' => 'SSET1234',
'result' => [
{
'school_name' => 'school_abc',
'display_value' => 'IL25',
'school_link' => 'example.com',
'status' => 'registerd',
'status_message' => 'only arts',
'school_id' => '58c388d40596191f',
}
],
'school_table' => 'arts_schools'
};
for my $item ( #{$dataset->{result}} ) {
say "school_name is = $item->{school_name}\n"
. "school_status is = $item->{status}";
}
exit 0;
Output
school_name is = school_abc
school_status is = registerd
I use an external module (say Foo.pm) that I don't have control over. The way to use it is like below, which works fine:
use Foo ();
my %config = (
MODE => 'NORMAL',
ERROR => \&my_error, # error handling routine
);
Foo::init(%config);
sub my_error {
my ($message) = #_;
...
}
However I'm having trouble to pass in my_error() to the external module when I'm writing in OO style as the first parameter to my_error() is now $self:
package MyPackage;
use Foo ();
sub new {
my $self = bless {
environment => 'TEST',
config => {
MODE => 'NORMAL',
ERROR => \&my_error, # WRONG ??!
},
}, __PACKAGE__;
Foo::init( %{$self->{config}} );
}
sub my_error {
my ($self, $message) = #_;
...
}
How do I solve it? Passing &{ $self->my_error } does not seem to work.
Thanks!
If you need a sub when you don't have one, you need to make one. You can make an anonymous one.
sub { $self->my_error(#_) }
So that means
my $self = bless {
environment => 'TEST',
config => {
MODE => 'NORMAL',
ERROR => sub { $self->my_error(#_) },
},
}, $class;
But there are complications. In your code, $self hasn't been declared yet when you try to capture it. Fix:
my $self = bless({}, $class);
%$self = (
environment => 'TEST',
config => {
MODE => 'NORMAL',
ERROR => sub { $self->my_error(#_) },
},
);
But that creates a memory leak. The sub captures $self, which references a hash that contains a reference to the sub. Fix:
use Scalar::Util qw( weaken );
my $self = bless({}, $class);
{
weaken( my $self = $self );
%$self = (
environment => 'TEST',
config => {
MODE => 'NORMAL',
ERROR => sub { $self->my_error(#_) },
},
);
}
As simbabque points out, the curry::weak module can simplify(?) this a little.
use curry::weak qw( );
my $self = bless({}, $class);
%$self = (
environment => 'TEST',
config => {
MODE => 'NORMAL',
ERROR => $self->curry::weak::my_error(),
},
);
But I think it'll just add confusion.
A good alternative to the final part of ikegami's excellent and detailed answer is to use curry::weak.
use curry::weak;
my $self = bless({}, $class);
%$self = (
environment => 'TEST',
config => {
MODE => 'NORMAL',
ERROR => $self->curry::weak::my_error(),
},
);
mst, the author of curry, gives a reasonably understandble explanation of how that works in this lightning talk.
Puppy meta data gets read in from config file using (General::Config) and creates this hash of hashes
$puppy_hashes = {
puppy_blue => { name => 'charlie', age => 4 },
puppy_red => { name => 'sam', age => 9 },
puppy_yellow => { name => 'jerry', age => 2 },
puppy_green => { name => 'phil', age => 5 },
}
the MotherDogRobot package consumes the puppies hash to birth an array of puppy objects (lol)
package MotherDogRobot;
use Moose;
use Puppy;
use Data::Dumper;
#moose includes warn and strict
sub init_puppy{
my($self,%options) = #_;
my $puppy = Puppy->new( %options );
return ($puppy);
}
sub birth_puppies{
my($self,$puppy_hashes) = #_;
my #keys = keys %{$puppy_hashes};
my #puppies = map { $self->init_puppy( $puppy_hashes->{$_} ) } #keys;
return(#puppies);
}
sub show_me_new_puppies{
my($self,$puppy_hashes) #_;
print Dumper($self->birth_puppies($puppy_hashes));
}
Error odd number of arguments
passing %options to Puppy->new(%options)
no luck birthing puppies -- which means I can't put lasers on their heads =/
UPDATE
I think the problem is that I'm passing a Hash Ref to init_puppy() instead of an array or hash, so when I try to pass %options to the new constructor, it's not getting a proper ( key => value) pair -- hence the odd number of arguments error.
But from this standpoint I've been looking at this code too long I cant figure out how to dereference this properly.
btw this is my official day 22 of using Perl!
you're using empty variables as if they're not empty, that is, you're not doing anything at all
print "hi $_ " for my #foo;
This assumes that the incomplete snippet you've shown is what you're really using
update: Similarly in sub init_puppy, you never initialize my($self,%options)=#_;
#!/usr/bin/perl --
use strict;
use warnings;
Main( #ARGV );
exit( 0 );
sub Main {
my $puppy_hashes = {
puppy_blue => { name => 'charlie', age => 4 },
puppy_red => { name => 'sam', age => 9 },
puppy_yellow => { name => 'jerry', age => 2 },
puppy_green => { name => 'phil', age => 5 },
};
for my $puppy ( MotherDogRobot->birth_puppies($puppy_hashes) ) {
print join ' ', $puppy, $puppy->name, $puppy->age, $puppy->dump, "\n";
}
}
BEGIN {
package Puppy;
BEGIN { $INC{'Puppy.pm'} = __FILE__; }
use Any::Moose;
has 'name' => ( is => 'rw', isa => 'Str' );
has 'age' => ( is => 'rw', isa => 'Int' );
package MotherDogRobot;
BEGIN { $INC{'MotherDogRobot.pm'} = __FILE__; }
use Moose;
use Puppy;
sub init_puppy {
my ( $self, %options ) = #_;
my $puppy = Puppy->new(%options);
return ($puppy);
}
sub birth_puppies {
my ( $self, $puppy_hashes ) = #_;
my #puppies = map { $self->init_puppy( %{$_} ) } values %$puppy_hashes;
return (#puppies);
}
no Moose;
}
The standard Moose constructor will accept both
->new( %{ $puppy_hashes->{$_} } )
and
->new( $puppy_hashes->{$_} )
if $puppy_hashes contains what you say it does, and $_ is an existing key.
Furthermore, Moose will not give the error Error odd number of argments when you pass no arguments. (You're not assigning anything to %config.)
I can't tell which part of what you said is wrong, but what you said doesn't add up.
I am new to object oriented Perl and i have to access member variable of same object in another subrutine of same object. Sample code is here :
use Class::Struct;
struct Breed =>
{
name => '$',
cross => '$',
};
struct Cat =>
[
name => '$',
kittens => '#',
markings => '%',
breed => 'Breed',
breed2 => '$',
];
my $cat = Cat->new( name => 'Socks',
kittens => ['Monica', 'Kenneth'],
markings => { socks=>1, blaze=>"white" },
breed => { name=>'short-hair', cross=>1 },
** //breed2 => sub { return $cat->breed->name;}**
);
print "Once a cat called ", $cat->name, "\n";
**print "(which was a ", $cat->breed->name, ")\n";**
print "had two kittens: ", join(' and ', #{$cat->kittens}), "\n";
But i am not sure how to use that $cat->breed->name in subroutine for breed2 ? Can some one help me with this.
The problem in breed2 is that you are trying to refer to a variable that you haven't defined yet. It looks like it is the same name, but it's not the object you are creating. It's a bit of a chicken-and-egg problem.
I'm not so sure that you want an anonymous subroutine like that in that slot anyway. Are you
just trying to shorten $cat->breed->name to $cat->breed2? You can start with undef in breed2 and change its value right after the constructor since you'll have the reference to the object then. However, even if you put a subroutine there, you have to dereference it:
my $cat = Cat->new( name => 'Socks',
kittens => ['Monica', 'Kenneth'],
markings => { socks=>1, blaze=>"white" },
breed => { name=>'short-hair', cross=>1 },
breed2 => undef,
);
$cat->breed2( sub { $cat->breed->name } );
print "Once a cat called ", $cat->name, "\n";
print "(which was a ", $cat->breed2->(), ")\n";
print "had two kittens: ", join(' and ', #{$cat->kittens}), "\n";
You can't use $cat->breed->name inside the Cat constructor.
But you can define breed2() as a method after the constructor:
sub Cat::breed2 {
my ($self) = #_;
return $self->breed->name;
}
First, I'll start with several comments, then I'll get to the meat of your question.
OO Perl is a bit different than other OO systems. There is a very thin layer of basic support for OO that makes it possible to make your objects do just about anything you want. On the down side, you can make your objects do just about anything you want. Classical OO Perl involves a lot of boilerplate code, as you implement accessors and mutators for each attribute, perhaps add type checking and so forth. This has given rise to a wide variety of tools to automate the production of boilerplate code.
There are three ways that I approach OO Perl: Moose, classical hash based all hand coded, and Class::Struct. Moose is great for systems where you have complex needs, but it has a big impact on app start-up time. If launch time is important for your application, Moose is, for now, out of the question. Class::Struct is a great way to get a lowest common denominator, quick, simple OO app together, on the downside it doesn't support inheritance. This is where hand coded OOP comes in. If Moose or Class::Struct aren't viable options for one reason or another, I fall back on the basics. This strategy has worked well for me. The only change I have felt the need to make over the last few years, is to add Moose to my standard toolkit. It's a welcome addition.
Damian Conway's Object Oriented Perl is an amazing book that clearly explains OOP, how OO Perl works, and how to build objects that can do amazing things. It's a bit dated, but the book still holds up. Any serious student of OO Perl should read this book.
Now, for your question--
It looks to me like breed2 is not an attribute of your object, it is instead a method.
use Class::Struct;
use strict;
use warnings;
struct Breed =>
{
name => '$',
cross => '$',
};
struct Cat =>
[
name => '$',
kittens => '#',
markings => '%',
breed => 'Breed',
];
my $cat = Cat->new( name => 'Socks',
kittens => ['Monica', 'Kenneth'],
markings => { socks=>1, blaze=>"white" },
breed => { name=>'short-hair', cross=>1 },
);
# Delegate to Breed::name
sub Cat::breed2 {
my $self = shift;
my $breed = $self->breed; # Get the breed object
my $name;
eval { $name = $breed->name(#_) };
warn "No breed specified for ".( $self->name )."\n"
unless defined $name;
return $name;
}
print "Once a cat called ", $cat->name, "\n",
"(which was a ", $cat->breed2, ")\n",
"had two kittens: ", join(' and ', #{$cat->kittens}), "\n";
Things get a bit hairier if you want to keep a set of pre-defined breeds, and have breed2 select a breed object by name if no value is set.
This stripped down Cat implementation uses class data to keep track of allowed cat breeds, and
package Cat;
use strict;
use warnings;
use Carp qw( croak );
my %breeds = map { $_->{name}, Breed->new( %$_ ) } (
{ name=>'short-hair', cross=>1 },
{ name=>'long-hair', cross=>1 },
{ name=>'siamese', cross=>0 },
);
sub new {
my $class = shift;
my %args = #_;
my $self = {};
bless $self, $class;
for my $arg ( keys %args ) {
$self->$arg( $args{$arg} ) if $self->can($arg);
}
return $self;
}
sub breed {
my $self = shift;
if( #_ ) {
my $v = shift;
croak "Illegal cat breed" unless eval {$v->isa( 'Breed' ) };
$self->{breed} = $v;
}
return $self->{breed};
}
sub breed2 {
my $self = shift;
my #breed_args;
if( #_ ) {
my $v = shift;
croak "$v is not a supported breed\n"
unless exists $breeds{$v};
#breed_args = ( $breeds{$v} );
}
my $breed = $self->breed(#breed_args);
return unless $breed;
return $breed->name;
}
Now, lets look at a Moose solution that uses all sorts of advanced goodies like type coercion and overloading:
BEGIN {
package Breed;
use Moose;
has 'name' => ( isa => 'Str', is => 'ro', required => 1 );
has 'cross' => ( isa => 'Bool', is => 'ro', required => 1 );
use overload '""' => \&_overload_string;
sub _overload_string {
my $self = shift;
return $self->name;
}
__PACKAGE__->meta->make_immutable;
no Moose;
1;
}
BEGIN {
package Cat;
use Moose;
use Moose::Util::TypeConstraints;
use Carp;
subtype 'MyTypes::CatBreed' => as class_type('Breed');
coerce 'MyTypes::CatBreed' =>
from 'Str'
=> via { Cat->supported_breed_by_name( $_ ) };
has 'name' => ( isa => 'Str', is => 'rw', required => 1 );
has 'kittens' => (
traits => ['Array'],
is => 'ro',
isa => 'ArrayRef[Str]',
default => sub{ [] },
handles => {
all_kittens => 'elements',
add_kittens => 'push',
get_kitten => 'get',
count_kittens => 'count',
has_kittens => 'count',
},
);
has 'markings' => (
traits => ['Hash'],
is => 'ro',
isa => 'HashRef[Str]',
default => sub{ {} },
handles => {
set_marking => 'set',
get_marking => 'get',
has_marking => 'exists',
all_markings => 'keys',
delete_marking => 'delete',
},
);
has 'breed' => (
isa => 'MyTypes::CatBreed',
is => 'ro',
coerce => 1,
);
my %breeds;
sub supported_breed_by_name {
my $class = shift;
my $name = shift;
croak 'No breed name specified'
unless defined $name and length $name;
return $breeds{$name};
}
sub add_breed {
my $class = shift;
my $breed = shift;
croak 'No breed specified'
unless eval { $breed->isa('Breed') };
croak 'Breed already exists'
if exists $breeds{$breed};
$breeds{$breed} = $breed;
return $class;
}
sub delete_breed {
my $class = shift;
my $name = shift;
croak 'No breed name specified'
unless defined $name and length $name;
return delete $breeds{$name};
}
__PACKAGE__->meta->make_immutable;
no Moose;
1;
}
# Set up the supported breeds
Cat->add_breed($_) for map Breed->new( %$_ ), (
{ name=>'short-hair', cross=>1 },
{ name=>'long-hair', cross=>1 },
{ name=>'siamese', cross=>0 },
);
# Make a cat
my $cat = Cat->new( name => 'Socks',
kittens => ['Monica', 'Kenneth'],
markings => { socks=>1, blaze=>"white" },
breed => 'short-hair',
);
print
"Once a cat called ", $cat->name, "\n",
"(which was a ", $cat->breed, ")\n",
"had ", , " kittens: ", join(' and ', #{$cat->kittens}), "\n";
Don't use Class::Struct use Moose.
package Breed;
use Moose;
has 'name' => ( isa => 'Str', is => 'ro', required => 1 );
has 'cross' => ( isa => 'Bool', is => 'ro' );
package Cat;
use Moose;
has 'name' => ( isa => 'Str', is => 'ro', required => 1 );
has 'kittens' => ( isa => 'ArrayRef[Cat]', is => 'ro' );
has 'markings' => ( isa => 'HashRef', is => 'ro', default => sub { +{} } );
has 'breeds' => ( isa => 'ArrayRef[Breed]', is => 'ro' );
package main;
use Modern::Perl;
my $cat = Cat->new({
name => 'Socks',
, kittens => [ Cat->new({name=>'Monica'}), Cat->new({name=>'Kenneth'}) ]
, markings => { socks=>1, blaze=>"white" }
, breeds => [ Breed->new({ name=>'short-hair', cross => 1 }) ]
});
say "Once a cat called ", $cat->name;
say "Which was a:";
say "\t".$_->name for #{$cat->breeds};
say "had kittens:";
say "\t".$_->name for #{$cat->kittens};
In this scheme, a cat can have any number of Breeds, and a Cat can have any number of kittens which are also objects of Cat.
update to solve your problem specifically
You can make it implicit in the constructor the second breed is the first if it isn't supplied.
package Cat;
sub BUILD {
my $self = shift;
$self->breeds->[1] = $self->breeds->[0]
if $self->breeds->[0] && ! $self->breeds->[1]
}
You can pass in a token that identifies it as such, in the constructor (this should be easy but I can add an example if you want)
You can make Cat understand that if there is only one breed then both of the parents are the same
package Cat;
sub is_pure_bred { length #{$_[0]->breeds} == 1 ? 1 : 0 }
You can make ignore the breed of the cat, by setting it to undef, and determine the breed by that of the parents. This is because your breed is always a function of your lineage anyway. You can constraint this in a Moose trigger, the cat either requires two cat parents, or it requires a breed.
footnote Moose objects serialize fairly nice with XXX too:
... use XXX; YYY $cat;
--- !!perl/hash:Cat
breeds:
- !!perl/hash:Breed
cross: 1
name: short-hair
kittens:
- !!perl/hash:Cat
markings: {}
name: Monica
- !!perl/hash:Cat
markings: {}
name: Kenneth
markings:
blaze: white
socks: 1
name: Socks
...
You can fix this in a few ways, here are two of them:
use warnings;
use strict;
sub say {print #_, "\n"}
use Class::Struct;
struct Breed =>
{
name => '$',
cross => '$',
};
struct Cat =>
[
name => '$',
kittens => '#',
markings => '%',
breed => 'Breed',
breed2 => '$',
];
sub Cat::breed_name {shift->breed->name} #create a new accessor method
my $cat; # or declare $cat first
$cat = Cat->new( name => 'Socks',
kittens => ['Monica', 'Kenneth'],
markings => { socks=>1, blaze=>"white" },
breed => { name=>'short-hair', cross=>1 },
breed2 => sub { return $cat->breed->name;},
# this is now ok, but a bit awkward to call
);
print "Once a cat called ", $cat->name, "\n";
print "(which was a ", $cat->breed2->(), ")\n"; #returns then calls code ref
print "(which was a ", $cat->breed_name, ")\n"; #new accessor method
print "had two kittens: ", join(' and ', #{$cat->kittens}), "\n";
The reason your closure did not work right is because you can not close over a variable that is defined in the current statement. When the sub {...} tried to close around $cat it couldn't because it was not in scope yet. The solution is simply to predeclare the variable.
However, it doesn't seem like Class::Struct lets you install methods that way cleanly. Instead, adding a new accessor method to the Cat:: package lets you call the method as you would expect.