File Coverage

blib/lib/Dancer2/Session/Memcached.pm
Criterion Covered Total %
statement 38 42 90.4
branch 4 8 50.0
condition 2 6 33.3
subroutine 12 13 92.3
pod 0 1 0.0
total 56 70 80.0


line stmt bran cond sub pod time code
1 2     2   9201 use strict;
  2         3  
  2         94  
2 2     2   10 use warnings;
  2         3  
  2         87  
3              
4             package Dancer2::Session::Memcached;
5             our $AUTHORITY = 'cpan:YANICK';
6             # ABSTRACT: Dancer 2 session storage with Cache::Memcached
7             $Dancer2::Session::Memcached::VERSION = '0.006';
8 2     2   8 use Moo;
  2         4  
  2         11  
9 2     2   1096 use Cache::Memcached;
  2         60697  
  2         59  
10 2     2   11 use Carp qw/ croak /;
  2         3  
  2         114  
11              
12 2     2   12 use Types::Standard qw/ Str ArrayRef InstanceOf /;
  2         3  
  2         27  
13              
14 2     2   2272 use Type::Tiny;
  2         4  
  2         1045  
15              
16             my $Server = Type::Tiny->new(
17             name => 'MemcachedServer',
18             parent => Str,
19             constraint => sub { ! /^\d+\.\d+\.\d+\.\d+$/ },
20             message => sub {
21             "server `$_' is invalid; port is missing, use `server:port'"
22             },
23              
24             );
25              
26             my $Servers = Type::Tiny->new(
27             name => 'MemcachedServers',
28             parent => ArrayRef[$Server],
29             coercion => Type::Coercion->new( type_coercion_map => [
30             Str ,=> sub { [ split ',', $_ ] },
31             ]),
32             );
33              
34              
35             #--------------------------------------------------------------------------#
36             # Public attributes
37             #--------------------------------------------------------------------------#
38              
39              
40             has memcached_servers => (
41             is => 'ro',
42             isa => $Servers,
43             required => 1,
44             coerce => $Servers->coercion,
45             );
46              
47             has fatal_cluster_unreachable => (
48             is => 'ro',
49             required => 0,
50             default => sub { 0 },
51             );
52              
53             #--------------------------------------------------------------------------#
54             # Private attributes
55             #--------------------------------------------------------------------------#
56              
57             has _memcached => (
58             is => 'lazy',
59             isa => InstanceOf ['Cache::Memcached'],
60             handles => {
61             _destroy => 'delete',
62             },
63             );
64              
65             sub _retrieve {
66 7     7   23 my ($self) = shift;
67              
68             croak "Memcache cluster unreachable _retrieve"
69 7 50 33     23 if $self->fatal_cluster_unreachable && not keys %{$self->_memcached->stats(['misc'])};
  0         0  
70              
71 7         91 return $self->_memcached->get( @_ );
72             }
73              
74             sub _flush {
75 7     7   130939 my ($self) = shift;
76              
77             croak "Memcache cluster unreachable _flush"
78 7 50 33     26 if $self->fatal_cluster_unreachable && not keys %{$self->_memcached->stats(['misc'])};
  0         0  
79              
80 7         91 return $self->_memcached->set( @_ );
81             }
82              
83             # Adapted from Dancer::Session::Memcached
84             sub _build__memcached {
85 1     1   11 my ($self) = @_;
86              
87 1         17 my $servers = $self->memcached_servers;
88              
89 1 50       4 croak "The setting memcached_servers must be defined"
90             unless defined $servers;
91              
92 1         5 $servers = [ split /,\s*/, $servers ];
93              
94             # make sure the servers look good
95 1         3 foreach my $s (@$servers) {
96 1 50       5 if ( $s =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
97 0         0 croak "server `$s' is invalid; port is missing, use `server:port'";
98             }
99             }
100              
101 1         9 return Cache::Memcached->new( servers => $servers );
102             }
103              
104             #--------------------------------------------------------------------------#
105             # Role composition
106             #--------------------------------------------------------------------------#
107              
108             with 'Dancer2::Core::Role::SessionFactory';
109              
110             # _retrieve, _flush, _destroy handled by _memcached object
111              
112             # memcached doesn't have any easy way to list keys it knows about
113             # so we cheat and return an empty array ref
114 0     0   0 sub _sessions { [] }
115              
116             sub _change_id {
117 1     1   320 my ( $self, $old_id, $new_id ) = @_;
118 1         3 $self->_flush( $new_id, $self->_retrieve( $old_id ) );
119 1         21 $self->_destroy( $old_id );
120             }
121              
122             # reject anything where the first two bytes are below \x20 once
123             # Base64 decoded, ensuring Storable doesnt attempt to thaw such cruft.
124             sub validate_id {
125 6     6 0 58766 $_[1] =~ m/^[I-Za-z0-9_\-~][A-Za-z0-9_\-~]+$/;
126             }
127              
128             1;
129              
130              
131             # vim: ts=4 sts=4 sw=4 et:
132              
133             __END__