<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">package Catalyst::Plugin::Session::Store::DBIC;

use strict;
use warnings;
use base qw/Catalyst::Plugin::Session::Store::Delegate/;
use Catalyst::Exception;
use Catalyst::Plugin::Session::Store::DBIC::Delegate;
use MIME::Base64 ();
use MRO::Compat;
use Storable ();

our $VERSION = '0.12';

=head1 NAME

Catalyst::Plugin::Session::Store::DBIC - Store your sessions via DBIx::Class

=head1 SYNOPSIS

    # Create a table in your database for sessions
    CREATE TABLE sessions (
        id           CHAR(72) PRIMARY KEY,
        session_data TEXT,
        expires      INTEGER
    );

    # Create the corresponding table class
    package MyApp::Schema::Session;

    use base qw/DBIx::Class/;

    __PACKAGE__-&gt;load_components(qw/Core/);
    __PACKAGE__-&gt;table('sessions');
    __PACKAGE__-&gt;add_columns(qw/id session_data expires/);
    __PACKAGE__-&gt;set_primary_key('id');

    1;

    # In your application
    use Catalyst qw/Session Session::Store::DBIC Session::State::Cookie/;

    __PACKAGE__-&gt;config(
        # ... other items ...
        'Plugin::Session' =&gt; {
            dbic_class =&gt; 'DBIC::Session',  # Assuming MyApp::Model::DBIC
            expires    =&gt; 3600,
        },
    );

    # Later, in a controller action
    $c-&gt;session-&gt;{foo} = 'bar';

=head1 DESCRIPTION

This L&lt;Catalyst::Plugin::Session&gt; storage module saves session data in
your database via L&lt;DBIx::Class&gt;.  It's actually just a wrapper around
L&lt;Catalyst::Plugin::Session::Store::Delegate&gt;; if you need complete
control over how your sessions are stored, you probably want to use
that instead.

=head1 METHODS

=head2 setup_finished

Hook into the configured session class.

=cut

sub setup_finished {
    my $c = shift;

    return $c-&gt;next::method unless @_;

    # Try to determine id_field if it isn't set
    unless ($c-&gt;_session_plugin_config-&gt;{id_field}) {
        my $model = $c-&gt;session_store_model;
        my $rs = ref $model ? $model
            : $model-&gt;can('resultset_instance') ? $model-&gt;resultset_instance
            : $model;
        my @primary_columns = $rs-&gt;result_source-&gt;primary_columns;

        Catalyst::Exception-&gt;throw(
            message =&gt; __PACKAGE__ . qq/: Primary key consists of more than one column; please set id_field manually/
        ) if @primary_columns &gt; 1;

        $c-&gt;_session_plugin_config-&gt;{id_field} = $primary_columns[0];
    }

    $c-&gt;next::method(@_);
}

=head2 session_store_dbic_class

Return the L&lt;DBIx::Class&gt; class name to be passed to C&lt;&lt; $c-&gt;model &gt;&gt;.
Defaults to C&lt;DBIC::Session&gt;.

=cut

sub session_store_dbic_class {
    shift-&gt;_session_plugin_config-&gt;{dbic_class} || 'DBIC::Session';
}

=head2 session_store_dbic_id_field

Return the configured ID field name.  Defaults to C&lt;id&gt;.

=cut

sub session_store_dbic_id_field {
    shift-&gt;_session_plugin_config-&gt;{id_field} || 'id';
}

=head2 session_store_dbic_data_field

Return the configured data field name.  Defaults to C&lt;session_data&gt;.

=cut

sub session_store_dbic_data_field {
    shift-&gt;_session_plugin_config-&gt;{data_field} || 'session_data';
}

=head2 session_store_dbic_expires_field

Return the configured expires field name.  Defaults to C&lt;expires&gt;.

=cut

sub session_store_dbic_expires_field {
    shift-&gt;_session_plugin_config-&gt;{expires_field} || 'expires';
}

=head2 session_store_model

Return the model used to find a session.

=cut

sub session_store_model {
    my ($c, $id) = @_;

    my $dbic_class = $c-&gt;session_store_dbic_class;
    $c-&gt;model($dbic_class, $id) or die "Couldn't find a model named $dbic_class";
}

=head2 get_session_store_delegate

Load the row corresponding to the specified session ID.  If none is
found, one is automatically created.

=cut

sub get_session_store_delegate {
    my ($c, $id) = @_;

    Catalyst::Plugin::Session::Store::DBIC::Delegate-&gt;new({
        model      =&gt; $c-&gt;session_store_model($id),
        id_field   =&gt; $c-&gt;session_store_dbic_id_field,
        data_field =&gt; $c-&gt;session_store_dbic_data_field,
    });
}

=head2 session_store_delegate_key_to_accessor

Match the specified key and operation to the session ID and field
name.

=cut

sub session_store_delegate_key_to_accessor {
    my $c = shift;
    my $key = $_[0];
    my ($field, @args) = $c-&gt;next::method(@_);

    my ($type) = ($key =~ /^(\w+):/);

    $field = $c-&gt;session_store_dbic_id_field      if $field eq 'id';
    $field = $c-&gt;session_store_dbic_expires_field if $field eq 'expires';
    $field = $c-&gt;session_store_dbic_data_field    if $field eq 'session' or $field eq 'flash';

    my $accessor = sub { shift-&gt;$type($key)-&gt;$field(@_) };

    if ($field eq $c-&gt;session_store_dbic_data_field) {
        @args = map { MIME::Base64::encode(Storable::nfreeze($_ || '')) } @args;
        $accessor = sub {
            my $value = shift-&gt;$type($key)-&gt;$field(@_);
            return unless $value;
            return Storable::thaw(MIME::Base64::decode($value));
        };
    }

    return ($accessor, @args);
}

=head2 delete_session_data

Delete the specified session from the backend store.

=cut

sub delete_session_data {
    my ($c, $key) = @_;

    # expires is stored on the session row for compatibility with Store::DBI
    return if $key =~ /^expires/;

    $c-&gt;session_store_model-&gt;search({
        $c-&gt;session_store_dbic_id_field =&gt; $key,
    })-&gt;delete;
}

=head2 delete_expired_sessions

Delete all expired sessions.

=cut

sub delete_expired_sessions {
    my $c = shift;

    $c-&gt;session_store_model-&gt;search({
        $c-&gt;session_store_dbic_expires_field =&gt; { '&lt;', time() },
    })-&gt;delete;
}

=head1 CONFIGURATION

The following parameters should be placed in your application
configuration under the C&lt;Plugin::Session&gt; key.

=head2 dbic_class

(Required) The name of the L&lt;DBIx::Class&gt; that represents a session in
the database.  It is recommended that you provide only the part after
C&lt;MyApp::Model&gt;, e.g. C&lt;DBIC::Session&gt;.

If you are using L&lt;Catalyst::Model::DBIC::Schema&gt;, the following
layout is recommended:

=over 4

=item * C&lt;MyApp::Schema&gt; - your L&lt;DBIx::Class::Schema&gt; class

=item * C&lt;MyApp::Schema::Session&gt; - your session table class

=item * C&lt;MyApp::Model::DBIC&gt; - your L&lt;Catalyst::Model::DBIC::Schema&gt; class

=back

This module will then use C&lt;&lt; $c-&gt;model &gt;&gt; to access the appropriate
result source from the composed schema matching the C&lt;dbic_class&gt;
name.

For more information, please see L&lt;Catalyst::Model::DBIC::Schema&gt;.

=head2 expires

Number of seconds for which sessions are active.

Note that no automatic cleanup is done on your session data.  To
delete expired sessions, you can use the L&lt;/delete_expired_sessions&gt;
method with L&lt;Catalyst::Plugin::Scheduler&gt;.

=head2 id_field

The name of the field on your sessions table which stores the session
ID.  Defaults to C&lt;id&gt;.

=head2 data_field

The name of the field on your sessions table which stores session
data.  Defaults to C&lt;session_data&gt; for compatibility with
L&lt;Catalyst::Plugin::Session::Store::DBI&gt;.

=head2 expires_field

The name of the field on your sessions table which stores the
expiration time of the session.  Defaults to C&lt;expires&gt;.

=head1 SCHEMA

Your sessions table should contain the following columns:

    id           CHAR(72) PRIMARY KEY
    session_data TEXT
    expires      INTEGER

The C&lt;id&gt; column should probably be 72 characters.  It needs to handle
the longest string that can be returned by
L&lt;Catalyst::Plugin::Session/generate_session_id&gt;, plus another eight
characters for internal use.  This is less than 72 characters when
SHA-1 or MD5 is used, but SHA-256 will need all 72 characters.

The C&lt;session_data&gt; column should be a long text field.  Session data
is encoded using L&lt;MIME::Base64&gt; before being stored in the database.

Note that MySQL C&lt;TEXT&gt; fields only store 64 kB, so if your session
data will exceed that size you'll want to use C&lt;MEDIUMTEXT&gt;,
C&lt;MEDIUMBLOB&gt;, or larger. If you configure your
L&lt;DBIx::Class::ResultSource&gt; to include the size of the column, you
will receive warnings for this problem:

    This session requires 1180 bytes of storage, but your database
    column 'session_data' can only store 200 bytes. Storing this
    session may not be reliable; increase the size of your data field

See L&lt;DBIx::Class::ResultSource/add_columns&gt; for more information.

The C&lt;expires&gt; column stores the future expiration time of the
session.  This may be null for per-user and flash sessions.

Note that you can change the column names using the L&lt;/id_field&gt;,
L&lt;/data_field&gt;, and L&lt;/expires_field&gt; configuration parameters.
However, the column types must match the above.

=head1 AUTHOR

Daniel Westermann-Clark E&lt;lt&gt;danieltwc@cpan.orgE&lt;gt&gt;

=head1 ACKNOWLEDGMENTS

=over 4

=item * Andy Grundman, for L&lt;Catalyst::Plugin::Session::Store::DBI&gt;

=item * David Kamholz, for most of the testing code (from
        L&lt;Catalyst::Plugin::Authentication::Store::DBIC&gt;)

=item * Yuval Kogman, for assistance in converting to
        L&lt;Catalyst::Plugin::Session::Store::Delegate&gt;

=item * Jay Hannah, for tests and warning when session size 
        exceeds DBIx::Class storage size.

=back

=head1 COPYRIGHT

Copyright (c) 2006 - 2009
the Catalyst::Plugin::Session::Store::DBIC L&lt;/AUTHOR&gt;
as listed above.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut

1;
</pre></body></html>