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

hi, I have a simple standalone scripts, then they have subroutines (as u may expect :")).
Now I want to do the following when the -x /path/to/ExtModule.pm switch is used the script automagicaly grab all defaults from external module, plus start using some subroutines in place of its original ones...and some such
Why I want this ?
I want to have several such scripts for doing small concentrate tasks, but want when I plug them in my framework they start to fetch defaults, options from my envoirment and addapt their behaviour to it..
One example so u can understand what I'm talking about :
temlpate parser which extract/calculate "rate" (format : field=val|field2=val2|rate=basic|..). Now say I have in my script :
sub rate { return 50 if $_[0] eq 'basic'; return 150 if $_[0] eq 'bronze'; }
then when this -x switch is used (or by some other means), rate() is overloaded with a more complex evaluation in external module/s.
Now I know how I can do this i.e. in the module create main::rate() sub, modify variables in the same way $main::Var.. but is this the right way to do it !!
May be there is CPAN solution :")
So how do u do such things... what changes u do to your frameworks.... do u do such things at all :")
Part of the reason for this is 'cause I want to give some of these scripts for others to use them, but I dont want them to overcome them with all of the rest of the code that uses them...i.e. make their life easier, but still retain the abillity to add functionality easy..

tia

Replies are listed 'Best First'.
Re: defaults via Module from outside
by Zaxo (Archbishop) on Dec 01, 2004 at 08:37 UTC

    First, for your command line switches, take a look at Getopt::Long. You can call the GetOptions function in a BEGIN{} block before loading modules with use. That will allow you to influence module loading with the command-line switches. G::L is shipped with perl, so there should be no hassle using it.

    Why do I mention modules?

    Your desire for overriding subroutines can easily be handled in OO Perl. You can make the ordinary case a base class, and subclass that to override. Having your data handling tucked away in modules which define the methods which apply to particular types of data allows you the flewxibility of reuse.

    You could just write your subroutines to look at some state set by switches, but that strategy is brittle and doesn't scale well.

    The if.pm module is very helpful for conditional module loading.

    After Compline,
    Zaxo

Re: defaults via Module from outside
by bart (Canon) on Dec 01, 2004 at 12:02 UTC
    I'd use subclassing, and class methods. That way you can override base methods, and leave in others...

    Do be aware that when calling a method, an extra first parameter is introduced: the class name, when calling it as a class method (or an object when calling the same sub as an object method).

    As for the command line, I'd just use the -MFoo=bar syntax, to load the master module (Foo, file "Foo.pm"), and use the parameter ("bar") to actually load the submodule (Foo::bar, file "Foo/bar.pm"). Just like B/O do. For example, the command line switch -MO=Xref loads the module O, calls its import with parameter "Xref", which in turn loads B::Xref. So, you know where to look for an example implementation... :)

    Actually you can also take a look at File::Spec and AnyDBM_File too, to see how those modules dynamically change their behaviour by loading a backend module, and then "patch themselves". That way your code can be oblivious of what backend is actually being used.

    If that's too advanced to implement for your taste, you could instead simply use a global variable to store the package to use in the method calls, like

    $pkg = "Foo::bar"; my $rate = $pkg->rate('basic');
    or have an (empty) global object, blessed into that class:
    $calculator = Foo::bar->new; my $rate = $calculator->rate('basic');
    which would require very little change in your actual implementation.

    Finally, you could have the base module export its functions, and have those refer back to the backend methods. Your code could simply call rate('basic'), which would invoke the function Foo::rate, which in turn would call Foo::bar->rate('basic'), which in turn could be inherited from Foo::Base, a class which you can actually implement inside Foo.pm.

    Let me know if any of these ideas appeal to you, so I can bother with actually fleshing it out a little.

Re: defaults via Module from outside
by ikegami (Patriarch) on Dec 01, 2004 at 16:00 UTC

    Not a answer, but a side bar.

    sub rate { return 50 if $_[0] eq 'basic'; return 150 if $_[0] eq 'bronze'; } $rate = rate($package);

    could be written as

    my %rate = ( basic => 50, bronze => 150, ); $rate = $rate{$package};

    or

    { # %rate is private to rate(). my %rate = ( basic => 50, bronze => 150, ); sub rate { return $rate{$_[0]}; } } $rate = rate($package);