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