File Coverage

blib/lib/Dancer2/Session/Redis.pm
Criterion Covered Total %
statement 54 83 65.0
branch 8 48 16.6
condition 0 18 0.0
subroutine 14 17 82.3
pod n/a
total 76 166 45.7


line stmt bran cond sub pod time code
1             package Dancer2::Session::Redis;
2 2     2   1164153 use strictures 1;
  2         12  
  2         76  
3             # ABSTRACT: Perl Dancer2 session provider for storing session data within key-value-store Redis.
4             #
5             # This file is part of Dancer2-Session-Redis
6             #
7             # This software is Copyright (c) 2016 by BURNERSK .
8             #
9             # This is free software, licensed under:
10             #
11             # The MIT (X11) License
12             #
13              
14             BEGIN {
15 2     2   198 our $VERSION = '0.006'; # VERSION: generated by DZP::OurPkgVersion
16             }
17              
18 2     2   7 use Carp qw( carp croak );
  2         4  
  2         95  
19 2     2   10 use Types::Standard qw( Maybe Undef InstanceOf );
  2         2  
  2         22  
20 2     2   1543 use Moo;
  2         3  
  2         10  
21 2     2   417 use Redis;
  2         2  
  2         31  
22 2     2   5 use Safe::Isa;
  2         3  
  2         206  
23 2     2   7 use Try::Tiny;
  2         2  
  2         76  
24 2     2   6 use Type::Tiny;
  2         2  
  2         1545  
25              
26             with 'Dancer2::Core::Role::SessionFactory';
27              
28              
29             ############################################################################
30              
31             my $TYPE_SERIALIZATIONOBJECT = Type::Tiny->new(
32             name => 'SerializationObject',
33             constraint => sub { $_->$_call_if_object( 'does' => 'Dancer2::Session::Redis::SerializationRole' ) },
34             message => sub { qq{$_ does not consume a SerializationRole} },
35             );
36              
37             has redis_server => ( is => 'ro' );
38             has redis_sock => ( is => 'ro' );
39             has redis_password => ( is => 'ro' );
40             has redis_reconnect => ( is => 'ro' );
41             has redis_on_connect => ( is => 'ro' );
42             has redis_every => ( is => 'ro' );
43             has redis_debug => ( is => 'ro' );
44             has redis_name => ( is => 'ro' );
45             has redis_key => ( is => 'ro', default => 'session:%s' );
46             has redis_serialization => ( is => 'ro' );
47             has redis_test_mock => ( is => 'ro', default => sub { $ENV{DANCER_SESSION_REDIS_TEST_MOCK} || 0 } );
48              
49             has _serialization => (
50             is => 'lazy',
51             isa => Maybe [ $TYPE_SERIALIZATIONOBJECT ],
52             );
53              
54             sub _build__serialization {
55 1     1   518 my ($dsl1) = @_;
56 1         2 my $serialization;
57 1 50       6 return unless $dsl1->redis_serialization;
58              
59             # Setup serialization.
60 1 50       8 if ( my $serialization_module = delete $dsl1->redis_serialization->{module} ) {
61 1 50       4 $serialization_module =~ s/^/Dancer2::Session::Redis::Serialization::/
62             if $serialization_module !~ m/^Dancer2::Session::Redis::Serialization::/;
63 1 50       5 croak qq{Invalid serialization module: $serialization_module}
64             if $serialization_module !~ m/^Dancer2::Session::Redis::Serialization::[a-zA-Z][a-zA-Z0-9_]*$/;
65             try {
66 1 50   1   86 eval "require $serialization_module" or croak $@;
67 1         3 $serialization = "$serialization_module"->new( %{ $dsl1->redis_serialization } );
  1         7  
68             }
69             catch {
70 0     0   0 croak(qq{Unable to set up serialization '$serialization_module': $_});
71 1         8 };
72             }
73 1         1495 return $serialization;
74             }
75              
76             has _redis => (
77             is => 'lazy',
78             isa => InstanceOf ['Redis'] | InstanceOf ['t::TestApp::RedisMock'],
79             );
80              
81             sub _build__redis {
82 1     1   482 my ($dsl2) = @_;
83              
84 1 50       5 if ( $dsl2->redis_test_mock ) {
85 1         373 require t::TestApp::RedisMock;
86 1         1479 return t::TestApp::RedisMock->new;
87             }
88              
89             # Build Redis->new settings.
90 0 0       0 my %opts = (
    0          
    0          
    0          
    0          
    0          
    0          
91             ( $dsl2->redis_server ? ( server => $dsl2->redis_server ) : () ),
92             ( $dsl2->redis_sock ? ( sock => $dsl2->redis_sock ) : () ),
93             ( $dsl2->redis_password ? ( password => $dsl2->redis_password ) : () ),
94             ( $dsl2->redis_reconnect ? ( reconnect => $dsl2->redis_reconnect ) : () ),
95             ( $dsl2->redis_every ? ( every => $dsl2->redis_every ) : () ),
96             ( $dsl2->redis_name ? ( name => $dsl2->redis_name ) : () ),
97             ( $dsl2->redis_debug ? ( debug => $dsl2->redis_debug ) : () ),
98             );
99              
100             # Cleanup settings.
101 0 0       0 delete $opts{server} if $opts{sock}; # prefer UNIX/Linux sockets.
102 0 0       0 delete $opts{sock} if $opts{server};
103 0 0 0     0 delete $opts{password} if exists $opts{password} && ( !defined $opts{password} || $opts{password} eq '' );
      0        
104 0 0       0 delete $opts{name} unless $opts{name};
105              
106             # Validate reconnect settings.
107 0 0 0     0 if ( ( exists $opts{reconnect} || exists $opts{every} ) && ( !$opts{reconnect} || !$opts{every} ) ) {
      0        
      0        
108 0         0 croak(q{Incomplete Redis configuration for 'reconnect' and 'every', skipping...});
109 0         0 delete $opts{reconnect};
110 0         0 delete $opts{every};
111             }
112              
113             # Validate on_connect settings.
114 0 0       0 if ( $dsl2->redis_on_connect ) {
115 0 0       0 if ( !$dsl2->redis_on_connect ) {
116 0         0 croak(q{Invalid Redis configuration for 'on_connect', skipping...});
117             }
118             else {
119 0         0 $opts{on_connect} = $dsl2->redis_on_connect;
120             }
121             }
122              
123             # Validate connection settings.
124             croak(q{Incomplete Redis configuration: required is either 'server' or 'sock'})
125 0 0 0     0 if !$opts{server} && !$opts{sock};
126              
127 0 0       0 croak(q{Incomplete Redis configuration: session_duration is required})
128             unless $dsl2->has_session_duration;
129              
130 0         0 return Redis->new(%opts);
131             }
132              
133             ############################################################################
134              
135             # Get session data.
136             sub _retrieve {
137 1     1   14153 my ( $dsl, $session_id ) = @_;
138 1         6 my $key = sprintf $dsl->redis_key, $session_id;
139 1         35 my $data = $dsl->_redis->get($key);
140 1 50       30 if ( my $serialization = $dsl->_serialization ) {
141 1         12 $data = $serialization->decode($data);
142             }
143 1         48 $dsl->_redis->expire( $key => $dsl->session_duration );
144 1         16 return $data;
145             }
146              
147             # Set session data.
148             sub _flush {
149 2     2   43871 my ( $dsl, $session_id, $data ) = @_;
150 2         15 my $key = sprintf $dsl->redis_key, $session_id;
151 2 50       18 if ( my $serialization = $dsl->_serialization ) {
152 2         107 $data = $serialization->encode($data);
153             }
154 2         64 $dsl->_redis->set( $key => $data );
155 2         980 $dsl->_redis->expire( $key => $dsl->session_duration );
156 2         20 return;
157             }
158              
159             # Delete session data.
160             sub _destroy {
161 0     0     my ( $dsl, $session_id ) = @_;
162 0           my $key = sprintf $dsl->redis_key, $session_id;
163 0           $dsl->_redis->del($key);
164 0           return;
165             }
166              
167             # Get all session ids.
168             sub _sessions {
169 0     0     my ($dsl) = @_;
170 0           my $key = sprintf $dsl->redis_key, '*';
171 0           my $key_pattern = quotemeta sprintf $dsl->redis_key, '';
172 0           my @keys = $dsl->_redis->keys($key);
173 0           return [ map { my $a = $_; $a =~ s/^$key_pattern(.*)$/$1/; $a } @keys ];
  0            
  0            
  0            
174             }
175              
176             ############################################################################
177              
178              
179             1;
180              
181             __END__