package Dancer::Session::YAML;
{
    $Dancer::Session::YAML::VERSION = '1.9999_02';
}

# ABSTRACT: YAML-file-based session backend for Dancer

use Moo;
use Dancer::Core::Types;
use Carp;
use Fcntl ':flock';
use Dancer::FileUtils qw(path set_file_mode);
use YAML::Any;

with 'Dancer::Core::Role::SessionFactory';


has session_dir => (
    is       => 'ro',
    isa      => Str,
    required => 1,
);

sub BUILD {
    my $self = shift;

    if (!-d $self->session_dir) {
        mkdir $self->session_dir
          or croak "Unable to create session dir : "
          . $self->session_dir . ' : '
          . $!;
    }
}

sub _sessions {
    my ($self) = @_;
    my $sessions = [];

    opendir(my $dh, $self->session_dir)
      or croak "Unable to open directory " . $self->session_dir . " : $!";

    while (my $file = readdir($dh)) {
        next if $file eq '.' || $file eq '..';
        if ($file =~ /(\w+)\.yml/) {
            push @{$sessions}, $1;
        }
    }
    closedir($dh);

    return $sessions;
}

sub yaml_file {
    my ($self, $id) = @_;
    return path($self->session_dir, "$id.yml");
}

sub _retrieve {
    my ($self, $id) = @_;
    my $session_file = $self->yaml_file($id);

    return unless -f $session_file;

    open my $fh, '+<', $session_file or die "Can't open '$session_file': $!\n";
    flock $fh, LOCK_EX or die "Can't lock file '$session_file': $!\n";
    my $new_session = YAML::Any::LoadFile($fh);
    close $fh or die "Can't close '$session_file': $!\n";

    return $new_session;
}

sub _destroy {
    my ($self, $id) = @_;
    my $session_file = $self->yaml_file($id);
    return if !-f $session_file;

    unlink $session_file;
}

sub _flush {
    my ($self, $session) = @_;
    my $session_file = $self->yaml_file($session->id);

    open my $fh, '>', $session_file or die "Can't open '$session_file': $!\n";
    flock $fh, LOCK_EX or die "Can't lock file '$session_file': $!\n";
    set_file_mode($fh);
    print {$fh} YAML::Any::Dump($session);
    close $fh or die "Can't close '$session_file': $!\n";

    return $session;
}

1;

__END__

=pod

=head1 NAME

Dancer::Session::YAML - YAML-file-based session backend for Dancer

=head1 VERSION

version 1.9999_02

=head1 DESCRIPTION

This module implements a session engine based on YAML files. Session are stored
in a I<session_dir> as YAML files. The idea behind this module was to provide a
human-readable session storage for the developer.

This backend is intended to be used in development environments, when digging
inside a session can be useful.

This backend an perfectly be used in production environments, but two things
should be kept in mind: The content of the session files is in plain text, and
the session files should be purged by a CRON job.

=head1 ATTRIBUTES

=head2 session_dir

Where to store the session files.

=head1 CONFIGURATION

The setting B<session> should be set to C<YAML> in order to use this session
engine in a Dancer application.

Files will be stored to the value of the setting C<session_dir>, whose default
value is C<appdir/sessions>.

Here is an example configuration that use this session engine and stores session
files in /tmp/dancer-sessions

    session: "YAML"
    session_dir: "/tmp/dancer-sessions"

=head1 DEPENDENCY

This module depends on L<YAML>.

=head1 SEE ALSO

See L<Dancer::Session> for details about session usage in route handlers.

=head1 AUTHOR

Dancer Core Developers

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2012 by Alexis Sukrieh.

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

=cut
