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

Monks. Following a previous example I've written the following perfectly WORKING code in my Item class constructor.

However, I'd like to take the next obvious step while furthering my perl knowledge and replace the last if block with a hash lookup. i.e replace the follwoing:

*$name = $setter; and *$name = $getter;

with something like:

*$name = $accessors->{$name}
or *$name = ${$accessors->{$name}}

But after reading 'prelref' over and over and futzing with different combinations I keep coming up with undefined values and *$name not holding the reference to my built function. i.e. no matter what I try I cant get $item->status() to work after refactoring. It works as is and builds the correct status() and team() methods but not if I try to do it using the lookup. Whats the proper way to refactor the last block and make the symbolic references work?
package Item; sub new{ ...<make hash, bless, etc, etc>.... my $accessors = { team => "getter", status => "setter", }; for my $name (keys %{$accessors}){ # Defines read-write access to a DB column. my $setter = sub { my $s = shift; # Setting of value requested. if( @_ ){ ...<set value in database>... ...<other setter only code>... $self->{$name} = $value; } return $self->{$name}; }; my $getter = sub { my $s = shift; if( @_ ){ carp("Attempting to set value $_[0] for read-only var +$name!\n"); } return $self->{$name}; }; no strict 'refs'; if( $accessors->{$name} eq "setter" ){ *$name = $setter; }else{ *$name = $getter; } return $self; }

Replies are listed 'Best First'.
Re: symbolic references in a hash lookup
by ikegami (Patriarch) on Jun 12, 2009 at 17:17 UTC
    Works for me after I fixed the compile errors (Missing bless, $s vs $self, $value not defined or initialised, missing curly bracket)
      Thats really odd. With this form:
      *$name = ${$accessors->{$name}}
      I get these errors:

      Undefined value assigned to typeglob at lib//EDS/liveQueue/Item.pm line 104.
      Can't locate object method "status" via package "EDS::liveQueue::Item" at ./item_driver.pl line 88.

        That didn't exist in your code, so I didn't use that.

        Symbolic refs only access package variables. You never assigned a value to the $setter package variable, the error message is correct.

        There's no reason to replace the simple if/else with a lookup table (which is what you are trying to do, really). In fact, why are you creating two subs when you only even want one? Simpler:

        for my $name (keys %{$accessors}){ my $accessor; if( $accessors->{$name} eq "setter" ){ $accessor = sub { ... }; } else { $accessor = sub { ... }; } no strict 'refs'; *$name = $accessor; }
        ${$accessors->{$name}} is equivalent to ${'getter'} so it is meaningless.

        I would suggest restructuring your my $accessors (untested):

        my $accessors = { map ({$_=>{TYPE=>'getter',CODE=>undef}} qw |my gett +er names| ), map ({$_=>{TYPE=>'setter',CODE=>undef}} qw |setter +names2|) };
        Then set the CODE values:
        for (keys %$accessors){ if ($accessor->{$_}{TYPE} eq 'getter'){ $accessor->{$_}->{CODE} = sub {set code}; }else{ .. setter ... }

             Potentia vobiscum ! (Si hoc legere scis nimium eruditionis habes)