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

So I thought I was writing clean code, friendly to any coders that might want to use parts of it, etc... I sepparated stuff into modules, checked to make sure the namespaces did not collide as i offer them out..

Made sure to comment stuff and name my vars so that any prying eyes would know what they do.. and then.. No. Turns out, my code is third grade.

I found an unforgivable hole in what I do.
I use global variables. Yes.
Here's why.

(this is a web app)

I have Webapp.pm
then i have Webapp/Bathroom.pm
then i have Webapp/Bathroom/Toilet.pm
then i have Webapp/Bathroom/Status.pm

I'm using a globally accessible hashes of data,
%Webapp::Bathroom

which might hold something like
@{$Webapp::Bathroom{toilets}}
and then
%{$Webapp::Bathroom{joetoilet}
%{$Webapp::Bathroom{janetoilet}

I need everything in the Webapp namespace to access the %Webapp::Bathroom data. And I need any module I want to be able to modify data in there as well.

I seem to have been doing it this way.. I declare that Webbapp/Bathroom.pm has an our %Bathroom and that this symbol is exported.
so, other modules can access and change data in %Bathroom by pointing to %Webapp::Bathroom, without calling use Bathroom

Ok. I seem to be going insane. That's ok. Is this sounding like I should be playing with OO at this point ? maybe making a $Webapp object to store all data into ? How will this be accessible to all dependant packages ? Ouch.. Point to where I should take my corpse to properly do this.. I know I'm not supposed to use our, ever. I don't want to write useless code. Or worse, troublemaking code.

What is a propper way to make a chunk of data accessible to all my packages for retrieval and modification ?

  • Comment on what is a propper way to make a chunk of data accessible to all my packages for retrieval and modification ?
  • Select or Download Code

Replies are listed 'Best First'.
Re: what is a propper way to make a chunk of data accessible to all my packages for retrieval and modification ?
by dragonchild (Archbishop) on Feb 18, 2006 at 18:10 UTC
    Grandfather has pointed you in the standard direction, which is to use a singleton object. However, I'm more willing to challenge your assertions that:

    I need everything in the Webapp namespace to access the %Webapp::Bathroom data. And I need any module I want to be able to modify data in there as well.

    That just sounds like an incomplete design* to me. There are several reason why I don't think you really want what you think you want. Some are webapp-specific and some aren't.

    • In a webapp, everything is in the context of the request.
      • If you're in a persistent environment (like mod_perl), then you just made changes in one of the child processes that isn't seen by the other child processes. Since which child process handles which request is essentially random, you have just created a random change in your data. Which will give you random results.
      • If you're not, then any changes you make to the shared data disappears unless it's backed by some datastore.
      • If you are backed by a datastore, then the only global thing should be the connection to the datastore.
    • Changing something that's held globally results in what's known as "Action at a Distance". Essentially, if code in one subsystem changes to now alter one of the Bathrooms, code in every other subsystem might break. This increases the coupling between pieces of your application. Essentially, any piece of code that uses any part of the Bathrooms is coupled to every other piece of code that potentially could use the Bathrooms. If you alter your piece, you not only have to test every other piece. You sure you want that requirement?

    *: If you claim it's a complete design, then it's a bad design. I went with the nicer possibility.


    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?

      oh boy.. this is freaky. oo.. so.. you never actually touch or interact with the data in an object.. ever. you ask for it like you would ask your mother to please get you a coke from the kitchen - instead of getting it yourself. wow. this is shocking. i feel suddenly.. weirded out to all heck.

      i think i can see the use in this.. it really does keep stuff appart from each other, doesn't it ? - instead of having a master move memorized by which you reach into the cookie jar.. you have a trained monkey that goes to the cookie jar and brings it back to you.. how this happens is beyond your control or interest.. doesn't this slow things down a ton?

        Well, it's the concepts of "specialization" and "localization". You create an expert in the field of cookie jars and have that expert be the only person who knows how to handle cookie jars. That way, you can pretty much guarantee that no-one's going to screw up the cookie jar because they didn't know the correct way to get a cookie out while three other people are trying to do so as well.

        As for slowing things down ... which do you prefer - "slow and correct" or "fast and wrong"? Except, "fast and wrong" is usually "runs fast, is wrong, and took way too long to write", while "slow and correct" is more often than not "runs fast enough, is provably correct, and came in ahead of schedule". I know which one I prefer ...


        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: what is a propper way to make a chunk of data accessible to all my packages for retrieval and modification ?
by GrandFather (Saint) on Feb 18, 2006 at 11:08 UTC

    Brief answer (I'm heading to bed - really, I am): use a singleton object to hold your global configuration information. You can use new anywhere you want to, but after the first new (which creates the object), subsequent news simply return a reference to the singleton object.


    DWIM is Perl's answer to Gödel

      How is that any different from global variables? The singleton’s class name is a global identifier.

      (For more arguments other than those applicable to this particular use case, see Singleton Considered Stupid.)

      Makeshifts last the longest.

        What i read here in that link.. ominous.. Yeah im getting the feeling that i might as well use global variables then singleton objects.

        so, i still have these "master hashes" that i'd like to have my whole program access, want to add new modules as time goes by and no matter where the code is, i want to know that %::Garage will hold all the cars- and i have to do that by declaring our %Garage in main.

        am i going to coding hell?

        Please keep in mind im a poop virgin.

        i'm feeling like i've really missed something here with scoping and protecting symbol tables and even why- im thinking maybe i need to .. take a vacation. or play with static electricity close to the box fan.

        where can i read about ways around poop? about protecting the symbol table, why... any specific suggestions?

Re: what is a propper way to make a chunk of data accessible to all my packages for retrieval and modification ?
by mattr (Curate) on Feb 18, 2006 at 15:51 UTC
    Yes. How about Class::DBI? It combines object definition and storage, not blazingly fast but nice for Webapp/*.pm.

    But first yes, you can use class data or make a singleton class (singleton means there can only be one instance, which you don't necessarily want to have happen). You can store data in the class Bathroom itself (not in an instance of a given Bathroom object). This is for example easy to do with the "-static" option if you are using Class::MethodMaker. (which I am using now for a new soap project.. anybody have a better suggestion?)

    Or check out Programming Perl, Tom's OO on class data or perltoot (scroll to class data).
    The first link is easy to understand, especially about a decision you have to make when you start subclassing (i.e. does the data stick around or not)

    So about Class::DBI.

    Bathroom.pm maps to the bathroom table in your db.
    Toilets.pm will map to the toilets table. DBI is being used so you don't need a real database, a database in a file or in memory is also useable.
    So joetoilet and janetoilet are one row each in the toilets table, and the bathroom table has a toilet column, with the id of a toilet in the toilet table. So to get a toilet object for a given bathroom you say

    my $mybathroom = Webapp::Bathroom->retrieve(id => 5); my $mytoilet = $mybathroom->toilet; or my $plunger = $mybathroom->toilet->plunger;
    If you want to allow more than one toilet per bathroom, that is fine you can do this with Class::DBI using has_a and the neat thing is that a relation will return a full object. Check out this code from an old project of mine (I used a separate link table but you could just check the toilets table for the bathroom it has_a too):

    In MgrCompanyLink.pm (which has fields id, company and manager): WebApp::MgrCompanyLink->has_a(company => 'WebApp::Company'); WebApp::MgrCompanyLink->has_a(manager => 'WebApp::Manager'); In Manager.pm (which has fields id and person): WebApp::Manager->has_a(person => 'WebApp::Person'); WebApp::Manager->has_many('companies', [ 'WebApp::MgrCompanyLink' => 'companyid'], 'mgrid', { order_by => 'companyid' } );
    This way you can get an array of companies from the companies method of a manager, and each manager has a person field (id of a record in the person table) which returns a Person.pm object. You can write custom queries too.

    Hope this helps. Reading the perltootc probably will answer what you're asking.