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