File Coverage

blib/lib/Catalyst/Plugin/Session/Store/DBIC.pm
Criterion Covered Total %
statement 24 64 37.5
branch 0 32 0.0
condition 0 5 0.0
subroutine 8 20 40.0
pod 10 10 100.0
total 42 131 32.0


line stmt bran cond sub pod time code
1             package Catalyst::Plugin::Session::Store::DBIC;
2              
3 1     1   841 use strict;
  1         2  
  1         26  
4 1     1   5 use warnings;
  1         2  
  1         31  
5 1     1   4 use base qw/Catalyst::Plugin::Session::Store::Delegate/;
  1         10  
  1         842  
6 1     1   603505 use Catalyst::Exception;
  1         184007  
  1         35  
7 1     1   796 use Catalyst::Plugin::Session::Store::DBIC::Delegate;
  1         3  
  1         8  
8 1     1   886 use MIME::Base64 ();
  1         712  
  1         27  
9 1     1   6 use MRO::Compat;
  1         2  
  1         22  
10 1     1   969 use Storable ();
  1         3462  
  1         813  
11              
12             our $VERSION = '0.14';
13              
14             =head1 NAME
15              
16             Catalyst::Plugin::Session::Store::DBIC - Store your sessions via DBIx::Class
17              
18             =head1 SYNOPSIS
19              
20             # Create a table in your database for sessions
21             CREATE TABLE sessions (
22             id CHAR(72) PRIMARY KEY,
23             session_data TEXT,
24             expires INTEGER
25             );
26              
27             # Create the corresponding table class
28             package MyApp::Schema::Session;
29              
30             use base qw/DBIx::Class/;
31              
32             __PACKAGE__->load_components(qw/Core/);
33             __PACKAGE__->table('sessions');
34             __PACKAGE__->add_columns(qw/id session_data expires/);
35             __PACKAGE__->set_primary_key('id');
36              
37             1;
38              
39             # In your application
40             use Catalyst qw/Session Session::Store::DBIC Session::State::Cookie/;
41              
42             __PACKAGE__->config(
43             # ... other items ...
44             'Plugin::Session' => {
45             dbic_class => 'DBIC::Session', # Assuming MyApp::Model::DBIC
46             expires => 3600,
47             },
48             );
49              
50             # Later, in a controller action
51             $c->session->{foo} = 'bar';
52              
53             =head1 DESCRIPTION
54              
55             This L<Catalyst::Plugin::Session> storage module saves session data in
56             your database via L<DBIx::Class>. It's actually just a wrapper around
57             L<Catalyst::Plugin::Session::Store::Delegate>; if you need complete
58             control over how your sessions are stored, you probably want to use
59             that instead.
60              
61             =head1 METHODS
62              
63             =head2 setup_finished
64              
65             Hook into the configured session class.
66              
67             =cut
68              
69             sub setup_finished {
70 0     0 1   my $c = shift;
71              
72 0 0         return $c->next::method unless @_;
73              
74             # Try to determine id_field if it isn't set
75 0 0         unless ($c->_session_plugin_config->{id_field}) {
76 0           my $model = $c->session_store_model;
77 0 0         my $rs = ref $model ? $model
    0          
78             : $model->can('resultset_instance') ? $model->resultset_instance
79             : $model;
80 0           my @primary_columns = $rs->result_source->primary_columns;
81              
82 0 0         Catalyst::Exception->throw(
83             message => __PACKAGE__ . qq/: Primary key consists of more than one column; please set id_field manually/
84             ) if @primary_columns > 1;
85              
86 0           $c->_session_plugin_config->{id_field} = $primary_columns[0];
87             }
88              
89 0           $c->next::method(@_);
90             }
91              
92             =head2 session_store_dbic_class
93              
94             Return the L<DBIx::Class> class name to be passed to C<< $c->model >>.
95             Defaults to C<DBIC::Session>.
96              
97             =cut
98              
99             sub session_store_dbic_class {
100 0 0   0 1   shift->_session_plugin_config->{dbic_class} || 'DBIC::Session';
101             }
102              
103             =head2 session_store_dbic_id_field
104              
105             Return the configured ID field name. Defaults to C<id>.
106              
107             =cut
108              
109             sub session_store_dbic_id_field {
110 0 0   0 1   shift->_session_plugin_config->{id_field} || 'id';
111             }
112              
113             =head2 session_store_dbic_data_field
114              
115             Return the configured data field name. Defaults to C<session_data>.
116              
117             =cut
118              
119             sub session_store_dbic_data_field {
120 0 0   0 1   shift->_session_plugin_config->{data_field} || 'session_data';
121             }
122              
123             =head2 session_store_dbic_expires_field
124              
125             Return the configured expires field name. Defaults to C<expires>.
126              
127             =cut
128              
129             sub session_store_dbic_expires_field {
130 0 0   0 1   shift->_session_plugin_config->{expires_field} || 'expires';
131             }
132              
133             =head2 session_store_model
134              
135             Return the model used to find a session.
136              
137             =cut
138              
139             sub session_store_model {
140 0     0 1   my ($c, $id) = @_;
141              
142 0           my $dbic_class = $c->session_store_dbic_class;
143 0 0         $c->model($dbic_class, $id) or die "Couldn't find a model named $dbic_class";
144             }
145              
146             =head2 get_session_store_delegate
147              
148             Load the row corresponding to the specified session ID. If none is
149             found, one is automatically created.
150              
151             =cut
152              
153             sub get_session_store_delegate {
154 0     0 1   my ($c, $id) = @_;
155              
156 0           Catalyst::Plugin::Session::Store::DBIC::Delegate->new({
157             model => $c->session_store_model($id),
158             id_field => $c->session_store_dbic_id_field,
159             data_field => $c->session_store_dbic_data_field,
160             });
161             }
162              
163             =head2 session_store_delegate_key_to_accessor
164              
165             Match the specified key and operation to the session ID and field
166             name.
167              
168             =cut
169              
170             sub session_store_delegate_key_to_accessor {
171 0     0 1   my $c = shift;
172 0           my $key = $_[0];
173 0           my ($field, @args) = $c->next::method(@_);
174              
175 0           my ($type) = ($key =~ /^(\w+):/);
176              
177 0 0         $field = $c->session_store_dbic_id_field if $field eq 'id';
178 0 0         $field = $c->session_store_dbic_expires_field if $field eq 'expires';
179 0 0 0       $field = $c->session_store_dbic_data_field if $field eq 'session' or $field eq 'flash';
180              
181 0     0     my $accessor = sub { shift->$type($key)->$field(@_) };
  0            
182              
183 0 0         if ($field eq $c->session_store_dbic_data_field) {
184 0   0       @args = map { MIME::Base64::encode(Storable::nfreeze($_ || '')) } @args;
  0            
185             $accessor = sub {
186 0     0     my $value = shift->$type($key)->$field(@_);
187 0 0         return unless $value;
188 0           return Storable::thaw(MIME::Base64::decode($value));
189 0           };
190             }
191              
192 0           return ($accessor, @args);
193             }
194              
195             =head2 delete_session_data
196              
197             Delete the specified session from the backend store.
198              
199             =cut
200              
201             sub delete_session_data {
202 0     0 1   my ($c, $key) = @_;
203              
204             # expires is stored on the session row for compatibility with Store::DBI
205 0 0         return if $key =~ /^expires/;
206              
207 0           $c->session_store_model->search({
208             $c->session_store_dbic_id_field => $key,
209             })->delete;
210             }
211              
212             =head2 delete_expired_sessions
213              
214             Delete all expired sessions.
215              
216             =cut
217              
218             sub delete_expired_sessions {
219 0     0 1   my $c = shift;
220              
221 0           $c->session_store_model->search({
222             $c->session_store_dbic_expires_field => { '<', time() },
223             })->delete;
224             }
225              
226             =head1 CONFIGURATION
227              
228             The following parameters should be placed in your application
229             configuration under the C<Plugin::Session> key.
230              
231             =head2 dbic_class
232              
233             (Required) The name of the L<DBIx::Class> that represents a session in
234             the database. It is recommended that you provide only the part after
235             C<MyApp::Model>, e.g. C<DBIC::Session>.
236              
237             If you are using L<Catalyst::Model::DBIC::Schema>, the following
238             layout is recommended:
239              
240             =over 4
241              
242             =item * C<MyApp::Schema> - your L<DBIx::Class::Schema> class
243              
244             =item * C<MyApp::Schema::Session> - your session table class
245              
246             =item * C<MyApp::Model::DBIC> - your L<Catalyst::Model::DBIC::Schema> class
247              
248             =back
249              
250             This module will then use C<< $c->model >> to access the appropriate
251             result source from the composed schema matching the C<dbic_class>
252             name.
253              
254             For more information, please see L<Catalyst::Model::DBIC::Schema>.
255              
256             =head2 expires
257              
258             Number of seconds for which sessions are active.
259              
260             Note that no automatic cleanup is done on your session data. To
261             delete expired sessions, you can use the L</delete_expired_sessions>
262             method with L<Catalyst::Plugin::Scheduler>.
263              
264             =head2 id_field
265              
266             The name of the field on your sessions table which stores the session
267             ID. Defaults to C<id>.
268              
269             =head2 data_field
270              
271             The name of the field on your sessions table which stores session
272             data. Defaults to C<session_data> for compatibility with
273             L<Catalyst::Plugin::Session::Store::DBI>.
274              
275             =head2 expires_field
276              
277             The name of the field on your sessions table which stores the
278             expiration time of the session. Defaults to C<expires>.
279              
280             =head1 SCHEMA
281              
282             Your sessions table should contain the following columns:
283              
284             id CHAR(72) PRIMARY KEY
285             session_data TEXT
286             expires INTEGER
287              
288             The C<id> column should probably be 72 characters. It needs to handle
289             the longest string that can be returned by
290             L<Catalyst::Plugin::Session/generate_session_id>, plus another eight
291             characters for internal use. This is less than 72 characters when
292             SHA-1 or MD5 is used, but SHA-256 will need all 72 characters.
293              
294             The C<session_data> column should be a long text field. Session data
295             is encoded using L<MIME::Base64> before being stored in the database.
296              
297             Note that MySQL C<TEXT> fields only store 64 kB, so if your session
298             data will exceed that size you'll want to use C<MEDIUMTEXT>,
299             C<MEDIUMBLOB>, or larger. If you configure your
300             L<DBIx::Class::ResultSource> to include the size of the column, you
301             will receive warnings for this problem:
302              
303             This session requires 1180 bytes of storage, but your database
304             column 'session_data' can only store 200 bytes. Storing this
305             session may not be reliable; increase the size of your data field
306              
307             See L<DBIx::Class::ResultSource/add_columns> for more information.
308              
309             The C<expires> column stores the future expiration time of the
310             session. This may be null for per-user and flash sessions.
311              
312             Note that you can change the column names using the L</id_field>,
313             L</data_field>, and L</expires_field> configuration parameters.
314             However, the column types must match the above.
315              
316             =head1 AUTHOR
317              
318             Daniel Westermann-Clark E<lt>danieltwc@cpan.orgE<gt>
319              
320             =head1 ACKNOWLEDGMENTS
321              
322             =over 4
323              
324             =item * Andy Grundman, for L<Catalyst::Plugin::Session::Store::DBI>
325              
326             =item * David Kamholz, for most of the testing code (from
327             L<Catalyst::Plugin::Authentication::Store::DBIC>)
328              
329             =item * Yuval Kogman, for assistance in converting to
330             L<Catalyst::Plugin::Session::Store::Delegate>
331              
332             =item * Jay Hannah, for tests and warning when session size
333             exceeds DBIx::Class storage size.
334              
335             =back
336              
337             =head1 COPYRIGHT
338              
339             Copyright (c) 2006 - 2009
340             the Catalyst::Plugin::Session::Store::DBIC L</AUTHOR>
341             as listed above.
342              
343             This program is free software; you can redistribute it and/or modify it
344             under the same terms as Perl itself.
345              
346             =cut
347              
348             1;