views:

67

answers:

1

I'm asking this question because I finally solved a problem that I have been trying to find a technique for in a number of cases. I think it's pretty neat so I'm doing a Q-and-A on this.

See, if I could use eval, I would just do this:

eval join( "\n"
         , map { 
             my $v = $valcashe{$_}; 
             sprintf( '$Text::Wrap::%s = %s', $_
                    , ( looks_like_number( $v ) ? $v : "'$v'" )
                    )
          }
        );
Text::Wrap::wrap( '', '', $text );

I even tried being tricky, but it seems that local localizes the symbol to the virtual block, not the physical block. So this doesn't work:

ATTR_NAME: while ( @attr_names ) {
    no strict 'refs';
    my $attr_name       = shift;
    my $attr_name       = shift @attr_names;
    my $attr_value      = $wrapped_attributes{$attr_name};
    my $symb_path       = "Text\::Wrap\::$attr_name";
    local ${$symb_path} = $attr_value;
    next ATTR_NAME if @attr_names;

    Text::Wrap::wrap( '', '', $text );

}

Same physical block, and I tested the package variables before and after being set, and they even showed the proper value on their time through the loop. But testing showed that only the last variable passed through retained its value for the call to wrap. So values only stayed localized until the end of the loop.

I think the solution is neat--even if arcane perl magick. But the end result is good because it means I can wrap legacy code that relies on package-scoped variables and be assured that the values set will be as short-lived as possible.

+6  A: 

Axeman, you idiot! The package stash is a hash too! This works:

local @Text::Wrap::{ keys %wrapped_attributes } 
    = \( values %wrapped_attributes )
    ;

This does the following:

  1. It accesses the array slice of Text::Wrap's symbol table and returns the named symbols.
  2. Because it's a symbol and not a scalar, in order for perl to know the correct slot, you have to assign references. This is true for arrays and hashes as well, but we all know that their references are more common.
  3. The \( ... ) syntax passes back a reference for each item in the list.

So we localize each variable for each key in %wrapped_attributes, and we assign it a reference for each value there.

It's ugly and arcane, and it's hard to move it out of the way each time I need it--but with it, I can pre-specify and delay the localization to select places where I can put up Damien Conway's suggested barbed wire and danger signs.

If I'm going to use it, it has to be just that ugly where I use it.

Axeman
It's ugly in that the package has globals as it's interface, that's for sure, and there is a lot going on there, making it a solution difficult to understand...but as far as a solution goes, that's pretty slick, I think. The fact that you can even do this in Perl is a testament to it's all around greatness. If this were a C module you were working with, for example, you would have little to no way to protect those globals from being smashed without some TRUE ancient and arcane magicks!
Robert P