fixed bug with last_error
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Response.pm
CommitLineData
fc7ec1d9 1package Catalyst::Response;
2
059c085b 3use Moose;
6680c772 4use HTTP::Headers;
faa02805 5use Moose::Util::TypeConstraints;
6use namespace::autoclean;
b194746d 7use Scalar::Util 'blessed';
e8361cf8 8use Catalyst::Response::Writer;
6adc45cf 9use Catalyst::Utils ();
fc7ec1d9 10
531f1ab6 11with 'MooseX::Emulate::Class::Accessor::Fast';
12
6adc45cf 13our $DEFAULT_ENCODE_CONTENT_TYPE_MATCH = qr{text|xml$|javascript$};
14
15has encodable_content_type => (
16 is => 'rw',
17 required => 1,
18 default => sub { $DEFAULT_ENCODE_CONTENT_TYPE_MATCH }
19);
20
faa02805 21has _response_cb => (
22 is => 'ro',
46fff667 23 isa => 'CodeRef',
faa02805 24 writer => '_set_response_cb',
25 clearer => '_clear_response_cb',
26 predicate => '_has_response_cb',
27);
28
29subtype 'Catalyst::Engine::Types::Writer',
30 as duck_type([qw(write close)]);
31
32has _writer => (
33 is => 'ro',
46fff667 34 isa => 'Catalyst::Engine::Types::Writer', #Pointless since we control how this is built
35 #writer => '_set_writer', Now that its lazy I think this is safe to remove
faa02805 36 clearer => '_clear_writer',
37 predicate => '_has_writer',
46fff667 38 lazy => 1,
39 builder => '_build_writer',
faa02805 40);
41
46fff667 42sub _build_writer {
43 my $self = shift;
44
45 ## These two lines are probably crap now...
46 $self->_context->finalize_headers unless
47 $self->finalized_headers;
48
49 my @headers;
50 $self->headers->scan(sub { push @headers, @_ });
51
52 my $writer = $self->_response_cb->([ $self->status, \@headers ]);
53 $self->_clear_response_cb;
54
55 return $writer;
56}
57
e37f92f5 58has write_fh => (
59 is=>'ro',
a3c9ab76 60 predicate=>'_has_write_fh',
eb1f4b49 61 lazy=>1,
1f2a8069 62 builder=>'_build_write_fh',
63);
64
e8361cf8 65sub _build_write_fh {
66 my $writer = $_[0]->_writer; # We need to get the finalize headers side effect...
6adc45cf 67 my $requires_encoding = $_[0]->encodable_response;
e8361cf8 68 my %fields = (
69 _writer => $writer,
688e2420 70 _context => $_[0]->_context,
e8361cf8 71 _requires_encoding => $requires_encoding,
72 );
73
74 return bless \%fields, 'Catalyst::Response::Writer';
75}
e37f92f5 76
77sub DEMOLISH {
78 my $self = shift;
a3c9ab76 79 return if $self->_has_write_fh;
e37f92f5 80 if($self->_has_writer) {
81 $self->_writer->close
82 }
83}
faa02805 84
6680c772 85has cookies => (is => 'rw', default => sub { {} });
ffb43803 86has body => (is => 'rw', default => undef);
87sub has_body { defined($_[0]->body) }
99a543ee 88
059c085b 89has location => (is => 'rw');
6680c772 90has status => (is => 'rw', default => 200);
91has finalized_headers => (is => 'rw', default => 0);
059c085b 92has headers => (
93 is => 'rw',
9c331634 94 isa => 'HTTP::Headers',
6adc45cf 95 handles => [qw(content_encoding content_length content_type content_type_charset header)],
6680c772 96 default => sub { HTTP::Headers->new() },
97 required => 1,
98 lazy => 1,
059c085b 99);
258733f1 100has _context => (
101 is => 'rw',
102 weak_ref => 1,
103 clearer => '_clear_context',
104);
fc7ec1d9 105
9ae060f0 106before [qw(status headers content_encoding content_length content_type header)] => sub {
107 my $self = shift;
108
109 $self->_context->log->warn(
6adc45cf 110 "Useless setting a header value after finalize_headers and the response callback has been called." .
9ae060f0 111 " Not what you want." )
ca6d4ff6 112 if ( $self->_context && $self->finalized_headers && !$self->_has_response_cb && @_ );
9ae060f0 113};
114
059c085b 115sub output { shift->body(@_) }
116
aa9e8261 117sub code { shift->status(@_) }
118
9c4288ea 119sub write {
120 my ( $self, $buffer ) = @_;
121
122 # Finalize headers if someone manually writes output
89ba65d5 123 $self->_context->finalize_headers unless $self->finalized_headers;
9c4288ea 124
125 $buffer = q[] unless defined $buffer;
5c397774 126
6adc45cf 127 if($self->encodable_response) {
128 $buffer = $self->_context->encoding->encode( $buffer, $self->_context->_encode_check )
129 }
9c4288ea 130
131 my $len = length($buffer);
132 $self->_writer->write($buffer);
133
134 return $len;
135}
136
9c056c82 137sub unencoded_write {
138 my ( $self, $buffer ) = @_;
139
140 # Finalize headers if someone manually writes output
141 $self->_context->finalize_headers unless $self->finalized_headers;
142
143 $buffer = q[] unless defined $buffer;
144
145 my $len = length($buffer);
146 $self->_writer->write($buffer);
147
148 return $len;
149}
150
9c4288ea 151sub finalize_headers {
152 my ($self) = @_;
9c4288ea 153 return;
154}
155
e67f0874 156sub from_psgi_response {
157 my ($self, $psgi_res) = @_;
b194746d 158 if(blessed($psgi_res) && $psgi_res->can('as_psgi')) {
159 $psgi_res = $psgi_res->as_psgi;
160 }
e67f0874 161 if(ref $psgi_res eq 'ARRAY') {
162 my ($status, $headers, $body) = @$psgi_res;
163 $self->status($status);
4491e0cc 164 $self->headers(HTTP::Headers->new(@$headers));
67fd25bc 165 # Can be arrayref or filehandle...
166 if(defined $body) { # probably paranoia
167 ref $body eq 'ARRAY' ? $self->body(join('', @$body)) : $self->body($body);
168 }
e67f0874 169 } elsif(ref $psgi_res eq 'CODE') {
170 $psgi_res->(sub {
4491e0cc 171 my $response = shift;
172 my ($status, $headers, $maybe_body) = @$response;
e67f0874 173 $self->status($status);
4491e0cc 174 $self->headers(HTTP::Headers->new(@$headers));
8a3dcb98 175 if(defined $maybe_body) {
67fd25bc 176 # Can be arrayref or filehandle...
177 ref $maybe_body eq 'ARRAY' ? $self->body(join('', @$maybe_body)) : $self->body($maybe_body);
e67f0874 178 } else {
179 return $self->write_fh;
180 }
4491e0cc 181 });
182 } else {
e67f0874 183 die "You can't set a Catalyst response from that, expect a valid PSGI response";
184 }
d2000928 185
186 # Encoding compatibilty. If the response set a charset, well... we need
187 # to assume its properly encoded and NOT encode for this response. Otherwise
188 # We risk double encoding.
189 if($self->content_type_charset) {
51b34249 190 # We have to do this since for backcompat reasons having a charset doesn't always
191 # mean that the body is already encoded :(
d2000928 192 $self->_context->clear_encoding;
193 }
e67f0874 194}
195
fc7ec1d9 196=head1 NAME
197
910410b8 198Catalyst::Response - stores output responding to the current client request
fc7ec1d9 199
200=head1 SYNOPSIS
201
fbcc39ad 202 $res = $c->response;
203 $res->body;
aa9e8261 204 $res->code;
fbcc39ad 205 $res->content_encoding;
206 $res->content_length;
207 $res->content_type;
208 $res->cookies;
fbcc39ad 209 $res->header;
210 $res->headers;
211 $res->output;
212 $res->redirect;
213 $res->status;
214 $res->write;
b22c6668 215
fc7ec1d9 216=head1 DESCRIPTION
217
910410b8 218This is the Catalyst Response class, which provides methods for responding to
46372e65 219the current client request. The appropriate L<Catalyst::Engine> for your environment
220will turn the Catalyst::Response into a HTTP Response and return it to the client.
b22c6668 221
222=head1 METHODS
fc7ec1d9 223
08a2c908 224=head2 $res->body( $text | $fh | $iohandle_object )
e060fe05 225
226 $c->response->body('Catalyst rocks!');
06e1b616 227
46372e65 228Sets or returns the output (text or binary data). If you are returning a large body,
77b5811a 229you might want to use a L<IO::Handle> type of object (Something that implements the getline method
230in the same fashion), or a filehandle GLOB. These will be passed down to the PSGI
231handler you are using and might be optimized using server specific abilities (for
232example L<Twiggy> will attempt to server a real local file in a non blocking manner).
06e1b616 233
6adc45cf 234If you are using a filehandle as the body response you are responsible for
566678d0 235making sure it conforms to the L<PSGI> specification with regards to content
6adc45cf 236encoding. Unlike with scalar body values or when using the streaming interfaces
237we currently do not attempt to normalize and encode your filehandle. In general
238this means you should be sure to be sending bytes not UTF8 decoded multibyte
239characters.
240
241Most of the time when you do:
242
243 open(my $fh, '<:raw', $path);
244
245You should be fine. If you open a filehandle with a L<PerlIO> layer you probably
246are not fine. You can usually fix this by explicitly using binmode to set
247the IOLayer to :raw. Its possible future versions of L<Catalyst> will try to
248'do the right thing'.
249
490b7a80 250When using a L<IO::Handle> type of object and no content length has been
251already set in the response headers Catalyst will make a reasonable attempt
252to determine the size of the Handle. Depending on the implementation of your
253handle object, setting the content length may fail. If it is at all possible
254for you to determine the content length of your handle object,
4a178c0d 255it is recommended that you set the content length in the response headers
490b7a80 256yourself, which will be respected and sent by Catalyst in the response.
257
efeeb257 258Please note that the object needs to implement C<getline>, not just
77b5811a 259C<read>. Older versions of L<Catalyst> expected your filehandle like objects
260to do read. If you have code written for this expectation and you cannot
261change the code to meet the L<PSGI> specification, you can try the following
262middleware L<Plack::Middleware::AdaptFilehandleRead> which will attempt to
263wrap your object in an interface that so conforms.
efeeb257 264
265Starting from version 5.90060, when using an L<IO::Handle> object, you
266may want to use L<Plack::Middleware::XSendfile>, to delegate the
267actual serving to the frontend server. To do so, you need to pass to
268C<body> an IO object with a C<path> method. This can be achieved in
269two ways.
270
271Either using L<Plack::Util>:
272
273 my $fh = IO::File->new($file, 'r');
274 Plack::Util::set_io_path($fh, $file);
275
276Or using L<IO::File::WithPath>
277
278 my $fh = IO::File::WithPath->new($file, 'r');
279
280And then passing the filehandle to body and setting headers, if needed.
281
282 $c->response->body($fh);
283 $c->response->headers->content_type('text/plain');
284 $c->response->headers->content_length(-s $file);
285 $c->response->headers->last_modified((stat($file))[9]);
286
287L<Plack::Middleware::XSendfile> can be loaded in the application so:
288
289 __PACKAGE__->config(
290 psgi_middleware => [
291 'XSendfile',
292 # other middlewares here...
293 ],
294 );
295
296B<Beware> that loading the middleware without configuring the
297webserver to set the request header C<X-Sendfile-Type> to a supported
298type (C<X-Accel-Redirect> for nginx, C<X-Sendfile> for Apache and
299Lighttpd), could lead to the disclosure of private paths to malicious
300clients setting that header.
301
302Nginx needs the additional X-Accel-Mapping header to be set in the
303webserver configuration, so the middleware will replace the absolute
304path of the IO object with the internal nginx path. This is also
305useful to prevent a buggy app to server random files from the
306filesystem, as it's an internal redirect.
307
308An nginx configuration for FastCGI could look so:
309
310 server {
311 server_name example.com;
312 root /my/app/root;
313 location /private/repo/ {
314 internal;
315 alias /my/app/repo/;
316 }
317 location /private/staging/ {
318 internal;
319 alias /my/app/staging/;
320 }
321 location @proxy {
322 include /etc/nginx/fastcgi_params;
323 fastcgi_param SCRIPT_NAME '';
324 fastcgi_param PATH_INFO $fastcgi_script_name;
325 fastcgi_param HTTP_X_SENDFILE_TYPE X-Accel-Redirect;
326 fastcgi_param HTTP_X_ACCEL_MAPPING /my/app=/private;
327 fastcgi_pass unix:/my/app/run/app.sock;
328 }
329 }
330
331In the example above, passing filehandles with a local path matching
332/my/app/staging or /my/app/repo will be served by nginx. Passing paths
333with other locations will lead to an internal server error.
334
335Setting the body to a filehandle without the C<path> method bypasses
336the middleware completely.
337
338For Apache and Lighttpd, the mapping doesn't apply and setting the
339X-Sendfile-Type is enough.
340
02570318 341=head2 $res->has_body
342
343Predicate which returns true when a body has been set.
344
aa9e8261 345=head2 $res->code
346
347Alias for $res->status.
348
b5ecfcf0 349=head2 $res->content_encoding
b5176d9e 350
910410b8 351Shortcut for $res->headers->content_encoding.
b5176d9e 352
b5ecfcf0 353=head2 $res->content_length
b5176d9e 354
910410b8 355Shortcut for $res->headers->content_length.
b5176d9e 356
b5ecfcf0 357=head2 $res->content_type
b5176d9e 358
910410b8 359Shortcut for $res->headers->content_type.
b5176d9e 360
87e9f9ab 361This value is typically set by your view or plugin. For example,
362L<Catalyst::Plugin::Static::Simple> will guess the mime type based on the file
363it found, while L<Catalyst::View::TT> defaults to C<text/html>.
364
6adc45cf 365=head2 $res->content_type_charset
366
367Shortcut for $res->headers->content_type_charset;
368
b5ecfcf0 369=head2 $res->cookies
fc7ec1d9 370
910410b8 371Returns a reference to a hash containing cookies to be set. The keys of the
372hash are the cookies' names, and their corresponding values are hash
7e743798 373references used to construct a L<CGI::Simple::Cookie> object.
fc7ec1d9 374
375 $c->response->cookies->{foo} = { value => '123' };
376
7e743798 377The keys of the hash reference on the right correspond to the L<CGI::Simple::Cookie>
910410b8 378parameters of the same name, except they are used without a leading dash.
379Possible parameters are:
ac965e92 380
b0ad47c1 381=over
ac965e92 382
71453caf 383=item value
ac965e92 384
71453caf 385=item expires
ac965e92 386
71453caf 387=item domain
ac965e92 388
71453caf 389=item path
390
391=item secure
392
b21bc468 393=item httponly
394
71453caf 395=back
ac965e92 396
b5ecfcf0 397=head2 $res->header
fbcc39ad 398
910410b8 399Shortcut for $res->headers->header.
fbcc39ad 400
b5ecfcf0 401=head2 $res->headers
fc7ec1d9 402
910410b8 403Returns an L<HTTP::Headers> object, which can be used to set headers.
fc7ec1d9 404
405 $c->response->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
406
b5ecfcf0 407=head2 $res->output
fc7ec1d9 408
910410b8 409Alias for $res->body.
fc7ec1d9 410
b5ecfcf0 411=head2 $res->redirect( $url, $status )
fc7ec1d9 412
2f381252 413Causes the response to redirect to the specified URL. The default status is
414C<302>.
fc7ec1d9 415
73a52566 416 $c->response->redirect( 'http://slashdot.org' );
417 $c->response->redirect( 'http://slashdot.org', 307 );
418
2f381252 419This is a convenience method that sets the Location header to the
420redirect destination, and then sets the response status. You will
ee24f3a8 421want to C< return > or C<< $c->detach() >> to interrupt the normal
2f381252 422processing flow if you want the redirect to occur straight away.
423
824a5eb0 424B<Note:> do not give a relative URL as $url, i.e: one that is not fully
425qualified (= C<http://...>, etc.) or that starts with a slash
426(= C</path/here>). While it may work, it is not guaranteed to do the right
427thing and is not a standard behaviour. You may opt to use uri_for() or
428uri_for_action() instead.
429
00038a21 430B<Note:> If $url is an object that does ->as_string (such as L<URI>, which is
431what you get from ->uri_for) we automatically call that to stringify. This
432should ease the common case usage
433
434 return $c->res->redirect( $c->uri_for(...));
435
73a52566 436=cut
437
438sub redirect {
439 my $self = shift;
fbcc39ad 440
441 if (@_) {
73a52566 442 my $location = shift;
f1bbebac 443 my $status = shift || 302;
73a52566 444
00038a21 445 if(blessed($location) && $location->can('as_string')) {
446 $location = $location->as_string;
447 }
448
73a52566 449 $self->location($location);
450 $self->status($status);
451 }
452
453 return $self->location;
454}
fc7ec1d9 455
059c085b 456=head2 $res->location
457
458Sets or returns the HTTP 'Location'.
459
b5ecfcf0 460=head2 $res->status
fc7ec1d9 461
910410b8 462Sets or returns the HTTP status.
fc7ec1d9 463
464 $c->response->status(404);
aa9e8261 465
466$res->code is an alias for this, to match HTTP::Response->code.
b0ad47c1 467
b5ecfcf0 468=head2 $res->write( $data )
fbcc39ad 469
dd096a3a 470Writes $data to the output stream. Calling this method will finalize your
471headers and send the headers and status code response to the client (so changing
472them afterwards is a waste... be sure to set your headers correctly first).
473
474You may call this as often as you want throughout your response cycle. You may
475even set a 'body' afterward. So for example you might write your HTTP headers
476and the HEAD section of your document and then set the body from a template
477driven from a database. In some cases this can seem to the client as if you had
478a faster overall response (but note that unless your server support chunked
479body your content is likely to get queued anyway (L<Starman> and most other
480http 1.1 webservers support this).
481
482If there is an encoding set, we encode each line of the response (the default
483encoding is UTF-8).
fbcc39ad 484
e5ac67e5 485=head2 $res->unencoded_write( $data )
486
487Works just like ->write but we don't apply any content encoding to C<$data>. Use
488this if you are already encoding the $data or the data is arriving from an encoded
489storage.
490
e37f92f5 491=head2 $res->write_fh
492
e8361cf8 493Returns an instance of L<Catalyst::Response::Writer>, which is a lightweight
494decorator over the PSGI C<$writer> object (see L<PSGI.pod\Delayed-Response-and-Streaming-Body>).
495
496In addition to proxying the C<write> and C<close> method from the underlying PSGI
497writer, this proxy object knows any application wide encoding, and provides a method
498C<write_encoded> that will properly encode your written lines based upon your
499encoding settings. By default in L<Catalyst> responses are UTF-8 encoded and this
500is the encoding used if you respond via C<write_encoded>. If you want to handle
501encoding yourself, you can use the C<write> method directly.
502
503Encoding only applies to content types for which it matters. Currently the following
504content types are assumed to need encoding: text (including HTML), xml and javascript.
505
506We provide access to this object so that you can properly close over it for use in
507asynchronous and nonblocking applications. For example (assuming you are using a supporting
508server, like L<Twiggy>:
e37f92f5 509
510 package AsyncExample::Controller::Root;
511
512 use Moose;
513
514 BEGIN { extends 'Catalyst::Controller' }
515
516 sub prepare_cb {
517 my $write_fh = pop;
518 return sub {
519 my $message = shift;
520 $write_fh->write("Finishing: $message\n");
521 $write_fh->close;
522 };
523 }
524
525 sub anyevent :Local :Args(0) {
526 my ($self, $c) = @_;
527 my $cb = $self->prepare_cb($c->res->write_fh);
528
529 my $watcher;
530 $watcher = AnyEvent->timer(
531 after => 5,
532 cb => sub {
533 $cb->(scalar localtime);
534 undef $watcher; # cancel circular-ref
535 });
536 }
537
dd096a3a 538Like the 'write' method, calling this will finalize headers. Unlike 'write' when you
539can this it is assumed you are taking control of the response so the body is never
540finalized (there isn't one anyway) and you need to call the close method.
541
e4cc83b2 542=head2 $res->print( @data )
543
544Prints @data to the output stream, separated by $,. This lets you pass
545the response object to functions that want to write to an L<IO::Handle>.
546
8738b8fe 547=head2 $self->finalize_headers($c)
548
549Writes headers to response if not already written
550
e67f0874 551=head2 from_psgi_response
552
553Given a PSGI response (either three element ARRAY reference OR coderef expecting
554a $responder) set the response from it.
555
556Properly supports streaming and delayed response and / or async IO if running
557under an expected event loop.
558
b194746d 559If passed an object, will expect that object to do a method C<as_psgi>.
560
e67f0874 561Example:
562
563 package MyApp::Web::Controller::Test;
564
565 use base 'Catalyst::Controller';
566 use Plack::App::Directory;
567
568
569 my $app = Plack::App::Directory->new({ root => "/path/to/htdocs" })
570 ->to_app;
571
572 sub myaction :Local Args {
573 my ($self, $c) = @_;
faa1bcff 574 $c->res->from_psgi_response($app->($c->req->env));
e67f0874 575 }
576
577Please note this does not attempt to map or nest your PSGI application under
aca337aa 578the Controller and Action namespace or path. You may wish to review 'PSGI Helpers'
579under L<Catalyst::Utils> for help in properly nesting applications.
580
581B<NOTE> If your external PSGI application returns a response that has a character
582set associated with the content type (such as "text/html; charset=UTF-8") we set
583$c->clear_encoding to remove any additional content type encoding processing later
584in the application (this is done to avoid double encoding issues).
e67f0874 585
6adc45cf 586=head2 encodable_content_type
587
588This is a regular expression used to determine of the current content type
589should be considered encodable. Currently we apply default encoding (usually
590UTF8) to text type contents. Here's the default regular expression:
591
592This would match content types like:
593
594 text/plain
595 text/html
596 text/xml
597 application/javascript
598 application/xml
599 application/vnd.user+xml
600
601B<NOTE>: We don't encode JSON content type responses by default since most
602of the JSON serializers that are commonly used for this task will do so
603automatically and we don't want to double encode. If you are not using a
604tool like L<JSON> to produce JSON type content, (for example you are using
605a template system, or creating the strings manually) you will need to either
606encoding the body yourself:
607
608 $c->response->body( $c->encoding->encode( $body, $c->_encode_check ) );
609
610Or you can alter the regular expression using this attribute.
611
612=head2 encodable_response
613
614Given a L<Catalyst::Response> return true if its one that can be encoded.
615
616 make sure there is an encoding set on the response
617 make sure the content type is encodable
618 make sure no content type charset has been already set to something different from the global encoding
619 make sure no content encoding is present.
620
621Note this does not inspect a body since we do allow automatic encoding on streaming
622type responses.
623
624=cut
625
626sub encodable_response {
627 my ($self) = @_;
628 return 0 unless $self->_context; # Cases like returning a HTTP Exception response you don't have a context here...
629 return 0 unless $self->_context->encoding;
630
d2000928 631 # The response is considered to have a 'manual charset' when a charset is already set on
632 # the content type of the response AND it is not the same as the one we set in encoding.
633 # If there is no charset OR we are asking for the one which is the same as the current
634 # required encoding, that is a flag that we want Catalyst to encode the response automatically.
6adc45cf 635 my $has_manual_charset = 0;
636 if(my $charset = $self->content_type_charset) {
637 $has_manual_charset = (uc($charset) ne uc($self->_context->encoding->mime_name)) ? 1:0;
638 }
639
d2000928 640 # Content type is encodable if it matches the regular expression stored in this attribute
641 my $encodable_content_type = $self->content_type =~ m/${\$self->encodable_content_type}/ ? 1:0;
642
643 # The content encoding is allowed (for charset encoding) only if its empty or is set to identity
644 my $allowed_content_encoding = (!$self->content_encoding || $self->content_encoding eq 'identity') ? 1:0;
645
646 # The content type must be an encodable type, and there must be NO manual charset and also
647 # the content encoding must be the allowed values;
6adc45cf 648 if(
d2000928 649 $encodable_content_type and
650 !$has_manual_charset and
651 $allowed_content_encoding
652 ) {
6adc45cf 653 return 1;
654 } else {
655 return 0;
656 }
657}
658
faa02805 659=head2 DEMOLISH
660
661Ensures that the response is flushed and closed at the end of the
662request.
663
664=head2 meta
665
666Provided by Moose
667
e4cc83b2 668=cut
669
670sub print {
671 my $self = shift;
672 my $data = shift;
673
674 defined $self->write($data) or return;
675
676 for (@_) {
677 defined $self->write($,) or return;
678 defined $self->write($_) or return;
679 }
fe3083a8 680 defined $self->write($\) or return;
b0ad47c1 681
e4cc83b2 682 return 1;
683}
684
910410b8 685=head1 AUTHORS
fc7ec1d9 686
2f381252 687Catalyst Contributors, see Catalyst.pm
fc7ec1d9 688
689=head1 COPYRIGHT
690
b0ad47c1 691This library is free software. You can redistribute it and/or modify
61b1e958 692it under the same terms as Perl itself.
fc7ec1d9 693
694=cut
695
e5ecd5bc 696__PACKAGE__->meta->make_immutable;
697
fc7ec1d9 6981;