File Coverage

lib/Web/ComposableRequest/Session.pm
Criterion Covered Total %
statement 59 59 100.0
branch 11 14 78.5
condition 13 15 86.6
subroutine 12 12 100.0
pod 6 6 100.0
total 101 106 95.2


line stmt bran cond sub pod time code
1             package Web::ComposableRequest::Session;
2              
3 1     1   580 use namespace::autoclean;
  1         1  
  1         6  
4              
5 1     1   55 use Web::ComposableRequest::Constants qw( EXCEPTION_CLASS FALSE NUL TRUE );
  1         1  
  1         7  
6 1     1   322 use Web::ComposableRequest::Util qw( bson64id is_arrayref throw );
  1         2  
  1         4  
7 1         6 use Unexpected::Types qw( ArrayRef Bool HashRef
8             NonEmptySimpleStr NonZeroPositiveInt
9 1     1   300 Object SimpleStr Undef );
  1         1  
10 1     1   755 use Moo;
  1         1  
  1         6  
11              
12             # Public attributes
13             has 'authenticated' => is => 'rw', isa => Bool, default => FALSE;
14              
15             has 'messages' => is => 'ro', isa => HashRef[ArrayRef],
16 3     3   195 builder => sub { {} };
17              
18             has 'updated' => is => 'ro', isa => NonZeroPositiveInt, required => TRUE;
19              
20             has 'username' => is => 'rw', isa => SimpleStr, default => NUL;
21              
22             # Private attributes
23             has '_config' => is => 'ro', isa => Object, init_arg => 'config',
24             required => TRUE;
25              
26             has '_mid' => is => 'rwp', isa => NonEmptySimpleStr | Undef;
27              
28             has '_request' => is => 'ro', isa => Object, init_arg => 'request',
29             required => TRUE, weak_ref => TRUE;
30              
31             has '_session' => is => 'ro', isa => HashRef, init_arg => 'session',
32             required => TRUE;
33              
34             # Private functions
35             my $_session_attr = sub {
36             my $conf = shift; my @public = qw( authenticated messages updated username );
37              
38             return keys %{ $conf->session_attr }, @public;
39             };
40              
41             # Construction
42             around 'BUILDARGS' => sub {
43             my ($orig, $self, @args) = @_; my $attr = $orig->( $self, @args );
44              
45             for my $k ($_session_attr->( $attr->{config} )) {
46             my $v = $attr->{session}->{ $k }; defined $v and $attr->{ $k } = $v;
47             }
48              
49             $attr->{updated} //= time;
50              
51             return $attr;
52             };
53              
54             sub BUILD {
55 6     6 1 739 my $self = shift; my $conf = $self->_config;
  6         11  
56              
57 6         12 my $max_time = $conf->max_sess_time;
58              
59 6 100 66     85 if ($self->authenticated and $max_time
      100        
60             and time > $self->updated + $max_time) {
61 1         18 my $req = $self->_request;
62 1         21 my $msg = $conf->expire_session->( $self, $req );
63              
64 1         18 $self->authenticated( FALSE );
65 1         20 $self->_set__mid( $self->add_status_message( $msg ) );
66             $req->_log->( { level => 'debug',
67 1         25 message => $req->loc_default( @{ $msg } ) } );
  1         10  
68             }
69              
70 6         129 return;
71             }
72              
73             # Public methods
74             sub add_status_message {
75 6     6 1 97 my ($self, $msg) = @_;
76              
77 6 50       14 is_arrayref $msg or throw 'Parameter [_1] not an array reference', [ $msg ];
78              
79 6         21 $self->messages->{ my $mid = bson64id } = $msg;
80              
81 6         33 return $mid;
82             }
83              
84             sub collect_message_id {
85 3     3 1 892 my ($self, $req) = @_;
86              
87 3 100 100     42 return $self->_mid && exists $self->messages->{ $self->_mid }
88             ? $self->_mid : $req->query_params->( 'mid', { optional => TRUE } );
89             }
90              
91             sub collect_status_message {
92 3     3 1 316 my ($self, $req) = @_; my ($mid, $msg);
  3         3  
93              
94             $mid = $self->_mid
95             and $msg = delete $self->messages->{ $mid }
96 3 100 100     19 and return $req->loc( @{ $msg } );
  1         5  
97              
98             $mid = $req->query_params->( 'mid', { optional => TRUE } )
99             and $msg = delete $self->messages->{ $mid }
100 2 100 66     8 and return $req->loc( @{ $msg } );
  1         4  
101              
102 1         6 return;
103             }
104              
105             sub collect_status_messages {
106 1     1 1 18 my ($self, $req) = @_; my @messages = ();
  1         2  
107              
108 1 50       4 my $mid = $req->query_params->( 'mid', { optional => TRUE } )
109             or return \@messages;
110              
111 1         4 my @keys = reverse sort keys %{ $self->messages };
  1         6  
112              
113 1         4 while (my $key = $keys[ 0 ]) {
114 3 50       7 $key gt $mid and next; my $msg = delete $self->messages->{ $key };
  3         8  
115              
116 3         2 push @messages, $req->loc( @{ $msg } ); shift @keys;
  3         9  
  3         104  
117             }
118              
119 1         4 return \@messages;
120             }
121              
122             sub trim_message_queue {
123 4     4 1 4 my $self = shift; my @queue = sort keys %{ $self->messages };
  4         4  
  4         22  
124              
125 4         30 while (@queue > $self->_config->max_messages) {
126 1         1 my $mid = shift @queue; delete $self->messages->{ $mid };
  1         5  
127             }
128              
129 4         4 return;
130             }
131              
132             sub update {
133             my $self = shift;
134              
135             for my $k ($_session_attr->( $self->_config )) {
136             $self->_session->{ $k } = $self->$k();
137             }
138              
139             $self->_session->{updated} = time;
140             return;
141             }
142              
143             before 'update' => sub {
144             my $self = shift; $self->trim_message_queue; return;
145             };
146              
147             1;
148              
149             __END__