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