Mercurial > pub > Impl
annotate Lib/IMPL/Web/Application/Response.pm @ 59:0f3e369553bd
Rewritten property implementation (probably become slower but more flexible)
Configuration infrastructure in progress (in the aspect of the lazy activation)
Initial concept for the code generator
| author | wizard |
|---|---|
| date | Tue, 09 Mar 2010 02:50:45 +0300 |
| parents | Lib/IMPL/Web/Response.pm@a35b60b16a99 |
| children | 76b878ad6596 |
| rev | line source |
|---|---|
|
59
0f3e369553bd
Rewritten property implementation (probably become slower but more flexible)
wizard
parents:
58
diff
changeset
|
1 package IMPL::Web::Application::Response; |
| 57 | 2 use strict; |
| 3 | |
| 4 use base qw(IMPL::Object IMPL::Object::Autofill); | |
| 5 | |
| 6 require IMPL::Exception; | |
| 7 require CGI; | |
| 8 require CGI::Cookie; | |
| 9 | |
| 58 | 10 use Carp; |
| 57 | 11 use IMPL::Class::Property; |
| 12 | |
| 13 BEGIN { | |
| 14 public property query => prop_get | owner_set; # cgi query | |
| 15 public property status => prop_all, { validator => \&_checkHeaderPrinted }; | |
| 16 public property contentType => prop_all, { validator => \&_checkHeaderPrinted }; # String | |
| 58 | 17 public property charset => { get => \&_charset, set => \&_charset }, { validator => \&_checkHeaderPrinted }; |
| 57 | 18 public property expires => prop_all, { validator => \&_checkHeaderPrinted }; |
| 19 public property cookies => prop_all, { validator => \&_checkHeaderPrinted }; # Hash | |
| 20 | |
| 58 | 21 public property buffered => prop_all, { validator => \&_canChangeBuffer }; # Boolean |
| 57 | 22 public property streamOut => prop_get | owner_set; # stream |
| 23 public property streamBody => {get => \&getStreamBody }; # stream | |
| 58 | 24 public property isHeaderPrinted => prop_get | owner_set; # Boolean |
| 57 | 25 |
| 26 private property _bufferBody => prop_all; | |
| 27 private property _streamBody => prop_all; | |
| 28 } | |
| 29 | |
| 30 __PACKAGE__->PassThroughArgs; | |
| 31 | |
| 32 sub CTOR { | |
| 33 my ($this,%args) = @_; | |
| 34 | |
| 58 | 35 $this->query(CGI->new($this->query() | {})) unless $this->query; |
| 36 $this->charset($this->query->charset) unless $this->charset; | |
| 57 | 37 |
| 58 | 38 $this->streamOut(*STDOUT) unless $this->streamOut; |
| 57 | 39 } |
| 40 | |
| 41 sub _checkHeaderPrinted { | |
| 42 my ($this,$value) = @_; | |
| 43 | |
| 44 die new IMPL::InvalidOperationException() if $this->isHeaderPrinted; | |
| 45 } | |
| 46 | |
| 58 | 47 sub _canChangeBuffer { |
| 48 my ($this,$value) = @_; | |
| 49 | |
| 50 die new IMPL::InvalidOperationException() if $this->isHeaderPrinted or $this->_streamBody; | |
| 51 } | |
| 52 | |
| 53 sub _charset { | |
| 54 my $this = shift; | |
| 55 | |
| 56 return $this->query->charset(@_); | |
| 57 } | |
| 58 | |
| 57 | 59 sub _PrintHeader { |
| 60 my ($this) = @_; | |
| 61 | |
| 62 unless ($this->isHeaderPrinted) { | |
| 63 $this->isHeaderPrinted(1); | |
| 64 | |
| 65 my %opt; | |
| 66 | |
| 67 $opt{-type} = $this->contentType if $this->contentType; | |
| 68 $opt{-status} = $this->status if $this->status; | |
| 69 $opt{-expires} = $this->expires if $this->expires; | |
| 70 | |
| 71 my $refCookies = $this->cookies; | |
| 72 $opt{-cookie} = [map CGI::Cookie->new(-name => $_, $refCookies->{$_} ), keys %$refCookies] if $refCookies; | |
| 73 | |
| 74 my $hOut = $this->streamOut; | |
| 75 | |
| 76 print $hOut $this->query->header( | |
| 77 %opt | |
| 78 ); | |
| 79 } | |
| 80 } | |
| 81 | |
| 82 sub getStreamBody { | |
| 83 my ($this) = @_; | |
| 84 | |
| 58 | 85 return undef unless $this->streamOut; |
| 57 | 86 |
| 58 | 87 unless ($this->_streamBody) { |
| 88 if ($this->buffered) { | |
| 89 my $buffer = ""; | |
| 90 $this->_bufferBody(\$buffer); | |
| 91 | |
| 92 open my $hBody, ">", \$buffer or die new IMPL::Exception("Failed to create buffer",$!); | |
| 93 | |
| 94 $this->_streamBody($hBody); | |
| 95 } else { | |
| 96 $this->_PrintHeader(); | |
| 97 $this->_streamBody($this->streamOut); | |
| 98 } | |
| 57 | 99 } |
| 58 | 100 |
| 101 return $this->_streamBody; | |
| 57 | 102 } |
| 103 | |
| 104 sub Complete { | |
| 105 my ($this) = @_; | |
| 106 | |
| 107 return 0 unless $this->streamOut; | |
| 108 | |
| 109 my $hOut = $this->streamOut; | |
| 110 | |
| 111 $this->_PrintHeader(); | |
| 112 | |
| 113 if ($this->buffered) { | |
| 114 print $hOut ${$this->_bufferBody}; | |
| 115 } | |
| 116 | |
| 117 $this->_streamBody(undef); | |
| 118 $this->_bufferBody(undef); | |
| 119 $this->streamOut(undef); | |
| 120 | |
| 121 return 1; | |
| 122 } | |
| 123 | |
| 124 sub Discard { | |
| 125 my ($this) = @_; | |
| 126 | |
| 58 | 127 carp "Discarding sent response" if $this->isHeaderPrinted; |
| 128 | |
| 57 | 129 $this->_streamBody(undef); |
| 130 $this->_bufferBody(undef); | |
| 131 $this->streamOut(undef); | |
| 132 } | |
| 133 | |
| 134 1; | |
| 135 | |
| 136 __END__ | |
| 137 | |
| 138 =pod | |
| 139 | |
| 58 | 140 =head1 DESCRIPTION |
| 57 | 141 |
| 58 | 142 Ответ сервера на CGI запрос, позволяет сформировать основные свойства заголовка и тело запроса. |
| 143 | |
| 144 Объект позволяет буфферизировать вывод в тело ответа, что позволяет отменить или изменить | |
| 145 ответ в последний момент. | |
| 146 | |
| 147 Свойство C< isHeaderPrinted > можно использовать для определения были ли отправлены какие-нибудь | |
| 148 данные клиенту. | |
| 149 | |
| 150 =head1 PROPERTIES | |
| 151 | |
| 152 =head2 HTTP Header | |
| 153 | |
| 154 Свойства отвечающие за заголовок HTTP ответа. Эти своства могут быть изменены до тех пор пока | |
| 155 не будет отправлен заголовок. В противном случае выдается исключение C< IMPL::InvalidOperationException >. | |
| 156 | |
| 157 =over | |
| 158 | |
| 159 =item C< query > | |
| 160 | |
| 161 CGI запрос, который используется для вывода данных, заголовка и пр. Существует всегда. | |
| 162 | |
| 163 =item C< status > | |
| 164 | |
| 165 Код ошибки HTTP. Например, '200 OK'. По умолчанию не установлен, при отправке клиенту бедт отправлен '200 ОК'. | |
| 166 | |
| 167 =item C< contentType > | |
| 168 | |
| 169 Тип MIME. По умолчанию не установлен, подразумивается 'text/html'. | |
| 170 | |
| 171 =item C< charset > | |
| 172 | |
| 173 Кодировка, синоним свойства query->charset. | |
| 174 | |
| 175 =item C< expires > | |
| 176 | |
| 177 Определяет время жизни контента, например '+10m'. По умолчанию не задано и не передается. | |
| 178 | |
| 179 =item C< cookies > | |
| 180 | |
| 181 Хеш массив с cookies, например C< { cart => ['foo','bar'], display => 'list' } >. | |
| 182 | |
| 183 =back | |
| 184 | |
| 185 =head2 Response behaviour | |
| 186 | |
| 187 Свойства отвечающие за поведение ответа. | |
| 188 | |
| 189 =over | |
| 190 | |
| 191 =item C< buffered > | |
| 192 | |
| 193 C< True > - то тело ответа пишется в буффер и будет отправлено при вызове метода C< Complete >, | |
| 194 заголовок также будет отправлен после вызова метода C< Complete >. | |
| 195 | |
| 196 C< False > - тело ответа пишется непосредственно в поток к клиенту, при этом заголовок | |
| 197 будет отправлен при первом обращении к свойству C< streamBody > | |
| 198 | |
| 199 Это свойство можно менять до первого обращения к потоку для записи в тело ответа. | |
| 200 | |
| 201 =item C< streamOut > | |
| 202 | |
| 203 Стандартный вывод CGI приложения. | |
| 204 | |
| 205 =item C< streamBody > | |
| 206 | |
| 207 Поток для записи в тело ответа. | |
| 208 | |
| 209 =item C< isHeadPrinted > | |
| 210 | |
| 211 Признак того, что заголовок уже был отправлен клиенту. | |
| 212 | |
| 213 =back | |
| 214 | |
| 215 =head1 METHODS | |
| 216 | |
| 217 =over | |
| 218 | |
| 219 =item C< Complete > | |
| 220 | |
| 221 Завершает отправку ответа. | |
| 222 | |
| 223 =item C< Discard > | |
| 224 | |
| 225 Отменяет отправку ответа, при этом если часть данных (например, заголовок) | |
| 226 уже была отправлена, выдает предупреждение в STDERR. | |
| 227 | |
| 228 =back | |
| 57 | 229 |
| 230 =cut |
