Blow up rather than failing to call ->can if everything is totally screwed
[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();
610bc6ec 113 $self->_body(@_) if scalar @_;
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) = @_;
8fc0d39e 123 gethostbyaddr( inet_aton( $self->address ), AF_INET ) || 'localhost'
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
85d9fce6 213 package MyApp::C::Foo;
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
3e19f4f6 240Returns the message body of the request, unless Content-Type is
e060fe05 241C<application/x-www-form-urlencoded> or C<multipart/form-data>.
242
b5ecfcf0 243=head2 $req->body_parameters
fbcc39ad 244
3e19f4f6 245Returns a reference to a hash containing body (POST) parameters. Values can
fbcc39ad 246be either a scalar or an arrayref containing scalars.
247
248 print $c->request->body_parameters->{field};
249 print $c->request->body_parameters->{field}->[0];
c436c1e8 250
d631b5f9 251These are the parameters from the POST part of the request, if any.
e5ecd5bc 252
b5ecfcf0 253=head2 $req->body_params
fbcc39ad 254
3e19f4f6 255Shortcut for body_parameters.
fbcc39ad 256
b5ecfcf0 257=head2 $req->content_encoding
b5176d9e 258
3e19f4f6 259Shortcut for $req->headers->content_encoding.
b5176d9e 260
b5ecfcf0 261=head2 $req->content_length
b5176d9e 262
3e19f4f6 263Shortcut for $req->headers->content_length.
b5176d9e 264
b5ecfcf0 265=head2 $req->content_type
b5176d9e 266
3e19f4f6 267Shortcut for $req->headers->content_type.
b5176d9e 268
b5ecfcf0 269=head2 $req->cookie
3ad654e0 270
3e19f4f6 271A convenient method to access $req->cookies.
3ad654e0 272
273 $cookie = $c->request->cookie('name');
274 @cookies = $c->request->cookie;
275
276=cut
277
278sub cookie {
279 my $self = shift;
280
281 if ( @_ == 0 ) {
b77e7869 282 return keys %{ $self->cookies };
3ad654e0 283 }
284
285 if ( @_ == 1 ) {
286
287 my $name = shift;
288
b77e7869 289 unless ( exists $self->cookies->{$name} ) {
3ad654e0 290 return undef;
291 }
fbcc39ad 292
b77e7869 293 return $self->cookies->{$name};
3ad654e0 294 }
295}
296
b5ecfcf0 297=head2 $req->cookies
fc7ec1d9 298
b22c6668 299Returns a reference to a hash containing the cookies.
fc7ec1d9 300
301 print $c->request->cookies->{mycookie}->value;
302
3e19f4f6 303The cookies in the hash are indexed by name, and the values are L<CGI::Cookie>
c436c1e8 304objects.
305
b5ecfcf0 306=head2 $req->header
b5176d9e 307
3e19f4f6 308Shortcut for $req->headers->header.
b5176d9e 309
b5ecfcf0 310=head2 $req->headers
fc7ec1d9 311
3e19f4f6 312Returns an L<HTTP::Headers> object containing the headers for the current request.
fc7ec1d9 313
314 print $c->request->headers->header('X-Catalyst');
315
b5ecfcf0 316=head2 $req->hostname
0556eb49 317
3e19f4f6 318Returns the hostname of the client.
e5ecd5bc 319
b5ecfcf0 320=head2 $req->input
61bacdcc 321
3e19f4f6 322Alias for $req->body.
61bacdcc 323
3b4d1251 324=head2 $req->query_keywords
325
326Contains the keywords portion of a query string, when no '=' signs are
327present.
328
329 http://localhost/path?some+keywords
b0ad47c1 330
3b4d1251 331 $c->request->query_keywords will contain 'some keywords'
332
b5ecfcf0 333=head2 $req->match
fc7ec1d9 334
3e19f4f6 335This contains the matching part of a Regex action. Otherwise
2c83fd5a 336it returns the same as 'action', except for default actions,
337which return an empty string.
fc7ec1d9 338
b5ecfcf0 339=head2 $req->method
b5176d9e 340
341Contains the request method (C<GET>, C<POST>, C<HEAD>, etc).
342
b5ecfcf0 343=head2 $req->param
e7c0c583 344
b0ad47c1 345Returns GET and POST parameters with a CGI.pm-compatible param method. This
3e19f4f6 346is an alternative method for accessing parameters in $c->req->parameters.
e7c0c583 347
a82c2894 348 $value = $c->request->param( 'foo' );
349 @values = $c->request->param( 'foo' );
e7c0c583 350 @params = $c->request->param;
351
3e705254 352Like L<CGI>, and B<unlike> earlier versions of Catalyst, passing multiple
a82c2894 353arguments to this method, like this:
354
85d9fce6 355 $c->request->param( 'foo', 'bar', 'gorch', 'quxx' );
a82c2894 356
357will set the parameter C<foo> to the multiple values C<bar>, C<gorch> and
358C<quxx>. Previously this would have added C<bar> as another value to C<foo>
3e19f4f6 359(creating it if it didn't exist before), and C<quxx> as another value for
360C<gorch>.
a82c2894 361
83312afd 362B<NOTE> this is considered a legacy interface and care should be taken when
363using it. C<< scalar $c->req->param( 'foo' ) >> will return only the first
364C<foo> param even if multiple are present; C<< $c->req->param( 'foo' ) >> will
365return a list of as many are present, which can have unexpected consequences
366when writing code of the form:
367
368 $foo->bar(
369 a => 'b',
370 baz => $c->req->param( 'baz' ),
371 );
372
373If multiple C<baz> parameters are provided this code might corrupt data or
374cause a hash initialization error. For a more straightforward interface see
375C<< $c->req->parameters >>.
376
e7c0c583 377=cut
378
379sub param {
380 my $self = shift;
381
382 if ( @_ == 0 ) {
383 return keys %{ $self->parameters };
384 }
385
bfde09a2 386 if ( @_ == 1 ) {
e7c0c583 387
bfde09a2 388 my $param = shift;
6bd2b72c 389
bfde09a2 390 unless ( exists $self->parameters->{$param} ) {
391 return wantarray ? () : undef;
392 }
393
394 if ( ref $self->parameters->{$param} eq 'ARRAY' ) {
395 return (wantarray)
396 ? @{ $self->parameters->{$param} }
397 : $self->parameters->{$param}->[0];
398 }
399 else {
400 return (wantarray)
401 ? ( $self->parameters->{$param} )
402 : $self->parameters->{$param};
403 }
d7945f32 404 }
a82c2894 405 elsif ( @_ > 1 ) {
406 my $field = shift;
407 $self->parameters->{$field} = [@_];
d7945f32 408 }
e7c0c583 409}
b5176d9e 410
b5ecfcf0 411=head2 $req->parameters
61b1e958 412
3e19f4f6 413Returns a reference to a hash containing GET and POST parameters. Values can
d08ced28 414be either a scalar or an arrayref containing scalars.
fc7ec1d9 415
e7c0c583 416 print $c->request->parameters->{field};
417 print $c->request->parameters->{field}->[0];
fc7ec1d9 418
c436c1e8 419This is the combination of C<query_parameters> and C<body_parameters>.
420
b5ecfcf0 421=head2 $req->params
3e19f4f6 422
423Shortcut for $req->parameters.
424
b5ecfcf0 425=head2 $req->path
fc7ec1d9 426
3e19f4f6 427Returns the path, i.e. the part of the URI after $req->base, for the current request.
fc7ec1d9 428
b5ecfcf0 429=head2 $req->path_info
fbcc39ad 430
10011c19 431Alias for path, added for compatibility with L<CGI>.
fbcc39ad 432
433=cut
434
435sub path {
02fb5d78 436 my ( $self, @params ) = @_;
4f5ebacd 437
02fb5d78 438 if (@params) {
439 $self->uri->path(@params);
02570318 440 $self->_clear_path;
fbcc39ad 441 }
02570318 442 elsif ( $self->_has_path ) {
443 return $self->_path;
e561386f 444 }
02fb5d78 445 else {
446 my $path = $self->uri->path;
447 my $location = $self->base->path;
448 $path =~ s/^(\Q$location\E)?//;
449 $path =~ s/^\///;
02570318 450 $self->_path($path);
fbcc39ad 451
02fb5d78 452 return $path;
453 }
fbcc39ad 454}
455
b5ecfcf0 456=head2 $req->protocol
bfde09a2 457
3e19f4f6 458Returns the protocol (HTTP/1.0 or HTTP/1.1) used for the current request.
bfde09a2 459
b5ecfcf0 460=head2 $req->query_parameters
fbcc39ad 461
def54ce2 462=head2 $req->query_params
463
3e19f4f6 464Returns a reference to a hash containing query string (GET) parameters. Values can
fbcc39ad 465be either a scalar or an arrayref containing scalars.
466
467 print $c->request->query_parameters->{field};
468 print $c->request->query_parameters->{field}->[0];
b0ad47c1 469
b5ecfcf0 470=head2 $req->read( [$maxlength] )
fbcc39ad 471
3e19f4f6 472Reads a chunk of data from the request body. This method is intended to be
473used in a while loop, reading $maxlength bytes on every call. $maxlength
fbcc39ad 474defaults to the size of the request if not specified.
475
9779c885 476You have to set MyApp->config(parse_on_demand => 1) to use this directly.
fbcc39ad 477
b5ecfcf0 478=head2 $req->referer
fc7ec1d9 479
3e19f4f6 480Shortcut for $req->headers->referer. Returns the referring page.
fc7ec1d9 481
b5ecfcf0 482=head2 $req->secure
bfde09a2 483
328f225e 484Returns true or false, indicating whether the connection is secure
485(https). Note that the URI scheme (eg., http vs. https) must be determined
486through heuristics, and therefore the reliablity of $req->secure will depend
487on your server configuration. If you are serving secure pages on the standard
488SSL port (443) and/or setting the HTTPS environment variable, $req->secure
489should be valid.
bfde09a2 490
2982e768 491=head2 $req->captures
492
5c6a56e0 493Returns a reference to an array containing captured args from chained
494actions or regex captures.
fc7ec1d9 495
2982e768 496 my @captures = @{ $c->request->captures };
497
498=head2 $req->snippets
fc7ec1d9 499
10011c19 500C<captures> used to be called snippets. This is still available for backwards
2982e768 501compatibility, but is considered deprecated.
fc7ec1d9 502
b5ecfcf0 503=head2 $req->upload
e7c0c583 504
3e19f4f6 505A convenient method to access $req->uploads.
e7c0c583 506
507 $upload = $c->request->upload('field');
508 @uploads = $c->request->upload('field');
509 @fields = $c->request->upload;
bfde09a2 510
e7c0c583 511 for my $upload ( $c->request->upload('field') ) {
146554c5 512 print $upload->filename;
e7c0c583 513 }
514
515=cut
516
517sub upload {
518 my $self = shift;
519
520 if ( @_ == 0 ) {
521 return keys %{ $self->uploads };
522 }
523
bfde09a2 524 if ( @_ == 1 ) {
e7c0c583 525
bfde09a2 526 my $upload = shift;
527
528 unless ( exists $self->uploads->{$upload} ) {
529 return wantarray ? () : undef;
530 }
6bd2b72c 531
bfde09a2 532 if ( ref $self->uploads->{$upload} eq 'ARRAY' ) {
533 return (wantarray)
534 ? @{ $self->uploads->{$upload} }
535 : $self->uploads->{$upload}->[0];
536 }
537 else {
538 return (wantarray)
fbcc39ad 539 ? ( $self->uploads->{$upload} )
540 : $self->uploads->{$upload};
bfde09a2 541 }
d7945f32 542 }
bfde09a2 543
a4f5c51e 544 if ( @_ > 1 ) {
bfde09a2 545
546 while ( my ( $field, $upload ) = splice( @_, 0, 2 ) ) {
547
548 if ( exists $self->uploads->{$field} ) {
549 for ( $self->uploads->{$field} ) {
550 $_ = [$_] unless ref($_) eq "ARRAY";
551 push( @$_, $upload );
552 }
553 }
554 else {
555 $self->uploads->{$field} = $upload;
556 }
557 }
e7c0c583 558 }
559}
560
b5ecfcf0 561=head2 $req->uploads
fc7ec1d9 562
bfde09a2 563Returns a reference to a hash containing uploads. Values can be either a
b0ad47c1 564L<Catalyst::Request::Upload> object, or an arrayref of
84e7aa89 565L<Catalyst::Request::Upload> objects.
e7c0c583 566
567 my $upload = $c->request->uploads->{field};
568 my $upload = $c->request->uploads->{field}->[0];
569
b5ecfcf0 570=head2 $req->uri
fbcc39ad 571
3e19f4f6 572Returns a URI object for the current request. Stringifies to the URI text.
fbcc39ad 573
a375a206 574=head2 $req->mangle_params( { key => 'value' }, $appendmode);
bd917b94 575
a375a206 576Returns a hashref of parameters stemming from the current request's params,
577plus the ones supplied. Keys for which no current param exists will be
578added, keys with undefined values will be removed and keys with existing
579params will be replaced. Note that you can supply a true value as the final
580argument to change behavior with regards to existing parameters, appending
581values rather than replacing them.
582
583A quick example:
584
585 # URI query params foo=1
586 my $hashref = $req->mangle_params({ foo => 2 });
587 # Result is query params of foo=2
588
589versus append mode:
590
591 # URI query params foo=1
592 my $hashref = $req->mangle_params({ foo => 2 }, 1);
593 # Result is query params of foo=1&foo=2
594
595This is the code behind C<uri_with>.
bd917b94 596
597=cut
598
a375a206 599sub mangle_params {
600 my ($self, $args, $append) = @_;
b0ad47c1 601
a375a206 602 carp('No arguments passed to mangle_params()') unless $args;
fbb513f7 603
2f381252 604 foreach my $value ( values %$args ) {
d0f0fcf6 605 next unless defined $value;
fbb513f7 606 for ( ref $value eq 'ARRAY' ? @$value : $value ) {
607 $_ = "$_";
7066a4d5 608 utf8::encode( $_ ) if utf8::is_utf8($_);
fc42a730 609 }
fc42a730 610 };
b0ad47c1 611
a375a206 612 my %params = %{ $self->uri->query_form_hash };
613 foreach my $key (keys %{ $args }) {
614 my $val = $args->{$key};
615 if(defined($val)) {
616
617 if($append && exists($params{$key})) {
618
619 # This little bit of heaven handles appending a new value onto
620 # an existing one regardless if the existing value is an array
621 # or not, and regardless if the new value is an array or not
622 $params{$key} = [
623 ref($params{$key}) eq 'ARRAY' ? @{ $params{$key} } : $params{$key},
624 ref($val) eq 'ARRAY' ? @{ $val } : $val
625 ];
626
627 } else {
628 $params{$key} = $val;
629 }
630 } else {
631
632 # If the param wasn't defined then we delete it.
633 delete($params{$key});
634 }
635 }
636
637
638 return \%params;
639}
640
641=head2 $req->uri_with( { key => 'value' } );
642
643Returns a rewritten URI object for the current request. Key/value pairs
644passed in will override existing parameters. You can remove an existing
645parameter by passing in an undef value. Unmodified pairs will be
646preserved.
647
648You may also pass an optional second parameter that puts C<uri_with> into
649append mode:
650
651 $req->uri_with( { key => 'value' }, { mode => 'append' } );
9779c885 652
a375a206 653See C<mangle_params> for an explanation of this behavior.
654
655=cut
656
657sub uri_with {
658 my( $self, $args, $behavior) = @_;
659
660 carp( 'No arguments passed to uri_with()' ) unless $args;
661
662 my $append = 0;
663 if((ref($behavior) eq 'HASH') && defined($behavior->{mode}) && ($behavior->{mode} eq 'append')) {
664 $append = 1;
665 }
666
667 my $params = $self->mangle_params($args, $append);
668
669 my $uri = $self->uri->clone;
670 $uri->query_form($params);
2f381252 671
bd917b94 672 return $uri;
673}
674
b5ecfcf0 675=head2 $req->user
7ce7ca2e 676
8026359e 677Returns the currently logged in user. B<Highly deprecated>, do not call,
678this will be removed in version 5.81.
679
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;