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