Using perl REST::Application to parse url query string - perl

I'm using perl's REST::Application to generate html dynamically on my server with cgi script that looks somewhat like:
my $url_map = {
qr|^/?$| => {
GET => \&get_help,
},
qr|^/builders/?$| => {
GET => \&get_builders,
},
};
I would like to add support for query strings. For example, if the url would be www.example.com/query?param1=a&param2=b I would like to be able to call a procedure with those values as parameters. However, If only one of the parameters is given, it should be supported as well.

Possible solution could be using HTML::Entities and CGI:
Add relevant modules:
use CGI;
use HTML::Entities;
Add prpoer line to $url_map:
qr|^/query(.*)| => {
GET => \&get_query,
}
And implement:
sub get_uploads {
$rest->header(-type => "text/plain, charset=utf8");
my $cgi = CGI->new;
my %params = map { $_ => HTML::Entities::encode(join("; ", split("\0", $cgi->Vars->{$_}))) } $cgi->param;
.
.
.
}
At this stage %params will hold the query string.
For example, if I used the url in the question, %params would be:
$VAR1 = {
'param1' => 'a',
'param2' => 'b'
};

Related

How to print an element from an array which is inside the hash in perl

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

XML::Simple not grabbing individual XML nodes

I'm using the MediaWiki API to get search results. I simply want to grab the URL to the first result, the XML element marked 'Url'. There will eventually be other things I will want to do with the XML, but I suppose in getting an answer for this I will realize what I'm doing wrong and be able to do the other stuff. Here's the page I'm working with.
require HTTP::Request;
require LWP::UserAgent;
require XML::Simple;
my $url = URI->new("http://en.wikipedia.org/w/api.php?action=opensearch&search=rooney&limit=10&namespace=0&format=xml");
my $request = HTTP::Request->new(GET => $url);
my $ua = LWP::UserAgent->new;
my $response = $ua->request($request);
my $xml = XML::Simple->new();
my $data = $xml->XMLin($response->content);
Everything up to here seems to work fine. My HTTP request goes through alright (if I just print $response->content it returns the XML content fine and if I print $data, I am told that it is a hash.
In attempt to get the 'Url' element, I have tried numerous approaches based on the searching I've done. A few below:
print $data->{'Url'};
print $data->{Url};
print $data{Url}
Pro tip: use Data::Dumper to look inside your data structure.
use Data::Dumper;
print Dumper($data);
You'll get something like this ...
$VAR1 = {
'xmlns' => 'http://opensearch.org/searchsuggest2',
'Section' => {
'Item' => [
{
'Url' => {
'content' => 'http://en.wikipedia.org/wiki/Rooney',
'xml:space' => 'preserve'
},
'Description' => {
'content' => 'Rooney may refer to:',
'xml:space' => 'preserve'
},
'Text' => {
'content' => 'Rooney',
'xml:space' => 'preserve'
}
},
... much much more ...
from which you can deduce that the route to your desired data is through
$data->{Section}{Item}[0]{Url}{content}
You should also look into using something like XML::XPath, which makes it much easier to conduct this kind of search.

Can $self be passed to a XML::Twig handler?

I'm trying to parse different XML that is returned depending on the command given in a class method... but I think I'm getting a bit deep here.
I'd like to be able to use other methods and access attributes of the instance from WITHIN the XML::Twig handler.
This is an instance method I defined in a Moose object in order to get and parse XML using XML::Twig:
sub get_xmls {
my $self = shift;
my $sehost = shift;
my $symm = shift;
$self->log->info("Getting XMLs for $sehost - $symm");
my %SYMMAPI_CALLS = (
"Config" => {
'command' => "symcfg list -sid ${symm} -v",
'handlers' => {
'SymCLI_ML/Symmetrix' => $self->can('process_symm_info')
},
'dbtable' => "inv_emc_array"
},
"Pools" => {
'command' => "symcfg -sid ${symm} list -pool -thin",
'handlers' => {
'DevicePool' => $self->can('process_symm_pool')
},
'dbtable' => "inv_emc_pool"
}
);
foreach my $key (sort(keys %SYMMAPI_CALLS)) {
my $xmldir = $self->xmlDir;
my $table = $SYMMAPI_CALLS{$key}{'tbl'};
my $handlers = $SYMMAPI_CALLS{$key}{'handlers'};
my $command = $SYMMAPI_CALLS{$key}{'command'};
my $xmlfile = qq(${xmldir}/${sehost}/${key}_${symm}.xml);
$self->log->info("\t$key");
if(!-d qq(${xmldir}/${sehost})) {
mkdir(qq(${xmldir}/${sehost}))
or $self->log->logdie("Cant make dir ${xmldir}/${sehost}: $!");
}
$self->_save_symxml($command, $xmlfile);
$self->twig(new XML::Twig( twig_handlers => $handlers ));
$self->log->info("Parsing $xmlfile...");
$self->twig->parsefile($xmlfile);
$self->log->info("\t\t...finished.");
die "Only running the first config case for now...";
}
}
And the definition of one of the handlers (not really doing anything right now while I figure out how to do this correctly:
sub process_symm_info {
my ($twig, $symminfo) = #_;
print Dumper($symminfo);
}
This works just fine, but what I'd like is for the process_symm_info method to have access to $self and all the methods and attributes $self brings along with it. Is that possible? Am I doing this all wrong? Since I can specify specific parts of the XML it'd be nice to be able to do other things with that data from within the handler.
This is sort of my first venture into Perl Moose (if you couldn't already tell).
Currently, you have
handlers => {
DevicePool => $self->can('process_symm_pool'),
},
Change it to
handlers => {
DevicePool => sub { $self->process_symm_pool(#_) },
},
The variable $self will be captured by the anonymous sub. This is why the following works:
sub make {
my ($s) = #_;
return sub { return $s };
}
my $x = make("Hello, ");
my $y = make("World!\n");
print $x->(), $y->(); # Hello, World!
The world of closures, that is :)

Is it possible to overwrite the response data in a Mojo::Message::Response?

In a Mojolicious app I have a route in my Controller code like the following:
/account/:id/users
The /account/:id part of the route has the following data in it when I get to the
users part of the chain:
$VAR1 = {
'signup_ip' => '172.17.5.146',
'z_id' => '382C58D8-529E-11E1-BDFB-A44585CCC763',
'signup_date' => '2012-03-12T12:11:10Z',
'name' => 'Some Cool Account Name',
'users' => [
{
'user_id' => '382C67EC-529E-11E1-BDFB-A44585CCC763'
}
],
'account_id' => '382C67EC-529E-11E1-BDFB-A44585CCC763',
};
In the users part of the chain I'm getting the above hash using
$self->tx->res->content->get_body_chunk(0)
sub users {
my $self = shift;
my $user_list = from_json( $self->tx->res->content->get_body_chunk(0) );
$self->respond_to( json => $user_list->{users} );
}
The problem I'm having is that I want to overwrite the response with only
the users arrayref. The code above in sub users(){} doesn't do that. That is,
when I dump the result in the test, I still getting the entire hash.
The $user_list is the arrayref I'm looking for in users() but I'm unable to overwrite it.
Anyone have an idea how to do that?
Hrm I think I put my previous answer in the wrong place. So here it is:
In the application I added the following routes:
my $base = $r->bridge('/account/:id')->to('account#read');
$base->route('/')->via('get')->to('account#index');
$base->route('/users')->via('get')->to('account#users');
In Acount.pm
sub read {
my $self = shift;
# do stuff
$self->stash->{account} = $data; # set the stash
return 1; #return 1. Don't render.
}
sub index {
my $self = shift;
my $data = $self->stash('account'); #get the stash
$self->render_json( $data );
}
sub users {
my $self = shift;
# do stuff
my $data = $self->stash('account');
$self->render_json( $data );
}
Doing this sets the result of /account/:id into the stash in the read sub.
Setting a route to $base->route('/')->via('get')->to('account#index');
causes calls to /account/:id to be rendered from the index sub.
The route $base->route('/users')->via('get')->to('account#users') causes
the calls to /account/:id/users to be rendered from the users sub.
I think you have to provide different parameters to respond_to method. I would expect this to work:
$self->respond_to(json => { json => $user_list->{users} });
Or just call render_json:
$self->render_json($user_list->{users});
Edit: I made simple testing script that works for me (using latter option above):
use Mojolicious::Lite;
get '/account/users' => sub {
my $self = shift;
my $user_list = {
'signup_ip' => '172.17.5.146',
'z_id' => '382C58D8-529E-11E1-BDFB-A44585CCC763',
'signup_date' => '2012-03-12T12:11:10Z',
'name' => 'Some Cool Account Name',
'users' => [{'user_id' => '382C67EC-529E-11E1-BDFB-A44585CCC763'}],
'account_id' => '382C67EC-529E-11E1-BDFB-A44585CCC763',
};
$self->render_json($user_list->{users});
};
app->start;
the request to http://localhost:3000/account/users returned this:
[{"user_id":"382C67EC-529E-11E1-BDFB-A44585CCC763"}]

Is this snippet creating an anonymous Perl hash?

While reading the snippets provided by FormFiller ( where I kinda got by accident ) , I noticed this line:
$f->add_filler( password => Interactive => []);
Is this password => Interactive => [] equivalent to {"password" => {"Interactive"=>[]}}? If not, what does it become?
=> is semantically (almost) identical to , (see "Comma operator" at perldoc perlop), so you're doing this:
$f->add_filler( 'password', 'Interactive', [] );
If this calling style is supported by the method (which it doesn't), then it itself would have to convert these arguments into
{ password => { Interactive => [] } }
However more typically, hash-style arguments would be passed as a legal hash to begin with:
$f->add_filler( password => { Interactive => 1 } );
This would be received by the function like this:
sub add_filler
{
my $this = shift;
my %configs = #_;
# ...
}
The Data::Dumper module is great for answering questions like this. Use the following mock:
package Foo;
use Data::Dumper;
sub new { bless {} => shift }
sub add_filler {
my $self = shift;
print Dumper \#_;
}
Then call it
package main;
my $f = Foo->new;
$f->add_filler( password => Interactive => []);
and see when you get:
$VAR1 = [
'password',
'Interactive',
[]
];
This shows that add_filler receives a flat list of three arguments: two strings and a reference to an anonymous array.
No, it's exactly the same as
$f->add_filler( "password", "Interactive", []);