/
Application.pm
142 lines (112 loc) · 3.45 KB
/
Application.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
unit class CGI::Application;
# XXX: should be dump-html
has %.run-modes is rw = (start => 'dump');
has $.start-mode is rw = 'start';
has $.mode-param is rw = 'rm';
has $.error-mode is rw;
# TODO: type-restrict it to any <header none redirect>
has $.header-type is rw = 'header';
has %.header-props is rw = {};
has $.current-runmode is rw;
has %.callbacks = (
prerun => [<prerun>],
postrun => [<postrun>],
error => [<error>],
teardown => [<teardown>],
);
# the CGI object or hash
has %.query is rw;
multi method run() {
my $rm = $.__get_runmode($.mode-param);
$.current-runmode = $rm;
# undefine $.__PRERUN_MODE_LOCKED;
$.call-hook('prerun', $rm);
# $.__PRERUN_MODE_LOCKED = 1
# my $prerun-mode = $.prerun-mode;
# if $prerun-mode {
# $rm = $prerun-mode;
# $.current-runmode = $rm;
# }
my $body = $.__get_body($rm);
$.call-hook('postrun', $body);
my $headers = $._send_headers();
my $output = $headers ~ $body;
print $output unless $*CGI_APP_RETURN_ONLY || %*ENV<CGI_APP_RETURN_ONLY>;
$.call-hook('teardown');
return $output;
}
multi method call-hook($hook, *@args, *%opts) {
die "Unknown hook ($hook)" unless %.callbacks{$hook};
my %executed_callback;
for @( %.callbacks{$hook} ) -> $callback {
next if %executed_callback{$callback};
try { self.*"$callback"(|@args, |%opts) };
%executed_callback{$callback} = 1;
die "Error executing callback '$callback' in $hook stage: $!" if $!;
}
# TODO: callbacks in classes. (blocking on: understanding them first)
}
multi method __get_runmode($rm-param) {
# warn "In __get_runmode\n";
my $rm = do given $rm-param {
when Callable { .(self) }
when Associative { .<run-mode> }
default { %.query{$_} }
}
# warn "Run mode (before): $rm.perl()";
$rm = $.start-mode unless defined($rm) && $rm.chars;
# warn "Run mode (after): $rm.perl()";
return $rm;
}
multi method __get_runmeth($rm) {
my $m = %.run-modes{$rm};
# TODO: implement AUTOLOAD/CANDO mode
die "No such run mode '$rm'\n" unless defined $m;
return $m;
}
multi method __get_body($rm) {
my $method = $.__get_runmeth($rm);
my $body;
try {
$body = $method ~~ Callable ?? $method() !! self."$method"();
CATCH {
default {
$.call-hook('error', $!);
if $.error-mode {
$body = self."$.error-mode"();
} else {
die "Error executing run mode '$rm': $!";
}
}
}
}
return $body;
}
method !header-helper-redirect() {
return '' unless $.header-type eq 'redirect';
die "Need a new URL for redirecting" unless %.header-props<url>;
return "Status: 302 Found\r\n"
~ "Location: %.header-props<url>\r\n";
}
multi method _send_headers() {
return '' if $.header-type eq 'none';
# TODO: more general stuff
return self!header-helper-redirect
~ "Content-Type: text/html\r\n\r\n";
}
multi method dump() {
[~] gather {
take "Runmode: '$.current-runmode'\n" if defined $.current-runmode;
take "Query parameters: %.query.perl()\n";
# TODO: dump %*ENV
}
}
# Callbacks, to be overridden if necessary
method prerun(*@args) {
# do nothing for now.
}
method postrun(*@args) {
# do nothing for now.
}
method teardown() {};
# vim: ft=perl6