ChangeLog and adding yuval to authors everywhere
[gitmo/Class-MOP.git] / lib / Class / MOP / Attribute.pm
CommitLineData
8b978dd5 1
2package Class::MOP::Attribute;
3
4use strict;
5use warnings;
6
2eb717d5 7use Carp 'confess';
9ec169fe 8use Scalar::Util 'blessed', 'reftype', 'weaken';
2eb717d5 9
b679e644 10our $VERSION = '0.10';
8b978dd5 11
727919c5 12sub meta {
13 require Class::MOP::Class;
aa448b16 14 Class::MOP::Class->initialize(blessed($_[0]) || $_[0]);
727919c5 15}
2eb717d5 16
727919c5 17# NOTE: (meta-circularity)
16e960bd 18# This method will be replaced in the
727919c5 19# boostrap section of Class::MOP, by
20# a new version which uses the
21# &Class::MOP::Class::construct_instance
22# method to build an attribute meta-object
23# which itself is described with attribute
24# meta-objects.
25# - Ain't meta-circularity grand? :)
8b978dd5 26sub new {
27 my $class = shift;
28 my $name = shift;
29 my %options = @_;
30
cbd9f942 31 (defined $name && $name)
8b978dd5 32 || confess "You must provide a name for the attribute";
5659d76e 33 $options{init_arg} = $name
34 if not exists $options{init_arg};
2eb717d5 35
8b978dd5 36 bless {
c50c603e 37 name => $name,
38 accessor => $options{accessor},
39 reader => $options{reader},
40 writer => $options{writer},
41 predicate => $options{predicate},
42 init_arg => $options{init_arg},
9ec169fe 43 default => $options{default},
44 # keep a weakened link to the
45 # class we are associated with
46 associated_class => undef,
8b978dd5 47 } => $class;
48}
49
7b31baf4 50# NOTE:
5659d76e 51# this is a primative (and kludgy) clone operation
16e960bd 52# for now, it will be replaced in the Class::MOP
5659d76e 53# bootstrap with a proper one, however we know
54# that this one will work fine for now.
55sub clone {
56 my $self = shift;
57 my %options = @_;
58 (blessed($self))
59 || confess "Can only clone an instance";
60 return bless { %{$self}, %options } => blessed($self);
61}
62
bd4e03f9 63sub initialize_instance_slot {
f892c0f0 64 my ($self, $meta_instance, $instance, $params) = @_;
291073fc 65 my $init_arg = $self->{init_arg};
bd4e03f9 66 # try to fetch the init arg from the %params ...
67 my $val;
68 $val = $params->{$init_arg} if exists $params->{$init_arg};
69 # if nothing was in the %params, we can use the
70 # attribute's default value (if it has one)
bb8dacfa 71 if (!defined $val && defined $self->{default}) {
2d711cc8 72 $val = $self->default($instance);
73 }
43715282 74 $meta_instance->set_slot_value($instance, $self->name, $val);
bd4e03f9 75}
76
5659d76e 77# NOTE:
7b31baf4 78# the next bunch of methods will get bootstrapped
79# away in the Class::MOP bootstrapping section
80
c50c603e 81sub name { $_[0]->{name} }
82
7b31baf4 83sub associated_class { $_[0]->{associated_class} }
84
727919c5 85sub has_accessor { defined($_[0]->{accessor}) ? 1 : 0 }
86sub has_reader { defined($_[0]->{reader}) ? 1 : 0 }
87sub has_writer { defined($_[0]->{writer}) ? 1 : 0 }
c50c603e 88sub has_predicate { defined($_[0]->{predicate}) ? 1 : 0 }
727919c5 89sub has_init_arg { defined($_[0]->{init_arg}) ? 1 : 0 }
90sub has_default { defined($_[0]->{default}) ? 1 : 0 }
c50c603e 91
92sub accessor { $_[0]->{accessor} }
93sub reader { $_[0]->{reader} }
94sub writer { $_[0]->{writer} }
95sub predicate { $_[0]->{predicate} }
96sub init_arg { $_[0]->{init_arg} }
97
7b31baf4 98# end bootstrapped away method section.
99# (all methods below here are kept intact)
100
c0cbf4d9 101sub is_default_a_coderef {
102 (reftype($_[0]->{default}) && reftype($_[0]->{default}) eq 'CODE')
103}
104
c50c603e 105sub default {
c0cbf4d9 106 my ($self, $instance) = @_;
107 if ($instance && $self->is_default_a_coderef) {
727919c5 108 # if the default is a CODE ref, then
109 # we pass in the instance and default
110 # can return a value based on that
111 # instance. Somewhat crude, but works.
c0cbf4d9 112 return $self->{default}->($instance);
c50c603e 113 }
114 $self->{default};
115}
8b978dd5 116
c57c8b10 117# slots
118
119sub slots { (shift)->name }
120
9ec169fe 121# class association
727919c5 122
9ec169fe 123sub attach_to_class {
124 my ($self, $class) = @_;
125 (blessed($class) && $class->isa('Class::MOP::Class'))
126 || confess "You must pass a Class::MOP::Class instance (or a subclass)";
127 weaken($self->{associated_class} = $class);
128}
129
130sub detach_from_class {
131 my $self = shift;
132 $self->{associated_class} = undef;
133}
134
16e960bd 135## Slot management
136
137sub set_value {
138 my ( $self, $instance, $value ) = @_;
139
140 Class::MOP::Class->initialize(Scalar::Util::blessed($instance))
141 ->get_meta_instance
142 ->set_slot_value( $instance, $self->name, $value );
143}
144
145sub get_value {
146 my ( $self, $instance ) = @_;
147
148 Class::MOP::Class->initialize(Scalar::Util::blessed($instance))
149 ->get_meta_instance
150 ->get_slot_value( $instance, $self->name );
151}
152
9ec169fe 153## Method generation helpers
154
155sub generate_accessor_method {
16e960bd 156 my $attr = shift;
43715282 157 return sub {
16e960bd 158 $attr->set_value( $_[0], $_[1] ) if scalar(@_) == 2;
159 $attr->get_value( $_[0] );
43715282 160 };
9ec169fe 161}
162
c0cbf4d9 163sub generate_accessor_method_inline {
164 my $self = shift;
165 my $attr_name = $self->name;
166 my $meta_instance = $self->associated_class->instance_metaclass;
167
168 my $code = eval 'sub {'
4d47b77f 169 . $meta_instance->inline_set_slot_value('$_[0]', "'$attr_name'", '$_[1]') . ' if scalar(@_) == 2; '
170 . $meta_instance->inline_get_slot_value('$_[0]', "'$attr_name'")
c0cbf4d9 171 . '}';
172 confess "Could not generate inline accessor because : $@" if $@;
173
174 return $code;
175}
176
9ec169fe 177sub generate_reader_method {
16e960bd 178 my $attr = shift;
43715282 179 return sub {
180 confess "Cannot assign a value to a read-only accessor" if @_ > 1;
16e960bd 181 $attr->get_value( $_[0] );
43715282 182 };
9ec169fe 183}
184
c0cbf4d9 185sub generate_reader_method_inline {
186 my $self = shift;
187 my $attr_name = $self->name;
188 my $meta_instance = $self->associated_class->instance_metaclass;
189
190 my $code = eval 'sub {'
191 . 'confess "Cannot assign a value to a read-only accessor" if @_ > 1;'
4d47b77f 192 . $meta_instance->inline_get_slot_value('$_[0]', "'$attr_name'")
c0cbf4d9 193 . '}';
194 confess "Could not generate inline accessor because : $@" if $@;
195
196 return $code;
197}
198
9ec169fe 199sub generate_writer_method {
16e960bd 200 my $attr = shift;
201 return sub {
202 $attr->set_value( $_[0], $_[1] );
43715282 203 };
9ec169fe 204}
205
c0cbf4d9 206sub generate_writer_method_inline {
207 my $self = shift;
208 my $attr_name = $self->name;
209 my $meta_instance = $self->associated_class->instance_metaclass;
210
211 my $code = eval 'sub {'
4d47b77f 212 . $meta_instance->inline_set_slot_value('$_[0]', "'$attr_name'", '$_[1]')
c0cbf4d9 213 . '}';
214 confess "Could not generate inline accessor because : $@" if $@;
215
216 return $code;
217}
218
9ec169fe 219sub generate_predicate_method {
08388f17 220 my $self = shift;
b880e0de 221 my $attr_name = $self->name;
43715282 222 return sub {
223 defined Class::MOP::Class->initialize(Scalar::Util::blessed($_[0]))
224 ->get_meta_instance
225 ->get_slot_value($_[0], $attr_name) ? 1 : 0;
226 };
9ec169fe 227}
228
c0cbf4d9 229sub generate_predicate_method_inline {
230 my $self = shift;
231 my $attr_name = $self->name;
232 my $meta_instance = $self->associated_class->instance_metaclass;
233
234 my $code = eval 'sub {'
4d47b77f 235 . 'defined ' . $meta_instance->inline_get_slot_value('$_[0]', "'$attr_name'") . ' ? 1 : 0'
c0cbf4d9 236 . '}';
237 confess "Could not generate inline accessor because : $@" if $@;
238
239 return $code;
240}
241
9ec169fe 242sub process_accessors {
c0cbf4d9 243 my ($self, $type, $accessor, $generate_as_inline_methods) = @_;
013b1897 244 if (reftype($accessor)) {
245 (reftype($accessor) eq 'HASH')
246 || confess "bad accessor/reader/writer/predicate format, must be a HASH ref";
4d47b77f 247 my ($name, $method) = %{$accessor};
a4258ffd 248 return ($name, Class::MOP::Attribute::Accessor->wrap($method));
2eb717d5 249 }
9ec169fe 250 else {
c0cbf4d9 251 my $inline_me = ($generate_as_inline_methods && $self->associated_class->instance_metaclass->is_inlinable);
252 my $generator = $self->can('generate_' . $type . '_method' . ($inline_me ? '_inline' : ''));
9ec169fe 253 ($generator)
254 || confess "There is no method generator for the type='$type'";
255 if (my $method = $self->$generator($self->name)) {
a4258ffd 256 return ($accessor => Class::MOP::Attribute::Accessor->wrap($method));
9ec169fe 257 }
343203ee 258 confess "Could not create the '$type' method for " . $self->name . " because : $@";
9ec169fe 259 }
260}
261
262sub install_accessors {
c0cbf4d9 263 my $self = shift;
264 my $inline = shift;
265 my $class = $self->associated_class;
c50c603e 266
9ec169fe 267 $class->add_method(
c0cbf4d9 268 $self->process_accessors('accessor' => $self->accessor(), $inline)
9ec169fe 269 ) if $self->has_accessor();
270
271 $class->add_method(
c0cbf4d9 272 $self->process_accessors('reader' => $self->reader(), $inline)
9ec169fe 273 ) if $self->has_reader();
274
275 $class->add_method(
c0cbf4d9 276 $self->process_accessors('writer' => $self->writer(), $inline)
9ec169fe 277 ) if $self->has_writer();
278
279 $class->add_method(
c0cbf4d9 280 $self->process_accessors('predicate' => $self->predicate(), $inline)
9ec169fe 281 ) if $self->has_predicate();
c0cbf4d9 282
9ec169fe 283 return;
2eb717d5 284}
285
b51af7f9 286{
287 my $_remove_accessor = sub {
288 my ($accessor, $class) = @_;
c50c603e 289 if (reftype($accessor) && reftype($accessor) eq 'HASH') {
290 ($accessor) = keys %{$accessor};
291 }
b51af7f9 292 my $method = $class->get_method($accessor);
293 $class->remove_method($accessor)
2eb717d5 294 if (blessed($method) && $method->isa('Class::MOP::Attribute::Accessor'));
b51af7f9 295 };
c50c603e 296
b51af7f9 297 sub remove_accessors {
9ec169fe 298 my $self = shift;
299 $_remove_accessor->($self->accessor(), $self->associated_class()) if $self->has_accessor();
300 $_remove_accessor->($self->reader(), $self->associated_class()) if $self->has_reader();
301 $_remove_accessor->($self->writer(), $self->associated_class()) if $self->has_writer();
302 $_remove_accessor->($self->predicate(), $self->associated_class()) if $self->has_predicate();
b51af7f9 303 return;
304 }
305
8b978dd5 306}
307
2eb717d5 308package Class::MOP::Attribute::Accessor;
309
310use strict;
311use warnings;
312
727919c5 313use Class::MOP::Method;
314
2eb717d5 315our $VERSION = '0.01';
316
317our @ISA = ('Class::MOP::Method');
318
8b978dd5 3191;
320
321__END__
322
323=pod
324
325=head1 NAME
326
327Class::MOP::Attribute - Attribute Meta Object
328
329=head1 SYNOPSIS
330
331 Class::MOP::Attribute->new('$foo' => (
fe122940 332 accessor => 'foo', # dual purpose get/set accessor
333 predicate => 'has_foo' # predicate check for defined-ness
334 init_arg => '-foo', # class->new will look for a -foo key
335 default => 'BAR IS BAZ!' # if no -foo key is provided, use this
8b978dd5 336 ));
337
338 Class::MOP::Attribute->new('$.bar' => (
fe122940 339 reader => 'bar', # getter
340 writer => 'set_bar', # setter
341 predicate => 'has_bar' # predicate check for defined-ness
342 init_arg => ':bar', # class->new will look for a :bar key
8b978dd5 343 # no default value means it is undef
344 ));
345
346=head1 DESCRIPTION
347
fe122940 348The Attribute Protocol is almost entirely an invention of this module,
349and is completely optional to this MOP. This is because Perl 5 does not
350have consistent notion of what is an attribute of a class. There are
351so many ways in which this is done, and very few (if any) are
352easily discoverable by this module.
552e3d24 353
354So, all that said, this module attempts to inject some order into this
fe122940 355chaos, by introducing a consistent API which can be used to create
356object attributes.
552e3d24 357
358=head1 METHODS
359
360=head2 Creation
361
362=over 4
363
fe122940 364=item B<new ($name, ?%options)>
365
366An attribute must (at the very least), have a C<$name>. All other
a2e85e6c 367C<%options> are contained added as key-value pairs. Acceptable keys
fe122940 368are as follows:
369
370=over 4
371
372=item I<init_arg>
373
374This should be a string value representing the expected key in
375an initialization hash. For instance, if we have an I<init_arg>
376value of C<-foo>, then the following code will Just Work.
377
378 MyClass->meta->construct_instance(-foo => "Hello There");
379
7b31baf4 380In an init_arg is not assigned, it will automatically use the
381value of C<$name>.
382
fe122940 383=item I<default>
384
385The value of this key is the default value which
386C<Class::MOP::Class::construct_instance> will initialize the
387attribute to.
388
389B<NOTE:>
390If the value is a simple scalar (string or number), then it can
391be just passed as is. However, if you wish to initialize it with
392a HASH or ARRAY ref, then you need to wrap that inside a CODE
393reference, like so:
394
395 Class::MOP::Attribute->new('@foo' => (
396 default => sub { [] },
397 ));
398
399 # or ...
400
401 Class::MOP::Attribute->new('%foo' => (
402 default => sub { {} },
403 ));
404
405If you wish to initialize an attribute with a CODE reference
406itself, then you need to wrap that in a subroutine as well, like
407so:
408
409 Class::MOP::Attribute->new('&foo' => (
410 default => sub { sub { print "Hello World" } },
411 ));
412
413And lastly, if the value of your attribute is dependent upon
414some other aspect of the instance structure, then you can take
415advantage of the fact that when the I<default> value is a CODE
416reference, it is passed the raw (unblessed) instance structure
417as it's only argument. So you can do things like this:
418
419 Class::MOP::Attribute->new('$object_identity' => (
420 default => sub { Scalar::Util::refaddr($_[0]) },
421 ));
422
423This last feature is fairly limited as there is no gurantee of
424the order of attribute initializations, so you cannot perform
425any kind of dependent initializations. However, if this is
426something you need, you could subclass B<Class::MOP::Class> and
427this class to acheive it. However, this is currently left as
428an exercise to the reader :).
429
430=back
431
a2e85e6c 432The I<accessor>, I<reader>, I<writer> and I<predicate> keys can
fe122940 433contain either; the name of the method and an appropriate default
434one will be generated for you, B<or> a HASH ref containing exactly one
435key (which will be used as the name of the method) and one value,
436which should contain a CODE reference which will be installed as
437the method itself.
59e7697f 438
439=over 4
440
441=item I<accessor>
442
fe122940 443The I<accessor> is a standard perl-style read/write accessor. It will
444return the value of the attribute, and if a value is passed as an argument,
445it will assign that value to the attribute.
446
447B<NOTE:>
448This method will properly handle the following code, by assigning an
449C<undef> value to the attribute.
450
451 $object->set_something(undef);
452
59e7697f 453=item I<reader>
454
fe122940 455This is a basic read-only accessor, it will just return the value of
456the attribute.
457
59e7697f 458=item I<writer>
459
fe122940 460This is a basic write accessor, it accepts a single argument, and
461assigns that value to the attribute. This method does not intentially
462return a value, however perl will return the result of the last
463expression in the subroutine, which returns in this returning the
464same value that it was passed.
59e7697f 465
fe122940 466B<NOTE:>
467This method will properly handle the following code, by assigning an
468C<undef> value to the attribute.
59e7697f 469
fe122940 470 $object->set_something();
471
472=item I<predicate>
473
474This is a basic test to see if the value of the attribute is not
475C<undef>. It will return true (C<1>) if the attribute's value is
476defined, and false (C<0>) otherwise.
59e7697f 477
478=back
552e3d24 479
bd4e03f9 480=item B<clone (%options)>
481
482=item B<initialize_instance_slot ($instance, $params)>
483
552e3d24 484=back
485
16e960bd 486=head2 Value management
487
488=over 4
489
490=item set_value $instance, $value
491
492Set the value without going through the accessor. Note that this may be done to
493even attributes with just read only accessors.
494
495=item get_value $instance
496
497Return the value without going through the accessor. Note that this may be done
498even to attributes with just write only accessors.
499
500=back
501
552e3d24 502=head2 Informational
503
fe122940 504These are all basic read-only value accessors for the values
505passed into C<new>. I think they are pretty much self-explanitory.
506
552e3d24 507=over 4
508
509=item B<name>
510
511=item B<accessor>
512
513=item B<reader>
514
515=item B<writer>
516
c50c603e 517=item B<predicate>
518
552e3d24 519=item B<init_arg>
520
495af518 521=item B<is_default_a_coderef>
522
fe122940 523=item B<default (?$instance)>
524
525As noted in the documentation for C<new> above, if the I<default>
526value is a CODE reference, this accessor will pass a single additional
527argument C<$instance> into it and return the value.
552e3d24 528
c57c8b10 529=item B<slots>
530
531Returns a list of slots required by the attribute. This is usually
532just one, which is the name of the attribute.
533
552e3d24 534=back
535
536=head2 Informational predicates
537
a2e85e6c 538These are all basic predicate methods for the values passed into C<new>.
fe122940 539
552e3d24 540=over 4
541
542=item B<has_accessor>
543
552e3d24 544=item B<has_reader>
545
552e3d24 546=item B<has_writer>
547
c50c603e 548=item B<has_predicate>
549
552e3d24 550=item B<has_init_arg>
551
552e3d24 552=item B<has_default>
553
552e3d24 554=back
555
9ec169fe 556=head2 Class association
557
558=over 4
559
560=item B<associated_class>
561
562=item B<attach_to_class ($class)>
563
564=item B<detach_from_class>
565
2d711cc8 566=item B<slot_name>
567
568=item B<allocate_slots>
569
570=item B<deallocate_slots>
571
9ec169fe 572=back
573
552e3d24 574=head2 Attribute Accessor generation
575
576=over 4
577
9ec169fe 578=item B<install_accessors>
2eb717d5 579
580This allows the attribute to generate and install code for it's own
a2e85e6c 581I<accessor/reader/writer/predicate> methods. This is called by
fe122940 582C<Class::MOP::Class::add_attribute>.
2eb717d5 583
9ec169fe 584This method will call C<process_accessors> for each of the possible
585method types (accessor, reader, writer & predicate).
586
587=item B<process_accessors ($type, $value)>
588
589This takes a C<$type> (accessor, reader, writer or predicate), and
590a C<$value> (the value passed into the constructor for each of the
591different types). It will then either generate the method itself
592(using the C<generate_*_method> methods listed below) or it will
593use the custom method passed through the constructor.
594
595=over 4
596
08388f17 597=item B<generate_accessor_method>
9ec169fe 598
08388f17 599=item B<generate_predicate_method>
9ec169fe 600
08388f17 601=item B<generate_reader_method>
9ec169fe 602
08388f17 603=item B<generate_writer_method>
9ec169fe 604
605=back
606
495af518 607=over 4
608
609=item B<generate_accessor_method_inline>
610
611=item B<generate_predicate_method_inline>
612
613=item B<generate_reader_method_inline>
614
615=item B<generate_writer_method_inline>
616
617=back
618
9ec169fe 619=item B<remove_accessors>
2eb717d5 620
621This allows the attribute to remove the method for it's own
a2e85e6c 622I<accessor/reader/writer/predicate>. This is called by
fe122940 623C<Class::MOP::Class::remove_attribute>.
2eb717d5 624
625=back
626
627=head2 Introspection
628
629=over 4
552e3d24 630
2eb717d5 631=item B<meta>
552e3d24 632
fe122940 633This will return a B<Class::MOP::Class> instance which is related
634to this class.
635
636It should also be noted that B<Class::MOP> will actually bootstrap
637this module by installing a number of attribute meta-objects into
638it's metaclass. This will allow this class to reap all the benifits
639of the MOP when subclassing it.
640
552e3d24 641=back
642
1a09d9cc 643=head1 AUTHORS
8b978dd5 644
a2e85e6c 645Stevan Little E<lt>stevan@iinteractive.comE<gt>
8b978dd5 646
1a09d9cc 647Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
648
8b978dd5 649=head1 COPYRIGHT AND LICENSE
650
651Copyright 2006 by Infinity Interactive, Inc.
652
653L<http://www.iinteractive.com>
654
655This library is free software; you can redistribute it and/or modify
656it under the same terms as Perl itself.
657
16e960bd 658=cut
659