| 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; | ||||||