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