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