http://www.perlmonks.org?node_id=369684

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

I seem to be in a bit of a pickle. I'm working on a web development team in a mod_perl environment for a company. There are two sets of libaries here: legacy ones and new, OO ones. My boss wants me to get rid of the code in the legacy libraries and get them to use the new OO code instead, while maintaining the API for the older apps. For example:
package Legacy; $new_obj = New->new(); sub Ugly_Sub_Name { $new_obj->pretty_method; } package New; sub new { ... } sub pretty_method { ... } package main; use Legacy; # should return the value from New->pretty_method print Legacy::Ugly_Sub_Name, "\n";
The problem is that the above code is a no no in mod_perl, where $new_obj will live between requests. The New class loads information from cookies and grants privileges, so I can't have this. How can I create a $new_obj for every request in the Legacy package or otherwise solve my problem?

Replies are listed 'Best First'.
Re: mod_perl and forwarding legacy functions to a global object
by Fletch (Bishop) on Jun 25, 2004 at 17:53 UTC

    Create an instance in an init handler and stash it in $r->notes. Write procedural wrappers which retrieve that instance and call the appropriate methods. That instance will only exist for the lifetime of the one request.

    Addendum: This also has the advantage of allowing new code to access the already existent instance and call the methods directly as well (either by using an already existing request record or by using Apache->request->notes).

    Update: Ooop, as is pointed out below I meant pnotes which lets you store an arbitrary perl value. That's what one gets after getting spoiled by HTML::Mason doing everything for you. :)

      Beautiful. Thank you. Apache->request->notes only stores strings, but I was able to store a flag that let me know if I'd created an object for that instance.
        pnotes allows you to store a reference to a data structure.

        -derby
Re: mod_perl and forwarding legacy functions to a global object
by Zaxo (Archbishop) on Jun 25, 2004 at 17:52 UTC

    The old Legacy probably didn't have objects hanging around, so how about restricting the object to a lexical scope?

    package Legacy; sub Ugly_Sub_Name { my $new_obj = New->new(); $new_obj->pretty_method; }
    Even just making the object a file scoped lexical would work, but I don't see a need to make a closure on it.

    After Compline,
    Zaxo

      The problem is there's more than one Ugly_Sub_Name and it doesn't make sense to make a new object (it's a bit of work) on every subroutine call.
Re: mod_perl and forwarding legacy functions to a global object
by perrin (Chancellor) on Jun 25, 2004 at 19:28 UTC
    Apache::Singleton might be useful for you. It stores the object in pnotes() so it gets cleaned up at the end of the request.
Re: mod_perl and forwarding legacy functions to a global object
by Juerd (Abbot) on Jun 25, 2004 at 17:51 UTC

    How can I create a $new_obj for every request in the Legacy package or otherwise solve my problem?

    First, I have to say that global objects are imho no better than just a bunch of subs that share variables.

    To answer your question: the easiest way is to subclass the handler you're using. For instance, to subclass Apache::Registry:

    package Apache::Registry::ThinkOfANiceName; use base 'Apache::Registry'; sub handler { my $r = shift; local $Legacy::new_obj = New->new; $r->SUPER::handler(@_); } 1;

    Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

Re: mod_perl and forwarding legacy functions to a global object
by dragonchild (Archbishop) on Jun 28, 2004 at 12:27 UTC
    The general question of how to provide a legacy API to a modern system is pretty easy to answer - you'll want to look at singletons for most of your work. The $64K issue is how to provide the state your objects need so that they do what they need to do. Generally, one would hope that the mapping between the parameters passed into Legacy::Ugly_Sub_Name() and New::pretty_method() is extremely close. The only issue I've run into doing this is when the Legacy package maintained a bunch of state using file-scoped globals. If that's the case, I'd look at some sort of external datastore, like a RDBMS, especially if you're in a persistent environment.

    As for specifics, the previous posters got you covered, I think.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested