Re: Double quoted string as method call
by Zaxo (Archbishop) on May 04, 2004 at 21:59 UTC
|
Avoiding comment on your DBI code, since I don't understand it, I think you want,
$obj->${"text_${_}"};
| [reply] [d/l] |
|
|
| [reply] [d/l] |
|
|
Thank you!
I have compiled and run and tested, and it does seem this works!
It doth confuse cperl-mode in emacs, tho!
Cperl-mode understands $caller->$ {\ "database_$_"}() (note spacing) just fine, however.
Whether humans will be able to parse such funny spacing remains to be seen ;->
| [reply] [d/l] |
|
|
UPDATE: Actually this compiles fine but doesn't actually work, see first response below for reason why. Thanks anyway.
---------- Old message ----------
Thank you!
I should note, however, that in my case this failed with a syntax error until I added a () to the end of the, as such:
$obj->${"text_${_}"}();
This may have to do with the particulars of my code.
(As for the DBI call, that stuff can safely be ignored. I would note it was actually flawed insofar as I tried to pass in taint information incorrectly, the correct parameter order is dsn, user, pass, then a hashref with other optional config info, which I usually compose as {Taint=>1}.)
| [reply] [d/l] |
|
|
$obj->${"text_${_}"}();
I don't think that does what you want it to: it does one too many levels of indirection.
if $_ has the value "foo", then I presume you want to do the equivalent of
$obj->text_foo()
In fact, supposing you have a variable called $text_foo with value bar, then what you are actually doing is the equivalent of
$obj->bar()
| [reply] [d/l] [select] |
|
|
Re: Double quoted string as method call
by jarich (Curate) on May 05, 2004 at 01:51 UTC
|
The only way I have found to make this work -- after several SuperSearches and trying perl5.6 and perl5.8 -- is to create a new simple scalar variable and use this as the method call, as such:
$$database_handle = DBI->connect(
map {
my $s = "database_$_";
exists $arguments{$_} ?
$arguments{$_} :
$s->()
}
qw(data_source_name username password
taint)
) or die "Could not connect to the ".
"database: $DBI::errstr";
The above code compiles fine. It is a bit kludgy though. Is there any way to accomplish a method call that mixes an interpolated variable and some known text, sort of like a double quoted string?
As far as I can work out there isn't such a way. :( In fact, to do what you're doing up there in the map you have to have strict refs turned off. You've probably turned them off for this whole section because otherwise you could well be having trouble with $$database_handle.
I can offer you two alternatives to this problem, however. Both strict compliant. If your database_* subroutines merely return a string value, ie:
sub database_username {
return "fred";
}
then hash defaults might be the way to go:
my %defaults = (
data_source_name => ".....",
username => "fred",
password => "fred",
taint => { Taint => 1 }
);
# then later...
%arguments = (%defaults, %arguments);
my $dbh = DBI->connect( @arguments{ qw(data_source_name
username password taint) })
or die "Could not connect to the database: ".
"$DBI::errstr";
When assigning to hashes, later keys of the same name overwrite the values from the earlier keys. This means that if "data_source_name" is provided in %arguments the %arguments version is kept. Obviously keys which are not included in %arguments remain those of the default hash.
The @arguments{ qw/.../ } structure is a hash slice. This ensures we get the values out in the order we want them to appear.
This ideas works if your database_* subroutines are ONLY returning simple stuff. If you want your database_* subroutines to read these values from a file or something similar and to do so only if needed then you might want to do something like the following:
my %defaults = (
data_source_name => \&database_data_source_name,
username => \&database_username,
password => \&database_password,
taint => \&database_taint,
);
# and later:
my $dbh = DBI->connect (
map {
exists $arguments{$_} ?
$arguments{$_} :
$defaults{$_}()
}
qw(data_source_name username password taint)
) or die "Could not connect to the database: ".
"$DBI::errstr";
# and somewhere else:
sub database_data_source_name {
# read stuff from file..
# do other stuff
return #something
}
This has the added advantage of looking a whole lot nicer than using a string as a coderef. What we're doing here is creating a hash of references to our subroutines. It's not until we do $defaults{$_}() that these subroutines actually get executed.
I hope this helps,
jarich
Update:
Heh. I post my node and already stand corrected. Well done Anonymous Monk. If you do wish to make your code strict compliant, however, you may find my suggestions useful.
map {
exists $arguments{$_} ?
$arguments{$_} :
${\"database_$_"}->()
}
| [reply] [d/l] [select] |
|
|
Apologies, but it appears you have posted my code incorrectly in the very first part of your post. Particularly, at the alleged $s->(). I am not sure how key this is to your assertion my code is not strict-compliant .
I have re-read what I posted and cannot find where I ever call $s as the object or class. I call $s as a method to $caller, ie $caller->$s.
In any case, I have a close relative of the code I posted running fine under strict:
$$database_handle = DBI->connect((
map {
my $s="database_$_";
exists $arguments{$_} ?
$arguments{$_} :
$caller->$s()
}
(qw(data_source_name username password))
), {Taint=>$arguments{taint}||$caller->database_ta
+int}
) or die "Could not connect to the database: $DBI::
+errstr" unless defined $$database_handle;
Please note $$database_handle is not meant to be a symbolic reference. You would need to see the whole module. $database_handle is a reference to a reference (to a handle), which I create earlier in the method because the particular reference (handle) used depends on whether the method is called on a class or an object.
Thanks for the rest of your post -- I will read it over. But, again, no problems with stricture! | [reply] [d/l] [select] |
|
|
My apologies, you're completely right.
I dropped the $caller bit for my testing and then promptly forgot that I had done so and therefore incorrectly concluded that since what I had wasn't strict compliant, what you had couldn't be either.
I considered that $$database_handle could be either a symbolic reference or a scalar reference and that is why I wrote that it too could cause you problems under strict, rather than it does.
I should have checked my assumptions.
All the best,
jarich
| [reply] [d/l] [select] |
Re: Double quoted string as method call
by Anonymous Monk on May 05, 2004 at 01:13 UTC
|
$caller->${\"database_$_"}()
| [reply] [d/l] |
|
|
| [reply] |
Re: Double quoted string as method call
by ozone (Friar) on May 05, 2004 at 07:54 UTC
|
| [reply] |
Re: Double quoted string as method call
by Belgarion (Chaplain) on May 04, 2004 at 21:57 UTC
|
Update
As other people below be have mentioned, the code I posted here doesn't work at all. That's what happens when I post just before leaving work. My code does work with a hashref, but does nothing for a method call. D'oh.
I'm going to take a stab at this and say that you need braces around the call, like so:
$caller->{"database_${_}"}()
I haven't actually tested this, but it compiles cleanly. More knowledgeable monks will probably have a better solution and explanation.
| [reply] [d/l] |
|
|
Unfortunately your idea doesn't work, at least on 5.8.4 on debian or 5.8.3 on win32. I think this is just an example of the constructs getting too complicated for the perl parser to handle, but I'm not sure exactly why. The work around is simple:
my $base = 'foo_';
my $obj = new obj;
for(qw/baz qux stuff/)
{
my $meth = "${base}_$_";
$obj->$meth;
}
Which works perfectly.
I find it a little odd that the $obj->"meth" form doesn't work, since the reverse works perfectly, at least for packages: "package"->$meth | [reply] [d/l] [select] |
|
|
Pardon, but it appears you are selecting a value from a dereferenced hash rather than making a method call on an object or class. No?
The latter is my goal, not the former. Perhaps I should have been clearer!
Thanks anyway.
| [reply] |
Re: Double quoted string as method call
by ihb (Deacon) on May 05, 2004 at 15:16 UTC
|
This is a bit ugly, but I'll post it anyway since no one has mentioned can().
Since you expect the method to exist, you can do
$obj->can('...')->($obj => @args)
It works because can() returns a code reference to the subroutine it found (undef if none), and then you simply provide the object as the first argument.
ihb
| [reply] [d/l] [select] |
|
|
| [reply] |
|
|
I find it ugly because
- you have to specify the object twice for one method call
- if you're assumption about the existance of the method is wrong you get a very poor error message
- it uses unnecessary dereferencing
ihb
| [reply] [d/l] |