File Coverage

lib/Web/ComposableRequest/Session.pm
Criterion Covered Total %
statement 58 58 100.0
branch 12 14 85.7
condition 13 15 86.6
subroutine 12 12 100.0
pod 6 6 100.0
total 101 105 96.1


line stmt bran cond sub pod time code
1             package Web::ComposableRequest::Session;
2              
3 1     1   722 use namespace::autoclean;
  1         2  
  1         10  
4              
5 1     1   89 use Web::ComposableRequest::Constants qw( EXCEPTION_CLASS FALSE NUL TRUE );
  1         3  
  1         11  
6 1     1   455 use Web::ComposableRequest::Util qw( bson64id is_arrayref throw );
  1         2  
  1         7  
7 1         7 use Unexpected::Types qw( ArrayRef Bool HashRef
8             NonEmptySimpleStr NonZeroPositiveInt
9 1     1   521 Object SimpleStr Undef );
  1         2  
10 1     1   1327 use Moo;
  1         3  
  1         11  
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   340 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 1153 my $self = shift; my $conf = $self->_config;
  6         21  
56              
57 6         19 my $max_time = $conf->max_sess_time;
58              
59 6 100 66     91 if ($self->authenticated and $max_time
      100        
60             and time > $self->updated + $max_time) {
61 1         23 my $req = $self->_request;
62 1         29 my $msg = $conf->expire_session->( $self, $req );
63              
64 1         23 $self->authenticated( FALSE );
65 1         33 $self->_set__mid( $self->add_status_message( $msg ) );
66             $req->_log->( { level => 'debug',
67 1         42 message => $req->loc_default( @{ $msg } ) } );
  1         11  
68             }
69              
70 6         153 return;
71             }
72              
73             # Public methods
74             sub add_status_message {
75 8     8 1 186 my ($self, $msg) = @_;
76              
77 8 50       27 is_arrayref $msg or throw 'Parameter [_1] not an array reference', [ $msg ];
78              
79 8         32 $self->messages->{ my $mid = bson64id } = $msg;
80              
81 8         45 return $mid;
82             }
83              
84             sub collect_message_id {
85 3     3 1 1831 my ($self, $req) = @_;
86              
87 3 100 100     49 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 1102 my ($self, $req) = @_; my ($mid, $msg);
  3         6  
93              
94             $mid = $self->_mid
95             and $msg = delete $self->messages->{ $mid }
96 3 100 100     24 and return $req->loc( @{ $msg } );
  1         6  
97              
98             $mid = $req->query_params->( 'mid', { optional => TRUE } )
99             and $msg = delete $self->messages->{ $mid }
100 2 100 66     11 and return $req->loc( @{ $msg } );
  1         6  
101              
102 1         10 return;
103             }
104              
105             sub collect_status_messages {
106 2     2 1 48 my ($self, $req) = @_; my @messages = ();
  2         4  
107              
108 2 50       11 my $mid = $req->query_params->( 'mid', { optional => TRUE } )
109             or return \@messages;
110              
111 2         11 my @keys = reverse sort keys %{ $self->messages };
  2         13  
112              
113 2         10 while (my $key = shift @keys) {
114 6 100       202 $key gt $mid and next; my $msg = delete $self->messages->{ $key };
  5         16  
115              
116 5         10 push @messages, $req->loc( @{ $msg } );
  5         18  
117             }
118              
119 2         195 return \@messages;
120             }
121              
122             sub trim_message_queue {
123 4     4 1 7 my $self = shift; my @queue = sort keys %{ $self->messages };
  4         8  
  4         22  
124              
125 4         36 while (@queue > $self->_config->max_messages) {
126 1         3 my $mid = shift @queue; delete $self->messages->{ $mid };
  1         7  
127             }
128              
129 4         9 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__