MOJO perl Deprecated warnings - perl

I am learning Mojo, this is my first script and it works fine - that is it does what i want it to do.
use Mojo::UserAgent;
use Mojo::DOM;
use Mojo::Collection;
use strict;
use warnings;
my $ua = Mojo::UserAgent ->new;
my $dom = Mojo::DOM ->new;
$dom = $ua->get('http://lalalala....')->res->dom;
open (my $file_zlec, "> zlc2012.csv") or die "couldn't open file!";
binmode $file_zlec, ":utf8";
for my $zlec($dom->find('table.tbl_zlc')->each){
print "$i \n"; $i++;
wypisz($zlec, 'td.tbl_zlc_d1',1);
print $file_zlec"\n";
}
sub wypisz{
my $ch= $_[0]-> find($_[1])->children;
if ($_[2]==1){
my $tekst = $ch->text;
print $file_zlec "$tekst;";
}
my $href= $ch->map(attr => 'href');
if (not $href=~/lalala\.pl/g) {$href="www.lalala.pl".$href};
print $file_zlec "$href;";
}
However, a get a lot of warnings regarding "children", "text" and "attr":
'Mojo::Collection::AOUTOLOAD (..) is DEPRECATED in favor of MOJO::Collection::map at (..)'
'Stringifacation support in Mojo::Collection::AOUTOLOAD (...) is DEPRECATED in favor of Mojo::Collection::join at (..)
I thought i was doing smth wrong, but I followed the example at http://mojocasts.com/e5 EXCACTLY and got the same warnings
use Mojo::UserAgent;
my $ua = Mojo::UserAgent ->new;
print $ua->get('http://mojolicio.us')->res->dom->html->head->title;
My questions are:
1. should i worry? like i said, scripts works fine
2. if ans(1) is no, is there a way to turn this off? i can't see anything in a command line..

Okay it looks like these deprecations have been added recently. For example the AUTOLOAD deprecation was introduced at the end of October this year, and my version of Mojolicious preceded that so I wasn't seeing the messages you were getting. I've now upgraded my installation and understand the problem.
Unfortunately, the Mojolicious DEPRECATED messages are generated by the Carp module and they don't respond to the no warnings pragma. The only way I know to disable then is to set the warning handler to a null subroutine, like this
$SIG{__WARN__} == sub { };
but that is very heavy-handed as it silences all warnings instead of just the nuisance ones you are seeing.
Since the facility you are using is deprecated, it is likely to be removed from the Mojolicious suite in the near future, so it is best anyway if you learn to adopt the new standard from the outset.
The message says
Mojo::DOM::AUTOLOAD (html) is DEPRECATED in favor of Mojo::DOM::children
which means that they would prefer that you used $dom->children('html') instead of just $dom->html. But children returns a Mojo::Collection object, which is a set of nodes, so you need to select the first item of that collection. That turns
$dom->html->head->title
into the cumbersome
$dom->children('html')->first->children('head')->first->children('title')->first
so it is far better to use the at method which takes a CSS3 expression, and you can write
$dom->at('html > head > title')
which is fine, and generates no warnings. Note that it isn't an exact replacement for the children/first chain, as the entire document is being searched for a title node that is a child of a head node that is a child of an html node. But since the only place that this can occur is at the root of the document, the result is identical. Even this can be fixed by using the CSS3 item :root in place of html.
The complete program would look like this
use strict;
use warnings;
use Mojolicious;
my $ua = Mojo::UserAgent->new;
my $dom = $ua->get('http://mojolicio.us')->res->dom;
print $dom->at('html > head > title')->text;
output
Mojolicious - Perl real-time web framework

"should i worry"
You should aim to remove all warning messages that a program generates. You first need to understand exactly why you are getting them, and then you can make the choice to write your code differently or to temporarily disable the warnings once you are certain that the problem is benign.
As a guide, I disable warnings in much less than one percent of my programs. They are almost always an indication of a coding problem that can and should be resolved.

Related

Perl: CGI redirect from multiple submit buttons

