Why am I getting a "Odd number of elements in anonymous hash" warning in Perl? - perl

Help, I'm trying to create a new post in my wordpress blog with custom fields using the following perl script using metaweblogAPI over XMLRPC, but there seems to be an issue with the custom fields. Only the second custom field (width) ever seems to get posted. Can't get the "height" to publish properly. When I add another field, I get the "Odd number of elements in anonymous hash" error. This has got to be something simple - would someone kindly sanity check my syntax? Thanks.
#!/usr/bin/perl -w
use strict;
use RPC::XML::Client;
use Data::Dumper;
my $cli=RPC::XML::Client->new('http://www.sitename.com/wp/xmlrpc.php');
my $appkey="perl"; # doesn't matter
my $blogid=1; # doesn't matter (except blogfarm)
my $username="Jim";
my $passwd='_____';
my $text=<<'END';
This is the post content...
You can also include html tags...
See you!
END
my $publish=0; # set to 1 to publish, 0 to put post in drafts
my $resp=$cli->send_request('metaWeblog.newPost',
$blogid,
$username,
$passwd,
{
'title' => "this is doodoo",
'description' => $text,
'custom_fields' => {
{ "key" => "height", "value" => 500 },
{ "key" => "width", "value" => 750 }
},
},
$publish);
exit 0;

While techically valid syntax, it's not doing what you think.
'custom_fields' => {
{ "key" => "height", "value" => 500 },
{ "key" => "width", "value" => 750 }
},
is roughly equivalent to something like:
'custom_fields' => {
'HASH(0x881a168)' => { "key" => "width", "value" => 750 }
},
which is certainly not what you want. (The 0x881a168 part will vary; it's actually the address where the hashref is stored.)
I'm not sure what the correct syntax for custom fields is. You can try
'custom_fields' => [
{ "key" => "height", "value" => 500 },
{ "key" => "width", "value" => 750 }
],
which will set custom_fields to an array of hashes. But that may not be right. It depends on what send_request expects.

Related

What difference does curly brace make in accessing an object

Not even sure if I used the right terminology..
This was the passed data.
$bug = bless({
'bug_id' => '25252',
'flag_types' => [
bless({
'name' => 'name1',
'flags' => [bless({
'id' => 488052,
}, 'Bugzilla::Flag')],
}, 'Bugzilla::FlagType'),
bless({
'name' => 'name2',
'flags' => [bless({
'id' => 488053,
}, 'Bugzilla::Flag')],
}, 'Bugzilla::FlagType'),
],
}, 'Bugzilla::Bug');
Why can I reference name with or without curly braces, but not the flags?
$_->flags gives error
Can't locate object method "flags"
my #isCrash = map {
print Dumper($_->name);
print Dumper($_->{name});
print Dumper($_->flags); # errors
print Dumper($_->{flags};
} #{ $bug->{flag_types} };
I get that flags is not a method, but why is it that I don't get such an error for name?
Your array contains objects of type Bugzilla::FlagType, they have a method name that "Returns the name of the flagtype".

How do I override compare_fields when using Rose::HTML::Form

So I'm trying use Rose::HTML::Form and I want my fields to appear based on 'rank' rather than by name (the default) .
I've written a comparator subroutine:
sub _order_by_rank {
my ($self, $one, $two) = #_;
return $one->rank <=> $two->rank;
};
and referenced it in my form constructor:
Rose::HTML::Form->new(method => 'post', compare_fields => \&_order_by_rank);
But I am then left with:
Can't call method "name" on unblessed reference at /usr/lib/perl5/site_perl/5.8.8/Rose/HTML/Form/Field/Collection.pm line 405.
It seems to call the comparator before I've added anything.
After constructing the form object, I add some fields and then call init_fields:
$form->add_fields(
id => { type => 'hidden', value => "", rank => 0 },
number => { type => 'int', size => 4, required => 1, label => 'Plant Number', rank => 1 },
name => { type => 'text', size => 25, required => 1, label => 'Plant Name', rank => 2 },
...
);
$form->init_fields;
According to the documentation this is something people usually do. What it doesn't explain is how to do it.
Hopefully someone can explain this to me before I have to buy a new keyboard :)
From the documentation it looks as though, rather than passing in a subroutine reference, you need to subclass Rose::HTML::Form and override the compare_fields method.
The default comparison method is Rose::HTML::Form::compare_fields. You have to create subclasses if you want different sorting methods for different forms.
It would help me to explain further if you showed your full code.

