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