Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
Ninja Rope
Oct 22, 2005

Wee.

checkeredshawn posted:

Ah, thanks for the ampersand tip, I was unsure about that also. The undefined value error message confuses me because I definitely declare it in the first block of code with

code:
my ($field, $alias, $fieldstring, $aliasstring);

An "undefined" variable does not mean it was not created with "my", it means it contains the value "undef" (similar to NULL value in a database). If you didn't declare the variable with "my" you would get a "Global symbol "$asdf" requires explicit package name" error.

Adbot
ADBOT LOVES YOU

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
Double check that whatever is setting alias is actually working. For fun, set the alias to a simple value like "apple" and see if that works. Your config parsing code looks okay, I'm starting to wonder what the actual lines look like.

Again, using lots of Data::Dumper would help a lot.

checkeredshawn
Jul 16, 2007

I figured it out, I had one block of text that didn't match either /^field/ or /^alias/ so it was coming out undef I guess. I altered the code to skip over that block of text and now it works. Thanks for all your help guys.

checkeredshawn
Jul 16, 2007

Anyone know of a Perl module that will gather command line arguments:

code:
 
perl someprogram.pl -option one two three -other four
into a hash like so:

-option => ('one', 'two', 'three')
-other => 'four'

Or how I might start thinking about writing this myself? Thanks.

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
The Getopt series. I recommend Getopt::Long, having used it many times at work.

checkeredshawn
Jul 16, 2007

Triple Tech posted:

The Getopt series. I recommend Getopt::Long, having used it many times at work.

Yeah, I've used Getopt::Long before, but the only problem is this time around there are 26 different options that can be passed to the command line. Is there any way to have Getopt just gather all of the options for me instead of having to type them all out manually?

Mario Incandenza
Aug 24, 2000

Tell me, small fry, have you ever heard of the golden Triumph Forks?
A naive implementation for your example:
code:
sub get_opts {
  my ($key, %opts);
  while (my $arg = shift @ARGV) {
    $arg =~ /^-/ and $key = $arg and $opts{$key} ||= [];
    $key and $arg !~ /^-/ and push @{ $opts{$key} }, $arg;
  }

  return \%opts;
}

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
Again I'm wont to question your initial design before proceeding. What one thing really has 26 dimensions?

checkeredshawn
Jul 16, 2007

Triple Tech posted:

Again I'm wont to question your initial design before proceeding. What one thing really has 26 dimensions?

It's a really long flat text file containing 26 fields of information for each object.

Subotai
Jan 24, 2004

checkeredshawn posted:

It's a really long flat text file containing 26 fields of information for each object.

Why dont you open the file and read the contents instead of accepting them on the command line?

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
Erm, why don't you slurp each line and construct a full model out of it, and then just selectively act on which fields you want? I don't see how this directly relates to command line options.

checkeredshawn
Jul 16, 2007

Subotai posted:

Why dont you open the file and read the contents instead of accepting them on the command line?

I forgot to mention the purpose of the script is to be able to filter the contents of the text file by field, and then to be able to print out specific attributes. Say I have 3 objects in the file with 3 attributes:

code:
-----------
Name: one
Color: red
Shape: circle
-----------
Name: two
Color: green
Shape: square
-----------
Name: three
Color: orange
Shape: triangle
-----------
I want to be able to say:

code:
$ perl someprogram.pl -name one -print color
red
Or something like

code:
$ perl someprogram.pl -name one two -shape square -print color
green
Edit: Also, it's ~20,000 lines of text, I already wrote something that would parse the entire text file and store a hash of each object's 26 attributes into an array, but I think it would make more sense to do the filtering while I'm parsing the text file, instead of filtering stuff after storing the entire text file into an array of hashes.

checkeredshawn fucked around with this message at 19:34 on Aug 6, 2008

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
drat, ain't that some poo poo. Well, if your arguments are really that dynamic, might as well parse @ARGV yourself, or look into the Getopt documentation. I've never had to deal with a moving target.

checkeredshawn
Jul 16, 2007

Triple Tech posted:

drat, ain't that some poo poo. Well, if your arguments are really that dynamic, might as well parse @ARGV yourself, or look into the Getopt documentation. I've never had to deal with a moving target.

I think I'm just gonna read all of the Getopt documentation I can find.

s139252
Jan 1, 1970
test

checkeredshawn posted:

I forgot to mention the purpose of the script is to be able to filter the contents of the text file by field, and then to be able to print out specific attributes. Say I have 3 objects in the file with 3 attributes:

So you are creating a way to query data. Would it not be simpler to import the data into a database? Have your script define which fields may be used for matching and which can be displayed, and generate SQL to pull what was requested. Maybe use a syntax such as this:

code:
$ perl show.pl --search="name=one,two" --print="color"

Filburt Shellbach
Nov 6, 2007

Apni tackat say tujay aaj mitta juu gaa!
Have a look at Getopt::Whatever and Getopt::Casual, I bet one of those will do what you need.

Kidane
Dec 15, 2004

DANGER TO MANIFOLD
I also like Getopt::Euclid, since it basically takes your documentation and generates command line arguments out of it. For this poster's needs though, I would probably parse @ARGV myself.

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
What's a compelling reason to use an INIT block versions just some code in main:: when making a module? My coworker says it's easier to tell how code in a module breaks if it's inside an INIT, versus main where it just says module use failed. I told him, you could see the error if you just ran the module as a script. Thoughts?

Subotai
Jan 24, 2004

Triple Tech posted:

What's a compelling reason to use an INIT block versions just some code in main:: when making a module? My coworker says it's easier to tell how code in a module breaks if it's inside an INIT, versus main where it just says module use failed. I told him, you could see the error if you just ran the module as a script. Thoughts?

INIT is the first thing to get called after compilation (except BEGIN blocks or 'use' statements, etc). The main reason to use it is to make sure you have certain code executed before anything else. You can think of it as a constructor I guess. I have never heard of it being used to find errors. Maybe he is thinking of eval?

*edit* typos

Subotai fucked around with this message at 17:34 on Aug 14, 2008

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
I've never heard of it being used for anything ever. What's a really good, typical example of someone using an INIT that can't just be in main before the subroutines?

Subotai
Jan 24, 2004

Triple Tech posted:

I've never heard of it being used for anything ever. What's a really good, typical example of someone using an INIT that can't just be in main before the subroutines?


It could be considered cleaner I suppose. I dunno, ask your co-worker to explain himself.

heeen
May 14, 2005

CAT NEVER STOPS

Triple Tech posted:

What's a compelling reason to use an INIT block versions just some code in main:: when making a module? My coworker says it's easier to tell how code in a module breaks if it's inside an INIT, versus main where it just says module use failed. I told him, you could see the error if you just ran the module as a script. Thoughts?

You put initializaton and unloading code into INIT, BEGIN and END blocks when you're writing scripts that get compiled once and then used precompiled, like FastCGI and PersistentPerl

edit: Irssi scripts, too.
edit: not necessarily irssi scripts, but I think I had a case once when I used it.

heeen fucked around with this message at 19:55 on Aug 14, 2008

s139252
Jan 1, 1970
test

Triple Tech posted:

What's a compelling reason to use an INIT block versions just some code in main:: when making a module? My coworker says it's easier to tell how code in a module breaks if it's inside an INIT, versus main where it just says module use failed. I told him, you could see the error if you just ran the module as a script. Thoughts?

If you're writing a module, why would you have related code in main::?

BEGIN blocks are eval'd as soon as they are defined, and then the block is undefined. An INIT block is executed before normal runtime, but like normal code (it is not eval'd). I think there is also CHECK and another type that happen even before INIT.

Remember use implies BEGIN around the code it loads.

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
I'm talking about the main:: section of the pm file itself. My understanding of a module is that 99% of it is subs. And there's some code outside of those subs sometimes, right? Things that need to be computed once, like lookup tables?

Or are you implying that there should pretty much never be that type of code and it should all be inside of some sort of block?

s139252
Jan 1, 1970
test

heeen posted:

