package Catalyst::Plugin::Session::Store::Jifty::DBI; use strict; use warnings; use base qw( Catalyst::Plugin::Session::Store ); use Storable qw( nfreeze thaw ); use MIME::Base64; use Catalyst::Exception; use NEXT; our $VERSION = '0.01'; sub setup_session { my $c = shift; $c->NEXT::setup_session(@_); unless ( $c->config->{session}->{moniker} ) { Catalyst::Exception->throw( "Session::Store::Jifty::DBI: 'moniker' is missing" ); } $c->config->{session}->{moniker_collection} ||= $c->config->{session}->{moniker} . 'Collection'; my %column = %{ $c->config->{session}->{columns} || {} }; unless ( $column{id} && $column{session_data} && $column{expires} ) { $column{id} ||= 'session_id'; $column{session_data} ||= 'session_data'; $column{expires} ||= 'expires'; $c->config->{session}->{columns} = \%column; } } sub get_session_data { my ($c, $key) = @_; my $moniker = $c->config->{session}->{moniker}; my %column = %{ $c->config->{session}->{columns} }; my $record = $c->model($moniker); if ( my ($sid) = $key =~ /^expires:(.*)/ ) { $key = "session:$sid"; $record->load_by_cols( $column{id} => $key ); return $record->_value( $column{expires} ); } else { $record->load_by_cols( $column{id} => $key ); my $data = $record->_value( $column{session_data} ); return ( $c->config->{session}->{use_custom_serialization} ) ? $data : thaw( decode_base64( $data ) ); } return; } sub store_session_data { my ($c, $key, $data) = @_; my $moniker = $c->config->{session}->{moniker}; my %column = %{ $c->config->{session}->{columns} }; my $record = $c->model($moniker); if ( my ($sid) = $key =~ /^expires:(.*)/ ) { $key = "session:$sid"; $record->load_by_cols( $column{id} => $key ); if ( $record->id ) { $record->_set( column => $column{expires}, value => $c->session_expires, ); } } else { unless ( $c->config->{session}->{use_custom_serialization} ) { $data = encode_base64( nfreeze( $data ) ); } my $expires = ( $key =~ /^(?:session|flash):/ ) ? $c->session_expires : undef; $record->load_by_cols( $column{id} => $key ); if ( $record->id ) { $c->model($moniker)->_handle->begin_transaction; eval { $record->_set( column => $column{session_data}, value => $data, ); $record->_set( column => $column{expires}, value => $expires, ); }; $@ ? $c->model($moniker)->_handle->rollback : $c->model($moniker)->_handle->commit; } else { $record->create( $column{id} => $key, $column{session_data} => $data, $column{expires} => $expires, ); } } return; } sub delete_session_data { my ($c, $key) = @_; return if $key =~ /^expires:/; my $moniker = $c->config->{session}->{moniker}; my %column = %{ $c->config->{session}->{columns} }; my $record = $c->model($moniker); $record->load_by_cols( $column{id} => $key ); $record->delete; } sub delete_expired_sessions { my $c = shift; # XXX: this must be much better to do with simple_query my $moniker = $c->config->{session}->{moniker_collection}; my %column = %{ $c->config->{session}->{columns} }; my $collection = $c->model($moniker); $collection->limit( column => $column{expires}, value => undef, operator => 'IS NOT', ); $collection->limit( column => $column{expires}, value => time(), operator => '<', ); while( my $record = $collection->next ) { $record->delete; } } 1; __END__ =head1 NAME Catalyst::Plugin::Session::Store::Jifty::DBI - Store your session with Jifty::DBI =head1 SYNOPSIS # prepare a table like this. # note that we use "session_id" instead of simple "id", # as "id" is usually reserved as serial by Jifty::DBI # (which is configurable but changing this is not recommended). package MyApp::Schema::Session; use Jifty::DBI::Schema; use Jifty::DBI::Record schema { column session_id => type is 'char(72)', is mandatory, is distinct, is indexed; column session_data => type is 'text'; column expires => type is 'integer'; }; # and a model package MyApp::Model::DB; use base qw( Catalyst::Model::Jifty::DBI ); __PACKAGE__->config->{schema_base} = 'MyApp::Schema'; # and your app. MyApp->config->{session} = { expires => 3600, moniker => 'DB::Session', }; # then in an action $c->session->[foo} = 'bar'; # will be saved =head1 DESCRIPTION This storage module will store session data in a database using a Jifty::DBI model. =head1 CONFIGURATION These parameters are placed in the configuration hash under the C key. =head2 expires The C column in your table will be set with the expiration value. Note that no automatic cleanup is done. You can use C method with L, but most probably you may want to implement your own cleanup script with raw L (or L) for speed and stability. =head2 moniker specify the moniker to access your session table (to get a session record) via $c->model(). This configuration is mandatory. If you dare, you also can set C to specify the moniker to get a collection of session records (but you usually don't need this). =head2 columns by default, this module uses the column names shown above, but if you want to change some of these, you can give this a hash reference like this: MyApp->config->{session}->{columns} = { id => 'sid', session_data => 'body', expires => 'until', }; =head2 use_custom_serialization If you want to use Ls to serialize/deserialize session data, set this to true. This may be handy when you want to use other Jifty::DBI's features like validation. =head1 METHODS =head2 get_session_data =head2 store_session_data =head2 delete_session_data =head2 delete_expired_sessions =head2 setup_session These are implementations of the required methods for a store. See L. =head1 SEE ALSO L, L, L =head1 AUTHOR Kenichi Ishigaki, Eishigaki@cpan.orgE =head1 COPYRIGHT AND LICENSE Copyright (C) 2008 by Kenichi Ishigaki. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut