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

hi monks! I have what is hopefully a simple Catalyst question.

Scripts written using CGI::Fast can normally have persistent variables, basically anything declared outside of the request loop is persitent between requests for the life of the process.

Can someone please tell me how I can do this with Catalyst when I'm using its FCGI engine?

Basically I want to be able to create variables such as $COUNTER below.

use CGI::Fast qw(:standard); $COUNTER = 0; while (new CGI::Fast) { print header; print start_html("Fast CGI Rocks"); print h1("Fast CGI Rocks"), "Invocation number ",b($COUNTER++), " PID +",b($$),".", hr; print end_html; }

I've searched for this, but if it's out there I'm not finding it. thanks monks in advance! cheers!

Replies are listed 'Best First'.
Re: Catalyst fcgi engine w. persistent variables
by Your Mother (Archbishop) on Jul 07, 2011 at 17:42 UTC

    This sounds like an XY Problem and any design that suggests global variables is probably a bad one, so I say. Here is a pretty simple way regardless-

    package MyApp; use Moose; use namespace::autoclean; use Catalyst::Runtime 5.80; use Catalyst qw( -Debug ); # etc... extends "Catalyst"; __PACKAGE__->config( name => "MyApp" ); our $MY_COUNTER = 0; sub my_counter { $MY_COUNTER; } before "dispatch" => sub { $MY_COUNTER++ }; __PACKAGE__->setup(); 1;
    package MyApp::Controller::Root; use Moose; use namespace::autoclean; BEGIN { extends 'Catalyst::Controller' } __PACKAGE__->config(namespace => ''); sub counter :Local Args(0) { my ( $self, $c ) = @_; $c->response->body("Count #" . $c->my_counter); } 1; __END__ http://localhost:3000/counter

    What you would want to do in any real-world case I can imagine is create a model class or perhaps use Plack middleware. If the information belongs to the application, it would best go in a model. If it's meta/tracking info it could go in the server or a logger. In fact the code could be standalone and then it can go in either the model or server layer or both. Suggested reading: Catalyst::Model::Adaptor and Plack::Middleware.

      thanks for the reply, it may be an XY problem :)

      Basically I want some data structures to persist in memory between requests. I don't want to write them to a file, a db, memcached etc, because that's somewhat slower and memory will work just fine.

      Ideally they'd expire after so long and then I'll reupdate them when I see they've expired. It doesn't matter to me if a module handles the expiration or I do it myself from a time stamp in the structure. The data itself doesn't literally need to be global everywhere, if I can get it from the context that's fine.

      Something like Catalyst::Plugin::Cache with Cache::Memory as a backend would work, but from my experimenting it doesn't look like the cache persists between requests :/. Perhaps I am no utilizing it correctly however.

      In my application class I merely do

      __PACKAGE__->config->{'Plugin::Cache'}{backend} = { + class => "Cache::Memory", default_expires => '600 sec' + };

      Then access it via $cache = $c->cache; in my controller. But the data isn't persisting between requests alas

      thank you again

        Yeah, I didn't check in before just trying this so I missed that you'd solved it. I'll leave some code for future readers. The docs are thin so I'm not surprised you didn't get it right the first time.

        # in MyApp.pm __PACKAGE__->config( "Plugin::Cache" => { backend => { class => "Cache::MemoryCache", } }); # in MyApp::Controller::Root sub counter :Local Args(0) { my ( $self, $c ) = @_; my $counter = $c->cache->get("counter") + 1; $c->cache->set( counter => $counter ); $c->response->body("Count #" . $counter); }

        Sounds like you already figured out the other parts like expiration. Have fun and realize these are volatile and if I read the docs right they share data or don't depending on the engine the app is running.

        Okay looks like the Cache modules I mentioned previously do indeed work, I typoed the timeout in my code, so now it appears to persist between requests. Thanks again, and if there's any smarter way to do this I'd be curious to know.
        cheers