in reply to Best practices passing database handles, cgi objects, etc.

This might not be the best approach but is workable. If we declare these subroutines:
{ my $cgi = CGI->new(); my $session = CGI::Session->new(...); my $dbh = $dbh->connect(...); sub get_cgi { return $cgi; } sub get_session { return $session; } sub get_dbh { return $dbh; } }
We can then shorten the subroutines' argument lists considerably:
MAIN: { my $result = do_something(); print get_cgi()->header().$result; } sub do_something { my $dbh = get_dbh(); my $cgi = get_cgi(); $dbh->foo(...); $cgi->bar(...); my $foo = another_subroutine($somevar, $othervar); return final_result($foo); } sub another_subroutine { my ($somevar, $othervar) = @_; return get_dbh()->baz($somevar, $othervar); } # and so on
The other usual options would be wrapping these subroutines inside a class, so you can store the objects in class or instance variables; or making a package or a (singleton?) class whose only purpose is to initialise, store, and return these objects.

Replies are listed 'Best First'.
Re^2: Best practices passing database handles, cgi objects, etc.
by tobyink (Canon) on Feb 18, 2014 at 12:18 UTC

    Fantastic! A technique to avoid using global variables, but while still keeping all the problems associated with global variables!

    The problem with using global variables is not an irrational fear of the our keyword. The problem is that it introduces global state. This means that if you write an app that, say, operates a blog, and has a database handle as part of its global state, it becomes very difficult to reuse any of that code when you want to write an app that manages multiple blogs (across multiple databases). All your functions are picking up the same database handle from some global place, so you can't tell one function that it needs to copy an article from blog A, and another function that the same article needs to be pasted into blog B.

    Global state makes testing very difficult. If function do_something isn't passed a copy of $dbh as an argument, but instead picks it up from global state, then it becomes difficult to test do_something by passing it test/dummy database handles.

    So the enemy is not global variables. The enemy is global state. Global variables are a manifestation of global state, but so are the techniques you suggest above.

    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
      Sigh.

      What is wrong with keeping a CGI object and a CGI::Session object as global state/variables/whatever? They are request-dependent. They need to be overridden at the start of each request. Are you planning to serve HTTP requests in a non-serial manner or something?

      The database handle: There's usually one per process. One FCGI-ish process usually handles a single web site. What is the problem storing a handle globally in such a case? Certainly, if you plan to connect to multiple databases in your program you don't use that design, but you have to concoct pretty elaborate scenarios to get to a point where this single-handle thing falls down.

      And before you say 'unit testing', you can override those functions, can't you?

        What's wrong? It hard-codes the assumption that one request equals one process, and thus makes it doubly hard to move on to a solution where multiple requests can be served from a single process.

        Whatsmore, it limits your objects and functions to only being used in CGI scripts. Some of these objects may be generically useful, for example, useful in cron jobs that perform various routine maintenance related to the website.

        Even if you ignore all that, there are valid reasons to make sure that sessions are passed around from function to function. For example, you may wish to allow the admin user, once they're logged in, to take over any other user's session - to help diagnose website problems for example. Doing that sort of thing is much easier if the session is not a singleton.

        use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
      I fail to see how your OO approach is any better; all it does is wrap some global variables inside a class.

      Also, it's silly to store multiple blogs (with identical schemas, I presume?) across multiple databases.

        I fail to see how your OO approach is any better; all it does is wrap some global variables inside a class.

        That is how its better, its called encapsulation

        You can have $foo and $bar both be of type Classname, with both being able to ->dance and ->sign

        Its better in the same way subroutines are better than not subroutines ( goto )

        Its also why  Main(@ARGV); sub Main { } is better than  MAIN: { } because you can only  goto MAIN , its a label, its not a subroutine, you can't call it

        Its better in that its : testable/mockable, reusable, not fragile Action at a distance (computer programming)

        http://programmers.stackexchange.com/questions/148108/why-is-global-state-so-evil

        You cannot have more than one instance of global state -- its global

        Also, it's silly to store multiple blogs (with identical schemas, I presume?) across multiple databases.

        So is everything you wrote :)

        Because classes can have multiple instances.

        (And the blogs may be operated by different people and organizations and be installed on entirely different servers. There may be slight variations in the schemas because of different versions of the blog engine being installed.)

        use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name