How to get rid of 'Name ... used only once: possible typo at ...'? - perl

use YAML::XS;
local $YAML::XS::DumpCode=1;
...
I get the warning:
Name "YAML::XS::DumpCode" used only once: possible typo at ..
Well, I know I can suppress this specific warning, but it's kind'a ugly. Am I doing anything wrong? I'm not used to be warned :)

It seems like $YAML::XS::DumpCode is only used from C code, and it is never initialized in YAML/XS.pm (it is there, but commented out). So that might be a bug to submit against that module.
In the mean time, no warnings 'once'; should do the trick.

There is no global variable declared with the name $YAML::XS::DumpCode. This configuration is in the YAML class, so you should set it with local $YAML::DumpCode = 1;: see the documentation.

Related

How to add a package alias by monkey-patching

I'm trying to work around a buggy package without modifying the source:
A long subroutine in Treex::PML::Schema::Container.pm contains two references to the non-existent package Treex::PML::FSFactory. It should be Treex::PML::Factory.
The offending subroutine is too long to copy and monkey-patch from another file, so I'd like to monkey-patch Treex::PML so that it offers Treex::PML::FSFactory as an alias for Treex::PML::Factory.
In my main program I tried the following (based on this recipe), but it doesn't seem to have any effect:
use Treex::PML;
package Treex::PML;
use Package::Alias FSFactory => 'Treex::PML::Factory';
package MyPackage;
...
How can I get Treex::PML::FSFactory to spring into existence, in such a way that it is visible to Treex::PML::Schema::Container?
Edit: This particular bug was fixed within hours of my bug report! But it's useful to know how to alias packages anyway. Although I solved my problem by subclassing, as the accepted answer recommends, I've added a self-answer showing how it can be done with aliases.
Given the references in question,
$content = Treex::PML::FSFactory->createSeq(...)
$content = Treex::PML::FSFactory->createList(...)
Adding the following should be sufficient:
{
package Treex::PML::FSFactory;
our #ISA = qw( Treex::PML::Factory );
}
I checked if Treex::PML::Factory::createSeq and Treex::PML::Factory::createList mind being called via a subclass, and they don't; they completely ignore their invocant.
#ikegami's suggestion, to derive a class instead of aliasing, worked well. But here is also how to alias one package name to another, as the question asks:
a. With Package::Alias:
use Package::Alias 'Treex::PML::FSFactory' => 'Treex::PML::Factory';
b. To avoid downloading Package::Alias from cpan, I followed this recipe which shows how Package::Alias actually does it. It worked :-)
*Treex::PML::FSFactory:: = \*Treex::PML::Factory::;

wampserver configuration - how to enable constants?

In my main php file I use the method define() to set some constants. The idea is to use them to load some user settings and other configuration controls, but when the time to use them comes an error message is displayed like this:
Notice: Use of undefined constant CONSTANT_NAME - assumed 'CONSTANT_NAME' in (file path) on line X
For some problems there is a work around, declaring a variable is useful but does not solve all the problems.
Thanks in advance!
PS: I use a settings file too but the problem persists.
OK, the mistake was that CONSTANT_NAME was interpreted as a string because of the absence of single or double quotes. A small mistake born only for eagle eyes!
Correct way is: defined('CONSTANT_NAME');
They do not need any special settings. You just create one like:
define('TEST_NAME', 'biotox');
and you should be able to call it with:
echo TEST_NAME;
and the output should be biotox. I tested it on my WampServer without a problem.

Why can't my Perl object find its skip() method, even though I can call it as a subroutine?

I'm working on a Perl module and whenever I call the skip() method I wrote in the following way:
$cursor->skip(4);
I get:
Undefined subroutine &MyModule::Cursor::skip called at t/tester.pl line 24.
(in cleanup) invalid object at t/tester.pl line 24.
When I call it like:
MyModule::Cursor::skip($cursor, 4);
Perl finds it!
Oddly, if I name "skip" anything else ("skipper", "hello"), this syntax works:
$cursor->skipper(4);
I thought maybe skip() was a "secret" reserved key word or something, but I've also got methods named sort() and next() (which I know are reserved), and those work fine.
I'd really like to name this method "skip." Does anyone know why Perl can't find it?
skip() is exported from Test::More, which you might have loaded since your executable is named t/tester.pl.
What does ref($cursor) yield you? It should be a blessed MyModule::Cursor object, but the "invalid object" error might be suggesting the object was not constructed properly.
EDIT: perldiag gives another clue: "in cleanup" signifies that a problem was encountered by the object's destructor. Assuming you don't already have a destructor in the object, create a MyModule::Cursor::DESTROY method that Data::Dumps the object to see what it looks like at this time.
A concise snippet of example code that exhibits this behaviour would be very helpful.
Without actual code, it's difficult to debug this.
Do you use MyModule::Cursor in your test code? When you replaced skip with skipper, were you calling it in exactly the same way from your test module? Are you able to use skip from a throw-away (one-liner or very short script)?
Where I'm going with this is looking for an error in your test code, rather than the module.
UPDATE: You're not doing something like declaring methods on MyModule::Cursor in two different files, are you? The error message you're getting tells me it has a blessed reference to an object of type MyModule::Cursor, so it knows about the class; but then it can't find the definition of skip. Do you happen to declare part of MyModule::Cursor in one file, and skip in another, and your test module isn't including the second file? Or do you have a syntax error somewhere around your definition of skip -- a missing semi-colon or unpaired curly brace? (But then again, why would MyModule::Cursor::skip work where $cursor->skip does not?)

