diff --git a/lib/Plack/Session/Store/Cache.pm b/lib/Plack/Session/Store/Cache.pm index cdfd4d4..18cb06f 100644 --- a/lib/Plack/Session/Store/Cache.pm +++ b/lib/Plack/Session/Store/Cache.pm @@ -9,33 +9,46 @@ use Scalar::Util qw[ blessed ]; use parent 'Plack::Session::Store'; -use Plack::Util::Accessor qw[ cache ]; +use Plack::Util::Accessor qw[ cache get_cache expires prefix ]; sub new { my ($class, %params) = @_; - die('cache require get, set and remove method.') - unless blessed $params{cache} - && $params{cache}->can('get') - && $params{cache}->can('set') - && $params{cache}->can('remove'); + my $cache; + if ( $params{get_cache} ) { + $cache = $params{get_cache}->(); + } + else { + $cache = $params{cache}; + $params{get_cache} = sub { $params{cache} }; + } - bless { %params } => $class; + die('cache require get, set and remove method.') + unless blessed $cache + && $cache->can('get') + && $cache->can('set') + && $cache->can('remove'); + + bless { + prefix => '', + %params, + } => $class; } + sub fetch { my ($self, $session_id ) = @_; - $self->cache->get($session_id); + $self->get_cache->()->get($self->prefix . $session_id); } sub store { my ($self, $session_id, $session) = @_; - $self->cache->set($session_id => $session); + $self->get_cache->()->set($self->prefix . $session_id => $session, $self->expires); } sub remove { my ($self, $session_id) = @_; - $self->cache->remove($session_id); + $self->get_cache->()->remove($self->prefix . $session_id); } 1; @@ -61,7 +74,18 @@ Plack::Session::Store::Cache - Cache session store builder { enable 'Session', store => Plack::Session::Store::Cache->new( - cache => CHI->new(driver => 'FastMmap') + cache => CHI->new(driver => 'FastMmap'), + expires => 86400, + prefix => 'session:', + ); + $app; + }; + +# set get_cache callback for ondemand + builder { + enable 'Session', + store => Plack::Session::Store::Cache->new( + get_cache => sub { MyAppSingleton->cache }, ); $app; }; @@ -81,7 +105,7 @@ its full interface. =item B -The constructor expects the I param to be an object instance +The constructor expects the I param or return of I to be an object instance which has the I, I, and I methods, it will throw an exception if that is not the case. @@ -89,6 +113,20 @@ exception if that is not the case. A simple accessor for the cache handle. +=item B + +A callback for the cache handle. + +=item B + +This value uses in set method, Like this + + $cache->set($key, $data, $expires) + +=item B + +The prefix associated with this cache. Defaults to "" if not explicitly set. + =back =head1 BUGS diff --git a/t/005_basic_w_cache_store.t b/t/005_basic_w_cache_store.t index 3cb9b24..6a978c0 100755 --- a/t/005_basic_w_cache_store.t +++ b/t/005_basic_w_cache_store.t @@ -16,7 +16,8 @@ use t::lib::TestSession; package TestCache; sub new { - bless {} => shift; + my $class = shift; + bless +{@_} => $class; } sub set { @@ -37,6 +38,58 @@ use t::lib::TestSession; delete $self->{$key}; } } +{ + package TestCacheMatchExpires; + use base 'TestCache'; + + sub set { + my ($self, $key, $val, $expires ) = @_; + + Test::More::is $self->{expires} => $expires; + $self->{$key} = $val; + } +} +{ + package TestCacheDenyExpires; + use base 'TestCache'; + + sub set { + my ($self, $key, $val, $expires ) = @_; + + Test::More::is $expires => undef; + + $self->{$key} = $val; + } +} +{ + package TestCachePrefix; + use base 'TestCache'; + + + sub set { + my ($self, $key, $val ) = @_; + + Test::More::like $key, qr/^$self->{prefix}/; + + $self->{$key} = $val; + } + + sub get { + my ($self, $key ) = @_; + + Test::More::like $key, qr/^$self->{prefix}/; + + $self->{$key}; + } + + sub remove { + my ($self, $key ) = @_; + + Test::More::like $key, qr/^$self->{prefix}/; + + delete $self->{$key}; + } +} t::lib::TestSession::run_all_tests( store => Plack::Session::Store::Cache->new( cache => TestCache->new ), @@ -55,5 +108,74 @@ t::lib::TestSession::run_all_tests( }, ); +my $cache = TestCache->new; +t::lib::TestSession::run_all_tests( + store => Plack::Session::Store::Cache->new( get_cache => sub { $cache } ), + state => Plack::Session::State->new, + env_cb => sub { + open my $in, '<', \do { my $d }; + my $env = { + 'psgi.version' => [ 1, 0 ], + 'psgi.input' => $in, + 'psgi.errors' => *STDERR, + 'psgi.url_scheme' => 'http', + SERVER_PORT => 80, + REQUEST_METHOD => 'GET', + QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, + }; + }, +); + +t::lib::TestSession::run_all_tests( + store => Plack::Session::Store::Cache->new( cache => TestCacheMatchExpires->new(expires => 111), expires => 111 ), + state => Plack::Session::State->new, + env_cb => sub { + open my $in, '<', \do { my $d }; + my $env = { + 'psgi.version' => [ 1, 0 ], + 'psgi.input' => $in, + 'psgi.errors' => *STDERR, + 'psgi.url_scheme' => 'http', + SERVER_PORT => 80, + REQUEST_METHOD => 'GET', + QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, + }; + }, +); + +t::lib::TestSession::run_all_tests( + store => Plack::Session::Store::Cache->new( cache => TestCacheDenyExpires->new ), + state => Plack::Session::State->new, + env_cb => sub { + open my $in, '<', \do { my $d }; + my $env = { + 'psgi.version' => [ 1, 0 ], + 'psgi.input' => $in, + 'psgi.errors' => *STDERR, + 'psgi.url_scheme' => 'http', + SERVER_PORT => 80, + REQUEST_METHOD => 'GET', + QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, + }; + }, +); + +t::lib::TestSession::run_all_tests( + store => Plack::Session::Store::Cache->new( cache => TestCachePrefix->new(prefix => 'test:'), prefix => 'test:' ), + state => Plack::Session::State->new, + env_cb => sub { + open my $in, '<', \do { my $d }; + my $env = { + 'psgi.version' => [ 1, 0 ], + 'psgi.input' => $in, + 'psgi.errors' => *STDERR, + 'psgi.url_scheme' => 'http', + SERVER_PORT => 80, + REQUEST_METHOD => 'GET', + QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, + }; + }, +); + done_testing; diff --git a/t/005a_basic_w_cache_store.t b/t/005a_basic_w_cache_store.t index 04b9f7c..c19b414 100644 --- a/t/005a_basic_w_cache_store.t +++ b/t/005a_basic_w_cache_store.t @@ -15,7 +15,8 @@ use t::lib::TestSessionHash; package TestCache; sub new { - bless {} => shift; + my $class = shift; + bless +{@_} => $class; } sub set { @@ -36,6 +37,58 @@ use t::lib::TestSessionHash; delete $self->{$key}; } } +{ + package TestCacheMatchExpires; + use base 'TestCache'; + + sub set { + my ($self, $key, $val, $expires ) = @_; + + Test::More::is $self->{expires} => $expires; + $self->{$key} = $val; + } +} +{ + package TestCacheDenyExpires; + use base 'TestCache'; + + sub set { + my ($self, $key, $val, $expires ) = @_; + + Test::More::is $expires => undef; + + $self->{$key} = $val; + } +} +{ + package TestCachePrefix; + use base 'TestCache'; + + + sub set { + my ($self, $key, $val ) = @_; + + Test::More::like $key, qr/^$self->{prefix}/; + + $self->{$key} = $val; + } + + sub get { + my ($self, $key ) = @_; + + Test::More::like $key, qr/^$self->{prefix}/; + + $self->{$key}; + } + + sub remove { + my ($self, $key ) = @_; + + Test::More::like $key, qr/^$self->{prefix}/; + + delete $self->{$key}; + } +} t::lib::TestSessionHash::run_all_tests( store => Plack::Session::Store::Cache->new( cache => TestCache->new ), @@ -54,5 +107,74 @@ t::lib::TestSessionHash::run_all_tests( }, ); +my $cache = TestCache->new; +t::lib::TestSessionHash::run_all_tests( + store => Plack::Session::Store::Cache->new( get_cache => sub { $cache } ), + state => Plack::Session::State->new, + env_cb => sub { + open my $in, '<', \do { my $d }; + my $env = { + 'psgi.version' => [ 1, 0 ], + 'psgi.input' => $in, + 'psgi.errors' => *STDERR, + 'psgi.url_scheme' => 'http', + SERVER_PORT => 80, + REQUEST_METHOD => 'GET', + QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, + }; + }, +); + +t::lib::TestSessionHash::run_all_tests( + store => Plack::Session::Store::Cache->new( cache => TestCacheMatchExpires->new(expires => 111), expires => 111 ), + state => Plack::Session::State->new, + env_cb => sub { + open my $in, '<', \do { my $d }; + my $env = { + 'psgi.version' => [ 1, 0 ], + 'psgi.input' => $in, + 'psgi.errors' => *STDERR, + 'psgi.url_scheme' => 'http', + SERVER_PORT => 80, + REQUEST_METHOD => 'GET', + QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, + }; + }, +); + +t::lib::TestSessionHash::run_all_tests( + store => Plack::Session::Store::Cache->new( cache => TestCacheDenyExpires->new ), + state => Plack::Session::State->new, + env_cb => sub { + open my $in, '<', \do { my $d }; + my $env = { + 'psgi.version' => [ 1, 0 ], + 'psgi.input' => $in, + 'psgi.errors' => *STDERR, + 'psgi.url_scheme' => 'http', + SERVER_PORT => 80, + REQUEST_METHOD => 'GET', + QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, + }; + }, +); + +t::lib::TestSessionHash::run_all_tests( + store => Plack::Session::Store::Cache->new( cache => TestCachePrefix->new(prefix => 'test:'), prefix => 'test:' ), + state => Plack::Session::State->new, + env_cb => sub { + open my $in, '<', \do { my $d }; + my $env = { + 'psgi.version' => [ 1, 0 ], + 'psgi.input' => $in, + 'psgi.errors' => *STDERR, + 'psgi.url_scheme' => 'http', + SERVER_PORT => 80, + REQUEST_METHOD => 'GET', + QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, + }; + }, +); + done_testing;