File Coverage

lib/Web/ComposableRequest/Session.pm
Criterion Covered Total %
statement 64 70 91.4
branch 15 20 75.0
condition 8 9 88.8
subroutine 13 14 92.8
pod 6 7 85.7
total 106 120 88.3


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