Checking in changes prior to tagging of version 0.11. Changelog diff is:
[catagits/Web-Session.git] / lib / Plack / Middleware / Session.pm
index aeacddc..e5ed2b3 100644 (file)
@@ -2,9 +2,9 @@ package Plack::Middleware::Session;
 use strict;
 use warnings;
 
-use Plack::Session;
-use Plack::Request;
-use Plack::Response;
+our $VERSION   = '0.11';
+our $AUTHORITY = 'cpan:STEVAN';
+
 use Plack::Util;
 use Scalar::Util;
 
@@ -13,15 +13,12 @@ use parent 'Plack::Middleware';
 use Plack::Util::Accessor qw(
     state
     store
-    session_class
 );
 
 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) );
 }
@@ -42,18 +39,69 @@ sub call {
     my $self = shift;
     my $env  = shift;
 
-    $env->{'plack.session'} = $self->session_class->new(
-        state   => $self->state,
-        store   => $self->store,
-        request => Plack::Request->new( $env )
-    );
+    my($id, $session) = $self->get_session($env);
+    if ($id && $session) {
+        $env->{'psgix.session'} = $session;
+    } else {
+        $id = $self->generate_id($env);
+        $env->{'psgix.session'} = {};
+    }
+
+    $env->{'psgix.session.options'} = { id => $id };
 
     my $res = $self->app->($env);
-    $self->response_cb($res, sub {
-        my $res = Plack::Response->new(@{$_[0]});
-        $env->{'plack.session'}->finalize( $res );
-        @{$_[0]} = @{$res->finalize};
-    });
+    $self->response_cb($res, sub { $self->finalize($env, $_[0]) });
+}
+
+sub get_session {
+    my($self, $env) = @_;
+
+    my $id = $self->state->extract($env)   or return;
+    my $session = $self->store->fetch($id) or return;
+
+    return ($id, $session);
+}
+
+sub generate_id {
+    my($self, $env) = @_;
+    $self->state->generate($env);
+}
+
+sub commit {
+    my($self, $env) = @_;
+
+    my $session = $env->{'psgix.session'};
+    my $options = $env->{'psgix.session.options'};
+
+    if ($options->{expire}) {
+        $self->store->remove($options->{id});
+    } else {
+        $self->store->store($options->{id}, $session);
+    }
+}
+
+sub finalize {
+    my($self, $env, $res) = @_;
+
+    my $session = $env->{'psgix.session'};
+    my $options = $env->{'psgix.session.options'};
+
+    $self->commit($env) unless $options->{no_store};
+    if ($options->{expire}) {
+        $self->expire_session($options->{id}, $res, $env);
+    } else {
+        $self->save_state($options->{id}, $res, $env);
+    }
+}
+
+sub expire_session {
+    my($self, $id, $res, $env) = @_;
+    $self->state->expire_session_id($id, $res, $env->{'psgix.session.options'});
+}
+
+sub save_state {
+    my($self, $id, $res, $env) = @_;
+    $self->state->finalize($id, $res, $env->{'psgix.session.options'});
 }
 
 1;
@@ -71,7 +119,13 @@ Plack::Middleware::Session - Middleware for session management
   use Plack::Builder;
 
   my $app = sub {
-      return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello Foo' ] ];
+      my $env = shift;
+      my $session = $env->{'psgix.session'};
+      return [
+          200,
+          [ 'Content-Type' => 'text/plain' ],
+          [ "Hello, you've been here for ", $session->{counter}++, "th time!" ],
+      ];
   };
 
   builder {
@@ -93,6 +147,13 @@ 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 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>.
+
 =head2 State
 
 =over 4
@@ -136,6 +197,31 @@ you can use this noop module.
 
 =back
 
+=head1 OPTIONS
+
+The following are options that can be passed to this mdoule.
+
+=over 4
+
+=item I<state>
+
+This is expected to be an instance of L<Plack::Session::State> or an
+object that implements the same interface. If no option is provided
+the default L<Plack::Session::State::Cookie> will be used.
+
+=item I<store>
+
+This is expected to be an instance of L<Plack::Session::Store> or an
+object that implements the same interface. If no option is provided
+the default L<Plack::Session::Store> will be used.
+
+It should be noted that this default is an in-memory volatile store
+is only suitable for development (or single process servers). For a
+more robust solution see L<Plack::Session::Store::File> or
+L<Plack::Session::Store::Cache>.
+
+=back
+
 =head1 BUGS
 
 All complex software has bugs lurking in it, and this module is no
@@ -150,7 +236,7 @@ Stevan Little E<lt>stevan.little@iinteractive.comE<gt>
 
 =head1 COPYRIGHT AND LICENSE
 
-Copyright 2009 Infinity Interactive, Inc.
+Copyright 2009, 2010 Infinity Interactive, Inc.
 
 L<http://www.iinteractive.com>