Mojolicious Lite route with regular expressions - mojolicious

I am trying to catch a route with regular expressions in Mojolicious Lite.
This is the route:
get qr!/messages/read/(.*).json! => sub {
my $id = $1;
my $c = shift;
return $c->render(json => { $id => 1 });
};
It just returns page not found. I was wondering if I was missing a plugin or something has changed but I can't find anything.
I tried some variations, like adding a ^ before the first slash, or changing the character after qr but I couldn't make it work.
Thank you for your time.

Mojolicious routes are not regular expressions, so I'm curious what led you to believe you could do this. It looks like you want a placeholder.
get '/messages/read/<:id>.json' => sub {
my $c = shift;
my $id = $c->param('id');
return $c->render(json => { $id => 1 });
};

Related

Pass value from one router to another using Mojolicious::Lite

from an ajax form this router foundname gets called, I need to process the value and pass it to another router, I can't figured it out how to do it, here is a sample of how I am trying:
#!/usr/bin/perl
use Mojolicious::Lite;
get '/foundname' => sub {
my $c = shift;
# Here I get the value from the form
my $name_on = $c->req->query_params->param('name');
if($name_on) {
# call another router and pass the name value to it
# It gives an error "Can't locate object method "get" ", I might not need to use "get", just don't know how to pass the value.
$c->get('/process_name')->to( searched => $name_on);
}
};
get '/process_name' => sub {
my $c = shift;
my $got_name = $c->req->query_params->param('searched');
...
};
Thank you!
You need to look up the routes through your Mojolicious::Routes object inside of your app. The name for the lookup is auto-generated by Mojolicious::Lite from the path-part of the URI, so /process_name has the name process_name.
You get back a Mojolicious::Routes::Route, which has a render method and you can pass your parameters along there.
use Mojolicious::Lite;
get '/foundname' => sub {
my $c = shift;
my $name_on = $c->req->query_params->param('name');
if( $name_on ) {
my $process_name = app->routes->lookup('process_name')->render( { searched => $name_on } );
$c->render( text => $process_name );
}
};
get '/process_name' => sub {
my $c = shift;
my $got_name = $c->req->query_params->param('searched');
$c->render( text => $got_name );
};
app->start;
When you curl this you get the parameter back as a response.
$ curl localhost:3000/foundname?name=foo
/process_name
However, this is probably not the right approach. If you want to implement business logic, you should not use internal or hidden routes for that. Remember that your application is still just Perl. You can write a sub and call that.
use Mojolicious::Lite;
get '/foundname' => sub {
my $c = shift;
my $name_on = $c->req->query_params->param('name');
if( $name_on ) {
my $got_name = process_name( $name_on );
$c->render( text => $got_name );
}
};
sub process_name {
my ( $got_name ) = #_;
# do stuff with $got_name
return uc $got_name;
};
app->start;
This will output
$ curl localhost:3000/foundname?name=foo
FOO
It's the more portable approach, as you can easily unit-test these functions. If you want to have $c, you have to pass it along. You also have the app keyword available in any sub you define.
For the original question, I would use
$c->redirect_to()
See this question for details on passing the variable over:
Passing arguments to redirect_to in mojolicious and using them in the target controller
======
But, I would look more into writing subs (as others have said).
If you have existing logic then you can wrap it in a helper or just toss the logic in a helper and call that.
helper('process_name'=> sub{
my $self,$args = #_;
# Do some logic with $args->{'name'}
return $something;
});
get '/foundname' => sub {
my $c = shift;
my $name_on = $c->req->query_params->param('name');
if( $name_on ) {
my $process_name = $c->process_name({name => $name_on});
$c->render( text => $process_name );
}else{
$c->redner(text => 'Error',status=>500);
}
};

Using perl REST::Application to parse url query string

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'
};

How do I hand off a Catalyst request to CGI::Application?

I have a CGI::Application loop like
while (my $q = new CGI::Fast){
S::W::Dispatch->dispatch(args_to_new => { QUERY => $q });
my $webapp = S::W::instance();
undef $CGI::Application::INSTANCE;
S::W::instance(undef);
S::clear_instance();
}
And, in short, I'd like to make that a new Catalyst dispatch..
So I want to do something like this
sub oldRoot :Local {
...
S::W::Dispatch->dispatch(args_to_new => { QUERY => $q });
}
So I need to know how to reconstruct the $q in Catalyst. Is this easily done? Or, how do I achive this easier?
This was actually a lot easier than I expected.
Simply make your controller extend Catalyst::Controller::WrapCGI, rather than Catalyst::Controller
BEGIN { extends 'Catalyst::Controller::WrapCGI' }
Then wrap the whole thing like this..
$self->cgi_to_response($c, sub {
my $q = new CGI::Fast;
S::W::Dispatch->dispatch(args_to_new => { QUERY => $q });
my $webapp = S::W::instance();
undef $CGI::Application::INSTANCE;
S::W::instance(undef);
S::clear_instance();
} );
It just worked..

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"}]