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);
}
};
Related
I'm writing a Mojolicious module/controller that needs to make two GET requests; one after the other. The second GET request depends on response data from the first.
I would like both requests to be non-blocking. However I can't easily "return" from the context of the first non-blocking callback to supply values to the second request.
sub my_controller {
my ($self) = #_;
$self->ua->get($first_endpoint, sub {
# handle response - extract value for second request?
});
my $second_endpoint = 'parameter not available here';
$self->ua->get($second_endpoint, sub {});
}
I would prefer not to nest the second request into the first callback if possible?
First need to call render_later method in controller because you write non-blocking code.
Exist 2 ways how to pass data:
1)
sub action_in_controller {
my $c = shift->render_later;
$c->delay(
sub {
my $delay = shift;
$c->ua->get('http://one.com' => $delay->begin);
},
sub {
my ($delay, $tx) = #_;
$c->ua->post('http://second.com' => $delay->begin);
},
sub {
my ($delay, $tx) = #_;
$c->render(text => 'la-la-la');
}
);
}
2)
sub action_in_controller {
my $c = shift->render_later;
$c->ua->get('http://one.com' => sub {
my ($ua, $tx) = #_;
$c->ua->post('http://second.com' => sub {
my ($ua, $tx) = #_;
$c->render(text => 'la-la-la');
});
});
}
UPD
Found another variant of calling using Coro.
But in perl 5.22 it not work and need to apply patch to repair it.
You need additionally to write plugin Coro.
Here example. You need only ua.pl and plugin Mojolicious::Plugin::Core.
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 :)
I'm working with zabbix and writing an interface to interact with the zabbix api. Since zabbix exposes a jsonrpc interface I decided to use MojoX::JSON::RPC::Service. The problem I'm running into is that I'm now faced with interacting with other services written using Mojolicious::Controllers where they're expecting a Mojolicious::Controller objects. There is no Mojolicious::Controller object available when using MojoX::JSON::RPC::Service.
my $obj = $rpc_obj->register(
'retrieve',
sub {
# do stuff
},
{ with_mojo_tx => 1 }
);
That registers a route called 'retrieve'. When the route is accessed and the anonymous
subroutine is run, the subroutine has access only to the Mojo::Transaction::HTTP object.
So, I don't have access to the app for using plugins and the stash and other things that Mojolicious offers. Is there a way to incorporate Mojolicious::Controller with MojoX::JSON::RPC::Service?
I could rewrite it to use a Mojolicious::Controller but I'm trying to avoid that if possible.
You should consider to use MojoX::JSON::RPC::Dispatcher, as it inherits all attributes from Mojolicious::Controller
SYNOPSIS:
# lib/your-application.pm
use base 'Mojolicious';
use MojoX::JSON::RPC::Service;
sub startup {
my $self = shift;
my $svc = MojoX::JSON::RPC::Service->new;
$svc->register(
'sum',
sub {
my #params = #_;
my $sum = 0;
$sum += $_ for #params;
return $sum;
}
);
$self->plugin(
'json_rpc_dispatcher',
services => {
'/jsonrpc' => $svc
}
);
}
[UPDATE]
Hook Example:
package Application;
use Mojo::Base 'Mojolicious';
use Application::Firewall;
# This method will run once at server start
sub startup {
my $app = shift;
# Routes
my $r = $app->routes;
# Validation Middleware
$app->hook(
before_dispatch => sub {
my $self = shift;
my $data = $self->req->params->to_hash;
my $vald = Application::Firewall->new($data);
# mojolicious bug at the time of coding
delete $data->{""} if defined $data->{""};
$app->{input} = {};
if ( keys %{$data} ) {
# validation the submitted data
unless ( $vald->validate( keys %{$data} ) ) {
$self->render(
text => join( "", #{ $vald->errors } ),
status => 500
);
return 0;
}
# Helper (optional)
# create a helper to get access to the transformed data
# if your validation rules had/has filters
# Note! due to a bug in the params function we must do this
# (... i know, so what)
$app->{input} = {
map { $_ => $vald->{fields}->{$_}->{value} }
keys %{ $vald->{fields} }
};
}
return 1;
}
);
# Normal route to controller * with auto-matic input validation *
$r->route('/')->to(
cb => sub {
my $self = shift;
$self->render(
text => 'Hello ' . ( $app->{input}->{foobar} || 'World' ),
status => 200
);
}
);
}
1;
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"}]
This is the mock module I'm using:
http://metacpan.org/pod/Test::MockModule
How to mock sub a to sub b,
where sub b just does something else before call sub a?
sub b {
#do something else
a(#_);
}
You can grab the un-mocked method with can ( UNIVERSAL::can ). After that you can either goto it or just use the ampersand calling style to pass the same arguments. That's what I did below.
my $old_a = Package::To::Be::Mocked->can( 'a' );
$pkg->mock( a => sub {
# do some stuff
&$old_a;
});
This of course assumes that your sub isn't AUTOLOAD or generated through AUTOLOAD without redefining can. (I learned years back that if you're going to mess with AUTOLOAD, it's probably best to do the work in can.)
You could also create your own utility that does this automatically, by invading modifying the Test::MockModule's namespace.
{ package Test::MockModule;
sub modify {
my ( $self, $name, $modfunc ) = #_;
my $mock_class = $self->get_package();
my $old_meth = $mock_class->can( $name );
croak( "Method $name not defined for $mock_class!" ) unless $old_meth;
return $self->mock( $name => $modfunc->( $old_meth ));
}
}
And you could call it like so:
$mock->modify( a => sub {
my $old_a = shift;
return sub {
my ( $self ) = #_;
# my stuff and I can mess with $self
local $Carp::CarpLevel += 1;
my #returns = &$old_a;
# do stuff with returns
return #returns;
};
});