http://qs321.pair.com?node_id=511949

hesco has asked for the wisdom of the Perl Monks concerning the following question:

OK. In an attempt to isolate the issue, I have rewritten my call to the connect_new subroutine I wrote about earlier. This time it errors out before I get to call that subroutine, eliminating it as a potential source of this issue.

Now my error reads, very much as it did yesterday, only with a new line number:

Can't use string ("databasename ") as a HASH ref while "strict refs" i +n use at /usr/lib/cgi-bin/bc/supporters.cgi line 264.
on the following line:

my($host) = $config{'db'}{'db_host_name'};
pointing out an additional issue, namely that somehow my database name (still with that annoying trailing space) has somehow been mis-assigned to my db_host_name key in the $config->{'db'} hash.

Since this is a hash slice to scalar assignment, it eliminates me having to wrap my head around the proper syntax for converting multiple element hash slices into arrays.

Again, the block which defines the $config->{'db'} hash reads as follows:

while (<DB>) { chomp; next if /^\s*\#/; next if /^\s*$/; unless (/=/) { die "invalid variable assignment in supporters.db: $_"; } my ($key, $val) = split(/\s*=\s*/,$_,2); $key =~ s/^\s*//; $val =~ s/ *$//g; $config{'db'}{"$key"} = $val; $config{"$key"} = $val; } close DB;
I assign $val to two different elements of the hash here. As an experiment. $config{"$key"} properly passes the values (I suspect), as the .cgi page resolves without error. But $config{'db'}{"$key"} does not, resulting in the error described above.

So I guess I can make my script work. But I was hoping to tighten up the code some and do in one line, what I have been doing in five. And I'm still confused about this HASH ref error. What does that mean? Where is this explained for me? How do I avoid it?

All help appreciated.

-- Hugh

Replies are listed 'Best First'.
Re: more fun w/ HASh ref's
by merlyn (Sage) on Nov 26, 2005 at 20:50 UTC
    $config{'db'}{"$key"} = $val; $config{"$key"} = $val;
    That's gonna blow up if $key can ever be "db". You can't have $config{db} be both a hashref and a simple value.

    Also, those double quotes are useless, as are the single quotes around db.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

      Mr. Schwartz:

      What an honor. It was partly reading your book a couple of years ago, which got me this far. Not that you are to blame, mind you.

      Its true, I think I've gotten this to work both with and without the double quotes.

      'db' will never be a key, although db_host_name, db_database, db_user and db_pw are defined.

      But $config{$key} is merely my work-around. My goal here is to consolidate and better organize my config hash so I can pass shorter argument lists to my subroutines. My subroutine calls have been getting quite unwieldy as this app has grown from its first deployed working version (at a few hundred lines) to its now comparatively more complex (but more feature-full) several thousand lines.

      And it is that transition which is throwing up these errors. What does it mean to "use string . . . as a HASH ref". I thought hash keys had to be scalars (i.e. strings). And I thought I had to specifically pass a hash reference (i.e. \%config) and then dereference it on the other side ($$config{db}{$key}) to have to face _references_. So I'm quite confused as to what to do about this. Perhaps I fell asleep on the chapter which explained this all to me. I don't know. Any insight appreciated.

      -- Hugh

        First, please show us an example that we can run, if you want quicker help.

        Second, given your error message, I bet "db=databasenameX" is one of your lines, where X is a space or tab. That would trigger the problem I mentioned earlier, and the corresponding error message.

        But without seeing the input data, we have to keep guessing.

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.

Re: more fun w/ HASh ref's
by jarich (Curate) on Nov 27, 2005 at 14:05 UTC

    G'day Hugh.

    Might I suggest, for starters, that writing your own config parsing code is probably a mistake already? While I'm certain we can help you to get it working, you might find an existing module such as Config::IniHash, Config::Tiny or Config::General a better solution, especially in the long term. For your case I'd probably recommend Config::IniHash.

    When is the error occuring? During compilation? During runtime? Can you compile the code as follows (outside of a CGI session)?

    perl -cw yourcode.cgi

    If it's happening during runtime, is it happening when you read in the config file, or when you later try to access it? If it's when you later try to access it, use Data::Dumper to have a look inside the config hash. Is it behaving as you'd expect?

    # Generate config hash then... use Data::Dumper; print Dumper %config;

    Try isolating the problem. Write a new program which reads in the config data and then accesses it as you want to. Try to make it as simple as possible, perhaps as below:

    # Config_reader.pm package Config_reader; use base Exporter; use strict; use warnings; # etc while (<DB>) { chomp; next if /^\s*\#/; next if /^\s*$/; unless (/=/) { die "invalid variable assignment in supporters.db: $_"; } my ($key, $val) = split(/\s*=\s*/,$_,2); $key =~ s/^\s*//; $val =~ s/ *$//g; $config{db}{$key} = $val; $config{$key} = $val; } close DB; ## test.pl use Config_reader qw/%config/; use Data::Dumper \%config; my($host) = $config{db}{db_host_name}; my($db) = $config{db}{db_name }; my($user) = $config{db}{db_user }; my($pw) = $config{db}{db_pw };

    If that doesn't work, then you've got something we can more easily help you debug. If it does work, then chances are that your problem is not actually where you think it is.

    Either way, I strongly recommend you have a look at the already existing configuration modules.

    All the very best,

    jarich
      Jarich:

      Thank you sir, for those leads.

      I've been running tests with Config::IniHash. Its working on every part of my barely edited config files, except for the heredoc variables in my supporters.copy.ini file.

      But now I'm getting this error:

      Wed Nov 30 16:41:47 2005 error client 24.67.197.93 Can't use an undefinedvalue as a HASH reference at /usr/lib/cgi-bin/bc/test.pl line 24.

      line 24 reads:

      my $options->{'heredoc'} = '1';
      I know someone here the other day was telling me I should not quote my hash keys, but on my installation it does not seem to work unless I do.

      #!/usr/bin/perl use strict; # use supporters_conf qw(%config); use Config::IniHash; # $hashreference = ReadINI ($filename, %options); use CGI; use CGI::Pretty qw(:all *table param); my $url = url(); # my $table1 = fqtn(@config{qw(db prefix)},"basetablename"); my $conf = parse_config_directory($url); my $path = $0; my $script = $0; $script =~ s/^(.*)\///; # $conf =~ s/$script//; # $path =~ s/$script/conf.d\/$conf\/supporters.conf/; my $config_conf = ReadINI ($conf); my $copy = $conf; $copy =~ s/\.conf/.copy/; my %options; my $options->{'heredoc'} = '1'; my $config_copy = ReadINI ($copy,%options); my $db = $conf; $db =~ s/\.conf/.db/; my $config_db = ReadINI ($db); print header("Testing parser for configuration files"), start_html("Testing parser for configuration files"), p("And the question of the hour is: Will the config module export t +he form copy. This experiment shall tell us."), p("Script path and name is: ".$0), p("Script name is: ".$script), # p("Scriptpath: ".$sp), # p("Scriptname: ".$sn), p("Path to configuration files at: <b>".$conf."</b>"), p("Script accessible at: ".$url), # p("Fully Qualified Table Name looks like: ".$table1), p("Config::IniHash says \$config = ".$config_conf."."), p("And the mail-abuse address is: ".$config_conf->{'mail-server'}->{ +'mail_abuse'}."."), p("The database name is: ".$config_db->{'db'}->{'db_name'}."."), p("The donor form disclaimer reads: <br>".$config_copy->{'copy'}->{' +donor_form_disclaimer_copy'}."."), p(),p(), $supporters_conf::config{'donor_thanks'}, end_html(); exit; sub parse_config_directory() { my($conf)=@_; # call as follows # use CGI; # $url = url(); # $conf = parse_config_directory($url); my $scriptpath = $0; my $scriptname = $0; $scriptname =~ s/^(.*)\///; $scriptpath =~ s/$scriptname//; $conf =~ s/https:\/\///; $conf =~ s/http:\/\///; $conf =~ s/\//./g; $conf =~ s/\.$scriptname//; $conf = $scriptpath."conf.d/".$conf."/supporters.conf.ini"; return $conf; # configuration file, ready for execution } # END parse_url sub fqtn(){ my($db,$prefix,$table)=@_; $db =~ s/ *$//; $prefix =~ s/ *$//; $table =~ s/ *$//; my $fullyqualifiedtablename = $db.".".$prefix.$table; return $fullyqualifiedtablename; } # END fqtn
      Any ideas how I might work through this one? I've written this multiple ways and keep getting one error or another.

      -- Hugh

        Consider the following code snippet:
        my %options; my $options->{'heredoc'} = '1'; my $config_copy = ReadINI ($copy,%options);

        What this is actually saying is annotated below.

        # Create a hash called %options my %options; # Create a *scalar* variable called $options and then try # to use it as a reference. Since it's just been created, # it's undefined. Hence this won't work. my $options->{'heredoc'} = '1'; # Call ReadINI passing it $copy and your %options hash. my $config_copy = ReadINI ($copy,%options);

        I imagine that the line you want to have in the middle there is:

        $options{heredoc} = 1;

        It's a hash look up. %options is a hash, not a hash reference. If its not a reference, you don't want the arrow.

        I hope this helps.

        jarich