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