Mojolicious + Hypnotoad.
I want my controllers to reroute the remaining portion of the request, so that I don't have to declare all routes at the level of the main script.
So for example, '/foo/bar/baz' should route to Controller 'FOO', which will then decide routing for 'bar/baz', internal to itself.
Main script :
package MyApp;
use Mojo::Base 'Mojolicious';
use Mojolicious::Plugin::Config;
sub startup {
my $self = shift;
$self->moniker('MyApp');
$self->plugin('Config');
my $r = $self->routes;
$r->any('/foo/*remainder')->to('FOO#rerouter')->name('TEST_NAME');
}
I've tried one way, by dynamically adding routes, but running it many times shows inconsistency - probably caused by a race condition or something :
package MyApp::Controller::FOO;
use Mojo::Base 'Mojolicious::Controller';
sub rerouter {
my $self = shift;
my $r = Mojolicious::Routes->new;
$r->get('bar/:baz')->to('myInternalControllerAction');
my $current_route = $self->current_route; # 'TEST_NAME'
$self->app->routes->find($current_route)->add_child($r);
}
sub myInternalControllerAction { #stuff }
That seems consistent with this other answer that :
"The router is dynamic until the first request has been served, after
that, the router cannot change routes"
https://stackoverflow.com/a/22291864/2304437
Even if that did work though, the execution would exit the controller FOO before re-entering it on a different action via the dynamically added route(s).
The other way I can do it is just create a dispatch table in the "re-router" of the controller, since I have access to the remainder of the request anyway :
sub rerouter {
my $self = shift;
my $remainder = $self->stash->{remainder};
# ...
}
But then I lose benefits of Mojolicious' dispatcher - I would have to parse the remaining request URL / path myself. Feels dirty.
Hooks don't seem appropriate here either.
How can I elegantly let the controller route its own child portion ?
I solved this as follows :
use MyApp::Controller::FOO;
my $r = $self->routes;
my $r_root = $r->under('/' => sub { return 1; });
my $baz = $r_root->under('/baz' => sub { return 1; });
$baz->add_child($MyApp::Controller::FOO::routes);
Then in the module :
package MyApp::Controller::FOO;
use Mojo::Base 'Mojolicious::Controller';
our $routes = Mojolicious::Routes->new;
my $r = $routes->under('/')->to('FOO#bar');
sub bar { ... }
Maybe not as elegant as it could be, but it works very well for me.
I have been building catalyst apps of a number of years now. This is the first time I am getting an error trying to open a view. When I call the view (there is only one view), I get the following error:
Can't call method "model" on an undefined value at....
The following snippet of code is used in Root.pm,. This is what is generating the error on the browser. Again, this is not the first time I am using this catalyst method to call a model.
my $model = $c->model( 'mypackage' );
my $result = $model->get_my_results();
The alternative is to use:
use mypackage;
my $model = 'mypackage';
my $results = $model->get_my_results();
Has any one ever encounter this before? If yes, can you tell me how you resolved it.
Thanks
EDITED
Please note, as indicated in the original post, this is a Perl Catalyst app. The $c is a Catalyst object. In the Root.pm file, $c is received as follows:
sub myform :Local {
my ($self, $c) = #_;
my $model = $c->model( 'mypackage' );
my $results = $model->get_my_results();
.
.
.
}
Typo? Did you mean $c->model in your first line?
So, I figured out the path of my evil ways. I inadvertently forgot to pass $c in the calling method. Therefore the $c in myform() had no value. Hence, it could not find the Catalyst method called "model".
sub myform :Local {
my ($self, $c) = #_;
my $model = $c->model( 'mypackage' );
my $results = $model->get_my_results();
.
.
.
}
sub someother_method :Local {
my ($self, $c) = #_;
.
.
.
my $myform_info = $self->myform($c) <---this was missing the $c
.
}
given the example plack app using lots of middleware components and an mojolicious app
enabled in builder (see below), how can i pass parameters from the app.psgi to Mojolicious
without using the ugly %ENV hack shown? of cause passing an config is just an example, this could be any scalar/object.
app.psgi
use Plack::Builder;
$ENV{CONFIG} = {...};
builder {
...
Mojolicious::Commands->start_app('MyApp');
};
MyApp.pm
package MyApp;
use Mojo::Base 'Mojolicious';
sub startup {
my $self = shift;
my $r = $self->routes;
$self->config( $ENV{CONFIG} );
$r->route('/')->to('home#');
}
This is an interesting question and is most easily tackled by looking at the source. In your example you rightly use
Mojolicious::Commands->start_app('MyApp');
Looking at the source shows that start_app is a rather simple wrapper:
sub start_app {
my $self = shift;
return Mojo::Server->new->build_app(shift)->start(#_);
}
It turns out that build_app is as well:
sub build_app {
my ($self, $app) = #_;
local $ENV{MOJO_EXE};
return $app->new unless my $e = Mojo::Loader->new->load($app);
die ref $e ? $e : qq{Couldn't find application class "$app".\n};
}
returning a new instance of your app's class. The Mojolicious class's new function is more involved, but in the end, it just calls the familiar startup method and returns the instance.
This means that you cannot easily pass arguments to the startup method from your middleware wrapper, used in the standard way. I can think of two mechanisms to accomplish what you want to do: 1) write your own build_app function to replace the server's method but which passes arguments to $app->new (which would get passed to startup in turn) or 2) write your own start_app function which could call another startup-like function.
# in MyApp.pm
sub startup {
... # as before
}
sub after_startup {
... # your new code here,
# or even most of what was in `startup` before
}
and
# app.psgi
builder {
...
my $app = Mojo::Server->new->build_app(shift);
$app->after_startup(#your_args_here);
$app->start(#_);
}
I'm trying to set up a jsonrpc application using Mojo. I built some controllers
and related modules using REST but then decided I needed to use JSONRPC.
Using the following code from http://metacpan.org/pod/Mojolicious::Plugin::JsonRpcDispatcher
# 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
}
);
I was able to replicate one of my Controller => Model relationships where the controller
was ported to this format in one file.
I'd like to set it up so that the application file basically can load the each of the files
as a service similar to what this page displays http://metacpan.org/pod/MojoX::JSON::RPC::Service
$svc->register(
'sum',
sub {
my #params = #_;
my $sum = 0;
$sum += $_ for #params;
return $sum;
}
);
In other words, what I'm envisioning is reworking each controller to act similar to
the snippet above but have them all loaded in the application module. Like this:
## Then in Mojolicious application
$self->plugin(
'json_rpc_dispatcher',
services => {
'/jsonrpc' => $svc,
}
);
Is it possible to create a file with functions set up like the code above and then load
them as servers in the application module? MojoX::JSON::RPC::Service appears to offer that. I've been unable to get it to work.
Cannot understand why the returned values from the function login bellow do not correspond to what is passed to it.
The following is a snippet of my code
package This_package;
.......
# returned from function that parses post data ($reqparam)
my $thisuser = $$reqparam{"username"};
# escape '#', username is an email
$thisuser =~ s/#/\#/;
my $thisuser_pass = $$reqparam{'password'};
print $thisuser; # ok
print $thisuser_pass; # ok
my $obj = new users;
my $valid_user = $obj->login($thisuser, $thisuser_pass);
.......
package Another_package;
sub new {
my ($class) = #_;
my $self = {
_login => undef,
_create_user => undef,
....
};
bless $self, $class;
return $self;
}
sub login ($$){
my ($user, $pass) = #_;
# some processing
.....
return $user; # prints users=HASH(...)
# return $pass; # prints the value of $user (the actual value)
# instead of the value of $pass
}
While trying to learn perl by converting some code from php into perl.
I have run into this problem, I have tried a few alternatives but obviously there is something I am not getting!
When you call a function like
my $valid_user = $obj->login($thisuser, $thisuser_pass);
The first parameter is this usually done as
sub login
{
my ( $self , $user , $password ) = #_;
}
You are missing $self
Because you are missing $self you user is actually the object and your password is actually the user.
If you are coming from another objected oriented language like C++ , Java or C#, this is a perl gotcha (no pun intended :)) . Another one is that even from an object method if you want to invoke another member method you have to use self like
$self->callAnotherObject( $user );
Simply calling wont do
callAnotherObject( $user );
Also I see that you are using function prototypes, It may not work as you intend it to be.
When you use object-oriented syntax ($obj->login($thisuser, $thisuser_pass)) to call a subroutine, the first argument will be the object itself. You should say, and you will typically see object-oriented modules use syntax like:
sub login {
my ($self, $user, $pass) = #_;
...
}
Incidentally, you shouldn't use prototypes ( ($$) ) without a good reason. Prototypes in Perl are not used in the same way they are in other languages, and in any case the prototype is ignored when you call a subroutine with indirect syntax (luckily, in your case, since you are actually calling it with 3 arguments).
You even watch Mythbusters?
Although you see Adam and Jamie do really, really dangerous stuff, they warn you at the beginning of every program, "Don't do this at home." Think of Perl prototypes in the same way. If you use them, there's a good likelihood you'll get badly burned.
Okay, now who is calling your login function? Or, maybe better, how is it called?
If I use your Perl module, do I call your login subroutine from my main program like this?
my $package_obj = Another_package->new;
$package_obj->login($user, $password);
Or, is this some subroutine that you use in your package for your convenience and you use it as a simple subroutine, and not a private method like this:
package Another_package;
sub new {
...
}
sub foo {
...
my $user = login ($user, $password);
}
If you're calling your login subroutine as a simple subroutine inside your package as in the second example, everything should be fine.
However, if you're treating your login subroutine like a full fledge method (like I do in the first example), you must remember that methods pass their class object as the first parameter of the subroutine.
Thus, you'll need to do something like this:
sub login {
my $self = shift; #Pointer to the Another_package object I'm using
my $user = shift;
my $password = shift; #I just love lining things up!
$self->{USER} = $user; #Bad way of doing it.
$self->{PASSWD} = $password;
... #Some processing.
return $user;
}
Why the #Bad way of doing it comment? Because you really want to keep your internals as separate as possible. That way, if you make a change to the structure of the Another_package class, your changes are isolated in a very specific part of your code. It makes debugging much easier.
A better way of writing the login subroutine would be:
sub Login { #In standard Perl, methods are capitalized.
my $self = shift; #Pointer to Another_package object
my $user = shift; #Allow user to pass user and password in constructor
my $password = shift; #I just love lining things up!
$self->User($user); #Way better: This is a setter/getter method
$self->Password($password);
... #Some processing.
return $user;
}
In this example, I'm using setter/getter methods for setting my user name and password. This way, I don't have to worry how they're actually stored in my object.
Here's your Another_Package module using setter/getter methods. I now allow the user to pass in the user and password when they call the new constructor if they'd like.
package Another_package;
sub new {
my $class = shift;
my $user = shift;
my $password = shift;
my $self = {};
bless $self, $class;
$self->User($user);
$self->Password($password);
...
return $self;
}
sub Login {
my $self = shift;
my $user = shift;
my $pass = shift;
$self->Password($pass);
if (not defined $self->User($user)) {
croak qq(Cannot log in without a user ID);
}
...
if ($login_successful) {
return $self->User; #Or maybe a session instant
else {
return;
}
}
Notice in my new constructor subroutine I create a $self anonymous hash (my $self = {}) and I immediately bless it. Now, $self is already a package object, and I can call a bunch of setter/getter methods to set the various fields in my object. My new constructor has no idea what my actual Another_module object looks like.
In my Login method subroutine, I also use the same setter/getter methods to set user and password. Again, my Login method knows nothing on how these fields are stored in the object.
One more thing you might notice is that I'm setting a scalar called $login_successful in my Login module to see whether or not my login was successful. In Perl, it is common to return nothing if the method fails, or return something on success. This way, the user's program can test to see if the call succeeded or failed. For example, maybe if the login fails, the user might want to try some default passwords before giving up:
my $package_obj = Another_package->new($user, $password);
my $foo = $package_obj->Login;
if (not defined $foo) {
foreach my $password qw(swordfish s3x mon3y 7ucky) {
$package_obj->Password($password);
last if $foo = $package_obj->Login;
}
if (not defined $foo) {
die "I don't know the password :-(";
}
}
So, what do my setter/getter methods look like? They're actually pretty simple:
sub User {
my $self = shift;
my $user = shift;
if(defined $user) {
$self->{USER_INFO}->{USER} = $user;
}
return $self->{USER_INFO}->{USER};
}
sub Password {
my $self = shift;
my $pass = shift;
if (defined $password) {
$self->{USER_INFO}->{PASSWORD} = $pass;
}
return $self->{USER_INFO}->{PASSWORD};
}
Why do I store $user in $self->{USER_INFO}->{USER} and not $self->{USER}? No reason a at all. However, it does show that the rest of the Another_package module doesn't care where or how I store the user and password.