added some more stopwords to spelling test
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Request.pm
CommitLineData
fc7ec1d9 1package Catalyst::Request;
2
b4ca0ee8 3use IO::Socket qw[AF_INET inet_aton];
bd917b94 4use Carp;
fc42a730 5use utf8;
de19de2e 6use URI::http;
7use URI::https;
e669e88a 8use URI::QueryParam;
6680c772 9use HTTP::Headers;
b4ca0ee8 10
059c085b 11use Moose;
12
6802c884 13use namespace::clean -except => 'meta';
14
b99ff5d8 15with 'MooseX::Emulate::Class::Accessor::Fast';
16
5fb12dbb 17has action => (is => 'rw');
18has address => (is => 'rw');
19has arguments => (is => 'rw', default => sub { [] });
20has cookies => (is => 'rw', default => sub { {} });
21has query_keywords => (is => 'rw');
22has match => (is => 'rw');
23has method => (is => 'rw');
24has protocol => (is => 'rw');
059c085b 25has query_parameters => (is => 'rw', default => sub { {} });
5fb12dbb 26has secure => (is => 'rw', default => 0);
27has captures => (is => 'rw', default => sub { [] });
6cb9e383 28has uri => (is => 'rw', predicate => 'has_uri');
8026359e 29has remote_user => (is => 'rw');
5fb12dbb 30has headers => (
e5ecd5bc 31 is => 'rw',
059c085b 32 isa => 'HTTP::Headers',
33 handles => [qw(content_encoding content_length content_type header referer user_agent)],
6680c772 34 default => sub { HTTP::Headers->new() },
35 required => 1,
36 lazy => 1,
059c085b 37);
38
39has _context => (
40 is => 'rw',
41 weak_ref => 1,
0fc2d522 42 handles => ['read'],
02570318 43 clearer => '_clear_context',
059c085b 44);
45
46has body_parameters => (
5fb12dbb 47 is => 'rw',
48 required => 1,
49 lazy => 1,
50 default => sub { {} },
fc7ec1d9 51);
52
059c085b 53has uploads => (
5fb12dbb 54 is => 'rw',
55 required => 1,
5fb12dbb 56 default => sub { {} },
059c085b 57);
58
059c085b 59has parameters => (
60 is => 'rw',
61 required => 1,
62 lazy => 1,
63 default => sub { {} },
64);
65
341620d5 66# TODO:
67# - Can we lose the before modifiers which just call prepare_body ?
68# they are wasteful, slow us down and feel cluttery.
69
70# Can we make _body an attribute, have the rest of
71# these lazy build from there and kill all the direct hash access
72# in Catalyst.pm and Engine.pm?
73
74before $_ => sub {
75 my ($self) = @_;
76 my $context = $self->_context || return;
77 $context->prepare_body;
78} for qw/parameters body_parameters/;
79
e99ec2dc 80around parameters => sub {
81 my ($orig, $self, $params) = @_;
82 if ($params) {
83 if ( !ref $params ) {
84 $self->_context->log->warn(
85 "Attempt to retrieve '$params' with req->params(), " .
86 "you probably meant to call req->param('$params')"
87 );
88 $params = undef;
89 }
90 return $self->$orig($params);
91 }
92 $self->$orig();
059c085b 93};
94
95has base => (
5fb12dbb 96 is => 'rw',
97 required => 1,
98 lazy => 1,
99 default => sub {
059c085b 100 my $self = shift;
6cb9e383 101 return $self->path if $self->has_uri;
059c085b 102 },
103);
104
069355da 105has _body => (
0f56bbcf 106 is => 'rw', clearer => '_clear_body', predicate => '_has_body',
059c085b 107);
610bc6ec 108# Eugh, ugly. Should just be able to rename accessor methods to 'body'
b0ad47c1 109# and provide a custom reader..
610bc6ec 110sub body {
111 my $self = shift;
059c085b 112 $self->_context->prepare_body();
14c057aa 113 croak 'body is a reader' if scalar @_;
610bc6ec 114 return blessed $self->_body ? $self->_body->body : $self->_body;
115}
059c085b 116
117has hostname => (
118 is => 'rw',
119 required => 1,
120 lazy => 1,
121 default => sub {
122 my ($self) = @_;
9fb936e5 123 gethostbyaddr( inet_aton( $self->address ), AF_INET ) || $self->address
059c085b 124 },
125);
126
02570318 127has _path => ( is => 'rw', predicate => '_has_path', clearer => '_clear_path' );
128
8026359e 129# XXX: Deprecated in docs ages ago (2006), deprecated with warning in 5.8000 due
130# to confusion between Engines and Plugin::Authentication. Remove in 5.8100?
131has user => (is => 'rw');
132
059c085b 133sub args { shift->arguments(@_) }
134sub body_params { shift->body_parameters(@_) }
135sub input { shift->body(@_) }
136sub params { shift->parameters(@_) }
137sub query_params { shift->query_parameters(@_) }
138sub path_info { shift->path(@_) }
139sub snippets { shift->captures(@_) }
f7e4e231 140
fc7ec1d9 141=head1 NAME
142
3e19f4f6 143Catalyst::Request - provides information about the current client request
fc7ec1d9 144
145=head1 SYNOPSIS
146
b22c6668 147 $req = $c->request;
148 $req->action;
149 $req->address;
b22c6668 150 $req->arguments;
3e19f4f6 151 $req->args;
b22c6668 152 $req->base;
06e1b616 153 $req->body;
fbcc39ad 154 $req->body_parameters;
b5176d9e 155 $req->content_encoding;
156 $req->content_length;
157 $req->content_type;
b77e7869 158 $req->cookie;
b22c6668 159 $req->cookies;
b5176d9e 160 $req->header;
b22c6668 161 $req->headers;
162 $req->hostname;
61bacdcc 163 $req->input;
3b4d1251 164 $req->query_keywords;
b22c6668 165 $req->match;
166 $req->method;
e7c0c583 167 $req->param;
e7c0c583 168 $req->parameters;
3e19f4f6 169 $req->params;
b22c6668 170 $req->path;
bfde09a2 171 $req->protocol;
fbcc39ad 172 $req->query_parameters;
173 $req->read;
b5176d9e 174 $req->referer;
bfde09a2 175 $req->secure;
2982e768 176 $req->captures; # previously knows as snippets
e7c0c583 177 $req->upload;
b22c6668 178 $req->uploads;
77d12cae 179 $req->uri;
7ce7ca2e 180 $req->user;
66294129 181 $req->user_agent;
b22c6668 182
3e22baa5 183See also L<Catalyst>, L<Catalyst::Request::Upload>.
fc7ec1d9 184
185=head1 DESCRIPTION
186
3e19f4f6 187This is the Catalyst Request class, which provides an interface to data for the
188current client request. The request object is prepared by L<Catalyst::Engine>,
189thus hiding the details of the particular engine implementation.
b22c6668 190
191=head1 METHODS
fc7ec1d9 192
b5ecfcf0 193=head2 $req->action
fc7ec1d9 194
aae8d418 195[DEPRECATED] Returns the name of the requested action.
196
197
198Use C<< $c->action >> instead (which returns a
199L<Catalyst::Action|Catalyst::Action> object).
fc7ec1d9 200
b5ecfcf0 201=head2 $req->address
0556eb49 202
3e19f4f6 203Returns the IP address of the client.
61b1e958 204
b5ecfcf0 205=head2 $req->arguments
61b1e958 206
b22c6668 207Returns a reference to an array containing the arguments.
fc7ec1d9 208
209 print $c->request->arguments->[0];
210
c436c1e8 211For example, if your action was
212
7d7519a4 213 package MyApp::Controller::Foo;
85d9fce6 214
215 sub moose : Local {
216 ...
217 }
c436c1e8 218
3e19f4f6 219and the URI for the request was C<http://.../foo/moose/bah>, the string C<bah>
c436c1e8 220would be the first and only argument.
221
6d920953 222Arguments get automatically URI-unescaped for you.
8f58057d 223
b5ecfcf0 224=head2 $req->args
3e19f4f6 225
01011731 226Shortcut for L</arguments>.
3e19f4f6 227
b5ecfcf0 228=head2 $req->base
fc7ec1d9 229
328f225e 230Contains the URI base. This will always have a trailing slash. Note that the
231URI scheme (eg., http vs. https) must be determined through heuristics;
232depending on your server configuration, it may be incorrect. See $req->secure
233for more info.
c436c1e8 234
3e19f4f6 235If your application was queried with the URI
236C<http://localhost:3000/some/path> then C<base> is C<http://localhost:3000/>.
fc7ec1d9 237
b5ecfcf0 238=head2 $req->body
06e1b616 239
843871cf 240Returns the message body of the request, as returned by L<HTTP::Body>: a string,
241unless Content-Type is C<application/x-www-form-urlencoded>, C<text/xml>, or
242C<multipart/form-data>, in which case a L<File::Temp> object is returned.
e060fe05 243
b5ecfcf0 244=head2 $req->body_parameters
fbcc39ad 245
3e19f4f6 246Returns a reference to a hash containing body (POST) parameters. Values can
fbcc39ad 247be either a scalar or an arrayref containing scalars.
248
249 print $c->request->body_parameters->{field};
250 print $c->request->body_parameters->{field}->[0];
c436c1e8 251
d631b5f9 252These are the parameters from the POST part of the request, if any.
e5ecd5bc 253
b5ecfcf0 254=head2 $req->body_params
fbcc39ad 255
3e19f4f6 256Shortcut for body_parameters.
fbcc39ad 257
b5ecfcf0 258=head2 $req->content_encoding
b5176d9e 259
3e19f4f6 260Shortcut for $req->headers->content_encoding.
b5176d9e 261
b5ecfcf0 262=head2 $req->content_length
b5176d9e 263
3e19f4f6 264Shortcut for $req->headers->content_length.
b5176d9e 265
b5ecfcf0 266=head2 $req->content_type
b5176d9e 267
3e19f4f6 268Shortcut for $req->headers->content_type.
b5176d9e 269
b5ecfcf0 270=head2 $req->cookie
3ad654e0 271
3e19f4f6 272A convenient method to access $req->cookies.
3ad654e0 273
274 $cookie = $c->request->cookie('name');
275 @cookies = $c->request->cookie;
276
277=cut
278
279sub cookie {
280 my $self = shift;
281
282 if ( @_ == 0 ) {
b77e7869 283 return keys %{ $self->cookies };
3ad654e0 284 }
285
286 if ( @_ == 1 ) {
287
288 my $name = shift;
289
b77e7869 290 unless ( exists $self->cookies->{$name} ) {
3ad654e0 291 return undef;
292 }
fbcc39ad 293
b77e7869 294 return $self->cookies->{$name};
3ad654e0 295 }
296}
297
b5ecfcf0 298=head2 $req->cookies
fc7ec1d9 299
b22c6668 300Returns a reference to a hash containing the cookies.
fc7ec1d9 301
302 print $c->request->cookies->{mycookie}->value;
303
7e743798 304The cookies in the hash are indexed by name, and the values are L<CGI::Simple::Cookie>
c436c1e8 305objects.
306
b5ecfcf0 307=head2 $req->header
b5176d9e 308
3e19f4f6 309Shortcut for $req->headers->header.
b5176d9e 310
b5ecfcf0 311=head2 $req->headers
fc7ec1d9 312
3e19f4f6 313Returns an L<HTTP::Headers> object containing the headers for the current request.
fc7ec1d9 314
315 print $c->request->headers->header('X-Catalyst');
316
b5ecfcf0 317=head2 $req->hostname
0556eb49 318
cfb8879d 319Returns the hostname of the client. Use $req->uri->host to get the hostname of the server.
e5ecd5bc 320
b5ecfcf0 321=head2 $req->input
61bacdcc 322
3e19f4f6 323Alias for $req->body.
61bacdcc 324
3b4d1251 325=head2 $req->query_keywords
326
327Contains the keywords portion of a query string, when no '=' signs are
328present.
329
330 http://localhost/path?some+keywords
b0ad47c1 331
3b4d1251 332 $c->request->query_keywords will contain 'some keywords'
333
b5ecfcf0 334=head2 $req->match
fc7ec1d9 335
3e19f4f6 336This contains the matching part of a Regex action. Otherwise
2c83fd5a 337it returns the same as 'action', except for default actions,
338which return an empty string.
fc7ec1d9 339
b5ecfcf0 340=head2 $req->method
b5176d9e 341
342Contains the request method (C<GET>, C<POST>, C<HEAD>, etc).
343
b5ecfcf0 344=head2 $req->param
e7c0c583 345
b0ad47c1 346Returns GET and POST parameters with a CGI.pm-compatible param method. This
3e19f4f6 347is an alternative method for accessing parameters in $c->req->parameters.
e7c0c583 348
a82c2894 349 $value = $c->request->param( 'foo' );
350 @values = $c->request->param( 'foo' );
e7c0c583 351 @params = $c->request->param;
352
3e705254 353Like L<CGI>, and B<unlike> earlier versions of Catalyst, passing multiple
a82c2894 354arguments to this method, like this:
355
85d9fce6 356 $c->request->param( 'foo', 'bar', 'gorch', 'quxx' );
a82c2894 357
358will set the parameter C<foo> to the multiple values C<bar>, C<gorch> and
359C<quxx>. Previously this would have added C<bar> as another value to C<foo>
3e19f4f6 360(creating it if it didn't exist before), and C<quxx> as another value for
361C<gorch>.
a82c2894 362
83312afd 363B<NOTE> this is considered a legacy interface and care should be taken when
364using it. C<< scalar $c->req->param( 'foo' ) >> will return only the first
365C<foo> param even if multiple are present; C<< $c->req->param( 'foo' ) >> will
366return a list of as many are present, which can have unexpected consequences
367when writing code of the form:
368
369 $foo->bar(
370 a => 'b',
371 baz => $c->req->param( 'baz' ),
372 );
373
374If multiple C<baz> parameters are provided this code might corrupt data or
375cause a hash initialization error. For a more straightforward interface see
376C<< $c->req->parameters >>.
377
e7c0c583 378=cut
379
380sub param {
381 my $self = shift;
382
383 if ( @_ == 0 ) {
384 return keys %{ $self->parameters };
385 }
386
bfde09a2 387 if ( @_ == 1 ) {
e7c0c583 388
bfde09a2 389 my $param = shift;
6bd2b72c 390
bfde09a2 391 unless ( exists $self->parameters->{$param} ) {
392 return wantarray ? () : undef;
393 }
394
395 if ( ref $self->parameters->{$param} eq 'ARRAY' ) {
396 return (wantarray)
397 ? @{ $self->parameters->{$param} }
398 : $self->parameters->{$param}->[0];
399 }
400 else {
401 return (wantarray)
402 ? ( $self->parameters->{$param} )
403 : $self->parameters->{$param};
404 }
d7945f32 405 }
a82c2894 406 elsif ( @_ > 1 ) {
407 my $field = shift;
408 $self->parameters->{$field} = [@_];
d7945f32 409 }
e7c0c583 410}
b5176d9e 411
b5ecfcf0 412=head2 $req->parameters
61b1e958 413
3e19f4f6 414Returns a reference to a hash containing GET and POST parameters. Values can
d08ced28 415be either a scalar or an arrayref containing scalars.
fc7ec1d9 416
e7c0c583 417 print $c->request->parameters->{field};
418 print $c->request->parameters->{field}->[0];
fc7ec1d9 419
c436c1e8 420This is the combination of C<query_parameters> and C<body_parameters>.
421
b5ecfcf0 422=head2 $req->params
3e19f4f6 423
424Shortcut for $req->parameters.
425
b5ecfcf0 426=head2 $req->path
fc7ec1d9 427
3e19f4f6 428Returns the path, i.e. the part of the URI after $req->base, for the current request.
fc7ec1d9 429
be6801fa 430 http://localhost/path/foo
431
432 $c->request->path will contain 'path/foo'
433
b5ecfcf0 434=head2 $req->path_info
fbcc39ad 435
10011c19 436Alias for path, added for compatibility with L<CGI>.
fbcc39ad 437
438=cut
439
440sub path {
02fb5d78 441 my ( $self, @params ) = @_;
4f5ebacd 442
02fb5d78 443 if (@params) {
444 $self->uri->path(@params);
02570318 445 $self->_clear_path;
fbcc39ad 446 }
02570318 447 elsif ( $self->_has_path ) {
448 return $self->_path;
e561386f 449 }
02fb5d78 450 else {
451 my $path = $self->uri->path;
452 my $location = $self->base->path;
453 $path =~ s/^(\Q$location\E)?//;
454 $path =~ s/^\///;
02570318 455 $self->_path($path);
fbcc39ad 456
02fb5d78 457 return $path;
458 }
fbcc39ad 459}
460
b5ecfcf0 461=head2 $req->protocol
bfde09a2 462
3e19f4f6 463Returns the protocol (HTTP/1.0 or HTTP/1.1) used for the current request.
bfde09a2 464
b5ecfcf0 465=head2 $req->query_parameters
fbcc39ad 466
def54ce2 467=head2 $req->query_params
468
3e19f4f6 469Returns a reference to a hash containing query string (GET) parameters. Values can
fbcc39ad 470be either a scalar or an arrayref containing scalars.
471
472 print $c->request->query_parameters->{field};
473 print $c->request->query_parameters->{field}->[0];
b0ad47c1 474
b5ecfcf0 475=head2 $req->read( [$maxlength] )
fbcc39ad 476
3e19f4f6 477Reads a chunk of data from the request body. This method is intended to be
478used in a while loop, reading $maxlength bytes on every call. $maxlength
fbcc39ad 479defaults to the size of the request if not specified.
480
9779c885 481You have to set MyApp->config(parse_on_demand => 1) to use this directly.
fbcc39ad 482
b5ecfcf0 483=head2 $req->referer
fc7ec1d9 484
3e19f4f6 485Shortcut for $req->headers->referer. Returns the referring page.
fc7ec1d9 486
b5ecfcf0 487=head2 $req->secure
bfde09a2 488
328f225e 489Returns true or false, indicating whether the connection is secure
490(https). Note that the URI scheme (eg., http vs. https) must be determined
ae7da8f5 491through heuristics, and therefore the reliability of $req->secure will depend
328f225e 492on your server configuration. If you are serving secure pages on the standard
493SSL port (443) and/or setting the HTTPS environment variable, $req->secure
494should be valid.
bfde09a2 495
2982e768 496=head2 $req->captures
497
5c6a56e0 498Returns a reference to an array containing captured args from chained
499actions or regex captures.
fc7ec1d9 500
2982e768 501 my @captures = @{ $c->request->captures };
502
503=head2 $req->snippets
fc7ec1d9 504
10011c19 505C<captures> used to be called snippets. This is still available for backwards
2982e768 506compatibility, but is considered deprecated.
fc7ec1d9 507
b5ecfcf0 508=head2 $req->upload
e7c0c583 509
3e19f4f6 510A convenient method to access $req->uploads.
e7c0c583 511
512 $upload = $c->request->upload('field');
513 @uploads = $c->request->upload('field');
514 @fields = $c->request->upload;
bfde09a2 515
e7c0c583 516 for my $upload ( $c->request->upload('field') ) {
146554c5 517 print $upload->filename;
e7c0c583 518 }
519
520=cut
521
522sub upload {
523 my $self = shift;
524
525 if ( @_ == 0 ) {
526 return keys %{ $self->uploads };
527 }
528
bfde09a2 529 if ( @_ == 1 ) {
e7c0c583 530
bfde09a2 531 my $upload = shift;
532
533 unless ( exists $self->uploads->{$upload} ) {
534 return wantarray ? () : undef;
535 }
6bd2b72c 536
bfde09a2 537 if ( ref $self->uploads->{$upload} eq 'ARRAY' ) {
538 return (wantarray)
539 ? @{ $self->uploads->{$upload} }
540 : $self->uploads->{$upload}->[0];
541 }
542 else {
543 return (wantarray)
fbcc39ad 544 ? ( $self->uploads->{$upload} )
545 : $self->uploads->{$upload};
bfde09a2 546 }
d7945f32 547 }
bfde09a2 548
a4f5c51e 549 if ( @_ > 1 ) {
bfde09a2 550
551 while ( my ( $field, $upload ) = splice( @_, 0, 2 ) ) {
552
553 if ( exists $self->uploads->{$field} ) {
554 for ( $self->uploads->{$field} ) {
555 $_ = [$_] unless ref($_) eq "ARRAY";
556 push( @$_, $upload );
557 }
558 }
559 else {
560 $self->uploads->{$field} = $upload;
561 }
562 }
e7c0c583 563 }
564}
565
b5ecfcf0 566=head2 $req->uploads
fc7ec1d9 567
bfde09a2 568Returns a reference to a hash containing uploads. Values can be either a
b0ad47c1 569L<Catalyst::Request::Upload> object, or an arrayref of
84e7aa89 570L<Catalyst::Request::Upload> objects.
e7c0c583 571
572 my $upload = $c->request->uploads->{field};
573 my $upload = $c->request->uploads->{field}->[0];
574
b5ecfcf0 575=head2 $req->uri
fbcc39ad 576
d26ee0d0 577Returns a L<URI> object for the current request. Stringifies to the URI text.
fbcc39ad 578
a375a206 579=head2 $req->mangle_params( { key => 'value' }, $appendmode);
bd917b94 580
a375a206 581Returns a hashref of parameters stemming from the current request's params,
582plus the ones supplied. Keys for which no current param exists will be
583added, keys with undefined values will be removed and keys with existing
584params will be replaced. Note that you can supply a true value as the final
585argument to change behavior with regards to existing parameters, appending
586values rather than replacing them.
587
588A quick example:
589
590 # URI query params foo=1
591 my $hashref = $req->mangle_params({ foo => 2 });
592 # Result is query params of foo=2
593
594versus append mode:
595
596 # URI query params foo=1
597 my $hashref = $req->mangle_params({ foo => 2 }, 1);
598 # Result is query params of foo=1&foo=2
599
600This is the code behind C<uri_with>.
bd917b94 601
602=cut
603
a375a206 604sub mangle_params {
605 my ($self, $args, $append) = @_;
b0ad47c1 606
a375a206 607 carp('No arguments passed to mangle_params()') unless $args;
fbb513f7 608
2f381252 609 foreach my $value ( values %$args ) {
d0f0fcf6 610 next unless defined $value;
fbb513f7 611 for ( ref $value eq 'ARRAY' ? @$value : $value ) {
612 $_ = "$_";
7066a4d5 613 utf8::encode( $_ ) if utf8::is_utf8($_);
fc42a730 614 }
fc42a730 615 };
b0ad47c1 616
a375a206 617 my %params = %{ $self->uri->query_form_hash };
618 foreach my $key (keys %{ $args }) {
619 my $val = $args->{$key};
620 if(defined($val)) {
621
622 if($append && exists($params{$key})) {
623
624 # This little bit of heaven handles appending a new value onto
625 # an existing one regardless if the existing value is an array
626 # or not, and regardless if the new value is an array or not
627 $params{$key} = [
628 ref($params{$key}) eq 'ARRAY' ? @{ $params{$key} } : $params{$key},
629 ref($val) eq 'ARRAY' ? @{ $val } : $val
630 ];
631
632 } else {
633 $params{$key} = $val;
634 }
635 } else {
636
637 # If the param wasn't defined then we delete it.
638 delete($params{$key});
639 }
640 }
641
642
643 return \%params;
644}
645
646=head2 $req->uri_with( { key => 'value' } );
647
648Returns a rewritten URI object for the current request. Key/value pairs
649passed in will override existing parameters. You can remove an existing
650parameter by passing in an undef value. Unmodified pairs will be
651preserved.
652
653You may also pass an optional second parameter that puts C<uri_with> into
654append mode:
655
656 $req->uri_with( { key => 'value' }, { mode => 'append' } );
9779c885 657
a375a206 658See C<mangle_params> for an explanation of this behavior.
659
660=cut
661
662sub uri_with {
663 my( $self, $args, $behavior) = @_;
664
665 carp( 'No arguments passed to uri_with()' ) unless $args;
666
667 my $append = 0;
668 if((ref($behavior) eq 'HASH') && defined($behavior->{mode}) && ($behavior->{mode} eq 'append')) {
669 $append = 1;
670 }
671
672 my $params = $self->mangle_params($args, $append);
673
674 my $uri = $self->uri->clone;
675 $uri->query_form($params);
2f381252 676
bd917b94 677 return $uri;
678}
679
8026359e 680=head2 $req->remote_user
681
682Returns the value of the C<REMOTE_USER> environment variable.
7ce7ca2e 683
b5ecfcf0 684=head2 $req->user_agent
b5176d9e 685
3e19f4f6 686Shortcut to $req->headers->user_agent. Returns the user agent (browser)
687version string.
b5176d9e 688
059c085b 689=head2 meta
690
691Provided by Moose
692
3e19f4f6 693=head1 AUTHORS
fc7ec1d9 694
2f381252 695Catalyst Contributors, see Catalyst.pm
fc7ec1d9 696
697=head1 COPYRIGHT
698
536bee89 699This library is free software. You can redistribute it and/or modify
61b1e958 700it under the same terms as Perl itself.
fc7ec1d9 701
702=cut
703
e5ecd5bc 704__PACKAGE__->meta->make_immutable;
705
fc7ec1d9 7061;