How can I mock %ENV in Perl tests?

I'm trying to retrofit some tests using Test::More to legacy code and I've bumped into a bit of a snag. I don't seem to be able to set %ENV in the test module. The called function definitely uses this variable so %ENV doesn't seem to be carried across to the test object.
#!/usr/bin/perl
use strict; use warnings;
use Test::More qw(no_plan);
BEGIN {
$ENV{HTTP_WWW_AUTHENTICATE} =
'WWW-Authenticate: MyType realm="MyRealm",userid="123",password="abc"';
use_ok('Util');
}
$ENV{HTTP_WWW_AUTHENTICATE} =
'WWW-Authenticate: MyType realm="MyRealm",userid="123",password="abc"';
printf qq{get_authentication_info = "%s"\n}, get_authentication_info();
ok(get_authentication_info(), 'Get authentication info');
I keep getting...
perl t\Util.t
ok 1 - use Util;
Use of uninitialized value in concatenation (.) or string at t\Util.t line 14.
get_authentication_info = ""
As with all things Perl, I'm pretty sure that some one has done this before.
UPDATE: Thanks to all for your help
The problem was between the keyboard & chair ... My test data was just plain wrong
It needed to be
$ENV{HTTP_WWW_AUTHENTICATE} =
'MyType realm="MyRealm",userid="123",password="abc"';
As Sinan said, the $ENV{...} lines are commented out, so it can't work. But if you want really testable code, I'd suggest to make the get_authentication_info function take a hash as an argument. That way you can test it without setting the global variable, and in the real code you can pass the real enviromnent hash. Global state will always become a problem eventually.
What does
get_authentication_info()
return?
My guess is nothing.
If this is always true, then line 14 will always return the "Use of uninitialized value..." warning.
If you expect a value, you need to investigate why get_authentication_info() is failing?
Agreed with Lukáš -- get your global environment (and perform validity checks etc) all in one place, such as in its own method, and then pass those values to all other methods that need it. That way in your unit tests you can just drop in a replacement method that determines the environment and config variables in a different way (such as from a file, or directly set at the top of your test script).
Why are the lines setting $ENV{HTTP_WWW_AUTHENTICATE} commented out?
Also, what are the specs for get_authentication_info()?
Try setting the env variable before BEGIN.
If not try this:
First, go to a command prompt and set the env var there. Then run your script. If the tests pass. Then as you predicted, the problem is with setting the env var.
If the tests fail, then the problem lies some where else (probably in get_authentication_info).

Perl: variable scope issue with CGI & DBI modules

