File Coverage

lib/PAGI/App/Cascade.pm
Criterion Covered Total %
statement 40 44 90.9
branch 6 6 100.0
condition 5 7 71.4
subroutine 6 7 85.7
pod 1 3 33.3
total 58 67 86.5


line stmt bran cond sub pod time code
1             package PAGI::App::Cascade;
2              
3 1     1   441 use strict;
  1         1  
  1         31  
4 1     1   3 use warnings;
  1         3  
  1         36  
5 1     1   3 use Future::AsyncAwait;
  1         1  
  1         6  
6              
7             =head1 NAME
8              
9             PAGI::App::Cascade - Try apps in sequence until success
10              
11             =head1 SYNOPSIS
12              
13             use PAGI::App::Cascade;
14              
15             my $app = PAGI::App::Cascade->new(
16             apps => [$static_app, $dynamic_app],
17             catch => [404, 405],
18             )->to_app;
19              
20             =cut
21              
22             sub new {
23 3     3 0 6202 my ($class, %args) = @_;
24              
25             return bless {
26             apps => $args{apps} // [],
27 3   50     12 catch => { map { $_ => 1 } @{$args{catch} // [404, 405]} },
  6   100     20  
  3         11  
28             }, $class;
29             }
30              
31             sub add {
32 0     0 1 0 my ($self, $app) = @_;
33              
34 0         0 push @{$self->{apps}}, $app;
  0         0  
35 0         0 return $self;
36             }
37              
38             sub to_app {
39 3     3 0 12 my ($self) = @_;
40              
41 3         3 my @apps = @{$self->{apps}};
  3         9  
42 3         3 my %catch = %{$self->{catch}};
  3         7  
43              
44 3     3   38 return async sub {
45 3         5 my ($scope, $receive, $send) = @_;
46 3         9 for my $i (0 .. $#apps) {
47 6         8 my $app = $apps[$i];
48 6         39 my $is_last = ($i == $#apps);
49              
50             # For non-last apps, we need to capture the response
51 6 100       13 if (!$is_last) {
52 4         4 my @captured_events;
53             my $captured_status;
54              
55 8         202 my $capture_send = async sub {
56 8         10 my ($event) = @_;
57 8         9 push @captured_events, $event;
58 8 100       23 if ($event->{type} eq 'http.response.start') {
59 4         24 $captured_status = $event->{status};
60             }
61 4         11 };
62              
63 4         9 await $app->($scope, $receive, $capture_send);
64              
65             # Check if we should try next app
66 4 100 66     205 if ($captured_status && $catch{$captured_status}) {
67 3         21 next; # Try next app
68             }
69              
70             # Send captured events
71 1         4 for my $event (@captured_events) {
72 2         27 await $send->($event);
73             }
74 1         26 return;
75             }
76              
77             # Last app - send directly
78 2         6 await $app->($scope, $receive, $send);
79 2         156 return;
80             }
81 3         15 };
82             }
83              
84             1;
85              
86             __END__