changes and the new test
[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
9ec169fe 10our $VERSION = '0.02';
8b978dd5 11
727919c5 12sub meta {
13 require Class::MOP::Class;
14 Class::MOP::Class->initialize($_[0])
15}
2eb717d5 16
727919c5 17# NOTE: (meta-circularity)
18# This method will be replaces in the
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";
2eb717d5 33 (!exists $options{reader} && !exists $options{writer})
34 || confess "You cannot declare an accessor and reader and/or writer functions"
35 if exists $options{accessor};
7b31baf4 36
37 $options{init_arg} = $name if not exists $options{init_arg};
2eb717d5 38
8b978dd5 39 bless {
c50c603e 40 name => $name,
41 accessor => $options{accessor},
42 reader => $options{reader},
43 writer => $options{writer},
44 predicate => $options{predicate},
45 init_arg => $options{init_arg},
9ec169fe 46 default => $options{default},
47 # keep a weakened link to the
48 # class we are associated with
49 associated_class => undef,
8b978dd5 50 } => $class;
51}
52
7b31baf4 53# NOTE:
54# the next bunch of methods will get bootstrapped
55# away in the Class::MOP bootstrapping section
56
c50c603e 57sub name { $_[0]->{name} }
58
7b31baf4 59sub associated_class { $_[0]->{associated_class} }
60
727919c5 61sub has_accessor { defined($_[0]->{accessor}) ? 1 : 0 }
62sub has_reader { defined($_[0]->{reader}) ? 1 : 0 }
63sub has_writer { defined($_[0]->{writer}) ? 1 : 0 }
c50c603e 64sub has_predicate { defined($_[0]->{predicate}) ? 1 : 0 }
727919c5 65sub has_init_arg { defined($_[0]->{init_arg}) ? 1 : 0 }
66sub has_default { defined($_[0]->{default}) ? 1 : 0 }
c50c603e 67
68sub accessor { $_[0]->{accessor} }
69sub reader { $_[0]->{reader} }
70sub writer { $_[0]->{writer} }
71sub predicate { $_[0]->{predicate} }
72sub init_arg { $_[0]->{init_arg} }
73
7b31baf4 74# end bootstrapped away method section.
75# (all methods below here are kept intact)
76
c50c603e 77sub default {
78 my $self = shift;
79 if (reftype($self->{default}) && reftype($self->{default}) eq 'CODE') {
727919c5 80 # if the default is a CODE ref, then
81 # we pass in the instance and default
82 # can return a value based on that
83 # instance. Somewhat crude, but works.
c50c603e 84 return $self->{default}->(shift);
85 }
86 $self->{default};
87}
8b978dd5 88
9ec169fe 89# class association
727919c5 90
9ec169fe 91sub attach_to_class {
92 my ($self, $class) = @_;
93 (blessed($class) && $class->isa('Class::MOP::Class'))
94 || confess "You must pass a Class::MOP::Class instance (or a subclass)";
95 weaken($self->{associated_class} = $class);
96}
97
98sub detach_from_class {
99 my $self = shift;
100 $self->{associated_class} = undef;
101}
102
103## Method generation helpers
104
105sub generate_accessor_method {
106 my ($self, $attr_name) = @_;
107 eval qq{sub {
108 \$_[0]->{'$attr_name'} = \$_[1] if scalar(\@_) == 2;
109 \$_[0]->{'$attr_name'};
110 }};
111}
112
113sub generate_reader_method {
114 my ($self, $attr_name) = @_;
115 eval qq{sub {
116 \$_[0]->{'$attr_name'};
117 }};
118}
119
120sub generate_writer_method {
121 my ($self, $attr_name) = @_;
122 eval qq{sub {
123 \$_[0]->{'$attr_name'} = \$_[1];
124 }};
125}
126
127sub generate_predicate_method {
128 my ($self, $attr_name) = @_;
129 eval qq{sub {
130 defined \$_[0]->{'$attr_name'} ? 1 : 0;
131 }};
132}
133
134sub process_accessors {
135 my ($self, $type, $accessor) = @_;
136 if (reftype($accessor) && reftype($accessor) eq 'HASH') {
137 my ($name, $method) = each %{$accessor};
138 return ($name, Class::MOP::Attribute::Accessor->wrap($method));
2eb717d5 139 }
9ec169fe 140 else {
141 my $generator = $self->can('generate_' . $type . '_method');
142 ($generator)
143 || confess "There is no method generator for the type='$type'";
144 if (my $method = $self->$generator($self->name)) {
145 return ($accessor => Class::MOP::Attribute::Accessor->wrap($method));
146 }
343203ee 147 confess "Could not create the '$type' method for " . $self->name . " because : $@";
9ec169fe 148 }
149}
150
151sub install_accessors {
152 my $self = shift;
153 my $class = $self->associated_class;
c50c603e 154
9ec169fe 155 $class->add_method(
156 $self->process_accessors('accessor' => $self->accessor())
157 ) if $self->has_accessor();
158
159 $class->add_method(
160 $self->process_accessors('reader' => $self->reader())
161 ) if $self->has_reader();
162
163 $class->add_method(
164 $self->process_accessors('writer' => $self->writer())
165 ) if $self->has_writer();
166
167 $class->add_method(
168 $self->process_accessors('predicate' => $self->predicate())
169 ) if $self->has_predicate();
170 return;
2eb717d5 171}
172
b51af7f9 173{
174 my $_remove_accessor = sub {
175 my ($accessor, $class) = @_;
c50c603e 176 if (reftype($accessor) && reftype($accessor) eq 'HASH') {
177 ($accessor) = keys %{$accessor};
178 }
b51af7f9 179 my $method = $class->get_method($accessor);
180 $class->remove_method($accessor)
2eb717d5 181 if (blessed($method) && $method->isa('Class::MOP::Attribute::Accessor'));
b51af7f9 182 };
c50c603e 183
b51af7f9 184 sub remove_accessors {
9ec169fe 185 my $self = shift;
186 $_remove_accessor->($self->accessor(), $self->associated_class()) if $self->has_accessor();
187 $_remove_accessor->($self->reader(), $self->associated_class()) if $self->has_reader();
188 $_remove_accessor->($self->writer(), $self->associated_class()) if $self->has_writer();
189 $_remove_accessor->($self->predicate(), $self->associated_class()) if $self->has_predicate();
b51af7f9 190 return;
191 }
192
8b978dd5 193}
194
2eb717d5 195package Class::MOP::Attribute::Accessor;
196
197use strict;
198use warnings;
199
727919c5 200use Class::MOP::Method;
201
2eb717d5 202our $VERSION = '0.01';
203
204our @ISA = ('Class::MOP::Method');
205
8b978dd5 2061;
207
208__END__
209
210=pod
211
212=head1 NAME
213
214Class::MOP::Attribute - Attribute Meta Object
215
216=head1 SYNOPSIS
217
218 Class::MOP::Attribute->new('$foo' => (
fe122940 219 accessor => 'foo', # dual purpose get/set accessor
220 predicate => 'has_foo' # predicate check for defined-ness
221 init_arg => '-foo', # class->new will look for a -foo key
222 default => 'BAR IS BAZ!' # if no -foo key is provided, use this
8b978dd5 223 ));
224
225 Class::MOP::Attribute->new('$.bar' => (
fe122940 226 reader => 'bar', # getter
227 writer => 'set_bar', # setter
228 predicate => 'has_bar' # predicate check for defined-ness
229 init_arg => ':bar', # class->new will look for a :bar key
8b978dd5 230 # no default value means it is undef
231 ));
232
233=head1 DESCRIPTION
234
fe122940 235The Attribute Protocol is almost entirely an invention of this module,
236and is completely optional to this MOP. This is because Perl 5 does not
237have consistent notion of what is an attribute of a class. There are
238so many ways in which this is done, and very few (if any) are
239easily discoverable by this module.
552e3d24 240
241So, all that said, this module attempts to inject some order into this
fe122940 242chaos, by introducing a consistent API which can be used to create
243object attributes.
552e3d24 244
245=head1 METHODS
246
247=head2 Creation
248
249=over 4
250
fe122940 251=item B<new ($name, ?%options)>
252
253An attribute must (at the very least), have a C<$name>. All other
a2e85e6c 254C<%options> are contained added as key-value pairs. Acceptable keys
fe122940 255are as follows:
256
257=over 4
258
259=item I<init_arg>
260
261This should be a string value representing the expected key in
262an initialization hash. For instance, if we have an I<init_arg>
263value of C<-foo>, then the following code will Just Work.
264
265 MyClass->meta->construct_instance(-foo => "Hello There");
266
7b31baf4 267In an init_arg is not assigned, it will automatically use the
268value of C<$name>.
269
fe122940 270=item I<default>
271
272The value of this key is the default value which
273C<Class::MOP::Class::construct_instance> will initialize the
274attribute to.
275
276B<NOTE:>
277If the value is a simple scalar (string or number), then it can
278be just passed as is. However, if you wish to initialize it with
279a HASH or ARRAY ref, then you need to wrap that inside a CODE
280reference, like so:
281
282 Class::MOP::Attribute->new('@foo' => (
283 default => sub { [] },
284 ));
285
286 # or ...
287
288 Class::MOP::Attribute->new('%foo' => (
289 default => sub { {} },
290 ));
291
292If you wish to initialize an attribute with a CODE reference
293itself, then you need to wrap that in a subroutine as well, like
294so:
295
296 Class::MOP::Attribute->new('&foo' => (
297 default => sub { sub { print "Hello World" } },
298 ));
299
300And lastly, if the value of your attribute is dependent upon
301some other aspect of the instance structure, then you can take
302advantage of the fact that when the I<default> value is a CODE
303reference, it is passed the raw (unblessed) instance structure
304as it's only argument. So you can do things like this:
305
306 Class::MOP::Attribute->new('$object_identity' => (
307 default => sub { Scalar::Util::refaddr($_[0]) },
308 ));
309
310This last feature is fairly limited as there is no gurantee of
311the order of attribute initializations, so you cannot perform
312any kind of dependent initializations. However, if this is
313something you need, you could subclass B<Class::MOP::Class> and
314this class to acheive it. However, this is currently left as
315an exercise to the reader :).
316
317=back
318
a2e85e6c 319The I<accessor>, I<reader>, I<writer> and I<predicate> keys can
fe122940 320contain either; the name of the method and an appropriate default
321one will be generated for you, B<or> a HASH ref containing exactly one
322key (which will be used as the name of the method) and one value,
323which should contain a CODE reference which will be installed as
324the method itself.
59e7697f 325
326=over 4
327
328=item I<accessor>
329
fe122940 330The I<accessor> is a standard perl-style read/write accessor. It will
331return the value of the attribute, and if a value is passed as an argument,
332it will assign that value to the attribute.
333
334B<NOTE:>
335This method will properly handle the following code, by assigning an
336C<undef> value to the attribute.
337
338 $object->set_something(undef);
339
59e7697f 340=item I<reader>
341
fe122940 342This is a basic read-only accessor, it will just return the value of
343the attribute.
344
59e7697f 345=item I<writer>
346
fe122940 347This is a basic write accessor, it accepts a single argument, and
348assigns that value to the attribute. This method does not intentially
349return a value, however perl will return the result of the last
350expression in the subroutine, which returns in this returning the
351same value that it was passed.
59e7697f 352
fe122940 353B<NOTE:>
354This method will properly handle the following code, by assigning an
355C<undef> value to the attribute.
59e7697f 356
fe122940 357 $object->set_something();
358
359=item I<predicate>
360
361This is a basic test to see if the value of the attribute is not
362C<undef>. It will return true (C<1>) if the attribute's value is
363defined, and false (C<0>) otherwise.
59e7697f 364
365=back
552e3d24 366
367=back
368
369=head2 Informational
370
fe122940 371These are all basic read-only value accessors for the values
372passed into C<new>. I think they are pretty much self-explanitory.
373
552e3d24 374=over 4
375
376=item B<name>
377
378=item B<accessor>
379
380=item B<reader>
381
382=item B<writer>
383
c50c603e 384=item B<predicate>
385
552e3d24 386=item B<init_arg>
387
fe122940 388=item B<default (?$instance)>
389
390As noted in the documentation for C<new> above, if the I<default>
391value is a CODE reference, this accessor will pass a single additional
392argument C<$instance> into it and return the value.
552e3d24 393
394=back
395
396=head2 Informational predicates
397
a2e85e6c 398These are all basic predicate methods for the values passed into C<new>.
fe122940 399
552e3d24 400=over 4
401
402=item B<has_accessor>
403
552e3d24 404=item B<has_reader>
405
552e3d24 406=item B<has_writer>
407
c50c603e 408=item B<has_predicate>
409
552e3d24 410=item B<has_init_arg>
411
552e3d24 412=item B<has_default>
413
552e3d24 414=back
415
9ec169fe 416=head2 Class association
417
418=over 4
419
420=item B<associated_class>
421
422=item B<attach_to_class ($class)>
423
424=item B<detach_from_class>
425
426=back
427
552e3d24 428=head2 Attribute Accessor generation
429
430=over 4
431
9ec169fe 432=item B<install_accessors>
2eb717d5 433
434This allows the attribute to generate and install code for it's own
a2e85e6c 435I<accessor/reader/writer/predicate> methods. This is called by
fe122940 436C<Class::MOP::Class::add_attribute>.
2eb717d5 437
9ec169fe 438This method will call C<process_accessors> for each of the possible
439method types (accessor, reader, writer & predicate).
440
441=item B<process_accessors ($type, $value)>
442
443This takes a C<$type> (accessor, reader, writer or predicate), and
444a C<$value> (the value passed into the constructor for each of the
445different types). It will then either generate the method itself
446(using the C<generate_*_method> methods listed below) or it will
447use the custom method passed through the constructor.
448
449=over 4
450
451=item B<generate_accessor_method ($attr_name)>
452
453=item B<generate_predicate_method ($attr_name)>
454
455=item B<generate_reader_method ($attr_name)>
456
457=item B<generate_writer_method ($attr_name)>
458
459=back
460
461=item B<remove_accessors>
2eb717d5 462
463This allows the attribute to remove the method for it's own
a2e85e6c 464I<accessor/reader/writer/predicate>. This is called by
fe122940 465C<Class::MOP::Class::remove_attribute>.
2eb717d5 466
467=back
468
469=head2 Introspection
470
471=over 4
552e3d24 472
2eb717d5 473=item B<meta>
552e3d24 474
fe122940 475This will return a B<Class::MOP::Class> instance which is related
476to this class.
477
478It should also be noted that B<Class::MOP> will actually bootstrap
479this module by installing a number of attribute meta-objects into
480it's metaclass. This will allow this class to reap all the benifits
481of the MOP when subclassing it.
482
552e3d24 483=back
484
8b978dd5 485=head1 AUTHOR
486
a2e85e6c 487Stevan Little E<lt>stevan@iinteractive.comE<gt>
8b978dd5 488
489=head1 COPYRIGHT AND LICENSE
490
491Copyright 2006 by Infinity Interactive, Inc.
492
493L<http://www.iinteractive.com>
494
495This library is free software; you can redistribute it and/or modify
496it under the same terms as Perl itself.
497
9ec169fe 498=cut