I have a small CGI Script with 2 submit buttons. I want that the current Script redirects the User to another script, depending on which submit button is pressed.
The CGi-Script:
#!/usr/bin/perl -w
# Modules
use strict;
use warnings;
use CGI;
my $q = CGI->new();
print $q->header();
print $q->submit(-name=>'button',-value => 'disable');
print $q->submit(-name=>'button',-value => 'enable');
if ($q->param('button') eq "disable"){
print $q->redirect(-uri=>"http://1.1.1.1./cgi-bin/services/switch_XXX.cgi?disable");
} elsf ($q->param('button') eq "enable"){
print $q->redirect(-uri=>"http://1.1.1.1./cgi-bin/services/switch_XXX.cgi?enable");
} else {
}
But none of the actions is actually performed. The Error-Log shows the following:
[Tue Mar 06 11:48:44 2018] [error] [client XXXX] Use of uninitialized value in string eq at /var/www/cgi-bin/test.cgi line 23.
[Tue Mar 06 11:48:44 2018] [error] [client XXXX] Use of uninitialized value in string eq at /var/www/cgi-bin/test.cgi line 26.
Could someone of you tell me what causes the error and why the redirect is not working?
Many thanks in advance!
See "Generating a redirection header" in the CGI docs: "If you use redirection like this, you should not print out a header as well."
The messages you're seeing in the log are referring to the $q->param('button') eq "disable" checks: $q->param('button') is returning undef because the field has not been submitted yet, so you're comparing "disable" to the undefined value. These are warning messages only, that you can avoid by first checking if $q->param('button') has a true value before doing the eq comparison. (Note: In other cases, one might want to use defined to check for undef, because there are some values in Perl that are defined but still false, see Truth and Falsehood - but in this case, both "disable" and "enable" are true values.)
Also, your submit buttons need to be in a <form>. And note you've got a typo with elsf. This works for me:
#!/usr/bin/env perl
use strict;
use warnings;
use CGI;
my $q = CGI->new();
if ( $q->param('button') && $q->param('button') eq "disable" ) {
print $q->redirect(-uri=>"...");
} elsif ( $q->param('button') && $q->param('button') eq "enable" ) {
print $q->redirect(-uri=>"...");
} else {
print $q->header();
print $q->start_html;
print $q->start_form;
print $q->submit(-name=>'button', -value=>'disable');
print $q->submit(-name=>'button', -value=>'enable');
print $q->end_form;
print $q->end_html;
}
If you are just starting to write web applications in Perl, I would urge you to read CGI::Alternatives and decide whether you really want to use such old technology when newer and better (and still Perl-based) alternatives are available.
However, if you decide to stick with CGI (and CGI.pm) then there are a couple of things that can make your life easier.
It's a rare CGI program that needs two CGI objects
For most CGI programs, using an object-oriented approach is overkill. You rarely need objects. CGI.pm has a, simpler, function-based approach that you can use instead. Simply, import the functions that you want to use as you load the module:
use CGI qw[param header redirect];
Then use them without creating an object first:
if (param) {
print redirect(...);
} else {
print header(...);
}
The CGI-Generation functions are a terrible idea
It's even in the documentation.
HTML Generation functions should no longer be used
All HTML generation
functions within CGI.pm are no longer being maintained. Any issues,
bugs, or patches will be rejected unless they relate to fundamentally
broken page rendering.
The rationale for this is that the HTML generation functions of CGI.pm
are an obfuscation at best and a maintenance nightmare at worst. You
should be using a template engine for better separation of concerns.
See CGI::Alternatives for an example of using CGI.pm with the
Template::Toolkit module.
These functions, and perldoc for them, are considered deprecated, they
are no longer being maintained and no fixes or features for them will
be accepted. They will, however, continue to exist in CGI.pm without
any deprecation warnings ("soft" deprecation) so you can continue to
use them if you really want to. All documentation for these functions
has been moved to CGI::HTML::Functions.
Putting your HTML into an external template is a much better idea. And it will be easier for your front-end developer to edit. Yes, I realise you probably don't have a front-end developer on your project right now - but don't you want to plan ahead?
Having said that, your program becomes something like this:
#!/usr/bin/perl
use strict;
use warnings;
use CGI qw[param header redirect];
my $disable_url = '...';
my $enable_url = '...';
if (param('button') {
if (param('button') eq 'disable') {
print redirect($disable_url);
} elsif (param('button') eq 'enable') {
print redirect($enable_url);
}
exit;
}
print header;
print $some_output_that_is_generated_from_a_template.

perltex to globally use strict

To enforce clean and explicit code, I customarily
use strict;
when programming Perl.
I would like to keep this habit in perltex, too.
So where should I put this use strict; statement so that it governs all successive \perldo, \perlnewcommand, \perlnewenvironment, \perlrenewcommand and \perlrenewenvironment invocations in the perltex input file?
The following perltex example file runs without raising an error:
\documentclass[12pt]{article}
\usepackage{perltex}
\perldo{
my $scalar = "ok";
our #array = qw( array is fine );
%HASH = (
subject => "hash",
result => "perfect"
);
use strict;
}
\perlnewcommand\printscalar{
return $scalar;
}
\perlnewcommand\printarray{
return join ", ", #array;
}
\perlnewcommand\printhash{
return join ", ", map { sprintf "%s = %s", $_, $HASH{$_} } keys %HASH;
}
\begin{document}
Scalar: \printscalar
Array: \printarray
Hash: \printhash
\end{document}
It produces something similar to
That no error is raised shows that use strict; in the top \perldo argument is disregarded in the definition of \printscalar. The result also shows that the setting of $scalar was not known there any more because of the my. To avoid such mistakes, I would wish to receive an error
Global symbol "$scalar" requires explicit package name
whenever I forget to specify my or our when introducing a new variable.
A workaround to my problem is to include the statement
use strict;
in all \perldo, ... commands, and this can be done using macros. Nevertheless I wonder whether there is no possibility to avoid such re-statements.
The perltex CTAN documentation gives in section 3.2.3 a list of the loaded modules and pragmas. These include use strict;.
The docs are a little unclear on when this is the default, but it appears to be under --nosafe. Then this option should be the way to toggle it and get those defaults loaded.
Did you try setting it only in the first command used (like \perldo)? That may well be enough.
The --permit option allows "features" described in Opcode module to be specified, what is done using the module Safe. While I don't see how to directly make use of this, the discussion under Safe::reval method may be helpful.
I don't have the module installed here and can't try. I hope that this is of some use.
As the final resort, why not contact the author? You may have revealed a bug (in documentation), since the observed behavior seems to conflict the docs. Also, this may well be feasible to add.

Using filehandles in Perl to alter actively running code

I've been learning about filehandles in Perl, and I was curious to see if there's a way to alter the source code of a program as it's running. For example, I created a script named "dynamic.pl" which contained the following:
use strict;
use warnings;
open(my $append, ">>", "dynamic.pl");
print $append "print \"It works!!\\n\";\n";
This program adds the line
print "It works!!\n";
to the end of it's own source file, and I hoped that once that line was added, it would then execute and output "It works!!"
Well, it does correctly append the line to the source file, but it doesn't execute it then and there.
So I assume therefore that when perl executes a program that it loads it to memory and runs it from there, but my question is, is there a way to access this loaded version of the program so you can have a program that can alter itself as you run it?
The missing piece you need is eval EXPR. This compiles, "evaluates", any string as code.
my $string = q[print "Hello, world!";];
eval $string;
This string can come from any source, including a filehandle.
It also doesn't have to be a single statement. If you want to modify how a program runs, you can replace its subroutines.
use strict;
use warnings;
use v5.10;
sub speak { return "Woof!"; }
say speak();
eval q[sub speak { return "Meow!"; }];
say speak();
You'll get a Subroutine speak redefined warning from that. It can be supressed with no warnings "redefine".
{
# The block is so this "no warnings" only affects
# the eval and not the entire program.
no warnings "redefine";
eval q[sub speak { return "Shazoo!"; }];
}
say speak();
Obviously this is a major security hole. There is many, many, many things to consider here, too long for an answer, and I strongly recommend you not do this and find a better solution to whatever problem you're trying to solve this way.
One way to mitigate the potential for damage is to use the Safe module. This is like eval but limits what built in functions are available. It is by no means a panacea for the security issues.
With a warning about all kinds of issues, you can reload modules.
There are packages for that, for example, Module::Reload. Then you can write code that you intend to change in a module, change the source at runtime, and have it reloaded.
By hand you would delete that from %INC and then require, like
# ... change source code in the module ...
delete $INC{'ModuleWithCodeThatChages.pm'};
require ModuleWithCodeThatChanges;
The only reason I can think of for doing this is experimentation and play. Otherwise, there are all kinds of concerns with doing something like this, and whatever your goal may be there are other ways to accomplish it.
Note The question does specify a filehandle. However, I don't see that to be really related to what I see to be the heart of the question, of modifying code at runtime.
The source file isn't used after it's been compiled.
You could just eval it.
use strict;
use warnings;
my $code = <<'__EOS__'
print "It works!!\n";
__EOS__
open(my $append_fh, ">>", "dynamic.pl")
or die($!);
print($append_fh $code);
eval("$code; 1")
or die($#);
There's almost definitely a better way to achieve your end goal here. BUT, you could recursively make exec() or system() calls -- latter if you need a return value. Be sure to setup some condition or the dominoes will keep falling. Again, you should rethink this, unless it's just practice of some sort, or maybe I don't get it!
Each call should execute the latest state of the file; also be sure to close the file before each call.
i.e.,
exec("dynamic.pl"); or
my retval;
retval = system("perl dynamic.pl");
Don't use eval ever.

why do i have to specify "use feature :5.1x" even when my installed perl is 5.14?

I have 5.14.2 installed in my machine but when i try to execute a print statement with say keyword it gives me error.
I have go with the use keyword to make the program run without error.
#!/usr/bin/perl
use feature ':5.10';
say "hello";
5.14.2 is latest when compared to 5.010 so it should have all those features enabled by default right ? then what is the point in specifying the version using use keyword ?
Perl attempts to maintain backward compatibility. It's quite possible that existing scripts might have subroutines named say. There is considerable on-going discussion of whether or not some future version of Perl should stop these efforts and streamline its internals. See, for instance, naming and numbering perl .
It prevents conflicts with existing programs written in Perl.
For example, say I wrote a program for Perl 5.6 which defined a subroutine called say.
use strict;
use warnings;
sub say { print 1; }
say();
That works fine (outputting 1), and it still works in perls that include the say feature.
Now let's enable the native say and see what happens:
use v5.14;
use strict;
use warnings;
sub say { print 1; }
say();
Now it falls over with *Use of uninitialized value $_ in say at - line 5.*
You need to use feature or use v5.xx so that the new features can be loaded safely, i.e. when the author knows he wants to use them.

I serialized my data in Perl with Data::Dumper. Now when I eval it I get "Global symbol "$VAR1" requires explicit package name"

I serialized my data to string in Perl using Data::Dumper. Now in another program I'm trying to deserialize it by using eval and I'm getting:
Global symbol "$VAR1" requires explicit package name
I'm using use warnings; use strict; in my program.
Here is how I'm evaling the code:
my $wiki_categories = eval($db_row->{categories});
die $# if $#;
/* use $wiki_categories */
How can I disable my program dying because of "$VAR1" not being declared as my?
Should I append "my " before the $db_row->{categories} in the eval? Like this:
my $wiki_categories = eval("my ".$db_row->{categories});
I didn't test this yet, but I think it would work.
Any other ways to do this? Perhaps wrap it in some block, and turn off strict for that block? I haven't ever done it but I've seen it mentioned.
Any help appreciated!
This is normal. By default, when Data::Dumper serializes data, it outputs something like:
$VAR1 = ...your data...
To use Data::Dumper for serialization, you need to configure it a little. Terse being the most important option to set, it turns off the $VAR thing.
use Data::Dumper;
my $data = {
foo => 23,
bar => [qw(1 2 3)]
};
my $dumper = Data::Dumper->new([]);
$dumper->Terse(1);
$dumper->Values([$data]);
print $dumper->Dump;
Then the result can be evaled straight into a variable.
my $data = eval $your_dump;
You can do various tricks to shrink the size of Data::Dumper, but on the whole it's fast and space efficient. The major down sides are that it's Perl only and wildly insecure. If anyone can modify your dump file, they own your program.
There are modules on CPAN which take care of this for you, and a whole lot more, such as Data::Serializer.
Your question has a number of implications, I'll try to address as many as I can.
First, read the perldoc for Data::Dumper. Setting $Data::Dumper::Terse = 1 may suffice for your needs. There are many options here in global variables, so be sure to localise them. But this changes the producer, not the consumer, of the data. I don't know how much control you have over that. Your question implies you're working on the consumer, but makes no mention of any control over the producer. Maybe the data already exists, and you have to use it as is.
The next implication is that you're tied to Data::Dumper. Again, the data may already exist, so too bad, use it. If this is not the case, I would recommend switching to another storable format. A fairly common one nowadays is JSON. While JSON isn't part of core perl, it's pretty trivial to install. It also makes this much easier. One advantage is that the data is useful in other languages, too. Another is that you avoid eval STRING which, should the data be compromised, could easily compromise your consumer.
The next item is just how to solve it as is. If the data exists, for example. A simple solution is to just add the my as you did. This works fine. Another one is to strip the $VAR1: (my $dumped = $db_row->{categories}) =~ s/^\s*\$\w+\s*=\s*//;. Another one is to put the "no warnings" right into the eval: eval ("no warnings; no strict; " . $db_row->{categories});.
Personally, I go with JSON whenever possible.
Your code would work as it stood except that the eval fails because $VAR1 is undeclared in the scope of the eval and use strict 'vars' is in effect.
Get around this by disabling strictures within as tight a block as possible. A do block does the trick, like this
my $wiki_categories = do {
no strict 'vars';
eval $db_row->{categories};
};