Tie Reading and Writing Perl Config Files

I'm using the PerlMonk example I found on:
Reading and Writing Perl Config Files
Configuration.pl:
%CFG = (
'servers' => {
'SRV1' => {
'IP' => 99.32.4.0,
'user' => 'aname',
'pswd' => 'p4ssw0rd',
'status' => 'unavailable'
},
'SRV2' => {
'IP' => 129.99.10.5
'user' => 'guest',
'pswd' => 'guest'
'status' => 'unavailable'
}
},
'timeout' => 60,
'log' => {
'file' => '/var/log/my_log.log',
'level' => 'warn',
},
'temp' => 'remove me'
);
It is working great, but the only issue is when reading and writing the HASH like configuration is being 'out of order'.
Is there a way to keep it TIED?
This important since the configuration file will be also edited manually, so I want the keys and values in the same order.
You could tie config variable before using it, so later hash keys will stay in same order as before,
use strict;
use warnings;
use Tie::IxHash;
tie my %CFG, 'Tie::IxHash';
%CFG = (
'servers' => {
'SRV1' => {
'IP' => '99.32.4.0',
'user' => 'aname',
'pswd' => 'p4ssw0rd',
'status' => 'unavailable'
},
'SRV2' => {
'IP' => '129.99.10.5',
'user' => 'guest',
'pswd' => 'guest',
'status' => 'unavailable'
}
},
'timeout' => 60,
'log' => {
'file' => '/var/log/my_log.log',
'level' => 'warn',
},
'temp' => 'remove me'
);
use Data::Dumper;
print Dumper \%CFG;
If you use JSON then you have the advantage that your software is safe from a malicious attack (or perhaps accidental corruption). JSON also has a simpler syntax than Perl data structures, and it is easier to recover from syntax errors.
Setting the canonical option will create the data with the keys in sorted order, and so generate the same output for the same Perl data every time. If you need the data in a specific order other than alphabetical then you can use the Tie::IxHash module as #mpapec describes in his answer.
Alternatively you can use the sort_by method from the Pure Perl version of the module that lets you pass a collation subroutine. That would let you prescribe the order of your keys, and could be as simple as using a hash that relates all the possible key values with a numerical sort order.
This program uses the sort_by method to reconstruct the JSON in the same order as the keys appear in your original hash. That is unlikely to be the order you want, but the mechanism is there. It works by looking up each key in a hash table to determine how they should be ordered. Any keys (like SVR1 and SVR2 here) that don't appear in the hash are sorted in alphabetical order by default.
use strict;
use warnings;
use JSON::PP ();
my %CFG = (
'servers' => {
'SRV1' => {
'IP' => '99.32.4.0',
'user' => 'aname',
'pswd' => 'p4ssw0rd',
'status' => 'unavailable'
},
'SRV2' => {
'IP' => '129.99.10.5',
'user' => 'guest',
'pswd' => 'guest',
'status' => 'unavailable'
}
},
'timeout' => 60,
'log' => {
'file' => '/var/log/my_log.log',
'level' => 'warn',
},
'temp' => 'remove me'
);
my %sort_order;
my $n = 0;
$sort_order{$_} = ++$n for qw/ servers timeout log temp /;
$sort_order{$_} = ++$n for qw/ IP user pswd status /;
$sort_order{$_} = ++$n for qw/ file level /;
my $json = JSON::PP->new->pretty->sort_by(\&json_sort);
print $json->encode(\%CFG);
sub json_sort {
my ($aa, $bb) = map $sort_order{$_}, $JSON::PP::a, $JSON::PP::b;
$aa and $bb and $aa <=> $bb or $JSON::PP::a cmp $JSON::PP::b;
}
generates this output
{
"servers" : {
"SRV1" : {
"IP" : "99.32.4.0",
"user" : "aname",
"pswd" : "p4ssw0rd",
"status" : "unavailable"
},
"SRV2" : {
"IP" : "129.99.10.5",
"user" : "guest",
"pswd" : "guest",
"status" : "unavailable"
}
},
"timeout" : 60,
"log" : {
"file" : "/var/log/my_log.log",
"level" : "warn"
},
"temp" : "remove me"
}
which can simply be saved to a file and similarly restored.

