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

Hi All,
  I've got a long list of sub routines in a file, each containing different variable definitions and each saved into a hash like so:-
$hash->{subs}->[0] = sub { $var1 = "a"; $var2 = "b"; }#sub 0 $hash->{subs}->[1] = sub { $var1 = "c"; $var2 = "d"; }#sub 1
This file is loaded into my script and the appropriate sub routines for the operation is called. I've got a function in the script that allows you to edit and save an individual routine.
At the moment, I'm reading the file as a string, using a regexp to remove the old sub and replace it with a new one. Then saving the file.
I'm wondering... as I've already got the sub routines loaded into memory, whether it's possible to just redefine the new routine and save all the subroutines in the hash to the file (i.e. saving reading the file as a sting and running the regexp).

Can this be done?

Thanks in advance

Lyle

Replies are listed 'Best First'.
Re: Saving sub routines.. Is it possible?
by grinder (Bishop) on Apr 05, 2008 at 15:45 UTC
    Can this be done?

    This is Perl, of course it can be done. In this particular case, we need to avail ourselves of demerphq's most excellent Data::Dump::Streamer.

    Even then, a major imposition is that you have to use package variables, since lexical variables can't be injected into scope from an eval.

    You can take your data structure and dump it out, being careful to name it so that you can find it again:

    use strict; use warnings; use Data::Dump::Streamer; use vars qw($hash $var1 $var2); $hash->{subs}->[0] = sub { $var1 = "a"; $var2 = "b"; }; $hash->{subs}->[1] = sub { $var1 = "c"; $var2 = "d"; }; Dump($hash)->Names('$hash')->Out;

    This writes the sub to STDOUT. You will probably want to write it to a filehandle; read the documentation to find out how to do that.

    You can then thaw the subroutines by eval'ing the resulting dumped code:

    use strict; use warnings; use vars qw($hash $var1 $var2); my $file = shift or die "No file given\n"; open my $in, '<', $file or die "input $file: $!\n"; eval do { local $/ = undef; <$in>; }; $var1 = 'foo'; print "var1 = $var1\n"; $hash->{subs}[1]->(); print "var1 = $var1\n";

    You will observe that $var1 changes its value after calling the subroutine. Be that as it may, this is a pretty odd thing to want to do. If it's not impolite, I'm curious to know what you want to use it for.

    • another intruder with the mooring in the heart of the Perl

      Data::Dump::Streamer looks great... But I'm trying to keep to core modules.
      I'm coming up with another solution I think will work for my scenario. I'll post it shortly.
      My program has lots of user account and lots of different config options for each one. Calculations are made based on the options. When sorting through making overall calculations for a long list of users I need to be able to change variable values quickly. Some accounts will use default values, others part of a group of values and others individual. Main user data is obviously stored in a database, but these variables are stored in small flat files for quick access.
Re: Saving sub routines.. Is it possible?
by jasonk (Parson) on Apr 05, 2008 at 15:31 UTC

    Data::Dump::Streamer can dump code refs and closures.


    We're not surrounded, we're in a target-rich environment!
Re: Saving sub routines.. Is it possible?
by dragonchild (Archbishop) on Apr 05, 2008 at 17:31 UTC
    Take a look at what I do in Sub::Compose. I'm also working through the implications of doing something similar in DBM::Deep. You're welcome to join the discussions.

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Saving sub routines.. Is it possible?
by Cyrnus (Monk) on Apr 05, 2008 at 15:33 UTC
    I don't believe that you can redefine a subroutine during execution. You might be able to simulate it by calling a subprocess, however allowing for user define subroutines is risky at best. Based on your code I think you might be better served storing arrays in the hash. that way you can update the hash and have new values immediately available and write the updated hash to the file (or just tie the hash so updates are automatic) something like:
    %myhash = ( set0 => [ "a", "b" ], set1 => [ "c", "d" ], );
    then to access the values have a sub something like:
    sub updatevars { my $set = shift; $var1 = $myhash{$set}[0]; $var2 = $myhash{$set}[1]; }
    and call the sub with:
    my ($var1,$var2); updatevars("set0");


    John
Re: Saving sub routines.. Is it possible?
by educated_foo (Vicar) on Apr 06, 2008 at 03:00 UTC
    B::Deparse can be your friend:
    use B::Deparse; sub foo { my $x = shift; print "$x\n"; } $d = new B::Deparse qw(-p -sC); print $d->coderef2text(\&foo);