File Coverage

blib/lib/Catalyst/Plugin/Session/Store/MongoDB.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Catalyst::Plugin::Session::Store::MongoDB;
2 1     1   27204 use strict;
  1         3  
  1         50  
3 1     1   8 use warnings;
  1         1  
  1         63  
4              
5             our $VERSION = '0.02';
6              
7 1     1   693 use Moose;
  0            
  0            
8             use namespace::autoclean;
9              
10             use MongoDB::Connection;
11             use Data::Dumper;
12              
13             BEGIN { extends 'Catalyst::Plugin::Session::Store' }
14              
15             has hostname => (
16             isa => 'Str',
17             is => 'ro',
18             lazy_build => 1,
19             );
20              
21             has port => (
22             isa => 'Int',
23             is => 'ro',
24             lazy_build => 1,
25             );
26              
27             has dbname => (
28             isa => 'Str',
29             is => 'ro',
30             lazy_build => 1,
31             );
32              
33             has collectionname => (
34             isa => 'Str',
35             is => 'ro',
36             lazy_build => 1,
37             );
38              
39             has '_collection' => (
40             isa => 'MongoDB::Collection',
41             is => 'ro',
42             lazy_build => 1,
43             );
44              
45             has '_connection' => (
46             isa => 'MongoDB::Connection',
47             is => 'ro',
48             lazy_build => 1,
49             );
50              
51             has '_db' => (
52             isa => 'MongoDB::Database',
53             is => 'ro',
54             lazy_build => 1,
55             );
56              
57             sub _cfg_or_default {
58             my ($self, $name, $default) = @_;
59              
60             my $cfg = $self->_session_plugin_config;
61              
62             return $cfg->{$name} || $default;
63             }
64              
65             sub _build_hostname {
66             my ($self) = @_;
67             return $self->_cfg_or_default('hostname', 'localhost');
68             }
69              
70             sub _build_port {
71             my ($self) = @_;
72             return $self->_cfg_or_default('port', 27017);
73             }
74              
75             sub _build_dbname {
76             my ($self) = @_;
77             return $self->_cfg_or_default('dbname', 'catalyst');
78             }
79              
80             sub _build_collectionname {
81             my ($self) = @_;
82             return $self->_cfg_or_default('collectionname', 'session');
83             }
84              
85             sub _build__collection {
86             my ($self) = @_;
87              
88             return $self->_db->get_collection($self->collectionname);
89             }
90              
91             sub _build__connection {
92             my ($self) = @_;
93              
94             return MongoDB::Connection->new(
95             host => $self->hostname,
96             port => $self->port,
97             );
98             }
99              
100             sub _build__db {
101             my ($self) = @_;
102              
103             return $self->_connection->get_database($self->dbname);
104             }
105              
106             sub _serialize {
107             my ($self, $data) = @_;
108              
109             my $d = Data::Dumper->new([ $data ]);
110              
111             return $d->Indent(0)->Purity(1)->Terse(1)->Quotekeys(0)->Dump;
112             }
113              
114             sub get_session_data {
115             my ($self, $key) = @_;
116              
117             my ($prefix, $id) = split(/:/, $key);
118              
119             my $found = $self->_collection->find_one({ _id => $id },
120             { $prefix => 1, 'expires' => 1 });
121              
122             return undef unless $found;
123              
124             if ($found->{expires} && time() > $found->{expires}) {
125             $self->delete_session_data($id);
126             return undef;
127             }
128              
129             return eval($found->{$prefix});
130             }
131              
132             sub store_session_data {
133             my ($self, $key, $data) = @_;
134              
135             my ($prefix, $id) = split(/:/, $key);
136              
137             # we need to not serialize the expires date, since it comes in as an
138             # integer and we need to preserve that in order to be able to use
139             # mongodb's '$lt' function in delete_expired_sessions()
140             my $serialized;
141             if ($prefix =~ /^expires$/) {
142             $serialized = $data;
143             } else {
144             $serialized = $self->_serialize($data);
145             }
146              
147             $self->_collection->update({ _id => $id },
148             { '$set' => { $prefix => $serialized } }, { upsert => 1 });
149             }
150              
151             sub delete_session_data {
152             my ($self, $key) = @_;
153              
154             my ($prefix, $id) = split(/:/, $key);
155              
156             my $found = $self->_collection->find_one({ _id => $id });
157             return unless $found;
158              
159             if (exists($found->{$prefix})) {
160             if ((scalar(keys(%$found))) > 2) {
161             $self->_collection->update({ _id => $id },
162             { '$unset' => { $prefix => 1 }} );
163             return;
164             } else {
165             $self->_collection->remove({ _id => $id });
166             }
167             }
168             }
169              
170             sub delete_expired_sessions {
171             my ($self) = @_;
172              
173             $self->_collection->remove({ 'expires' => { '$lt' => time() } });
174             }
175              
176             __PACKAGE__->meta->make_immutable;
177              
178             1;
179              
180             __END__
181              
182             =head1 NAME
183              
184             Catalyst::Plugin::Session::Store::MongoDB - MongoDB session store for Catalyst
185              
186             =head1 SYNOPSIS
187              
188             In your MyApp.pm:
189              
190             use Catalyst qw/
191             Session
192             Session::Store::MongoDB
193             Session::State::Cookie # or similar
194             /;
195              
196             and in your MyApp.conf
197              
198             <Plugin::Session>
199             hostname foo # defaults to localhost
200             port 0815 # defaults to 27017
201             dbname test # defaults to catalyst
202             collectionname s2 # defaults to session
203             </Plugin::Session>
204              
205             Then you can use it as usual:
206              
207             $c->session->{foo} = 'bar'; # will be saved
208              
209             =head1 DESCRIPTION
210              
211             C<Catalyst::Plugin::Session::Store::MongoDB> is a session storage plugin using
212             MongoDB (L<http://www.mongodb.org>) as it's backend.
213              
214             =head1 USAGE
215              
216             =over 4
217              
218             =item B<Expired Sessions>
219              
220             This store automatically deletes sessions when they expire. Additionally it
221             implements the optional delete_expired_sessions() method.
222              
223             =back
224              
225             =head1 AUTHOR
226              
227             Stefan Völkel
228             bd@bc-bd.org
229             http://bc-bd.org
230              
231             =head1 COPYRIGHT
232              
233             Copyright 2010 Stefan Völkel <bd@bc-bd.org>
234              
235             This program is free software; you can redistribute it and/or modify it
236             under the terms of the GNU General Public License v2 as published
237             by the Free Software Foundation; or the Artistic License.
238              
239             See http://dev.perl.org/licenses/ for more information.
240              
241             =cut