I've run into what appears to be a variable scope issue I haven't encountered before. I'm using Perl's CGI module and a call to DBI's do() method. Here's the code structure, simplified a bit:
use DBI;
use CGI qw(:cgi-lib);
&ReadParse;
my $dbh = DBI->connect(...............);
my $test = $in{test};
$dbh->do(qq{INSERT INTO events VALUES (?,?,?)},undef,$in{test},"$in{test}",$test);
The #1 placeholder variable evaluates as if it is uninitialized. The other two placeholder variables work.
The question: Why is the %in hash not available within the context of do(), unless I wrap it in double quotes (#2 placeholder) or reassign the value to a new variable (#3 placeholder)?
I think it's something to do with how the CGI module's ReadParse() function assigns scope to the %in hash, but I don't know Perl scoping well enough to understand why %in is available at the top level but not from within my do() statement.
If someone does understand the scoping issue, is there a better way to handle it? Wrapping all the %in references in double quotes seems a little messy. Creating new variables for each query parameter isn't realistic.
Just to be clear, my question is about the variable scoping issue. I realize that ReadParse() isn't the recommended method to grab query params with CGI.
I'm using Perl 5.8.8, CGI 3.20, and DBI 1.52. Thank you in advance to anyone reading this.
#Pi & #Bob, thanks for the suggestions. Pre-declaring the scope for %in has no effect (and I always use strict). The result is the same as before: in the db, col1 is null while cols 2 & 3 are set to the expected value.
For reference, here's the ReadParse function (see below). It's a standard function that's part of CGI.pm. The way I understand it, I'm not meant to initialize the %in hash (other than satisfying strict) for purposes of setting scope, since the function appears to me to handle that:
sub ReadParse {
local(*in);
if (#_) {
*in = $_[0];
} else {
my $pkg = caller();
*in=*{"${pkg}::in"};
}
tie(%in,CGI);
return scalar(keys %in);
}
I guess my question is what is the best way to get the %in hash within the context of do()? Thanks again! I hope this is the right way to provide additional info to my original question.
#Dan: I hear ya regarding the &ReadParse syntax. I'd normally use CGI::ReadParse() but in this case I thought it was best to stick to how the CGI.pm documentation has it exactly.
It doesn't actually look like you're using it as described in the docs:
https://metacpan.org/pod/CGI#COMPATIBILITY-WITH-CGI-LIB.PL
If you must use it, then CGI::ReadParse(); seems more sensible and less crufty syntax. Although I can't see it making much difference in this situation, but then it is a tied variable, so who the hell knows what it's doing ;)
Is there a particular reason you can't use the more-common $cgi->param('foo') syntax? It's a little bit cleaner, and filths up your namespace in a considerably more predictable manner..
use strict;. Always.
Try declaring
our %in;
and seeing if that helps. Failing that, strict may produce a more useful error.
I don't know what's wrong, but I can tell you some things that aren't:
It's not a scoping issue. If it were then none of the instances of $in{test} would work.
It's not the archaic & calling syntax. (It's not "right" but it's harmless in this case.)
ReadParse is a nasty bit of code. It munges the symbol table to create the global variable %in in the calling package. What's worse is that it's a tied variable, so accessing it could (theoretically) do anything. Looking at the source code for CGI.pm, the FETCH method just invokes the params() method to get the data. I have no idea why the fetch in the $dbh->do() isn't working.
Firstly, that is not in the context/scope of do. It is still in the context of main or global. You dont leave context until you enter {} in some way relating to subroutines or different 'classes' in perl. Within () parens you are not leaving scope.
The sample you gave us is of an uninitialized hash and as Pi has suggested, using strict will certainly keep those from occuring.
Can you give us a more representative example of your code? Where are you setting %IN and how?
Something's very broken there. Perl's scoping is relatively simple, and you're unlikely to stumble upon anything odd like that unless you're doing something daft. As has been suggested, switch on the strict pragma (and warnings, too. In fact you should be using both anyway).
It's pretty hard to tell what's going on without being able to see how %in is defined (is it something to do with that nasty-looking ReadParse call? why are you calling it with the leading &, btw? that syntax has been considered dead and gone for a long time). I suggest posting a bit more code, so we can see what's going on..
What version of DBI are you using? From looking at the DBI changelog it appears that versions prior to 1.00 didn't support the attribute argument. I suspect that the "uninitialized" $in{test} is actually the undef that you're passing to $dbh->do().
From the example you gave, this is not a scoping issue, or none of the parameters would work.
Looks like DBI (or a DBD, not sure where bind parameters are used) isn't honoring tie magic.
The workaround would be to stringize or copy what you pass to it, like your second and third parameters do.
A simple test using SQLite and DBI 1.53 shows it working ok:
$ perl -MDBI -we'sub TIEHASH { bless {} } sub FETCH { "42" } tie %x, "main" or die; my $dbh = DBI->connect("dbi:SQLite:dbname=dbfile","",""); $dbh->do("create table foo (bar char(80))"); $dbh->do("insert into foo values (?)", undef, $x{foo}); print "got: " . $dbh->selectrow_array("select bar from foo") . "\n"; $dbh->do("drop table foo")'
got: 42
Care to share what database you are using?
Per the DBI documentation: Binding a tied variable doesn't work, currently.
DBI is pretty complicated under the hood, and unfortunately goes through some gyrations to be efficient that are causing your problem. I agree with everyone else who says to get rid of the ugly old cgi-lib style code. It's unpleasant enough to do CGI without a nice framework (go Catalyst), let alone something that's been obsolete for a decade.
Okay, try this:
use CGI;
my %in;
CGI::ReadParse(\%in);
That might help as it's actually using a variable that you've declared, and therefore can control the scope of (plus it'll let you use strict without other nastiness that could be muddying the waters)
As this is starting to look like a tie() problem, try the following experiment. Save this as a foo.pl and run it as perl foo.pl "x=1"
use CGI;
CGI::ReadParse();
p($in{x}, "$in{x}");
sub p { my #a = #_; print "#a\n" }
It should print 1 1. If it doesn't, we've found the culprit.
I just tried your test codce from http://www.carcomplaints.com/test/test.pl.txt, and it works right away on my computer, no problems. I get three values as expected. I didn't run it as CGI, but using:
...
use CGI qw/-debug/;
...
I write a variable on the console (test=test) and your scripts inserts without a problem.
If however your leave this out, tt will insert an empty string and two NULLs. This is a because you interpolate a value into a string. This will makes a string with value of $in{test} which is undef at the moment. undef stringifies to an empty string, which is what is inserted into database.
Try this
%in = ReadParse();
but i doubt that. Are you trying to get query parameters or something?