moving things around to get ready to support Class::MOP 0.36
[gitmo/Moose.git] / lib / Moose / Meta / Attribute.pm
CommitLineData
c0e30cf5 1
2package Moose::Meta::Attribute;
3
4use strict;
5use warnings;
6
78cd1d3b 7use Scalar::Util 'blessed', 'weaken', 'reftype';
a15dff8d 8use Carp 'confess';
9
4fd69d6c 10our $VERSION = '0.08';
78cd1d3b 11
8ee73eeb 12use Moose::Meta::Method::Accessor;
a3c7e2fe 13use Moose::Util::TypeConstraints ();
bc1e29b5 14
c0e30cf5 15use base 'Class::MOP::Attribute';
16
452bac1b 17# options which are not directly used
18# but we store them for metadata purposes
98aae381 19__PACKAGE__->meta->add_attribute('isa' => (reader => '_isa_metadata'));
20__PACKAGE__->meta->add_attribute('does' => (reader => '_does_metadata'));
21__PACKAGE__->meta->add_attribute('is' => (reader => '_is_metadata'));
452bac1b 22
23# these are actual options for the attrs
1a563243 24__PACKAGE__->meta->add_attribute('required' => (reader => 'is_required' ));
25__PACKAGE__->meta->add_attribute('lazy' => (reader => 'is_lazy' ));
26__PACKAGE__->meta->add_attribute('coerce' => (reader => 'should_coerce' ));
27__PACKAGE__->meta->add_attribute('weak_ref' => (reader => 'is_weak_ref' ));
28__PACKAGE__->meta->add_attribute('auto_deref' => (reader => 'should_auto_deref'));
82168dbb 29__PACKAGE__->meta->add_attribute('type_constraint' => (
30 reader => 'type_constraint',
31 predicate => 'has_type_constraint',
32));
8c9d74e7 33__PACKAGE__->meta->add_attribute('trigger' => (
34 reader => 'trigger',
35 predicate => 'has_trigger',
36));
452bac1b 37__PACKAGE__->meta->add_attribute('handles' => (
38 reader => 'handles',
39 predicate => 'has_handles',
40));
82168dbb 41
78cd1d3b 42sub new {
43 my ($class, $name, %options) = @_;
1d768fb1 44 $class->_process_options($name, \%options);
98aae381 45 return $class->SUPER::new($name, %options);
1d768fb1 46}
47
ce0e8d63 48sub clone_and_inherit_options {
49 my ($self, %options) = @_;
50 # you can change default, required and coerce
51 my %actual_options;
52 foreach my $legal_option (qw(default coerce required)) {
53 if (exists $options{$legal_option}) {
54 $actual_options{$legal_option} = $options{$legal_option};
55 delete $options{$legal_option};
56 }
57 }
fcb7afc2 58 # isa can be changed, but only if the
59 # new type is a subtype
ce0e8d63 60 if ($options{isa}) {
61 my $type_constraint;
62 if (blessed($options{isa}) && $options{isa}->isa('Moose::Meta::TypeConstraint')) {
63 $type_constraint = $options{isa};
64 }
65 else {
66 $type_constraint = Moose::Util::TypeConstraints::find_type_constraint($options{isa});
67 (defined $type_constraint)
68 || confess "Could not find the type constraint '" . $options{isa} . "'";
69 }
2a0f3bd3 70 # NOTE:
71 # check here to see if the new type
72 # is a subtype of the old one
ce0e8d63 73 ($type_constraint->is_subtype_of($self->type_constraint->name))
74 || confess "New type constraint setting must be a subtype of inherited one"
2a0f3bd3 75 # iff we have a type constraint that is ...
ce0e8d63 76 if $self->has_type_constraint;
2a0f3bd3 77 # then we use it :)
ce0e8d63 78 $actual_options{type_constraint} = $type_constraint;
79 delete $options{isa};
80 }
81 (scalar keys %options == 0)
82 || confess "Illegal inherited options => (" . (join ', ' => keys %options) . ")";
83 $self->clone(%actual_options);
1d768fb1 84}
85
86sub _process_options {
87 my ($class, $name, $options) = @_;
452bac1b 88
1d768fb1 89 if (exists $options->{is}) {
90 if ($options->{is} eq 'ro') {
91 $options->{reader} = $name;
92 (!exists $options->{trigger})
8c9d74e7 93 || confess "Cannot have a trigger on a read-only attribute";
78cd1d3b 94 }
1d768fb1 95 elsif ($options->{is} eq 'rw') {
452bac1b 96 $options->{accessor} = $name;
98aae381 97 ((reftype($options->{trigger}) || '') eq 'CODE')
98 || confess "Trigger must be a CODE ref"
99 if exists $options->{trigger};
452bac1b 100 }
101 else {
102 confess "I do not understand this option (is => " . $options->{is} . ")"
78cd1d3b 103 }
104 }
105
1d768fb1 106 if (exists $options->{isa}) {
02a0fb52 107
1d768fb1 108 if (exists $options->{does}) {
109 if (eval { $options->{isa}->can('does') }) {
110 ($options->{isa}->does($options->{does}))
02a0fb52 111 || confess "Cannot have an isa option and a does option if the isa does not do the does";
112 }
7eaef7ad 113 else {
114 confess "Cannot have an isa option which cannot ->does()";
115 }
02a0fb52 116 }
117
78cd1d3b 118 # allow for anon-subtypes here ...
1d768fb1 119 if (blessed($options->{isa}) && $options->{isa}->isa('Moose::Meta::TypeConstraint')) {
120 $options->{type_constraint} = $options->{isa};
78cd1d3b 121 }
122 else {
c07af9d2 123
1d768fb1 124 if ($options->{isa} =~ /\|/) {
125 my @type_constraints = split /\s*\|\s*/ => $options->{isa};
126 $options->{type_constraint} = Moose::Util::TypeConstraints::create_type_constraint_union(
c07af9d2 127 @type_constraints
78cd1d3b 128 );
c07af9d2 129 }
130 else {
131 # otherwise assume it is a constraint
1d768fb1 132 my $constraint = Moose::Util::TypeConstraints::find_type_constraint($options->{isa});
c07af9d2 133 # if the constraing it not found ....
134 unless (defined $constraint) {
135 # assume it is a foreign class, and make
136 # an anon constraint for it
137 $constraint = Moose::Util::TypeConstraints::subtype(
138 'Object',
1d768fb1 139 Moose::Util::TypeConstraints::where { $_->isa($options->{isa}) }
c07af9d2 140 );
141 }
1d768fb1 142 $options->{type_constraint} = $constraint;
c07af9d2 143 }
78cd1d3b 144 }
145 }
1d768fb1 146 elsif (exists $options->{does}) {
02a0fb52 147 # allow for anon-subtypes here ...
1d768fb1 148 if (blessed($options->{does}) && $options->{does}->isa('Moose::Meta::TypeConstraint')) {
149 $options->{type_constraint} = $options->{isa};
02a0fb52 150 }
151 else {
152 # otherwise assume it is a constraint
1d768fb1 153 my $constraint = Moose::Util::TypeConstraints::find_type_constraint($options->{does});
02a0fb52 154 # if the constraing it not found ....
155 unless (defined $constraint) {
156 # assume it is a foreign class, and make
157 # an anon constraint for it
158 $constraint = Moose::Util::TypeConstraints::subtype(
159 'Role',
1d768fb1 160 Moose::Util::TypeConstraints::where { $_->does($options->{does}) }
02a0fb52 161 );
162 }
1d768fb1 163 $options->{type_constraint} = $constraint;
02a0fb52 164 }
165 }
78cd1d3b 166
1d768fb1 167 if (exists $options->{coerce} && $options->{coerce}) {
168 (exists $options->{type_constraint})
3ec7b7a3 169 || confess "You cannot have coercion without specifying a type constraint";
4b598ea3 170 confess "You cannot have a weak reference to a coerced value"
1d768fb1 171 if $options->{weak_ref};
ca01a97b 172 }
78cd1d3b 173
536f0b17 174 if (exists $options->{auto_deref} && $options->{auto_deref}) {
175 (exists $options->{type_constraint})
176 || confess "You cannot auto-dereference without specifying a type constraint";
94b8bbb8 177 ($options->{type_constraint}->is_a_type_of('ArrayRef') ||
178 $options->{type_constraint}->is_a_type_of('HashRef'))
536f0b17 179 || confess "You cannot auto-dereference anything other than a ArrayRef or HashRef";
180 }
181
1d768fb1 182 if (exists $options->{lazy} && $options->{lazy}) {
183 (exists $options->{default})
ca01a97b 184 || confess "You cannot have lazy attribute without specifying a default value for it";
1d768fb1 185 }
78cd1d3b 186}
c0e30cf5 187
d500266f 188sub initialize_instance_slot {
ddd0ec20 189 my ($self, $meta_instance, $instance, $params) = @_;
d500266f 190 my $init_arg = $self->init_arg();
191 # try to fetch the init arg from the %params ...
ddd0ec20 192
d500266f 193 my $val;
194 if (exists $params->{$init_arg}) {
195 $val = $params->{$init_arg};
196 }
197 else {
198 # skip it if it's lazy
199 return if $self->is_lazy;
200 # and die if it's required and doesn't have a default value
201 confess "Attribute (" . $self->name . ") is required"
202 if $self->is_required && !$self->has_default;
203 }
ddd0ec20 204
d500266f 205 # if nothing was in the %params, we can use the
206 # attribute's default value (if it has one)
207 if (!defined $val && $self->has_default) {
208 $val = $self->default($instance);
209 }
210 if (defined $val) {
211 if ($self->has_type_constraint) {
c07af9d2 212 my $type_constraint = $self->type_constraint;
213 if ($self->should_coerce && $type_constraint->has_coercion) {
0a5bd159 214 $val = $type_constraint->coerce($val);
d500266f 215 }
c07af9d2 216 (defined($type_constraint->check($val)))
217 || confess "Attribute (" .
218 $self->name .
8449e6e7 219 ") does not pass the type constraint (" .
c07af9d2 220 $type_constraint->name .
221 ") with '$val'";
d500266f 222 }
223 }
ddd0ec20 224
ac1ef2f9 225 $meta_instance->set_slot_value($instance, $self->name, $val);
226 $meta_instance->weaken_slot_value($instance, $self->name)
227 if ref $val && $self->is_weak_ref;
d500266f 228}
229
9e93dd19 230## Accessor inline subroutines
231
67ad26d9 232sub _inline_check_constraint {
ac1ef2f9 233 my ($self, $value) = @_;
67ad26d9 234 return '' unless $self->has_type_constraint;
235
236 # FIXME - remove 'unless defined($value) - constraint Undef
237 return sprintf <<'EOF', $value, $value, $value, $value
43123819 238defined($type_constraint->(%s))
8449e6e7 239 || confess "Attribute (" . $attr->name . ") does not pass the type constraint ("
67ad26d9 240 . $attr->type_constraint->name . ") with " . (defined(%s) ? "'%s'" : "undef")
241 if defined(%s);
242EOF
243}
244
9e93dd19 245sub _inline_check_coercion {
246 my $self = shift;
247 return '' unless $self->should_coerce;
0a5bd159 248 return 'my $val = $attr->type_constraint->coerce($_[1]);'
9e93dd19 249}
250
251sub _inline_check_required {
252 my $self = shift;
253 return '' unless $self->is_required;
254 return 'defined($_[1]) || confess "Attribute ($attr_name) is required, so cannot be set to undef";'
255}
256
257sub _inline_check_lazy {
258 my $self = shift;
259 return '' unless $self->is_lazy;
4fd69d6c 260 if ($self->has_type_constraint) {
261 # NOTE:
262 # this could probably be cleaned
263 # up and streamlined a little more
264 return 'unless (exists $_[0]->{$attr_name}) {' .
265 ' if ($attr->has_default) {' .
266 ' my $default = $attr->default($_[0]);' .
43123819 267 ' (defined($type_constraint->($default)))' .
4fd69d6c 268 ' || confess "Attribute (" . $attr->name . ") does not pass the type constraint ("' .
269 ' . $attr->type_constraint->name . ") with " . (defined($default) ? "\'$default\'" : "undef")' .
270 ' if defined($default);' .
271 ' $_[0]->{$attr_name} = $default; ' .
272 ' }' .
273 ' else {' .
274 ' $_[0]->{$attr_name} = undef;' .
275 ' }' .
276 '}';
277 }
9e93dd19 278 return '$_[0]->{$attr_name} = ($attr->has_default ? $attr->default($_[0]) : undef)'
279 . 'unless exists $_[0]->{$attr_name};';
280}
281
282
67ad26d9 283sub _inline_store {
ac1ef2f9 284 my ($self, $instance, $value) = @_;
67ad26d9 285
286 my $mi = $self->associated_class->get_meta_instance;
ac1ef2f9 287 my $slot_name = sprintf "'%s'", $self->slots;
67ad26d9 288
ac1ef2f9 289 my $code = $mi->inline_set_slot_value($instance, $slot_name, $value) . ";";
290 $code .= $mi->inline_weaken_slot_value($instance, $slot_name, $value) . ";"
291 if $self->is_weak_ref;
292 return $code;
8a7a9c53 293}
294
67ad26d9 295sub _inline_trigger {
ac1ef2f9 296 my ($self, $instance, $value) = @_;
67ad26d9 297 return '' unless $self->has_trigger;
298 return sprintf('$attr->trigger->(%s, %s, $attr);', $instance, $value);
8a7a9c53 299}
300
ddd0ec20 301sub _inline_get {
ac1ef2f9 302 my ($self, $instance) = @_;
ddd0ec20 303
304 my $mi = $self->associated_class->get_meta_instance;
ac1ef2f9 305 my $slot_name = sprintf "'%s'", $self->slots;
ddd0ec20 306
ac1ef2f9 307 return $mi->inline_get_slot_value($instance, $slot_name);
ddd0ec20 308}
309
1a563243 310sub _inline_auto_deref {
311 my ( $self, $ref_value ) = @_;
312
313 return $ref_value unless $self->should_auto_deref;
314
94b8bbb8 315 my $type_constraint = $self->type_constraint;
1a563243 316
536f0b17 317 my $sigil;
94b8bbb8 318 if ($type_constraint->is_a_type_of('ArrayRef')) {
1a563243 319 $sigil = '@';
536f0b17 320 }
94b8bbb8 321 elsif ($type_constraint->is_a_type_of('HashRef')) {
1a563243 322 $sigil = '%';
536f0b17 323 }
324 else {
94b8bbb8 325 confess "Can not auto de-reference the type constraint '" . $type_constraint->name . "'";
1a563243 326 }
327
328 "(wantarray() ? $sigil\{ ( $ref_value ) || return } : ( $ref_value ) )";
329}
330
a15dff8d 331sub generate_accessor_method {
67ad26d9 332 my ($attr, $attr_name) = @_;
333 my $value_name = $attr->should_coerce ? '$val' : '$_[1]';
334 my $mi = $attr->associated_class->get_meta_instance;
ac1ef2f9 335 my $slot_name = sprintf "'%s'", $attr->slots;
67ad26d9 336 my $inv = '$_[0]';
ca01a97b 337 my $code = 'sub { '
338 . 'if (scalar(@_) == 2) {'
9e93dd19 339 . $attr->_inline_check_required
340 . $attr->_inline_check_coercion
ac1ef2f9 341 . $attr->_inline_check_constraint($value_name)
342 . $attr->_inline_store($inv, $value_name)
343 . $attr->_inline_trigger($inv, $value_name)
ca01a97b 344 . ' }'
9e93dd19 345 . $attr->_inline_check_lazy
536f0b17 346 . 'return ' . $attr->_inline_auto_deref($attr->_inline_get($inv))
ca01a97b 347 . ' }';
43123819 348
349 # NOTE:
350 # set up the environment
351 my $type_constraint = $attr->type_constraint
352 ? $attr->type_constraint->_compiled_type_constraint
353 : undef;
354
ca01a97b 355 my $sub = eval $code;
67ad26d9 356 confess "Could not create accessor for '$attr_name' because $@ \n code: $code" if $@;
ca01a97b 357 return $sub;
a15dff8d 358}
359
360sub generate_writer_method {
67ad26d9 361 my ($attr, $attr_name) = @_;
362 my $value_name = $attr->should_coerce ? '$val' : '$_[1]';
363 my $inv = '$_[0]';
ca01a97b 364 my $code = 'sub { '
9e93dd19 365 . $attr->_inline_check_required
366 . $attr->_inline_check_coercion
ac1ef2f9 367 . $attr->_inline_check_constraint($value_name)
368 . $attr->_inline_store($inv, $value_name)
369 . $attr->_inline_trigger($inv, $value_name)
ca01a97b 370 . ' }';
43123819 371
372 # NOTE:
373 # set up the environment
374 my $type_constraint = $attr->type_constraint
375 ? $attr->type_constraint->_compiled_type_constraint
376 : undef;
377
ca01a97b 378 my $sub = eval $code;
379 confess "Could not create writer for '$attr_name' because $@ \n code: $code" if $@;
380 return $sub;
a15dff8d 381}
c0e30cf5 382
d7f17ebb 383sub generate_reader_method {
9e93dd19 384 my $attr = shift;
385 my $attr_name = $attr->slots;
ca01a97b 386 my $code = 'sub {'
387 . 'confess "Cannot assign a value to a read-only accessor" if @_ > 1;'
9e93dd19 388 . $attr->_inline_check_lazy
389 . 'return ' . $attr->_inline_auto_deref( '$_[0]->{$attr_name}' ) . ';'
ca01a97b 390 . '}';
391 my $sub = eval $code;
392 confess "Could not create reader for '$attr_name' because $@ \n code: $code" if $@;
393 return $sub;
d7f17ebb 394}
395
452bac1b 396sub install_accessors {
397 my $self = shift;
398 $self->SUPER::install_accessors(@_);
399
400 if ($self->has_handles) {
401
402 # NOTE:
403 # Here we canonicalize the 'handles' option
404 # this will sort out any details and always
405 # return an hash of methods which we want
406 # to delagate to, see that method for details
407 my %handles = $self->_canonicalize_handles();
408
409 # find the name of the accessor for this attribute
410 my $accessor_name = $self->reader || $self->accessor;
411 (defined $accessor_name)
412 || confess "You cannot install delegation without a reader or accessor for the attribute";
413
414 # make sure we handle HASH accessors correctly
415 ($accessor_name) = keys %{$accessor_name}
416 if ref($accessor_name) eq 'HASH';
417
418 # install the delegation ...
419 my $associated_class = $self->associated_class;
420 foreach my $handle (keys %handles) {
421 my $method_to_call = $handles{$handle};
422
423 (!$associated_class->has_method($handle))
424 || confess "You cannot overwrite a locally defined method ($handle) with a delegation";
425
426 if ((reftype($method_to_call) || '') eq 'CODE') {
427 $associated_class->add_method($handle => $method_to_call);
428 }
429 else {
430 $associated_class->add_method($handle => sub {
b805c70c 431 # FIXME
432 # we should check for lack of
433 # a callable return value from
434 # the accessor here
452bac1b 435 ((shift)->$accessor_name())->$method_to_call(@_);
436 });
437 }
438 }
439 }
440
441 return;
442}
443
98aae381 444# private methods to help delegation ...
445
452bac1b 446sub _canonicalize_handles {
447 my $self = shift;
448 my $handles = $self->handles;
449 if (ref($handles) eq 'HASH') {
450 return %{$handles};
451 }
452 elsif (ref($handles) eq 'ARRAY') {
453 return map { $_ => $_ } @{$handles};
454 }
455 elsif (ref($handles) eq 'Regexp') {
456 ($self->has_type_constraint)
457 || confess "Cannot delegate methods based on a RegExpr without a type constraint (isa)";
458 return map { ($_ => $_) }
459 grep { $handles } $self->_get_delegate_method_list;
460 }
461 elsif (ref($handles) eq 'CODE') {
462 return $handles->($self, $self->_find_delegate_metaclass);
463 }
464 else {
465 confess "Unable to canonicalize the 'handles' option with $handles";
466 }
467}
468
469sub _find_delegate_metaclass {
470 my $self = shift;
98aae381 471 if (my $class = $self->_isa_metadata) {
452bac1b 472 # if the class does have
473 # a meta method, use it
474 return $class->meta if $class->can('meta');
475 # otherwise we might be
476 # dealing with a non-Moose
477 # class, and need to make
478 # our own metaclass
479 return Moose::Meta::Class->initialize($class);
480 }
98aae381 481 elsif (my $role = $self->_does_metadata) {
452bac1b 482 # our role will always have
483 # a meta method
98aae381 484 return $role->meta;
452bac1b 485 }
486 else {
487 confess "Cannot find delegate metaclass for attribute " . $self->name;
488 }
489}
490
491sub _get_delegate_method_list {
492 my $self = shift;
493 my $meta = $self->_find_delegate_metaclass;
494 if ($meta->isa('Class::MOP::Class')) {
093b12c2 495 return map { $_->{name} } # NOTE: !never! delegate &meta
496 grep { $_->{class} ne 'Moose::Object' && $_->{name} ne 'meta' }
452bac1b 497 $meta->compute_all_applicable_methods;
498 }
499 elsif ($meta->isa('Moose::Meta::Role')) {
500 return $meta->get_method_list;
501 }
502 else {
503 confess "Unable to recognize the delegate metaclass '$meta'";
504 }
505}
506
c0e30cf5 5071;
508
509__END__
510
511=pod
512
513=head1 NAME
514
6ba6d68c 515Moose::Meta::Attribute - The Moose attribute metaclass
c0e30cf5 516
517=head1 DESCRIPTION
518
e522431d 519This is a subclass of L<Class::MOP::Attribute> with Moose specific
6ba6d68c 520extensions.
521
522For the most part, the only time you will ever encounter an
523instance of this class is if you are doing some serious deep
524introspection. To really understand this class, you need to refer
525to the L<Class::MOP::Attribute> documentation.
e522431d 526
c0e30cf5 527=head1 METHODS
528
6ba6d68c 529=head2 Overridden methods
530
531These methods override methods in L<Class::MOP::Attribute> and add
532Moose specific features. You can safely assume though that they
533will behave just as L<Class::MOP::Attribute> does.
534
c0e30cf5 535=over 4
536
537=item B<new>
538
d500266f 539=item B<initialize_instance_slot>
540
a15dff8d 541=item B<generate_accessor_method>
542
543=item B<generate_writer_method>
544
d7f17ebb 545=item B<generate_reader_method>
546
452bac1b 547=item B<install_accessors>
548
a15dff8d 549=back
550
6ba6d68c 551=head2 Additional Moose features
552
8449e6e7 553Moose attributes support type-constraint checking, weak reference
6ba6d68c 554creation and type coercion.
555
a15dff8d 556=over 4
557
9e93dd19 558=item B<clone_and_inherit_options>
559
560This is to support the C<has '+foo'> feature, it clones an attribute
561from a superclass and allows a very specific set of changes to be made
562to the attribute.
563
a15dff8d 564=item B<has_type_constraint>
565
6ba6d68c 566Returns true if this meta-attribute has a type constraint.
567
a15dff8d 568=item B<type_constraint>
569
6ba6d68c 570A read-only accessor for this meta-attribute's type constraint. For
571more information on what you can do with this, see the documentation
572for L<Moose::Meta::TypeConstraint>.
a15dff8d 573
452bac1b 574=item B<has_handles>
575
576Returns true if this meta-attribute performs delegation.
577
578=item B<handles>
579
580This returns the value which was passed into the handles option.
581
6ba6d68c 582=item B<is_weak_ref>
a15dff8d 583
02a0fb52 584Returns true if this meta-attribute produces a weak reference.
4b598ea3 585
ca01a97b 586=item B<is_required>
587
02a0fb52 588Returns true if this meta-attribute is required to have a value.
ca01a97b 589
590=item B<is_lazy>
591
02a0fb52 592Returns true if this meta-attribute should be initialized lazily.
ca01a97b 593
594NOTE: lazy attributes, B<must> have a C<default> field set.
595
34a66aa3 596=item B<should_coerce>
4b598ea3 597
02a0fb52 598Returns true if this meta-attribute should perform type coercion.
6ba6d68c 599
536f0b17 600=item B<should_auto_deref>
601
602Returns true if this meta-attribute should perform automatic
603auto-dereferencing.
604
605NOTE: This can only be done for attributes whose type constraint is
606either I<ArrayRef> or I<HashRef>.
607
8c9d74e7 608=item B<has_trigger>
609
02a0fb52 610Returns true if this meta-attribute has a trigger set.
611
8c9d74e7 612=item B<trigger>
613
02a0fb52 614This is a CODE reference which will be executed every time the
615value of an attribute is assigned. The CODE ref will get two values,
616the invocant and the new value. This can be used to handle I<basic>
617bi-directional relations.
618
c0e30cf5 619=back
620
621=head1 BUGS
622
623All complex software has bugs lurking in it, and this module is no
624exception. If you find a bug please either email me, or add the bug
625to cpan-RT.
626
c0e30cf5 627=head1 AUTHOR
628
629Stevan Little E<lt>stevan@iinteractive.comE<gt>
630
98aae381 631Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
632
c0e30cf5 633=head1 COPYRIGHT AND LICENSE
634
635Copyright 2006 by Infinity Interactive, Inc.
636
637L<http://www.iinteractive.com>
638
639This library is free software; you can redistribute it and/or modify
640it under the same terms as Perl itself.
641
8a7a9c53 642=cut