In a unit test I need to set a global variable used in a perl script that I have changed into a modulino. I'm calling subs in the modulino quite happily.
Using perl (v5.18.2) built for x86_64-linux-gnu-thread-multi, on Ubuntu.
Note the modulino is so simple it doesn't even require the "caller()" trick.
test.pl
#!/usr/bin/perl
use strict;
use warnings;
my %config =
(
Item => 5,
);
sub return_a_value{
return 3;
}
test1.t
#!/user/bin/perl -w
use warnings;
use strict;
use Test::More;
use lib '.';
require_ok ( 'test.pl' );
print return_a_value();
test2.t
#!/user/bin/perl -w
use warnings;
use strict;
use Test::More;
use lib '.';
require_ok ( 'test.pl' );
$config{'Item'} = 6;
test1.t displays as expected
ok 1 - require 'test.pl';
3# Tests were run but no plan was declared and done_testing() was not seen
test2.t (fails to compile)
Global symbol "%config" requires explicit package name at test.t line 8.
Execution of test.t aborted due to compilation errors.
As pointed out by choroba, my variables aren't global. The best solution for me, and what I should have done to start with is to add a setter sub to the modulino, something like:
sub setItem
{
$config{'Item'} = shift;
return;
}
and since I am now going to want a unit test for that, a getter is also a good idea
sub getItem
{
return $config{'Item'};
}
Related
I am writing new Perl 5 module Class::Tiny::ConstrainedAccessor to check type constraints when you touch object attributes, either by setting or by getting a default value. I am writing the unit tests and want to run the accessors for the latter case. However, I am concerned that Perl may optimize away my accessor-function call since the return value is discarded. Will it? If so, can I tell it not to? Is the corresponding behaviour documented? If the answer is as simple as "don't worry about it," that's good enough, but a reference to the docs would be appreciated :) .
The following MCVE succeeds when I run it on my Perl 5.26.2 x64 Cygwin. However, I don't know if that is guaranteed, or if it just happens to work now and may change someday.
use 5.006; use strict; use warnings; use Test::More; use Test::Exception;
dies_ok { # One I know works
my $obj = Klass->new; # Default value of "attribute" is invalid
diag $obj->accessor; # Dies, because the default is invalid
} 'Bad default dies';
dies_ok {
my $obj = Klass->new;
$obj->accessor; # <<< THE QUESTION --- Will this always run?
} 'Dies even without diag';
done_testing();
{ package Klass;
sub new { my $class = shift; bless {#_}, $class }
sub check { shift; die 'oops' if #_ and $_[0] eq 'bad' }
sub default { 'bad' }
sub accessor {
my $self = shift;
if(#_) { $self->check($_[0]); return $self->{attribute} = $_[0] } # W
elsif(exists $self->{attribute}) { return $self->{attribute} } # R
else {
# Request to read the attribute, but no value is assigned yet.
# Use the default.
$self->check($self->default); # <<<---- What I want to exercise
return $self->{attribute} = $self->default;
}
} #accessor()
} #Klass
This question deals with variables, but not functions. perlperf says that Perl will optimize away various things, but other than ()-prototyped functions, it's not clear to me what.
In JavaScript, I would say void obj.accessor();, and then I would know for sure it would run but the result would be discarded. However, I can't use undef $obj->accessor; for a similar effect; compilation legitimately fails with Can't modify non-lvalue subroutine call of &Klass::accessor.
Perl doesn't ever optimize away sub calls, and sub calls with side effects shouldn't be optimised away in any language.
undef $obj->accessor means something similar to $obj->accessor = undef
I need the perl regex to split the following value
$path = 'C:\Users\goudarsh\Desktop\Perl_test_scripts\sample';
i tried following code seems not working
my #var = split(/\\/,$path);
print #var;
if(grep /rtl2gds/, #var){
print $path;
}
i am not getting where i am doing wrong.
even i tried following
my #var = split(//\/,$path);
print #var;
if(grep /rtl2gds/, #var){
print $path;
}
Instead of relying on manual splitting, I recommend using File::Spec
use File::Spec;
my ($volume, $dir, $file) = File::Spec->splitpath($path);
my #components = File::Spec->splitdir($dir);
push #components, $file;
Now #components is your desired array with a safer and more portable implementation.
Your example works fine... have you actually run it?
use strict;
use warnings;
use Data::Dumper;
my $path = 'C:\Users\goudarsh\Desktop\Perl_test_scripts\sample';
my #var = split(/\\/, $path);
print Dumper(\#var);
Output:
$VAR1 = [
'C:',
'Users',
'goudarsh',
'Desktop',
'Perl_test_scripts',
'sample'
];
Because the Path::File docs are a bit convoluted, here is an example:
use strict;
use warnings;
use 5.020;
use Path::Class; # Exports file() by default
my $path = file('/Users/7stud/perl_programs/myprog.pl');
say $path->basename; # => myprog.pl
say $path->dir; # => /Users/7stud/perl_programs
say $path->volume; # => ""
my #components = $path->components;
for my $component (#components) {
say "-->$component<--";
}
--output:--
--><--
-->Users<--
-->7stud<--
-->perl_programs<--
-->myprog.pl<--
On Windows, a module will load automatically that understands Window's style paths. To examine a Window's style path on a Unix system:
use strict;
use warnings;
use 5.020;
use Path::Class qw{ foreign_file };
my $path = foreign_file('Win32', 'C:\Users\goudarsh\Desktop\Perl_test_scripts\sample');
say $path->basename; # => sample
say $path->dir; # => C:\Users\goudarsh\Desktop\Perl_test_scripts
say $path->volume; # => C:
my #components = $path->components;
for my $component (#components) {
say "-->$component<--";
}
--output:--
--><--
-->Users<--
-->goudarsh<--
-->Desktop<--
-->Perl_test_scripts<--
-->sample<--
I have a URL like "www.google.com/aabc/xyz". How can I get host name from this? I used this code:
my $referer = URI->new('www.google.com/aabc/xyz');
my $host = $referer->host; //compiler error
I'm getting error at the second line.
use URI;
use URI::Heuristic qw(uf_uristr);
my $referrer = URI->new( uf_uristr('www.google.com/aabc/xyz') );
print $referrer->host;
The question changed significantly since my first answer, which I've deleted. With high enough rep you can see it.
You have in the code (it's better to post complete programs):
my $referer = URI->new('www.google.com/aabc/xyz');
my $host = $referer->host; //compiler error
You say that you're getting a compiler error, but it's really a runtime error:
Can't locate object method "host" via package "URI::_generic"
When you made the new object, you gave URI a string. From that, it's going to guess what sort of URI it is. Since there's no scheme, such as http://, in front of it, it doesn't guess that it's that sort of URI. Instead, it falls back to a "generic" class URI::_generic. By the underscore in its name and the fact there's no documentation for it, you may surmise it's not meant for you to know about.
But, here it is complaining. It thinks the URI is a path (and some other things). The part you recognize as the host it parses as a path:
use v5.10;
use URI;
my $referer = URI->new('www.google.com/aabc/xyz');
my $path = $referer->path;
say "path is $path";
Now you see what it did:
path is www.google.com/aabc/xyz
The generic URI doesn't know anything about a host, so when you call host on its object, it blows up. It would be nicer for it to return undef, perhaps, but that's not what it does.
oanders already has an interesting answer that guesses for you to fill in schemes when it thinks they might be missing, but there's another thing you can do. Before you call host, check that the object can respond to it:
use v5.10;
use URI;
my $url = 'www.google.com/aabc/xyz';
my $referer = URI->new( $url );
if( $referer->can( 'host' ) ) {
say "Host is " . $referer->host;
}
else {
say "Weird hostless URL: $referer";
}
Now your program shouldn't blow up for the same reason and you can look at the output to discover strings that you couldn't process.
$ echo -e "http://www.google.www.com/abc/xyz\nhttps://google.com\nwww.google.www.com"
http://www.google.www.com/abc/xyz
https://google.com
www.google.www.com
$ echo -e "http://www.google.www.com/abc/xyz\nhttps://google.com\nwww.google.www.com" | perl -pe "s/^(http(s)?:\/\/)?(www\.)?//"
google.www.com/abc/xyz
google.com
google.www.com
You can do it much simpler than above.
CODE
use strict;
use warnings;
while (<DATA>) {
$_ =~ s/^(https?:\/\/)?(www.)?\b//;
print $_ ;
}
__DATA__
http://www.google.com/abc/xyz
https://google.com
www.google.com
Results
google.com/abc/xyz
google.com
google.com
I have a problem passing a string argument using Perl. The following code
#!/usr/bin/perl -w
use SOAP::Lite;
my $service = SOAP::Lite->service('http://localhost:8080/greeting?wsdl');
print $service->greetClient('perl wooooo'), "\n";
Results in
Greeting null! Have a nice day...
A similar python code
from suds.client import Client
client = Client('http://localhost:8080/greeting?wsdl')
print client.service.greetClient('python wooooo')
works perfectly
Greeting python wooooo! Have a nice day...
I tried to set different encodings
print $service->encoding('utf-8')->greetClient("perl wooooo"), "\n";
with the same result.
A SOAP Monitor shows that there is no arg0 in a case of Perl
<greetClient xsi:nil="true" xsi:type="tns:greetClient" />
which is present in a case of Python
<ns0:greetClient>
<arg0>python wooooo</arg0>
</ns0:greetClient>
What can be a problem?
Why it's so complicated to implement a SOAP client with Perl compared to Python?
EDIT:
SOLUTION
Finally the following solution is working
#!/usr/bin/perl -w
use strict;
use warnings;
use XML::Compile::SOAP11;
use XML::Compile::WSDL11;
use XML::Compile::Transport::SOAPHTTP;
my $soap = XML::Compile::WSDL11->new('c:/temp/greeting.wsdl');
my $call = $soap->compileClient('greetClient');
print $call->(arg0 => 'perl wooooo'){'greetClientResponse'}{'return'}, "\n";
SOAP::Lite can be infuriatingly bad. You might give XML::Compile::SOAP a try:
use strict;
use warnings;
use XML::Compile::SOAP11;
use XML::Compile::WSDL11;
use XML::Compile::Transport::SOAPHTTP;
my $soap = XML::Compile::WSDL11->new(
'http://localhost:8080/greeting?wsdl',
schema_dirs => [
'c:/soft/Perl/site/lib/XML/Compile/SOAP11/xsd'
'c:/soft/Perl/site/lib/XML/Compile/XOP/xsd'
'c:/soft/Perl/site/lib/XML/Compile/xsd'
]
);
$soap->compileCalls;
my ( $response, $trace ) = $soap->call( 'greetClient', arg0 => 'perl wooooo' );
$trace->printResponse;
$response will be the call response converted to a hashref via XML::Simple, which may be all you need. The $trace object is handy to see what the raw XML response looks like.
Unfortunately, I can't see your WSDL.
But in regards to SOAP::Lite, I don't see you setting up neither a proxy (endpoint) nor an uri.
You're also probably going to have to change the on_action behavior as well. By default, SOAP::Lite wants to use the '#' concatenation.
So something along these lines might work.
$service->proxy( $uri_of_my_end_point );
$service->uri( $schema_namespace );
$service->on_action( sub {
my ( $uri, $method ) = #_;
my $slash = $uri =~ m{/$} ? '' : '/';
return qq{"$uri$slash$method"};
});
We've developed an open web API using Apache and mod_perl, where you can pass text created by Data::Dumper to make requests.
Our data generally looks like this:
$VAR1 = {
'OurField' => 'OurValue'
};
Currently, I noticed we're using an eval to get the data back into a Perl hash server side:
my $VAR1;
eval $our_dumper_string;
#$VAR1 is now filled with hash value
The problem with this, is it is a major security issue. You can pass malicious perl code in there and it will run server side...
It there a better way to safely take a Data::Dumper string and turn it into a hash?
Yes. Use JSON::XS and use JSON rather than Data::Dumper format. That is much more compatible with other web APIs
If your data is simple and predictable you can even try to write a simple "parser" to read back the values in a data stricture
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $data = { 'key1' => 'value' };
my $dumper = Dumper($data);
print $dumper;
my $data_2;
while( $dumper =~ /(.+)$/mg) {
if ( $1 =~ m/'(.*)' => '(.*)'/ ) {
$data_2->{$1} = $2;
}
}
print Dumper( $data_2 );
(this is just an example and wont work with integers or nested data structures)