I am developing a Mojolcious standalone application. In the code I am contacting an internal backend where the user should provide some credentials.
Currently I am using the credentials inside the code as variables. It looks some something like:
$password = 'somthing';
I tried to use the config plugin to store the credentials there, but is there an option with Mojolicious to let the user provide his credentials when running the daemon? Maybe like:
perl myapp.pl daemon -user username -password password
What is the best strategy with Mojolicious to handle this use case?
I will provide two solutions to this problem. One is rather easy while the other one uses a few advanced techniques.
You can simply pass environment variables to your app when you start it.
$ USERNAME=foo PASSWORD=bar perl app.pl daemon
Mojolicious is Perl, so they end up in %ENV
. We can access them with $ENV{USERNAME}
and $ENV{PASSWORD}
. Typically those environment variables are all-caps. It would make sense to pick something app-specific, so MYAPP_USERNAME
is probably a better choice than USERNAME
.
The good thing about this is that you can also set it in your shell environment and then you never have to worry about it.
package MyApp;
use Mojolicious::Lite;
use Data::Printer;
helper(
credentials => sub {
state $credentials = {
username => $ENV{USERNAME},
password => $ENV{PASSWORD},
}
}
);
get '/' => sub {
my $c = shift;
$c->render( text => np $c->app->credentials );
};
app->start;
In the above example I have created a helper
that keeps the credentials for us so it's easy to access them. Of course you could just use $ENV{USERNAME}
throughout the code, but I believe it's nicer to have it properly encapsulated.
If we curl localhost:3000
the daemon we get the this output.
$ curl localhost:3000
\ {
password "bar",
username "foo"
}
daemon
CommandCommand line arguments all handled by Mojolicious::Command.
You can create your own Mojolicious::Command subclass. This is documented in the Mojolicious::Cookbook. If you name it Mojolicious::Command::foo
then you don't even need to worry about adding another namespace for Mojo to look for commands in.
But you can unfortunately not pass several commands at the same time. So creating a subclass credentials
and then doing this does not work.
$ perl myapp.pl daemon credentials --username foo --password bar
So we need to do something else. A quick look at the Mojolicious::Command::daemon tells us that it only has the run
method, which gets called automatically when that command is called. We want it to do something additional, so we subclass that specific command.
package Mojolicious::Command::mydaemon;
use Mojo::Base 'Mojolicious::Command::daemon';
use Mojo::Util 'getopt';
sub run {
my ( $self, @args ) = @_;
getopt
'u|username=s' => \my $username,
'p|password=s' => \my $password;
$self->app->credentials->{username} = $username;
$self->app->credentials->{password} = $password;
return $self->SUPER::run;
}
We need to import getopt
, which helps us read stuff from the command line. We store the username and password in the credentials
helper just like in the first approach because encapsulation is nice.
Finally, we need to hand over to the original run
method. That's done by calling $self->SUPER::run
, which is Perl's way of saying call run
on my super-class from which I have inherited. It calls run
on the SUPER::
pseudo class. You can learn more about that in perlobj.
Now the actual app is pretty much the same.
package MyApp;
use Mojolicious::Lite;
use Data::Printer;
helper(
credentials => sub {
state $credentials = { username => q{}, password => q{}, };
}
);
get '/' => sub {
my $c = shift;
$c->render( text => np $c->app->credentials );
};
app->start;
We run it like this:
$ perl code/scratch.pl mydaemon --username foo --password bar
And if we curl localhost:3000
we get the same output.
$ curl localhost:3000
\ {
password "bar",
username "foo"
}
Which of those ways you choose is up to you. I am not sure which one I would prefer myself. Both have merits.
The environment variables are easier to implement and more portable, but the code is not as clear. The command subclass can document itself when done properly, but maybe your users would not expect something else than daemon
. It's also a lot more code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With