File Coverage

blib/lib/Catalyst/Plugin/Session/Store/DOD.pm
Criterion Covered Total %
statement 18 50 36.0
branch 0 20 0.0
condition 0 6 0.0
subroutine 6 11 54.5
pod 5 5 100.0
total 29 92 31.5


line stmt bran cond sub pod time code
1             package Catalyst::Plugin::Session::Store::DOD;
2 1     1   1145 use strict;
  1         2  
  1         42  
3 1     1   6 use warnings;
  1         3  
  1         39  
4              
5 1     1   17 use base qw/Class::Data::Inheritable Catalyst::Plugin::Session::Store/;
  1         2  
  1         2606  
6 1     1   4521 use MIME::Base64;
  1         1516  
  1         108  
7 1     1   2014 use NEXT;
  1         2549  
  1         35  
8 1     1   1249 use Storable qw/nfreeze thaw/;
  1         4120  
  1         681  
9              
10             our $VERSION = '0.01';
11              
12             __PACKAGE__->mk_classdata('_sth_get_session_data');
13             __PACKAGE__->mk_classdata('_sth_get_expires');
14             __PACKAGE__->mk_classdata('_sth_check_existing');
15             __PACKAGE__->mk_classdata('_sth_update_session');
16             __PACKAGE__->mk_classdata('_sth_insert_session');
17             __PACKAGE__->mk_classdata('_sth_update_expires');
18             __PACKAGE__->mk_classdata('_sth_delete_session');
19             __PACKAGE__->mk_classdata('_sth_delete_expired_sessions');
20              
21             sub get_session_data {
22 0     0 1   my ( $c, $key ) = @_;
23            
24             # expires:sid expects an expiration time
25 0 0         if ( my ($sid) = $key =~ /^expires:(.*)/ ) {
26 0           $key = "session:$sid";
27 0           my $session = $c->config->{session}->{model}->lookup($key);
28 0 0         return $session->expires
29             if $session;
30             }
31             else {
32 0           my $session = $c->config->{session}->{model}->lookup($key);
33 0 0         return thaw( decode_base64($session->session_data) )
34             if $session;
35             }
36 0           return;
37             }
38              
39             sub store_session_data {
40 0     0 1   my ( $c, $key, $data ) = @_;
41            
42             # expires:sid keys only update the expiration time
43 0 0         if ( my ($sid) = $key =~ /^expires:(.*)/ ) {
44 0           $key = "session:$sid";
45              
46             # Update or create new
47 0           my $session = $c->config->{session}->{model}->new(
48             id => $key,
49             expires => $c->session_expires,
50             );
51 0 0         $session->exists() ? $session->update() : $session->insert;
52              
53             } else {
54             # Prepare the data
55 0           my $frozen = encode_base64( nfreeze($data) );
56 0 0         my $expires = $key =~ /^(?:session|flash):/
57             ? $c->session_expires
58             : undef;
59              
60             # Update or create new
61 0           my $session = $c->config->{session}->{model}->new(
62             id => $key,
63             session_data => $frozen,
64             expires => $expires,
65             );
66 0 0         $session->exists() ? $session->update() : $session->insert;
67             }
68            
69 0           return;
70             }
71              
72             sub delete_session_data {
73 0     0 1   my ( $c, $key ) = @_;
74            
75 0 0         return if $key =~ /^expires/;
76              
77 0           my $session = $c->config->{session}->{model}->lookup($key);
78 0 0         $session->remove
79             if $session;
80              
81 0           return;
82             }
83              
84             sub delete_expired_sessions {
85 0     0 1   my $c = shift;
86              
87 0           my @sessions = $c->config->{session}->{model}->search({
88             expires => {
89             op => "IS NOT NULL AND expires <",
90             value => time(),
91             }
92             });
93             # This sucks, it will pound the DB
94 0           foreach (@sessions) {
95 0           $_->remove;
96             }
97              
98 0           return;
99             }
100              
101             sub setup_session {
102 0     0 1   my $c = shift;
103              
104 0           $c->NEXT::setup_session(@_);
105            
106 0 0 0       unless ( $c->config->{session}->{model}->has_column('id') &&
      0        
107             $c->config->{session}->{model}->has_column('session_data') &&
108             $c->config->{session}->{model}->has_column('expires')
109             ) {
110 0           Catalyst::Exception->throw(
111             message => 'The DOD object does not have the required columns '
112             . 'to store session data.'
113             );
114             }
115             }
116              
117             1;
118             __END__
119              
120             =head1 NAME
121              
122             Catalyst::Plugin::Session::Store::DOD - Store your sessions in a database
123             using Data::ObjectDriver.
124              
125             =head1 SYNOPSIS
126              
127             # Create a table in your database for sessions
128             CREATE TABLE sessions (
129             id char(72) primary key,
130             session_data text,
131             expires int(10)
132             );
133            
134             # Create a Data::ObjectDriver model
135             package BaseObject::M::Session;
136             use base qw( Data::ObjectDriver::BaseObject );
137              
138             use Data::ObjectDriver::Driver::DBI;
139              
140             __PACKAGE__->install_properties({
141             columns => [ 'id', 'session_data', 'expires' ],
142             primary_key => [ 'id' ],
143             datasource => 'sessions',
144             get_driver => sub {
145             Data::ObjectDriver::Driver::DBI->new(
146             dsn => "dbi:SQLite:session.db",
147             ),
148             },
149             });
150              
151             # In your app
152             use Catalyst qw/Session Session::Store::DOD Session::State::Cookie/;
153            
154             # Connect directly to the database
155             MyApp->config->{session} = {
156             expires => 3600,
157             model => "BaseObject::M::Session",
158             };
159              
160             # ... in an action:
161             $c->session->{foo} = 'bar'; # will be saved
162              
163             =head1 DESCRIPTION
164              
165             This storage module will store session data in a database using a
166             Data::ObjectDriver model. It is based on version 0.13 of
167             Catalyst::Plugin::Session::Store::DBI by Andy Grundman
168             <andy@hybridized.org> and is basically a port of his module to use
169             D::OD instead of directly interacting via DBI.
170              
171             =head1 CONFIGURATION
172              
173             These parameters are placed in the configuration hash under the C<session>
174             key.
175              
176             =head2 expires
177              
178             The expires column in your table will be set with the expiration value.
179             Note that no automatic cleanup is done on your session data, but you can use
180             the delete_expired_sessions method to perform clean up. You can make use of
181             the L<Catalyst::Plugin::Scheduler> plugin to schedule automated session
182             cleanup.
183              
184             =head2 model
185              
186             L<Data::ObjectDriver::BaseObject::Model> object which is configuered to
187             interact with your storage engine for session information.
188              
189             =head1 SCHEMA
190              
191             Your 'sessions' model must contain at minimum the following 3 attributes:
192              
193             id char(72) primary key
194             session_data text
195             expires int(10)
196              
197             The 'id' column should probably be 72 characters. It needs to handle the
198             longest string that can be returned by
199             L<Catalyst::Plugin::Authentication/generate_session_id>, plus another 8
200             characters for internal use. This is less than 72 characters in practice when
201             SHA-1 or MD5 are used, but SHA-256 will need all those characters.
202              
203             The 'session_data' column should be a long text field. Session data is
204             encoded using Base64 before being stored in the database.
205              
206             The 'expires' column stores the future expiration time of the session. This
207             may be null for per-user and flash sessions.
208              
209             =head1 METHODS
210              
211             =head2 get_session_data
212              
213             =head2 store_session_data
214              
215             =head2 delete_session_data
216              
217             =head2 delete_expired_sessions
218              
219             =head2 setup_session
220              
221             These are implementations of the required methods for a store. See
222             L<Catalyst::Plugin::Session::Store>.
223              
224             =head1 SEE ALSO
225              
226             L<Catalyst>, L<Catalyst::Plugin::Session>, L<Catalyst::Plugin::Scheduler>
227              
228             =head1 AUTHOR
229              
230             David Recordon, <david@sixapart.com>
231              
232             Based on Catalyst::Plugin::Session::Store::DBI by Andy Grundman
233             <andy@hybridized.org>
234              
235             =head1 COPYRIGHT
236              
237             This program is free software, you can redistribute it and/or modify it
238             under the same terms as Perl itself.
239              
240             =cut