Skip to content

Commit 92c3ccd

Browse files
committed
Adjust TH to the newest PAGI::Tools (take 2)
1 parent 9605eb2 commit 92c3ccd

11 files changed

Lines changed: 88 additions & 32 deletions

File tree

cpanfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ on 'test' => sub {
33
};
44

55
requires 'perl' => '5.040';
6-
requires 'PAGI' => '0.001023';
6+
requires 'PAGI::Tools' => '0.002000';
77
requires 'Gears' => '0.104';
88
requires 'Mooish::Base' => '1.005';
99
requires 'Future::AsyncAwait' => 0;

ex/full-app/lib/FullApp/Controller/API.pm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ sub flatten_locations ($self, $level)
2727
return @out;
2828
}
2929

30-
async sub list_locations ($self, $ctx)
30+
sub list_locations ($self, $ctx)
3131
{
3232
my @locations = map {
3333
+{
@@ -39,6 +39,6 @@ async sub list_locations ($self, $ctx)
3939
}
4040
} $self->flatten_locations($self->router);
4141

42-
await $ctx->res->json(\@locations);
42+
$ctx->res->json(\@locations);
4343
}
4444

lib/Thunderhorse.pm

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ sub build_handler ($controller, $destination)
110110
}
111111
}
112112

113+
# this needs to be here, since we want to use $send from this context
114+
await $ctx->try_send_res;
115+
113116
# if this is a bridge and bridge did not render, it means we are
114117
# free to go deeper. Avoid first match, as it was handled already
115118
# above
@@ -390,27 +393,27 @@ it is equally easy to do something else. Take the following destination example:
390393
391394
async sub send_custom ($self, $ctx)
392395
{
393-
await $ctx->res->text('Plaintext response');
396+
$ctx->res->text('Plaintext response');
394397
return 'this will not get rendered';
395398
}
396399
397400
This takes response (L<Thunderhorse::Response>) from context, and sends
398401
plaintext manually. This action I<consumes> the context, marking it as
399-
finished. In this case, return value of the destination is ignored. Note that
400-
the await call on C<< ->text >> method is mandatory.
402+
finished. In this case, return value of the destination is ignored.
403+
401404
402405
Another example:
403406
404407
sub send_custom2 ($self, $ctx)
405408
{
406-
$ctx->res->status(400)->content_type('text/plain');
407-
return 'this is rendered as plaintext and status 400';
409+
$ctx->res->content_type('text/plain');
410+
return 'this is rendered as plaintext';
408411
}
409412
410413
This time, the return value of the destination is not ignored, since only
411414
setting response metadata does not cause the context to be consumed. Status and
412415
I<Content-Type> header will not be overridden, so the response will be sent as
413-
plaintext. In this case, there is no need to await anything.
416+
plaintext.
414417
415418
While not very common, a destination can be unimplemented when C<to> is
416419
skipped. Unimplemented locations will be "stepped over" during request

lib/Thunderhorse/App.pm

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ async sub pagi ($self, $scope, $receive, $send)
208208
# the context
209209
if (!$ctx->is_consumed) {
210210
await $self->render_error(undef, $ctx, 404);
211+
await $ctx->send_res;
211212
}
212213

213214
return;
@@ -240,15 +241,12 @@ sub run ($self)
240241
async sub render_error ($self, $controller, $ctx, $code, $message = undef)
241242
{
242243
$message = defined $message && !$self->is_production ? $message : status_message($code);
243-
await $ctx->res->status($code)->text($message);
244+
$ctx->empty_res->status($code)->text($message);
244245
}
245246

246247
async sub render_response ($self, $controller, $ctx, $result)
247248
{
248-
await $ctx->res
249-
->status_try(200)
250-
->content_type_try('text/html')
251-
->send($result);
249+
$ctx->res->status_try(200)->html($result);
252250
}
253251

254252
#########################

lib/Thunderhorse/Context.pm

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use Mooish::Base -standard;
55

66
use Devel::StrictMode;
77

8+
use Future::AsyncAwait;
89
use Thunderhorse::Request;
910
use Thunderhorse::Response;
1011
use Thunderhorse::WebSocket;
@@ -30,9 +31,15 @@ has field 'req' => (
3031
default => sub ($self) { Thunderhorse::Request->new(context => $self) },
3132
);
3233

34+
has field 'connection' => (
35+
(STRICT ? (isa => HasMethods ['response_started']) : ()),
36+
lazy => sub ($self) { $self->scope->{'pagi.connection'} },
37+
);
38+
3339
has field 'res' => (
3440
(STRICT ? (isa => InstanceOf ['Thunderhorse::Response']) : ()),
35-
default => sub ($self) { Thunderhorse::Response->new(context => $self) },
41+
lazy => sub ($self) { Thunderhorse::Response->new(context => $self) },
42+
clearer => -hidden,
3643
);
3744

3845
# NOTE: websocket must be lazy, because it will die if scope is not websocket
@@ -105,11 +112,41 @@ sub consume ($self)
105112
sub is_consumed ($self)
106113
{
107114
return $self->_consumed
108-
|| $self->res->is_sent
115+
|| $self->connection->response_started
116+
|| $self->res->is_ready
109117
|| ($self->has_ws && $self->ws->is_closed)
110118
|| ($self->has_sse && $self->sse->is_closed);
111119
}
112120

