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