File Coverage

lib/Plack/Session/Store/RedisFast.pm
Criterion Covered Total %
statement 34 83 40.9
branch 4 30 13.3
condition 1 11 9.0
subroutine 10 15 66.6
pod 5 5 100.0
total 54 144 37.5


line stmt bran cond sub pod time code
1             package Plack::Session::Store::RedisFast;
2              
3 2     2   109680 use strict;
  2         7  
  2         62  
4 2     2   14 use warnings;
  2         7  
  2         77  
5              
6 2     2   51 use 5.008_005;
  2         8  
7              
8 2     2   14 use Carp qw( carp );
  2         14  
  2         136  
9 2     2   392 use Plack::Util::Accessor qw( prefix redis encoder expire );
  2         206  
  2         13  
10 2     2   1128 use Time::Seconds qw( ONE_MONTH );
  2         4208  
  2         122  
11              
12 2     2   389 use parent 'Plack::Session::Store';
  2         309  
  2         15  
13              
14 2     2   1612 use constant SESSIONS_PER_SCAN => 100;
  2         6  
  2         1475  
15              
16             our $VERSION = '0.03';
17             our $AUTHORITY = 'cpan:AKZHAN';
18              
19             sub new {
20 2     2 1 33 my ( $class, %param ) = @_;
21 2 50       16 $param{prefix} = __PACKAGE__ . ':' unless defined $param{prefix};
22 2 50       11 $param{expire} = ONE_MONTH unless exists $param{expire};
23              
24 2 50       10 unless ( $param{redis} ) {
25 2   50     17 my $builder = ( delete $param{builder} ) || \&_build_redis;
26 2         9 $param{redis} = $builder->();
27             }
28              
29             $param{encoder} ||=
30 0   0     0 _build_encoder( ( delete $param{inflate} ), ( delete $param{deflate} ) );
31              
32             $param{encoder} = $param{encoder}->new()
33 0 0       0 unless ref( $param{encoder} );
34              
35 0         0 bless {%param} => $class;
36             }
37              
38             sub _build_redis {
39 2     2   4 my $instance;
40             eval {
41 2         871 require Redis::Fast;
42 2         17628 $instance = Redis::Fast->new;
43 0         0 1;
44 2 50       5 } or do {
45 2         1321 require Redis;
46 0           $instance = Redis->new;
47             };
48 0           $instance;
49             }
50              
51             sub _build_encoder {
52 0     0     my ( $inflate, $deflate ) = @_;
53 0 0 0       if ( $inflate && $deflate ) {
54 0           require Plack::Session::Store::RedisFast::Encoder::Custom;
55 0           return Plack::Session::Store::RedisFast::Encoder::Custom->new( $inflate,
56             $deflate );
57             }
58 0           my $instance;
59             eval {
60 0           require Plack::Session::Store::RedisFast::Encoder::JSONXS;
61 0           $instance = Plack::Session::Store::RedisFast::Encoder::JSONXS->new;
62 0           1;
63             } or do {
64 0           require Plack::Session::Store::RedisFast::Encoder::MojoJSON;
65 0           $instance = Plack::Session::Store::RedisFast::Encoder::MojoJSON->new;
66             }
67 0 0 0       or do {
68 0           require Plack::Session::Store::RedisFast::Encoder::JSON;
69 0           $instance = Plack::Session::Store::RedisFast::Encoder::JSON->new;
70             };
71 0           $instance;
72             }
73              
74             sub fetch {
75 0     0 1   my ( $self, $session_id ) = @_;
76 0           my $data = $self->redis->get( $self->prefix . $session_id );
77 0 0         return undef unless defined $data;
78 0           $self->encoder->decode($data);
79             }
80              
81             sub store {
82 0     0 1   my ( $self, $session_id, $session ) = @_;
83 0 0         unless ( defined $session ) {
84 0           carp "store: no session provided";
85 0           return;
86             }
87 0           my $data = $self->encoder->encode($session);
88 0 0         $self->redis->set(
89             $self->prefix . $session_id => $data,
90             ( defined( $self->expire ) ? ( EX => $self->expire ) : () ),
91             );
92 0           1;
93             }
94              
95             sub remove {
96 0     0 1   my ( $self, $session_id ) = @_;
97 0           $self->redis->del( $self->prefix . $session_id );
98 0           1;
99             }
100              
101             sub each_session {
102 0     0 1   my ( $self, $cb ) = @_;
103 0 0         return if ref($cb) ne 'CODE';
104              
105 0           my $prefix = $self->prefix;
106              
107 0           my $cursor = 0;
108 0           for ( ; ; ) {
109 0           ( $cursor, my $keys ) = $self->redis->scan(
110             $cursor,
111             MATCH => $self->prefix . '*',
112             COUNT => SESSIONS_PER_SCAN
113             );
114 0 0         if ( scalar(@$keys) > 0 ) {
115 0           my @sessions = $self->redis->mget(@$keys);
116              
117 0           for ( my $i = 0 ; $i < scalar(@sessions) ; $i++ ) {
118 0 0         next unless $sessions[$i];
119              
120 0 0         next if $keys->[$i] !~ m/^\Q$prefix\E(.+)$/;
121 0           my $session_id = $1;
122              
123 0           $cb->(
124             $self->redis, $prefix, $session_id,
125             $self->encoder->decode( $sessions[$i] ),
126             );
127             }
128             }
129              
130 0 0         last if $cursor == 0;
131             }
132 0           1;
133             }
134              
135             1;
136              
137             __END__