You put initializaton and unloading code into INIT, BEGIN and END blocks when you're writing scripts that get compiled once and then used precompiled, like FastCGI and PersistentPerl

edit: Irssi scripts, too.
edit: not necessarily irssi scripts, but I think I had a case once when I used it.

Sort of. For FastCGI, you usually run a script that enters a loop to process requests. Any code run outside of the loop performs initialization that affects all requests regardless of BEGIN/INIT (ie, it is not run again within the loop). There's similar implications for forking, of course.

s139252
Jan 1, 1970
test

Triple Tech posted:

I'm talking about the main:: section of the pm file itself. My understanding of a module is that 99% of it is subs. And there's some code outside of those subs sometimes, right? Things that need to be computed once, like lookup tables?

Or are you implying that there should pretty much never be that type of code and it should all be inside of some sort of block?

A pm file is just a perl script. What really defines the scope is the package declaration, not the file. If you have this code:

code:
# lib/Foo.pm
package Foo;
my $bar = 'hooray';
sub buz { $bar }
1;
code:
# myscript.pl
use Foo ();
print Foo::buz();
This is the same as:

code:
# myscript.pl
BEGIN {
    require "lib/Foo.pm";
}
print Foo::buz();
So, the code in the pm file is just perl. If you left out the package statement, its just like require on any other script and would define $main::bar and main::buz() instead.