121+
sub empty_res ($self)
122+
{
123+
$self->_clear_res;
124+
return $self->res;
125+
}
126+
127+
async sub send_res ($self)
128+
{
129+
# NOTE: we check a general pagi constraint, but raise a thunderhorse exception -
130+
# should it just die()?
131+
Gears::X::Thunderhorse->raise('response was already sent')
132+
if $self->connection->response_started;
133+
134+
await $self->res->respond($self->sender);
135+
136+
return;
137+
}
138+
139+
async sub try_send_res ($self)
140+
{
141+
return
142+
if $self->connection->response_started;
143+
144+
await $self->res->respond($self->sender)
145+
if $self->res->is_ready;
146+
147+
return;
148+
}
149+
113150
__END__
114151
115152
=head1 NAME
@@ -124,7 +161,7 @@ Thunderhorse::Context - Request handling context
124161
my $stashed_value = $ctx->stash->get('key');
125162
my $session_value = $ctx->session->get('key');
126163
127-
await $ctx->res->text("Hello World");
164+
$ctx->res->text("Hello World");
128165
}
129166
130167
=head1 DESCRIPTION

lib/Thunderhorse/Controller.pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ Thunderhorse::Controller - Base controller class for Thunderhorse
117117
118118
async sub show ($self, $ctx, $id)
119119
{
120-
await $ctx->res->text("User ID: $id");
120+
$ctx->res->text("User ID: $id");
121121
}
122122
123123
=head1 DESCRIPTION

lib/Thunderhorse/Cookbook.pod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ L<Thunderhorse::App/render_error>:
2121
$html = $controller->template('error/error', {title => status_message($code)});
2222
}
2323

24-
await $ctx->res->status($code)->html($html);
24+
$ctx->res->status($code)->html($html);
2525
}
2626

2727
For example for error 404, code above will try to render C<error/404.tt>

lib/Thunderhorse/Response.pm

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,30 @@ sub FOREIGNBUILDARGS ($class, %args)
1313
Gears::X::Thunderhorse->raise('no context for response')
1414
unless $args{context};
1515

16-
return $args{context}->pagi->@[0, 2];
16+
return ($args{context}->pagi->[0]);
1717
}
1818

1919
sub update ($self, $scope, $receive, $send)
2020
{
2121
$self->{scope} = $scope;
22-
$self->{send} = $send;
22+
}
23+
24+
sub _requires_body ($self, $status)
25+
{
26+
# HTTP protocol hardcodes - these statuses can have empty bodies
27+
return $status < 200
28+
|| $status == 204
29+
|| ($status >= 300 && $status < 400);
30+
}
31+
32+
sub is_ready ($self)
33+
{
34+
return true if $self->has_body_source;
35+
36+
return $self->_requires_body($self->status)
37+
if $self->has_status;
38+
39+
return false;
2340
}
2441

2542
__END__
@@ -32,9 +49,9 @@ Thunderhorse::Response - Response wrapper for Thunderhorse
3249
3350
async sub show ($self, $ctx, $id)
3451
{
35-
await $ctx->res->text("Hello World");
36-
await $ctx->res->json({data => 'value'});
37-
await $ctx->res->redirect('/login');
52+
$ctx->res->text("Hello World");
53+
$ctx->res->json({data => 'value'});
54+
$ctx->res->redirect('/login');
3855
}
3956
4057
=head1 DESCRIPTION

t/facade.t

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ package FacadeApp::Controller::Test::Facade {
1616
async sub send_something_later ($self)
1717
{
1818
await $self->app->loop->delay_future(after => 0.5);
19-
await $self->res->text('Something');
19+
$self->res->text('Something');
20+
await $self->send_res;
2021
}
2122
}
2223

t/hooks-methods.t

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,20 @@ package HooksApp {
5454
{
5555
$self->set_render_error_called($self->render_error_called + 1);
5656
$message //= "app error: $code";
57-
await $ctx->res->status($code)->text($message);
57+
$ctx->res->status($code)->text($message);
5858
}
5959

6060
async sub render_response ($self, $controller, $ctx, $result)
6161
{
6262
$self->set_render_response_called($self->render_response_called + 1);
63-
await $ctx->res->text($result);
63+
$ctx->res->text($result);
6464
}
6565

6666
async sub on_error ($self, $controller, $ctx, $error)
6767
{
6868
$self->set_on_error_called($self->on_error_called + 1);
6969
die $error unless $error isa 'Gears::X::HTTP';
70-
await +($controller // $self->controller)->render_error($ctx, $error->code, "app caught: " . $error->code);
70+
await + ($controller // $self->controller)->render_error($ctx, $error->code, "app caught: " . $error->code);
7171
}
7272

7373
async sub on_startup ($self, $state)
@@ -140,21 +140,21 @@ package HooksApp::Controller::CustomHooks {
140140
async sub render_response ($self, $ctx, $result)
141141
{
142142
$self->set_render_response_called($self->render_response_called + 1);
143-
await $ctx->res->text($result);
143+
$ctx->res->text($result);
144144
}
145145

146146
async sub render_error ($self, $ctx, $code, $message = undef)
147147
{
148148
$self->set_render_error_called($self->render_error_called + 1);
149149
$message //= "custom error: $code";
150-
await $ctx->res->status($code)->text($message);
150+
$ctx->res->status($code)->text($message);
151151
}
152152

153153
async sub on_error ($self, $ctx, $error)
154154
{
155155
$self->set_on_error_called($self->on_error_called + 1);
156156
die $error unless $error isa 'Gears::X::HTTP';
157-
await $self->render_error($ctx, $error->code, "custom caught: " . $error->code);
157+
$self->render_error($ctx, $error->code, "custom caught: " . $error->code);
158158
}
159159
}
160160

0 commit comments

Comments
 (0)