I have subroutine in perl that accepts parameters. Now I am trying to read from the config file using the below code.
sub user{
my $self = shift;
my $apiBaseUrl = $self->app->config->{"apiBaseUrl"};
my $apiToken = $self->app->config->{"apiToken"};
}
But I am getting the error
Can't locate object method "app" via package "test#example.com" (perhaps you forgot to load "test#example.com"?)
The parameters I am passing to the subroutine are $username and $password
This is how I am calling that subroutine, with an email and password which I get from login form.
my $username = $self->param('username');
my $password = $self->param('password');
user($username, $password);
Below is the full code in the file.
use Mojo::Base 'Mojolicious::Controller';
use Mojo::UserAgent;
sub is_logged_in {
my $self = shift;
return 1 if $self->session('logged_in');
$self->render(
inline => "<h2>Forbidden</h2><p>You're not logged in. <%= link_to 'Go to login page.' => 'login_form' %>",
status => 403
);
}
sub user {
my ($username, $password) = #_;
my $self = shift;
my %returnResult;
my $apiBaseUrl = $self->app->config->{'apiBaseUrl'};
my $apiToken = $self->app->config->{'apiToken'};
my $url = $apiBaseUrl.'/auth/login/check?email='.$username.'&password='.$password.'';
my $header = {'api-token' => $apiToken};
my $ua = Mojo::UserAgent->new;
my $res = $ua->post( $url => $header )->result;
if($res->is_success)
{
my $content= $res -> json;
my $decoded_email = $content->{'email'};
$returnResult{'fn'} = $content->{'fn'};
$returnResult{'ln'} = $content->{'ln'};
$returnResult{'roles'} = $content->{'roles'};
if($username eq $decoded_email)
{$returnResult{'logged_in'} = 1;}
else
{$returnResult{'logged_in'} = 0;}
return %returnResult;
}
else
{
return $res->status_line;
}
}
sub on_user_login {
my $self = shift;
my $username = $self->param('username');
my $password = $self->param('password');
my %userDetails = user($username, $password);
if ($userDetails{'logged_in'} == 1) {
$self->session(logged_in => 1);
$self->session(username => $username);
$self->session(userDetails => \%userDetails);
$self->redirect_to('restricted');
} else {
$self->flash(message => 'Incorrect username/password!');
$self->redirect_to('/');
}
}
1;
What am I doing wrong?
my $username = $self->param('username');
my $password = $self->param('password');
user($username, $password);
You are using your user sub as a function, and not as a method. Perl is a very flexible programming language that allows you to mix functional, procedural and object oriented programming styles in the same program. Sometimes that can get confusing.
You can recognize OOP (object oriented programming) in Perl when a sub has $self as the first argument because there is a convention to always name the object itself like that. Other languages use this instead. Another giveaway is when there is a variable followed by an arrow -> followed by an identifier.
$obj->method(#args);
You've already done that in your code when you got the parameters from $self. I suspect you are using this in a Mojolicious::Controller, and you've put your sub user in the same file.
The error message you are seeing is because your first argument to the function is the email address in $username. You left out the $self-> part, which tells Perl to call this as a method on $self. Under the hood, it will look into the identifier on the left (which is $self) to see what class that thing has. It will then look into the namespaces (called packages in Perl) of all the things in the inheritance tree of that class to find the identifier on the right (which is user). In our case, it will find a sub user right in this same package. It then calls that function user and passes the thing on the left ($self) as the first argument, and $username and $password as the second and third arguments.
So what you need to do is:
$self->user($username, $password);
The code in your sub is correct (but does not actually use the two arguments).
Related
I have an existing application (my website) that I'm doing some code tidying in, and the tidy up is following the same sort of idea as the Mojo::Pg example here, with separate model and controller files to keep things defined. My site accesses both Flickr and Last.fm's APIs, and I have a helper defined in Site::Helpers:
$app->helper(
get_base_rest_url => sub {
my ( $self, $config ) = #_;
sswitch ( $config ) {
case 'photos': {
my $base_url = 'https://api.flickr.com/services/rest/';
my $user_id = '7281432#N05';
my $api_key = $self->app->config->{ 'api_token' }{ 'flickr' };
my $url =
"$base_url"
. "?user_id=$user_id"
. "&api_key=$api_key"
. "&per_page=" . $self->session->{ per_page }
. '&format=json'
. '&nojsoncallback=1';
return $url;
}
case 'music': {
my $base_url = 'https://ws.audioscrobbler.com/2.0/';
my $username = 'virtualwolf';
my $api_key = $self->app->config->{ 'api_token' }{ 'last_fm' };
my $per_page = $self->session->{ 'per_page' };
my $url = "$base_url?user=$username&limit=$per_page&api_key=$api_key&format=json";
return $url;
}
}
}
);
The problem I'm running into is that I don't know how to access that helper from the Site::Model::Photos module. The error is
Can't locate object method "get_base_rest_url" via package "Site::Model::Photos"
which is fair enough, but I can't work out how to actually get at that get_base_rest_url helper (or alternatively, how to access the api_token config).
The problem is that your module have not got app attribute/method which get access to your app.
So, when you create instance of Site::Model::Photos you need to pass app to it in param and make it weaken something like that:
package Site::Model::Photos
use Scalar::Util 'weaken';
sub new {
my $class = shift;
my $app = shift;
my $hash = {app => $app, ...};
weaken $hash->{app};
return bless $hash, $class;
}
sub your_method {
my $self = shift;
$self->{app}->get_base_rest_url(...);
}
1;
Or you may to use this module https://metacpan.org/release/Mojolicious-Plugin-Model which do it for you:
package Site::Model::Photos
use Mojo::Base 'MojoX::Model';
... code of your module ...
sub your_method {
my $self = shift;
$self->app->get_base_rest_url(...);
}
1;
And in your App.pm need to add this:
$app->plugin('Model', {namespaces => ['Site::Model']});
And use it that in controller:
$c->model('photos');
$c->app->model('photos');
I am trying to use MooseX::Method::Signatures and MooseX::Declare in an application, my need is to inject custom code at the beginning of each method at compile time not at run time:
instead of this:
use MooseX::Declare;
method check ($value) {
$return $value;
}
I want to inject a code at the beginning of each method at compile time to be like that:
method check ($value) {
my ($value) = $self->validate($value);
$return $value;
}
now I want the code
my ($value) = $self->validate($value);
to be injected automatically at the beginning of all methods in the package using the MooseX::Decalre module at compile time and not at run time, I mean not using the Moose method modifiers before, after, around etc.
This needs a modification of these module but I need someone to tell me where to start.
I was able to modify the module Method::Signatures::Simple to do this exactly and emailed the author for the modification but did not get a reply. The reason I can not use this even with modification because it does not support type checking and defaults like MooseX::Declare.
The modified version of the module Method::Signatures::Simple below for reference and I use it as follows:
use Method::Signatures::Simple (method => 'method,action', function =>
'function', invocant=>'$this', 'inject'=>'my ($me) = $this->me;');
now in all methods, I get the code my ($me) = $this->me; injected and I just can use it like that:
method check ($value) {
say $me
}
Here is the modified Method::Signatures::Simple module.
package Method::Signatures::Simple;
{
$Method::Signatures::Simple::VERSION = '1.07';
}
use warnings;
use strict;
=head1 NAME
Method::Signatures::Simple - Basic method declarations with signatures, without source filters
=head1 VERSION
version 1.07
=cut
use base 'Devel::Declare::MethodInstaller::Simple';
our $inject_code;
sub import {
my $class = shift;
my %opts = #_;
$opts{into} ||= caller;
my $meth = delete $opts{name} || delete $opts{method};
my $func = delete $opts{function};
my $invocant = delete $opts{invocant} || '$self';
$inject_code = delete $opts{inject};
$inject_code .= ";" if ($inject_code && $inject_code !~ /\;$/);
# if no options are provided at all, then we supply defaults
unless (defined $meth || defined $func) {
$meth = 'method';
$func = 'func';
}
my #meth = split /\s*\,+\s*/, $meth;
# we only install keywords that are requested
foreach $meth (#meth) {
if (defined $meth) {
$class->install_methodhandler(
name => $meth,
invocant => $invocant,
%opts,
);
}
}
if (defined $func) {
$class->install_methodhandler(
name => $func,
%opts,
invocant => undef,
);
}
}
sub strip_proto {
my $self = shift;
my ($proto) = $self->SUPER::strip_proto()
or return '';
# we strip comments and newlines here, and stash the number of newlines.
# we will re-inject the newlines in strip_attrs(), because DD does not
# like it when you inject them into the following code block. it does not
# object to tacking on newlines to the code attribute spec though.
# (see the call to inject_if_block() in DD::MethodInstaller::Simple->parser)
$proto =~ s/\s*#.*$//mg;
$self->{__nls} = $proto =~ s/[\r\n]//g;
$proto;
}
sub strip_attrs {
my $self = shift;
my ($attrs) = $self->SUPER::strip_attrs();
$attrs ||= '';
$attrs .= $/ x $self->{__nls} if $self->{__nls};
$attrs;
}
sub parse_proto {
my $self = shift;
my ($proto) = #_;
$proto ||= '';
$proto =~ s/\s*#.*$//mg;
$proto =~ s/^\s+//mg;
$proto =~ s/\s+$//mg;
$proto =~ s/[\r\n]//g;
my $invocant = $self->{invocant};
$invocant = $1 if $proto =~ s{(\$\w+)\s*:\s*}{};
my $inject = '';
$inject .= "my ${invocant} = shift;" if $invocant;
$inject .= "my ($proto) = \#_;" if defined $proto and length $proto;
$inject .= "$inject_code" if $inject_code;
$inject .= '();'; # fix for empty method body
return $inject;
}
Moops and Kavorka provide a syntax almost compatible with MooseX::Declare and MooseX::Method::Signatures, and are designed to be very extensible (even from within!) via traits. I'll draw your attention to the following section of documentation for MooseX::Declare:
Warning: MooseX::Declare is based on Devel::Declare, a giant bag of crack originally implemented by mst with the goal of upsetting the perl core developers so much by its very existence that they implemented proper keyword handling in the core.
[...]
If you want to use declarative syntax in new code, please for the love of kittens get yourself a recent perl and look at Moops instead.
MooseX::Declare itself is not very easy to extend. I know. I've tried.
So bearing all that in mind, and also because I wrote Moops, I'll use that for the example. Here we define a role Kavorka::TraitFor::Sub::ProvidesMe which is will inject a little bit of code into a method. We then apply that role to a method using does ProvideMe.
package main;
use Moops;
role Kavorka::TraitFor::Sub::ProvideMe
{
around inject_prelude (#_)
{
my $prelude = $self->$next(#_);
$prelude .= 'my ($me) = $self->me;();';
return $prelude;
}
}
class MyClass
{
method me () { "tobyink" }
method example () does ProvideMe
{
# This gets injected: my ($me) = $self->me;
return $me;
}
}
my $obj = MyClass->new;
say $obj->example; ## says "tobyink"
I'm writing an email service which sends data to my users with Email::Sender::Simple and Email::Sender::Transport::SMTPS. Right now, I've got a package which should just take some inputs and send an e-mail:
package MyApp::Service::Mail;
use Email::Sender::Simple qw(sendmail);
use Email::Simple;
use Email::Sender::Transport::SMTPS;
use Try::Tiny;
use Dancer;
use constant CANT_SEND_MAIL => -1;
use constant SENT_SUCCESSFULLY => 1;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub sendEmail {
my $self = shift;
my $to = shift;
my $subject = shift;
my $body = shift;
my $failed = 0;
my $email = Email::Simple->create(
header => [
To => $to,
From => 'noreply#myapp.com',
Subject => $subject
],
body => $body
);
my $transport = Email::Sender::Transport::SMTPS->new({
host => config->{smtp_host},
port => config->{smtp_port},
sasl_username => config->{smtp_username},
sasl_password => config->{smtp_password},
ssl => 'ssl'
});
try {
sendmail($email, {transport => $transport});
} catch {
$failed = 1;
}
return $self->CANT_SEND_MAIL if ($failed eq 1);
return $self->SENT_SUCCESSFULLY;
}
1;
This is based heavily on the example from the CPAN page for the modules involved.
Note that those config variables are coming from Dancers config.yml, and I have confirmed they are being passed in correctly. I have also confirmed that $to, $body and $subject contain what I expect them to.
The sendEmail function is being called and returning 1 (SENT_SUCCESSFULLY) but I cannot see anything in the Sent box on my e-mail client, and there is nothing at the receiving address either. I've been trying to find some kind of debug function to delve deeper into why this is failing, but to no avail.
Code calling this is:
package MyApp::Service::Mail::User;
use MyApp::Service::Mail;
our #ISA = qw(MyApp::Service::Mail);
sub sendPasswordByEmail {
my $self = shift;
my $to = shift;
my $username = shift;
my $subject = "Test E-Mail";
(my $body = << " END_MESSAGE") =~ s/^ {4}//gm;
Dear $username,
This is a test e-mail.
END_MESSAGE
return $self->sendEmail($to, $subject, $body);
}
1;
I can confirm the SMTP account definitely works as I use it in my e-mail client (Thunderbird). Any suggestions as to why this function could be returning 1 with no success? Is there a way to debug the connection between this and my SMTP server (it's 3rd party so can't check the logs) to see if a connection is being established and what's being passed / whether there's a problem?
There is a semicolon missing after the try {} catch {} block. Without it, the following line becomes part of the same statement, so the entire try/catch block is conditional based on $failed, which will never be 1 at that point.
This is an unfortunate side effect of the implementation of Try::Tiny, but it can't be avoided in a pure-perl implementation. Your existing code is parsed is like:
try(
sub {
sendmail($email, {transport => $transport});
},
catch(
sub {
$failed = 1;
},
return($self->CANT_SEND_MAIL),
),
) if ($failed eq 1);
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.
I'm trying to use the Net::OAuth module to authorise with the Yammer API and I have the following code snippet, pretty much taken from the Synopsis on CPAN.
$Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
my $q = new CGI;
my $request = Net::OAuth->request("request token")->from_hash($q->Vars,
request_url => $self->_request_token_url,
request_method => $q->request_method,
consumer_secret => $self->consumer_private,
);
But if I try and run my test it throws an error as follows:
Expected a hash! at /Library/Perl/5.8.8/Net/OAuth/Message.pm line 241.
Have I made an obvious syntax error or am I going to have to look at the OAuth module itself?
$q->Vars returns a hash reference in scalar context and a flattened hash in list context. Subroutine arguments create list context. Therefore, you should do:
my $request = Net::OAuth->request("request token")->from_hash(
scalar $q->Vars,
request_url => $self->_request_token_url,
request_method => $q->request_method,
consumer_secret => $self->consumer_private,
);
Thanks to Adam Bellaire for the comment that made me check this.
In the Net::OAuth::Message
sub from_hash {
my $proto = shift;
my $class = ref $proto || $proto;
my $hash = shift;
if (ref $hash ne 'HASH') {
die 'Expected a hash!';
}
my %api_params = #_;
Maybe you can make sure that $q->Vars returns a hash ref
my $vars = $q->Vars;
print ref($vars);