HTML-Template - Array of Hash

I have two Array of Hashes: The first contains values for a current time interval and the second contains values for a previous time interval.
#AoHcurrent=
( { node => "ABC",
link => "DEF",
time => "10:00",
value => "100",
},
{
node => "FGH",
link => "IJK",
time => "10:00",
value => "200",
},
);
#AoHprevious=
( { node => "ABC",
link => "DEF",
time => "09:45",
value => "10",
},
{ node => "FGH",
link => "IJK",
time => "09:45",
value => "50",
},
);
I want to now use HTML-Template to present this data. Something like :
NODE LINK VALUE
---------------------
ABC DEF 100(10)
FGH IJK 200 (50)
the values in brackets represent the previous value.
my %html_template_parameters =
( AOHCURRENT => \#AoHcurrent,
AOHPREVIOUS => \#AoHprevious, );
my $html_template=qq{Report.tmpl};
my $html_output=qq{Report.html};
htmlReport($html_template,$html_output,\%html_template_parameters);
where htmlReport is a function that generates the report
I require guidance on defining the Report.tmpl file.
Thanks you in advance
see also http://www.perlmonks.org/?node_id=972954
I gave an example there how this can be solved with HTML::Template::Compiled.
Basically you would navigate through the parameter stash like this:
[%= expr=".AOHPREVIOUS[__index__]{'value'}" %]
or with the classic syntax:
<TMPL_VAR expr=".AOHPREVIOUS[__index__]{'value'}" >
You can't do that with 2 separate lists just with HTML::Template. And trying to do it with HTML::Template::Expr would be a nightmare to maintain. Try collapsing them into a single list where the hash data is merged.

Perl - Array of Objects

Noob question here.
I'm sure the answer will be create objects, and store them in an array, but I want to see if there is an easier way.
In JSON notation I can create an array of objects like so:
[
{ width : 100, height : 50 },
{ width : 90, height : 30 },
{ width : 30, height : 10 }
]
Nice and simple. No arguing that.
I know Perl is not JS, but is there an easier way to duplicate an array of objects, then to create a new "class", new the objects, and push them in an array?
I guess what would make this possible is an object literal type notation that JS provides.
Or, is there another way to store two values, like above? I guess I could just have two arrays, each with scalar values, but that seems ugly...but much easier than creating a separate class, and all that crap. If I were writing Java or something, then no problem, but I don't want to be bothered with all that when I'm just writing a small script.
Here's a start. Each element of the #list array is a reference to a hash with keys "width" and "height".
#!/usr/bin/perl
use strict;
use warnings;
my #list = (
{ width => 100, height => 50 },
{ width => 90, height => 30 },
{ width => 30, height => 10 }
);
foreach my $elem (#list) {
print "width=$elem->{width}, height=$elem->{height}\n";
}
an Array of hashes would do it, something like this
my #file_attachments = (
{file => 'test1.zip', price => '10.00', desc => 'the 1st test'},
{file => 'test2.zip', price => '12.00', desc => 'the 2nd test'},
{file => 'test3.zip', price => '13.00', desc => 'the 3rd test'},
{file => 'test4.zip', price => '14.00', desc => 'the 4th test'}
);
then access it like this
$file_attachments[0]{'file'}
for more information check out this link http://htmlfixit.com/cgi-tutes/tutorial_Perl_Primer_013_Advanced_data_constructs_An_array_of_hashes.php
Pretty much the same way you do it in JSON, in fact, use the JSON and Data::Dumper modules to produce output from your JSON that you could use in your Perl code:
use strict;
use warnings;
use JSON;
use Data::Dumper;
# correct key to "key"
my $json = <<'EOJSON';
[
{ "width" : 100, "height" : 50 },
{ "width" : 90, "height" : 30 },
{ "width" : 30, "height" : 10 }
]
EOJSON
my $data = decode_json($json);
print Data::Dumper->Dump([$data], ['*data']);
which outputs
#data = (
{
'width' => 100,
'height' => 50
},
{
'width' => 90,
'height' => 30
},
{
'width' => 30,
'height' => 10
}
);
and all that is missing is the my