In this article I'll show how CGI scripts, e.g. based on CGI::Application and CGI::Session, can switch to Plack-style sessions.
The first step is to decide how to store the session data, i.e. in memory, in a cookie, in a file, or in a database.
The documentation for Plack::Middleware::Session shows some ways to do this.
I won't repeat that code, but will show how to use CHI to cache the data.
CHI is a sophisticated interface to many caching mechanisms. Here, I store session data in a hashref managed by CHI.
We create a new class to encapsulate the CHI-specific code:
1 package Local::Application::Util::Cache; 2 3 use CHI; 4 5 use Local::Application::Util::Config; 6 7 use Moose; 8 9 use namespace::autoclean; 10 11 our $VERSION = '1.02'; 12 13 # ----------------------------------------------- 14 15 sub cache 16 { 17 my($self) = @_; 18 my($config) = Local::Application::Util::Config -> new -> config; 19 my($datastore) = {}; 20 21 return CHI -> new 22 ( 23 datastore => $datastore, 24 driver => 'Memory', 25 expires_in => $$config{session_timeout}, 26 namespace => __PACKAGE__, 27 ); 28 29 } # End of cache. 30 31 # -------------------------------------------------- 32 33 __PACKAGE__ -> meta -> make_immutable; 34 35 1;
Points to note:
We use a config module to read a config file, to get a session timeout, which is used on line 25.
My config file contins this line:
session_timeout = 10h
and is read in using Config::Tiny.
The session data is held in a hashref, which is a local variable inside the cache() method.
The reason this works is that CHI keeps a reference to this hashref, which stops the variable going out of scope when the cache() method exits.
We pass to CHI -> new() the parameters required for a memory-based data store.
Different cache strategies have different parameter requirements, so check the docs carefully.
The format of session times, as used by CHI, is described in CHI's docs under Duration Expressions.
The next step is the PSGI script to use our cache class. Call it 'local.psgi':
1 #!/usr/bin/perl 2 3 use common::sense; 4 5 use CGI::Application::Dispatch::PSGI; 6 7 use Local::Application::Util::Cache; 8 9 use Plack::Builder; 10 use Plack::Session::Store::Cache; 11 12 # ---------------- 13 14 my($app) = CGI::Application::Dispatch -> as_psgi 15 ( 16 prefix => 'Local::Application::Controller', 17 table => 18 [ 19 '' => {app => 'Initialize', rm => 'display'}, 20 ':app' => {rm => 'display'}, 21 ':app/:rm/:id?' => {}, 22 ], 23 ); 24 25 builder 26 { 27 enable 'Session', 28 store => Plack::Session::Store::Cache -> new 29 (cache => Local::Application::Util::Cache -> new -> cache); 30 enable 'Static', 31 path => qr!^/(assets|favicon|yui)/!, 32 root => '/var/www'; 33 $app; 34 };
Run local.psgi with:
plackup -l 127.0.0.1:5002 local.psgi &
or, even better:
starman -l 127.0.0.1:5002 --workers 1 local.psgi &
Points to note:
We prepare to use our new class.
We prepare to interface to Plack's session-handling code.
Our common-or-garden, CGI::Application::Dispatch-based, Plack app.
If you don't use CGI::Application::Dispatch your code will be even simpler.
We tell Plack::Session::Store::Cache to use our new class.
Plack makes the session data available via the environment, so that's what we tap into next:
1 my($q) = $self -> query; 2 my($env) = $q -> env; 3 4 $self -> param(sid => $$env{'psgix.session.options'}{id}); 5 $self -> param(script_name => $$env{SCRIPT_NAME}); 6 7 my($session) = $$env{'psgix.session'}; 8 9 $$session{execution_count}++;
Points to note:
Since our app is CGI::Application-based, we get the query object (of type CGI) via a call to the query() method.
Query objects are usually only used to hold CGI form field key-value pairs.
But, since Plack's environment is made available via the query object, we call the env() method to retrieve a hashref of env data.
Again, since our app is CGI::Application-based, we have a param() method available to use to store key-value pairs, for later retrieval elsewhere in the app.
We grab the session itself, and update an arbitrary parameter within that session.
Yep - that's all there is to it.
Any session data value, here $$session{execution_count}, can be updated in this manner, and the new value is saved in the session automatically.
Of course, using the memory-based datastore as above means the value is not saved between runs of the program.
But by changing the return value of our class's cache() method to:
1 return CHI -> new 2 ( 3 driver => 'File', 4 root_dir => '/tmp/chi', 5 namespace => __PACKAGE__, 6 );
we can even stop and start local.psgi, and have the counter continue from where it was up to.
Neat, huh?
The Plack Home Page - http://plackperl.org/.
The Previous Article in this Series: http://savage.net.au/Perl/html/plack.for.beginners.html.
Ron Savage .
Home page: http://savage.net.au/index.html.
Australian Copyright © 2010 Ron Savage. All rights reserved.
All Programs of mine are 'OSI Certified Open Source Software'; you can redistribute them and/or modify them under the terms of The Artistic License, a copy of which is available at: http://www.opensource.org/licenses/index.html