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