File Coverage

blib/lib/CGI/Session/Driver/layered.pm
Criterion Covered Total %
statement 68 81 83.9
branch 12 18 66.6
condition 2 8 25.0
subroutine 11 12 91.6
pod 6 6 100.0
total 99 125 79.2


line stmt bran cond sub pod time code
1             package CGI::Session::Driver::layered;
2              
3 6     6   13362 use strict;
  6         11  
  6         218  
4 6     6   31 use warnings;
  6         10  
  6         248  
5 6     6   39 use base qw(CGI::Session::Driver);
  6         12  
  6         5592  
6              
7 6     6   10650 use Time::HiRes qw(time);
  6         17401  
  6         31  
8              
9             our $VERSION = '0.8';
10              
11             =head1 NAME
12              
13             CGI::Session::Driver::layered - Use multiple layered drivers
14              
15             =head1 SYNOPSIS
16              
17             use CGI::Session;
18            
19             my $session = CGI::Session->new("driver:layered", $sessionId, { Layers => [
20             {
21             Driver => 'file',
22             Directory => '/tmp/foo',
23             },
24             {
25             Driver => 'postgresql'
26             table => 'websessions',
27             handle => $dbh
28             }
29             ]});
30              
31             =head1 DESCRIPTION
32              
33             CGI::Session::Driver::Layered provides a interface for using multple drivers
34             to store sessions. Each session is stored in all the configured drivers. When
35             fetching a session, the driver with the most recent copy of the session is used.
36             The drivers are searched in the order they were configured.
37              
38             =head1 OPTIONS
39              
40             Unlike most drivers for CGI::Session, this driver requires options to
41             function. The driver args must has a layers field, which is an array ref of
42             hash references. Each hash reference should contain the driver name under
43             the key C, and the rest of the arguments for that driver. The order
44             of the layers argument is the order that the layer will check during a
45             retrieve.
46              
47             =cut
48              
49             sub init {
50 24     24 1 882903 my $self = shift;
51              
52 24         134 my $ret = $self->SUPER::init(@_);
53            
54 24         148 $self->{drivers} = [];
55            
56 24         49 foreach my $layer (@{$self->{Layers}}) {
  24         83  
57             # make a local copy of the driver, so we can delete it from the args
58             # we pass to Driver->new()
59 47         139 local $layer->{Driver} = $layer->{Driver};
60            
61 47   50     184 my $driver = delete $layer->{Driver} || return $self->set_error("A layer was missing a driver.");
62            
63 47         4934 require "CGI/Session/Driver/$driver.pm";
64            
65 47         12205 my $obj = eval { "CGI::Session::Driver::$driver"->new($layer) };
  47         345  
66 47 100       2161 push(@{$self->{drivers}}, $obj) if $obj;
  46         203  
67             }
68            
69 24 50       250 if (@{$self->{drivers}} == 0) {
  24         96  
70 0         0 return $self->set_error("Could not load any of the layers.")
71             }
72            
73 24         67 return $self;
74             }
75              
76              
77             sub store {
78 14     14 1 4753 my ($self, $sid, $datastr) = @_;
79            
80 14         151 $datastr = time . ':' . $datastr;
81            
82 14         84 my $ret = 1;
83            
84 14         23 foreach my $driver (@{$self->{drivers}}) {
  14         38  
85 27 50       3803 eval { $driver->store($sid, $datastr) } || do { $ret = 0 };
  27         220  
  0         0  
86             }
87            
88 14 50       2960 return $ret if $ret;
89 0         0 return;
90             }
91              
92             sub retrieve {
93 10     10 1 119 my ($self, $sid) = @_;
94            
95             # atime at 0, data at 1
96 10         28 my $latest = [0, ''];
97            
98 10         22 foreach my $driver (@{$self->{drivers}}) {
  10         29  
99 19         24 my $str = eval { $driver->retrieve($sid) };
  19         76  
100 19 100       2144 if ($str) {
101 11         46 my ($atime, $data) = split(m/:/, $str, 2);
102            
103 11 100       85 if ($atime > $latest->[0]) {
104 10         44 $latest = [$atime, $data];
105             }
106             }
107             }
108            
109 10         44 return $latest->[1];
110             }
111              
112             sub remove {
113 0     0 1 0 my ($self, $sid) = @_;
114              
115 0         0 my $ret = 1;
116            
117 0         0 foreach my $driver (@{$self->{drivers}}) {
  0         0  
118 0         0 my $ret = eval {
119 0         0 $driver->remove($sid);
120             };
121 0 0 0     0 if ($@ || !$ret) {
122 0         0 $ret = 0;
123             }
124             }
125            
126 0         0 return $ret;
127             }
128              
129             sub traverse {
130 1     1 1 46 my ($self, $coderef) = @_;
131             # execute $coderef for each session id passing session id as the first and the only
132             # argument
133            
134 1         3 my %seen;
135             # make closure over the coderef and our seen hash, this will make sure that
136             # we visit each session exactly once.
137             my $visitor = sub {
138 20     20   4716 my ($sid) = @_;
139            
140 20 100       97 return if $seen{$sid}++;
141            
142 10         22 $coderef->($sid);
143 1         6 };
144            
145 1         2 my $ok = 1;
146            
147 1         3 foreach my $driver (@{$self->{drivers}}) {
  1         5  
148 2   33     8 $ok &&= eval {
149 2         11 $driver->traverse($visitor);
150 2         503 1;
151             };
152             }
153            
154 1 50       6 if (!$ok) {
155 0         0 return $self->set_error($@);
156             }
157            
158 1         9 return 1;
159             }
160              
161              
162             sub _drivers {
163 2     2   21 return @{shift->{drivers}};
  2         9  
164             }
165              
166              
167             sub errstr {
168 1     1 1 39 my ($self) = @_;
169            
170 1         3 return join("\n", map { "[ $_ ]" } grep { length } map { $_->errstr } @{$self->{drivers}});
  2         11  
  2         9  
  2         18  
  1         2  
171             }
172              
173            
174              
175             =head1 COPYRIGHT
176              
177             Copyright (C) 2009 Liquidweb Inc.
178              
179             =head1 AUTHOR
180              
181             Chris Reinhardt
182              
183             =head1 SEE ALSO
184              
185             L, L
186              
187             =cut
188              
189             1;