package Dancer::Session::Redis;
# ABSTRACT: Redis backend for Dancer Session Engine
use strict;
use warnings;
use parent 'Dancer::Session::Abstract';
use Redis;
use Dancer::Config 'setting';
use Storable ();
use Carp ();
our $VERSION = '0.20';
my $_redis;
my %options = ();
sub init {
my $self = shift;
$self->SUPER::init(@_);
# backend settings
if (my $opts = setting('redis_session') ) {
if (ref $opts and ref $opts eq 'HASH' ) {
%options = (
'server' => $opts->{'server'} || undef,
'sock' => $opts->{'sock'} || undef,
'database' => $opts->{'database'} || 0,
'expire' => $opts->{'expire'} || 900,
'debug' => $opts->{'debug'} || 0,
'ping' => $opts->{'ping'} || 5,
'password' => $opts->{'password'} || undef,
);
} else {
Carp::croak "Settings 'redis_session' must be a hash reference!";
}
} else {
Carp::croak "Settings 'redis_session' is not defined!";
}
unless (defined $options{'server'} || defined $options{'sock'}) {
Carp::croak "Parameter 'redis_session.server' or 'redis_session.sock' have to be defined";
}
_redis_watchdog();
}
#
# check redis handle and re-establish connection
sub _redis_watchdog {
if ($_redis->{handle}) {
if (time - $_redis->{lastcheck} > $options{'ping'}) {
if ($_redis->{handle}->ping) {
$_redis->{lastcheck} = time;
return $_redis->{handle};
} else {
Carp::carp "Lost redis connection. Reconnecting...";
return _redis_get_handle();
}
}
} else {
return _redis_get_handle();
}
}
#
# connect to redis server and return handle (or croaks)
sub _redis_get_handle {
my %params = (
debug => $options{'debug'},
);
if (defined $options{'sock'}) {
$params{'sock'} = $options{'sock'};
} else {
$params{'server'} = $options{'server'};
}
$params{password} = $options{'password'} if $options{'password'};
$_redis->{handle} = Redis->new(%params);
$_redis->{lastcheck} = time if $_redis->{handle} && $_redis->{handle}->ping;
$_redis->{handle} and return $_redis->{handle};
Carp::croak "Unable connect to redis..." unless $_redis->{handle};
}
#
# create a new session
sub create {
my ($class) = @_;
$class->new->flush;
}
#
# fetch the session object by id
sub retrieve($$) {
my ($class, $id) = @_;
_redis_watchdog();
$_redis->{handle}->select($options{'database'});
$_redis->{handle}->expire($id => $options{'expire'});
Storable::thaw($_redis->{handle}->get($id));
}
#
# delete session
sub destroy {
my ($self) = @_;
_redis_watchdog();
$_redis->{handle}->select($options{'database'});
$_redis->{handle}->del($self->id);
}
#
# flush session
sub flush {
my ($self) = @_;
_redis_watchdog();
$_redis->{handle}->select($options{'database'});
$_redis->{handle}->set($self->id => Storable::freeze($self));
$_redis->{handle}->expire($self->id => $options{'expire'});
$self;
}
1;
__END__
=pod
=head1 NAME
Dancer::Session::Redis - Redis backend for Dancer Session Engine
=head1 VERSION
version 0.20
=head1 SYNOPSIS
# in the Dancer config.yml:
session: 'Redis'
redis_session:
sock: '/var/run/redis.sock'
password: 'QmG_kZECJAvAcDaWqqSqoNLUka5v3unMe_8sqYMh6ST'
database: 1
expire: 3600
debug: 0
ping: 5
# or in the Dancer application:
setting redis_session => {
server => 'redi.example.com:6379',
password => 'QmG_kZECJAvAcDaWqqSqoNLUka5v3unMe_8sqYMh6ST',
database => 1,
expire => 3600,
debug => 0,
ping => 5,
};
setting session => 'Redis';
=head1 DESCRIPTION
This module is a Redis backend for the session engine of Dancer application. This module is a descendant
of L<Dancer::Session::Abstract>. A simple demo apllication might be found in the C<eg/> directory of this
distribution.
=head1 CONFIGURATION
In order to use this session engine, you have to set up a few settings (in the app or app's configuration file).
=over
=item * session
Set the vaue B<Redis>. Required parameter.
=item * redis_session
Settings for backend.
=over
=item I<server>
Hostname and port of the redis-server instance which will be used to store session data. This one is B<required> unless I<sock> is defined.
=item I<sock>
unix socket path of the redis-server instance which will be used to store session data.
=item I<password>
Password string for redis-server's AUTH command to processing any other commands. Optional. Check the redis-server
manual for directive I<requirepass> if you would to use redis internal authentication.
=item I<database>
Database # to store session data. Optional. Default value is 0.
=item I<expire>
Session TTL. Optional. Default value is 900 (seconds).
=item I<ping>
Time (in seconds) to check connection alive and re-establish in case of closed connection. Optional. Default value
is 5 (seconds). Redis server close connection after a client is idle for seconds but server instance might be
configured to not close client's connection. Check the redis server manual.
=item I<debug>
Enables debug information to STDERR, including all interactions with the redis-server. Optional. Default value is 0.
=back
=back
=head1 METHODS
=head2 init()
Validate settings and creates the initial connection to redis-server.
=head2 create()
Creates a new object, runs C<flush> and returns the object.
=head2 flush()
Writes the session information to the Redis database.
=head2 retrieve()
Retrieves session information from the Redis database.
=head2 destroy()
Deletes session information from the Redis database.
=head1 BUGS
Please report any bugs or feature requests through the web interface at
L<https://github.com/Wu-Wu/Dancer-Session-Redis/issues>
=head1 SEE ALSO
L<Dancer>
L<Dancer::Session>
L<Storable>
L<Redis>
L<redis.io|http://redis.io>
=head1 AUTHOR
Anton Gerasimov, E<lt>chim@cpan.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2012 by Anton Gerasimov
This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut