File: | lib/Yukki/Web/Context.pm |
Coverage: | 73.6% |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package Yukki::Web::Context; | ||||||
2 | |||||||
3 | 3 3 | 27 13 | use v5.24; | ||||
4 | 3 3 3 | 10 3 41 | use utf8; | ||||
5 | 3 3 3 | 603 10809 9 | use Moo; | ||||
6 | |||||||
7 | 3 3 3 | 2107 752 101 | use Sub::Name qw( subname ); | ||||
8 | 3 3 3 | 487 27070 10 | use Type::Utils; | ||||
9 | 3 3 3 | 3132 54548 20 | use Types::Standard qw( ArrayRef HashRef Str ); | ||||
10 | 3 3 3 | 2097 106020 17 | use Types::URI qw( Uri ); | ||||
11 | 3 3 3 | 1119 6 52 | use Yukki::Web::Request; | ||||
12 | 3 3 3 | 665 6 50 | use Yukki::Web::Response; | ||||
13 | |||||||
14 | 3 3 3 | 10 4 11 | use namespace::clean; | ||||
15 | |||||||
16 | # ABSTRACT: request-response context descriptor | ||||||
17 | |||||||
18 - 43 | =head1 SYNOPSIS # Many components are handed a Context in $ctx... my $request = $ctx->request; my $session = $ctx->session; my $session_options = $ctx->session_options; my $response = $ctx->response; my $stash = $ctx->stash; $ctx->add_errors('bad stuff'); $ctx->add_warnings('not so good stuff'); $ctx->add_info('some stuff'); =head1 DESCRIPTION This describes information about a single request-repsonse to be handled by the server. =head1 ATTRIBUTES =head2 env This is the L<PSGI> environment. Do not use directly. This will probably be renamed to make it more difficult to use directly in the future. =cut | ||||||
44 | |||||||
45 | has env => ( | ||||||
46 | is => 'ro', | ||||||
47 | isa => HashRef, | ||||||
48 | required => 1, | ||||||
49 | ); | ||||||
50 | |||||||
51 - 55 | =head2 request This is the L<Yukki::Web::Request> object representing the incoming request. =cut | ||||||
56 | |||||||
57 | has request => ( | ||||||
58 | is => 'ro', | ||||||
59 | isa => class_type('Yukki::Web::Request'), | ||||||
60 | required => 1, | ||||||
61 | lazy => 1, | ||||||
62 | default => sub { Yukki::Web::Request->new(env => shift->env) }, | ||||||
63 | handles => [ qw( session session_options ) ], | ||||||
64 | ); | ||||||
65 | |||||||
66 - 71 | =head2 response This is the L<Yukki::Web::Response> object representing the response to send back to the client. =cut | ||||||
72 | |||||||
73 | has response => ( | ||||||
74 | is => 'ro', | ||||||
75 | isa => class_type('Yukki::Web::Response'), | ||||||
76 | required => 1, | ||||||
77 | lazy => 1, | ||||||
78 | default => sub { Yukki::Web::Response->new }, | ||||||
79 | ); | ||||||
80 | |||||||
81 - 87 | =head2 stash This is a temporary stash of information. Use of this should be avoided when possible. Global state like this (even if it only lasts for one request) should only be used as a last resort. =cut | ||||||
88 | |||||||
89 | has stash => ( | ||||||
90 | is => 'ro', | ||||||
91 | isa => HashRef, | ||||||
92 | required => 1, | ||||||
93 | default => sub { +{} }, | ||||||
94 | ); | ||||||
95 | |||||||
96 - 127 | =head2 base_url This is a L<URI> describing the base path to get to this Yukki wiki site. It is configured from the L<Yukki::Web::Settings/base_url> setting. The value of the setting will determine how this value is calculated or may set it explicitly. =over =item * C<SCRIPT_NAME>. When C<base_url> is set to C<SCRIPT_NAME>, then the full path to the script name will be used as the base URL. This is the default and, generally, the safest option. =item * C<REWRITE>. The C<REWRITE> option takes a slightly different approach to building the base URL. It looks at the C<REQUEST_URI> and compares that to the C<PATH_INFO> and finds the common components. For example: PATH_INFO=/page/view/main REQUEST_URI=/yukki-site/page/view/main this leads to a base URL of: /yukki-site If C<PATH_INFO> is not a sub-path of C<REQUEST_URI>, this will fall back to the same solution as C<SCRIPT_NAME> above. =item * Anything else will be considered an absolute URL and used as the base URL. =back This may be used to construct redirects or URLs for links and form actions. =cut | ||||||
128 | |||||||
129 | has base_url => ( | ||||||
130 | is => 'rw', | ||||||
131 | isa => Uri, | ||||||
132 | required => 1, | ||||||
133 | coerce => 1, | ||||||
134 | lazy => 1, | ||||||
135 | builder => '_build_base_url', | ||||||
136 | ); | ||||||
137 | |||||||
138 | sub _build_base_url { | ||||||
139 | 3 | 260 | my $self = shift; | ||||
140 | |||||||
141 | 3 | 15 | my $base_url = $self->env->{'yukki.settings'}->base_url; | ||||
142 | 3 | 7 | if ($base_url eq 'SCRIPT_NAME') { | ||||
143 | 3 | 41 | return $self->request->base; | ||||
144 | } | ||||||
145 | |||||||
146 | elsif ($base_url eq 'REWRITE') { | ||||||
147 | 0 | 0 | my $path_info = $self->env->{PATH_INFO}; | ||||
148 | 0 | 0 | my $request_uri = $self->env->{REQUEST_URI}; | ||||
149 | |||||||
150 | 0 | 0 | if ($request_uri =~ s/$path_info$//) { | ||||
151 | 0 | 0 | my $base_url = $self->request->uri; | ||||
152 | 0 | 0 | $base_url->path($request_uri); | ||||
153 | 0 | 0 | return $base_url->canonical; | ||||
154 | } | ||||||
155 | |||||||
156 | 0 | 0 | return $self->request->base; | ||||
157 | } | ||||||
158 | |||||||
159 | else { | ||||||
160 | 0 | 0 | return $_; | ||||
161 | } | ||||||
162 | } | ||||||
163 | |||||||
164 - 181 | =head2 errors =head2 warnings =head2 info These each contain an array of errors. The C<list_errors>, C<list_warnings>, and C<list_info> methods are provided to return the values as a list. The C<add_errors>, C<add_warnings>, and C<add_info> methods are provided to append new messages. The C<has_errors>, C<has_warnings>, and C<has_info> methods are provided to tell you if there are any messages. =cut | ||||||
182 | |||||||
183 | # TODO Store these in a flash stash | ||||||
184 | for my $message_type (qw( errors warnings info )) { | ||||||
185 | has $message_type => ( | ||||||
186 | is => 'ro', | ||||||
187 | isa => ArrayRef[Str], | ||||||
188 | required => 1, | ||||||
189 | default => sub { [] }, | ||||||
190 | ); | ||||||
191 | |||||||
192 | 3 3 3 | 1405 4 393 | no strict 'refs'; | ||||
193 | |||||||
194 | *{__PACKAGE__ . "::list_$message_type"} = subname "list_$message_type", sub { | ||||||
195 | 0 | 1 1 1 | 0 | my $self = shift; | |||
196 | 0 0 | 0 0 | map { ucfirst "$_." } $self->$message_type->@* | ||||
197 | }; | ||||||
198 | |||||||
199 | *{__PACKAGE__ . "::add_$message_type"} = subname "add_$message_type", sub { | ||||||
200 | 0 | 1 1 1 | 0 | my $self = shift; | |||
201 | 0 | 0 | push $self->$message_type->@*, @_; | ||||
202 | }; | ||||||
203 | |||||||
204 | *{__PACKAGE__ . "::has_$message_type"} = subname "add_$message_type", sub { | ||||||
205 | 7 | 1 1 1 | 2570 | my $self = shift; | |||
206 | 7 | 48 | scalar $self->$message_type->@*; | ||||
207 | }; | ||||||
208 | } | ||||||
209 | |||||||
210 | |||||||
211 - 219 | =head1 METHODS =head2 rebase_url my $url = $ctx->rebase_url($path); Given a relative URL, this returns an absolute URL using the L</base_url>. =cut | ||||||
220 | |||||||
221 | sub rebase_url { | ||||||
222 | 27 | 1 | 44 | my ($self, $url) = @_; | |||
223 | 27 | 71 | return URI->new($url)->abs($self->base_url); | ||||
224 | } | ||||||
225 | |||||||
226 - 250 | =head2 list_errors =head2 list_warnings =head2 list_info These methods return the list of errors, warnings, and info messages associated with the current flow. =head2 add_errors =head2 add_warnings =head2 add_info These methods add zero or more errors, warnings, and info messages to be associated with the current flow. =head2 has_errors =head2 has_warnings =head2 has_info These methods return a true value if there are any errors, warnings, or info messages associated with the current flow. =cut | ||||||
251 | |||||||
252 | 1; |