Make psgix.session a hash reference and plack.session an (optional)
Tatsuhiko Miyagawa [Sat, 9 Jan 2010 17:18:48 +0000 (09:18 -0800)]
session object. Moved around the code a lot from Plack::Session to
Middleware::Session to make Plack::Session a plain Perl object.

Tests are failing atm. Going to fix this...

lib/Plack/Middleware/Session.pm
lib/Plack/Middleware/Session/Cookie.pm
lib/Plack/Session.pm
lib/Plack/Session/State.pm
lib/Plack/Session/State/Cookie.pm
lib/Plack/Session/Store.pm
t/013_cookiestore.t

index 6a8fac3..782220f 100644 (file)
@@ -5,7 +5,6 @@ use warnings;
 our $VERSION   = '0.03';
 our $AUTHORITY = 'cpan:STEVAN';
 
-use Plack::Session;
 use Plack::Request;
 use Plack::Response;
 use Plack::Util;
@@ -22,11 +21,11 @@ use Plack::Util::Accessor qw(
 sub prepare_app {
     my $self = shift;
 
-    $self->session_class( 'Plack::Session' ) unless $self->session_class;
-    $self->state( 'Cookie' )                 unless $self->state;
-
+    $self->state( 'Cookie' ) unless $self->state;
     $self->state( $self->inflate_backend('Plack::Session::State', $self->state) );
     $self->store( $self->inflate_backend('Plack::Session::Store', $self->store) );
+
+    Plack::Util::load_class($self->session_class) if $self->session_class;
 }
 
 sub inflate_backend {
@@ -41,29 +40,61 @@ sub inflate_backend {
     Plack::Util::load_class(@class)->new();
 }
 
-sub fetch_or_create_session {
-    my($self, $req) = @_;
-    $self->session_class->fetch_or_create($req, $self);
-}
-
 sub call {
     my $self = shift;
     my $env  = shift;
 
-    my $session = $self->fetch_or_create_session(Plack::Request->new($env));
+    my $request = Plack::Request->new($env);
+
+    my($id, $session);
+    if ($id = $self->state->extract($request) and
+        $session = $self->store->fetch($id)) {
+        $env->{'psgix.session'} = $session;
+    } else {
+        $id = $self->state->generate($request);
+        $env->{'psgix.session'} = {};
+    }
+
+    $env->{'psgix.session.options'} = { id => $id };
 
-    $env->{'psgix.session'} = $env->{'plack.session'} = $session;
+    if ($self->session_class) {
+        $env->{'plack.session'} = $self->session_class->new(
+            manager => $self,
+            _data   => $env->{'psgix.session'},
+            options => $env->{'psgix.session.options'},
+        );
+    }
 
     my $res = $self->app->($env);
     $self->response_cb($res, sub {
         my $res = Plack::Response->new(@{$_[0]});
-        $env->{'psgix.session'}->finalize($res);
+        $self->finalize($env, $res);
         $res = $res->finalize;
         $_[0]->[0] = $res->[0];
         $_[0]->[1] = $res->[1];
     });
 }
 
+sub commit {
+    my($self, $session, $options) = @_;
+    if ($options->{expire}) {
+        $self->store->cleanup($options->{id});
+    } else {
+        $self->store->store($options->{id}, $session);
+    }
+}
+
+sub finalize {
+    my($self, $env, $response) = @_;
+
+    $self->commit($env->{'psgix.session'}, $env->{'psgix.session.options'});
+    if ($env->{'psgix.session.options'}->{expire}) {
+        $self->state->expire_session_id($env->{'psgix.session.options'}->{id}, $response);
+    } else {
+        $self->state->finalize($env->{'psgix.session.options'}->{id}, $response, $env->{'psgix.session.options'});
+    }
+}
+
 1;
 
 __END__
@@ -80,10 +111,11 @@ Plack::Middleware::Session - Middleware for session management
 
   my $app = sub {
       my $env = shift;
+      my $session = $env->{'psgix.session'};
       return [
           200,
           [ 'Content-Type' => 'text/plain' ],
-          [ 'Hello, your Session ID is ' . $env->{'psgix.session'}->id ]
+          [ "Hello, you've been here for ", $session->{counter}++, "th time!" ],
       ];
   };
 
@@ -106,16 +138,15 @@ default it will use cookies to keep session state and store data in
 memory. This distribution also comes with other state and store
 solutions. See perldoc for these backends how to use them.
 
-It should be noted that we store the current session in the
-C<psgix.session> key inside the C<$env> where you can access it
-as needed. Additionally, as of version 0.09, you can call the
-C<session> method of a L<Plack::Request> instance to fetch
-whatever is stored in C<psgix.session>.
+It should be noted that we store the current session as a hash
+reference in the C<psgix.session> key inside the C<$env> where you can
+access it as needed.
+
+B<NOTE:> As of version 0.04 the session is stored in C<psgix.session>
+instead of C<plack.session>.
 
-B<NOTE:> As of version 0.02 the session is stored in C<psgix.session>
-instead of C<plack.session>. We still keep a copy of it in
-C<plack.session>, but this is deprecated and will be removed
-in future versions.
+Also, if you set I<session_class> option (see below), we create a
+session object out of the hash reference in C<plack.session>.
 
 =head2 State
 
@@ -185,9 +216,10 @@ L<Plack::Session::Store::Cache>.
 
 =item I<session_class>
 
-This can be used to override the actual session class. It currently
-defaults to L<Plack::Session> but you can substitute any class which
-implements the same interface.
+This can be used to create an actual session object in
+C<plack.session> environment. Defaults to none, which means the
+session object is not created but you can set C<Plack::Session> to
+create an object for you.
 
 =back
 
index 7fa4bd7..f479823 100644 (file)
@@ -15,7 +15,7 @@ use Plack::Session::State::Cookie;
 sub prepare_app {
     my $self = shift;
 
-    $self->session_class("Plack::Session");
+    Plack::Util::load_class($self->session_class) if $self->session_class;
     $self->session_key("plack_session") unless $self->session_key;
 
     my $state_cookie = Plack::Session::State::Cookie->new;
@@ -34,11 +34,7 @@ sub prepare_app {
             return $cookie;
         },
         expire_session_id => sub { $state_cookie->expire_session_id(@_) },
-        finalize => sub {
-            my($id, $response, $session) = @_;
-            my $cookie = $self->_serialize($session->dump);
-            $state_cookie->finalize($cookie, $response);
-        };
+        finalize => sub { $state_cookie->finalize(@_) };
 
     my $store = Plack::Util::inline_object
         fetch => sub {
@@ -53,6 +49,17 @@ sub prepare_app {
     $self->store($store);
 }
 
+sub finalize {
+    my($self, $env, $response) = @_;
+
+    if ($env->{'psgix.session.options'}->{expire}) {
+        $self->state->expire_session_id($env->{'psgix.session.options'}->{id}, $response);
+    } else {
+        my $cookie = $self->_serialize($env->{'psgix.session'});
+        $self->state->finalize($cookie, $response, $env->{'psgix.session.options'});
+    }
+}
+
 sub _serialize {
     my($self, $session) = @_;
 
index 29c7b67..b437e29 100644 (file)
@@ -5,20 +5,7 @@ use warnings;
 our $VERSION   = '0.03';
 our $AUTHORITY = 'cpan:STEVAN';
 
-use Plack::Util::Accessor qw( id expired _manager );
-
-sub fetch_or_create {
-    my($class, $request, $manager) = @_;
-
-    my($id, $session);
-    if ($id = $manager->state->extract($request) and
-        $session = $manager->store->fetch($id)) {
-        return $class->new( id => $id, _stash => $session, _manager => $manager, _changed => 0 );
-    } else {
-        $id = $manager->state->generate($request);
-        return $class->new( id => $id, _stash => {}, _manager => $manager, _changed=> 1 );
-    }
-}
+use Plack::Util::Accessor qw( manager _data options );
 
 sub new {
     my ($class, %params) = @_;
@@ -29,65 +16,42 @@ sub new {
 
 sub dump {
     my $self = shift;
-    $self->{_stash};
+    $self->_data;
 }
 
 sub get {
     my ($self, $key) = @_;
-    $self->{_stash}{$key};
+    $self->_data->{$key};
 }
 
 sub set {
     my ($self, $key, $value) = @_;
-    $self->{_changed}++;
-    $self->{_stash}{$key} = $value;
+    delete $self->options->{no_commit};
+    $self->_data->{$key} = $value;
 }
 
 sub remove {
     my ($self, $key) = @_;
-    $self->{_changed}++;
-    delete $self->{_stash}{$key};
+    delete $self->options->{no_commit};
+    delete $self->_data->{$key};
 }
 
 sub keys {
     my $self = shift;
-    keys %{$self->{_stash}};
+    keys %{$self->_data};
 }
 
 ## Lifecycle Management
 
 sub expire {
     my $self = shift;
-    $self->{_stash} = {};
-    $self->expired(1);
+    $self->options->{expire} = 1;
 }
 
 sub commit {
     my $self = shift;
-
-    if ($self->expired) {
-        $self->_manager->store->cleanup($self->id);
-    } else {
-        $self->_manager->store->store($self->id, $self);
-    }
-
-    $self->{_changed} = 0;
-}
-
-sub is_changed {
-    my $self = shift;
-    $self->{_changed} > 0;
-}
-
-sub finalize {
-    my ($self, $response) = @_;
-
-    $self->commit if $self->is_changed || $self->expired;
-    if ($self->expired) {
-        $self->_manager->state->expire_session_id($self->id, $response);
-    } else {
-        $self->_manager->state->finalize($self->id, $response, $self);
-    }
+    $self->options->{no_commit} = 1;
+    $self->manager->commit($self->_data, $self->options);
 }
 
 1;
@@ -102,18 +66,13 @@ Plack::Session - Middleware for session management
 
 =head1 SYNOPSIS
 
-  use Plack::Session;
+  # Use with Middleware::Session
+  enable "Session", session_class => "Plack::Session";
 
-  my $store = Plack::Session::Store->new;
-  my $state = Plack::Session::State->new;
+  use Plack::Session;
 
-  my $s = Plack::Session->new(
-      store   => $store,
-      state   => $state,
-      request => Plack::Request->new( $env )
-  );
+  my $session = 
 
-  # ...
 
 =head1 DESCRIPTION
 
index 0556361..b369cd3 100644 (file)
@@ -56,7 +56,7 @@ sub generate {
 
 
 sub finalize {
-    my ($self, $id, $response) = @_;
+    my ($self, $id, $response, $options) = @_;
     ();
 }
 
index 9b51a02..36728bf 100644 (file)
@@ -31,7 +31,7 @@ sub expire_session_id {
 }
 
 sub finalize {
-    my ($self, $id, $response) = @_;
+    my ($self, $id, $response, $options) = @_;
     $response->cookies->{ $self->session_key } = +{
         value => $id,
         path  => ($self->path || '/'),
index 8bb6acd..9753671 100644 (file)
@@ -20,7 +20,7 @@ sub fetch {
 
 sub store {
     my ($self, $session_id, $session) = @_;
-    $self->_stash->{ $session_id } = $session->dump;
+    $self->_stash->{ $session_id } = $session;
 }
 
 sub cleanup {
index 2e7f753..62bb04a 100644 (file)
@@ -13,11 +13,9 @@ my $app = sub {
     my $env = shift;
     my $session = $env->{'psgix.session'};
 
-    my $counter = $session->get('counter') || 0;
-    if ($counter >= 2) {
-        $session->expire;
-    } else {
-        $session->set(counter => $counter + 1);
+    my $counter = $session->{counter} || 0;
+    if ($session->{counter}++ >= 2) {
+        $env->{'psgix.session.options'}->{expire} = 1;
     }
 
     return [ 200, [], [ "counter=$counter" ] ];