File Coverage

blib/lib/Dancer2/Core/Role/SessionFactory/File.pm
Criterion Covered Total %
statement 48 59 81.3
branch 15 32 46.8
condition 0 3 0.0
subroutine 11 12 91.6
pod 0 1 0.0
total 74 107 69.1


line stmt bran cond sub pod time code
1             # ABSTRACT: Role for file-based session factories
2             $Dancer2::Core::Role::SessionFactory::File::VERSION = '0.400000';
3             use Moo::Role;
4 4     4   9616 with 'Dancer2::Core::Role::SessionFactory';
  4         10  
  4         35  
5              
6             use Carp 'croak';
7 4     4   1732 use Dancer2::Core::Types;
  4         9  
  4         196  
8 4     4   24 use Dancer2::FileUtils qw(path set_file_mode escape_filename);
  4         8  
  4         33  
9 4     4   31979 use Fcntl ':flock';
  4         11  
  4         266  
10 4     4   26 use File::Copy ();
  4         8  
  4         526  
11 4     4   1903  
  4         8563  
  4         2714  
12             #--------------------------------------------------------------------------#
13             # Required by classes consuming this role
14             #--------------------------------------------------------------------------#
15              
16             requires '_suffix'; # '.yml', '.json', etc.
17             requires '_thaw_from_handle'; # given handle, return session 'data' field
18             requires '_freeze_to_handle'; # given handle and data, serialize it
19              
20              
21             #--------------------------------------------------------------------------#
22             # Attributes and methods
23             #--------------------------------------------------------------------------#
24              
25             has session_dir => (
26             is => 'ro',
27             isa => Str,
28             default => sub { path( '.', 'sessions' ) },
29             );
30              
31             my $self = shift;
32              
33 4     4 0 289 if ( !-d $self->session_dir ) {
34             mkdir $self->session_dir
35 4 100       255 or croak "Unable to create session dir : "
36 1 50       184 . $self->session_dir . ' : '
37             . $!;
38             }
39             }
40              
41             my ($self) = @_;
42             my $sessions = [];
43              
44 0     0   0 opendir( my $dh, $self->session_dir )
45 0         0 or croak "Unable to open directory " . $self->session_dir . " : $!";
46              
47 0 0       0 my $suffix = $self->_suffix;
48              
49             while ( my $file = readdir($dh) ) {
50 0         0 next if $file eq '.' || $file eq '..';
51             if ( $file =~ /(\w+)\Q$suffix\E/ ) {
52 0         0 push @{$sessions}, $1;
53 0 0 0     0 }
54 0 0       0 }
55 0         0 closedir($dh);
  0         0  
56              
57             return $sessions;
58 0         0 }
59              
60 0         0 my ( $self, $id ) = @_;
61             my $session_file = path( $self->session_dir, escape_filename($id) . $self->_suffix );
62              
63             croak "Invalid session ID: $id" unless -f $session_file;
64 17     17   46  
65 17         118 open my $fh, '+<', $session_file or die "Can't open '$session_file': $!\n";
66             flock $fh, LOCK_SH or die "Can't lock file '$session_file': $!\n";
67 17 100       814 my $data = $self->_thaw_from_handle($fh);
68             close $fh or die "Can't close '$session_file': $!\n";
69 16 50       879  
70 16 50       231 return $data;
71 16         126 }
72 16 50       46226  
73             my ($self, $old_id, $new_id) = @_;
74 16         170  
75             my $old_path =
76             path($self->session_dir, escape_filename($old_id) . $self->_suffix);
77              
78 1     1   2 return if !-f $old_path;
79              
80 1         5 my $new_path =
81             path($self->session_dir, escape_filename($new_id) . $self->_suffix);
82              
83 1 50       25 File::Copy::move($old_path, $new_path);
84             }
85 1         8  
86             my ( $self, $id ) = @_;
87             my $session_file = path( $self->session_dir, escape_filename($id) . $self->_suffix );
88 1         7 return if !-f $session_file;
89              
90             unlink $session_file;
91             }
92 5     5   14  
93 5         34 my ( $self, $id, $data ) = @_;
94 5 50       194 my $session_file = path( $self->session_dir, escape_filename($id) . $self->_suffix );
95              
96 5         613 open my $fh, '>', $session_file or die "Can't open '$session_file': $!\n";
97             flock $fh, LOCK_EX or die "Can't lock file '$session_file': $!\n";
98             seek $fh, 0, 0 or die "Can't seek in file '$session_file': $!\n";
99             truncate $fh, 0 or die "Can't truncate file '$session_file': $!\n";
100 21     21   336 set_file_mode($fh);
101 21         150 $self->_freeze_to_handle( $fh, $data );
102             close $fh or die "Can't close '$session_file': $!\n";
103 21 50       3080  
104 21 50       365 return $data;
105 21 50       200 }
106 21 50       525  
107 21         130 1;
108 21         201  
109 21 50       3491  
110             =pod
111 21         214  
112             =encoding UTF-8
113              
114             =head1 NAME
115              
116             Dancer2::Core::Role::SessionFactory::File - Role for file-based session factories
117              
118             =head1 VERSION
119              
120             version 0.400000
121              
122             =head1 DESCRIPTION
123              
124             This is a specialized SessionFactory role for storing session
125             data in files.
126              
127             This role manages the files. Classes consuming it only need to handle
128             serialization and deserialization.
129              
130             Classes consuming this must satisfy three requirements: C<_suffix>,
131             C<_freeze_to_handle> and C<_thaw_from_handle>.
132              
133             package Dancer2::Session::XYX;
134              
135             use Dancer2::Core::Types;
136             use Moo;
137              
138             has _suffix => (
139             is => 'ro',
140             isa => Str,
141             default => sub { '.xyz' },
142             );
143              
144             with 'Dancer2::Core::Role::SessionFactory::File';
145              
146             sub _freeze_to_handle {
147             my ($self, $fh, $data) = @_;
148              
149             # ... do whatever to get data into $fh
150              
151             return;
152             }
153              
154             sub _thaw_from_handle {
155             my ($self, $fh) = @_;
156             my $data;
157              
158             # ... do whatever to get data from $fh
159              
160             return $data;
161             }
162              
163             1;
164              
165             =head1 ATTRIBUTES
166              
167             =head2 session_dir
168              
169             Where to store the session files. Defaults to "./sessions".
170              
171             =head1 AUTHOR
172              
173             Dancer Core Developers
174              
175             =head1 COPYRIGHT AND LICENSE
176              
177             This software is copyright (c) 2022 by Alexis Sukrieh.
178              
179             This is free software; you can redistribute it and/or modify it under
180             the same terms as the Perl 5 programming language system itself.
181              
182             =cut