(edit: also I would point out that it is probably bad form to have code in a pm file outside of a package declaration, since that code would execute in the caller's namespace)

s139252 fucked around with this message at 17:28 on Aug 15, 2008

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
Okay, right, wow, totally my fault. Every single time I said main:: I just meant in the package but not tucked in a subroutine or block.

s139252
Jan 1, 1970
test

Triple Tech posted:

Okay, right, wow, totally my fault. Every single time I said main:: I just meant in the package but not tucked in a subroutine or block.

Oh I see - package scope. The easiest way to view the whole thing is that the compiler inlines all the code it uses into one big contiguous block of perl. The package statement really means "change current symbol table". That is what allows non-contiguous weirdness like this (which is actually useful in unit tests sometimes):

code:
package Foo;
sub bar { '*Foo::bar' }

package main;
sub bar { '*main::bar' }

package Foo;
sub fuz { '*Foo::fuz' }

package main;
sub fuz { '*main::fuz' }

1;
Oh yeah I had a point... if you are using use to pull in modules, you are wrapping that code in BEGIN anyway, so I'm curious why your coworker benefits from INIT. Assertions should be made with die and covered by a test. I would think using INIT is bad form because it executes out of sequence at runtime. For example, something buggy at line 100 may be hard to debug because an INIT block at line 600 did something unexpected. BEGIN is a bit less evil since it means "eval this block as soon as it is compiled (before runtime) and cleanup after".

Neat topic though. As an aside, aren't END blocks LIFO? :) I think they at least were at some point... I never use them so I forget.

Mario Incandenza
Aug 24, 2000

Tell me, small fry, have you ever heard of the golden Triumph Forks?
Yeah, they're LIFO:
code:
[matt@potato perl] for m in Foo Bar Baz; do echo -e "package $m; BEGIN { warn qq{BEGIN $m} }; END { warn qq{END $m} }; 1" > $m.pm; done
[matt@potato perl] perl -MFoo -MBar -MBaz -e '1'
BEGIN Foo at Foo.pm line 1.
BEGIN Bar at Bar.pm line 1.
BEGIN Baz at Baz.pm line 1.
END Baz at Baz.pm line 1.
END Bar at Bar.pm line 1.
END Foo at Foo.pm line 1.
END blocks are useful when aggregating the results of perl -ne (contrived example):
code:
$ ps auxww | perl -ne '$found{$_}++ if /wanted/; END { print for keys %found }'

ashgromnies
Jun 19, 2004
I need some assistance from a SOAP::Lite expert.

I am using SOAP::Lite to run a SOAP service. I have a very simple sanity check method and am querying it in soapUI to view its adherence to the WSDL.

I have two servers. One is running SOAP::Lite version 0.69, the other is running 0.60. The server running 0.60 returns the correct data and it validates against the WSDL fine.

The server running 0.69, however, leaves out the xmlns attribute on elements even when I try forcing it which results in it not being able to find their xsi:type in the namespace(from what I understand, SOAP is kind of weird).

I cannot change the version of SOAP::Lite installed, it is out of the question.

Here is the output from version 0.60:

code:
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema">
   <SOAP-ENV:Body>
      <namesp1:sanityCheckResponse xmlns:namesp1="http://soap.mysite.com">
         <status xsi:type="xsd:int" xmlns="http://soap.mysite.com">1</status>
      </namesp1:sanityCheckResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
and version 0.69:

code:
<soap:Envelope soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <sanityCheckResponse xmlns="http://soap.mysite.com">
         <status xsi:type="xsd:int">1</status>
      </sanityCheckResponse>
   </soap:Body>
</soap:Envelope>
Any ideas?

Triple Tech
Jul 28, 2006

So what, are you quitting to join Homo Explosion?
This is going to be a little abstract, forgive me... So I have an object that encapsulates a process, which would be seperated into steps.

code:
sub do_process {
  $self->boom;
  $self->blam;
  $self->kerplow;
}
And of course it's all side effect driven (staining a database with various tables and knicknacks). It's currently implemented in one class that handles this process and all of its private methods, including methods that test parts of the object. Sometimes I intentionally kill the process or skip steps to help test that boom works, blam does what it's supposed to, etc.

The thing that's bothering me is that I'm exposing interfaces (of course not technically since this is Perl) and building all this test code and it's making my class look ugly. Then an idea dawned on me. What if I just put all of the test methods and checks and what not into another module, a module that will have access to all the innards of this process class and can poke and prod at it freely. I figure that way the main class can have the appearance of looking clean while I'm still able test its insides in an encapsulated way.

Thoughts?

It's just that it's difficult and annoying to test this module because it's heavily data driven and there's a lot of data (20min, 4M rows).

Edit: I was thinking about this more, and what about

code:
sub do_process {
  Project::Boomer::boom($state);
  Project::Blamer::blam($state);
  Project::Kerplower::kerplow($state);
}
I would make the roles between the sub-processes more discrete, but I would still have to pass some sort of state between them since each process depends on the side effects of the previous one.

s139252
Jan 1, 1970
test

Triple Tech posted:

code:
sub do_process {
  $self->boom;
  $self->blam;
  $self->kerplow;
}

I would write separate tests to cover those individual methods (boom/blam/kerplow), including edge cases. Compare object state before/after running the method as part of the test if that's required. Devel::Cover helps and makes writing mundane tests rewarding for some reason.

If I need a method that only is useful for running unit tests, I put it with the tests and never in the module, but that's just personal preference of course.

quote:

Edit: I was thinking about this more, and what about

code:
sub do_process {
  Project::Boomer::boom($state);
  Project::Blamer::blam($state);
  Project::Kerplower::kerplow($state);
}
I would make the roles between the sub-processes more discrete, but I would still have to pass some sort of state between them since each process depends on the side effects of the previous one.

It sounds like maybe you want mock objects? Check out Test::MockObject.

SubG
Aug 19, 2004

It's a hard world for little things.
What's the syntax for POSTing a multiple SELECT input via LWP::UserAgent?

Say I have a form like:
code:
<FORM ID="form" NAME="form" METHOD="POST" ACTION="http://some_url">
   <INPUT NAME="frotz" VALUE="" ID="frotz"/>
   <INPUT NAME="frob" VALUE="" ID="frob"/>
   <SELECT MULTIPLE NAME="foozle">
      <OPTION VALUE="foo">foo</OPTION>
      <OPTION VALUE="bar">bar</OPTION>
      <OPTION VALUE="baz">baz</OPTION>
   </SELECT>
</FORM>
...and I'm using something like the below to POST the rest of the form:
code:
sub
post
{
   my($ua) = @_;  # previously returned from LWP::UserAgent->new
   my(%form, $response);

   $form{'frotz'} = 'light';
   $form{'frob'} = 'tweak';
   $form{'foozle'} = [ qw(foo bar) ];  # THIS DOES NOT DO WHAT I THINK IT DOES
   $response = $ua->post('http://some_url', \%form);
   if(!$response->is_success)
   {
      die "Failed.\n";
   }
}
Where frotz and frob are other inputs which are handled as I'm expecting them to be handled. The problem part is identified by the THIS DOES NOT DO WHAT I THINK IT DOES comment.

heeen
May 14, 2005

CAT NEVER STOPS

SubG posted:

What's the syntax for POSTing a multiple SELECT input via LWP::UserAgent?

If I remember correctly you have to submit foozle=>foo and foozle=>bar together.
wait, that doesn't work here...
try using an array instead of a hash:
$ua->post('http://some_url',[foozle=>'foo', foozle=>'bar']);

heeen fucked around with this message at 23:51 on Aug 22, 2008

SubG
Aug 19, 2004

It's a hard world for little things.

heeen posted:

If I remember correctly you have to submit foozle=>foo and foozle=>bar together.
wait, that doesn't work here...
try using an array instead of a hash:
$ua->post('http://some_url',[foozle=>'foo', foozle=>'bar']);
Edit:
I think this works. I tried it on the problem I'm actually working on and it didn't work, but then I tried it on my toy test page and it appears to work. So I think that's the correct answer and I just have to figure out what else is going on in the real world page.

Thanks.

SubG fucked around with this message at 00:25 on Aug 23, 2008

leedo
Nov 28, 2000

Is there any way to have a method detect the context it is being called in (list or scalar)? Right now I am returning a ton of arrayrefs in a module I am working on, but would like to return a list in list context. DBIx::Class seems to do this with it's ResultSets but I can't seem to see how.

Ideally it could do this:
code:
my $artists = $obj->search('ween'); # returns an arrayref

for my $artist ($obj->search('ween')) { } #returns an array
edit: annnnd I quickly answered my own question with
code:
perldoc -f wantarray

leedo fucked around with this message at 01:20 on Aug 27, 2008

npe
Oct 15, 2004

leedo posted:

edit: annnnd I quickly answered my own question with
code:
perldoc -f wantarray

Just an obscure word of warning (since this somewhat unlikely to crop up for most people) but man did it ruin my day once: "list context" can crop up when you aren't always thinking about it, and due to perl flattening lists when you're not looking this can have interesting results.

For example:

code:
sub get_nothing
{
  if (wantarray)
  {
    # return empty list
    return ();
  }
  else
  {
    return "";
  }
}

# works as you would expect
my $scalar = get_nothing();
my @empty = get_nothing();

# uh oh! 
my %hash = (
  foo => 1,
  bar => get_nothing(),
  baz => 2
);
Data::Dumper shows the problem:

code:
scalar: $VAR1 = '';
array: $VAR1 = [];
hash: $VAR1 = {
          'bar' => 'baz',
          'foo' => 1,
          '2' => undef
        };
So, just be careful with wantarray.

more falafel please
Feb 26, 2005

forums poster

I've never been much of a Perl expert, but what's the philosophical reason behind Perl flattening lists?

Subotai
Jan 24, 2004

more falafel please posted:

I've never been much of a Perl expert, but what's the philosophical reason behind Perl flattening lists?

I think it was just a mistake. I am sure you can find Larry Wall talking about it somewhere on the net.

Adbot
ADBOT LOVES YOU

Fenderbender
Oct 10, 2003

You have the right to remain silent.
It's a bit late so maybe I'm misunderstanding the discussion or the way it works, but to comment on the above code, => is just an alias for a comma. So when you declare a a hash, if you have (), it's essentially not putting an item in there. In the above example:
code:
my %hash = (
  foo => 1,
  bar => get_nothing(),
  baz => 2
);
is the same as

my %hash = ('foo', 1, 'bar', 'baz', 2);

You're placing an empty array as a value, so it's compiled as essentially just nothing, and so it offsets all the key/value pairs in the hash.

  • Locked thread